├── LICENSE
├── README.md
└── fix-fcitx-window-position.patch
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 uLxy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 最新版本的 JetbrainsRuntime 已经修复了候选框不跟随的问题
2 |
3 | # JetbrainRuntime-ChineseIMPositionFixed
4 | A patch to JetbrainsRuntime to fix fcitx popup window does not show near text cursor
5 |
6 | ## 克隆官方库和补丁
7 | ```shell
8 | git clone https://github.com/JetBrains/JetBrainsRuntime
9 | git clone https://github.com/uLxy/JetbrainsRuntime-ChineseIMPositionFixed.git
10 | ```
11 |
12 | ## 安装补丁
13 | ```shell
14 | cd JetBrainsRuntime
15 | git checkout jbr17
16 | git apply ../JetbrainsRuntime-ChineseIMPositionFixed/fix-fcitx-window-position.patch
17 | ```
18 |
19 | ## (Optional) 安装必要的依赖 (for openSUSE Leap 15.4)
20 | ``` shell
21 | sudo zypper install autoconf make zip gcc gcc-c++ libX11-devel libXext-devel libXrender-devel libXrandr-devel libXtst-devel libXt-devel libXi-devel cups-devel fontconfig-devel alsa-devel
22 | ```
23 |
24 | ## 构建 OpenJDK
25 | ``` shell
26 | bash configure --with-boot-jdk=/opt/java/jdk-17.0.4.1
27 | make images
28 | ```
29 |
30 | ## 使用构建产物替换 Jetbrains 的 jbr 目录
31 | 使用构建好的 OpenJDK (`build/linux-x86_64-server-release/jdk`) 替换 Jetbrains 的 jbr 目录, 如果 jdk 的 lib 目录存在软链接指向 support 目录, 需要连同 support 目录一起复制
32 |
33 | ```shell
34 | sudo mv /opt/intellij-idea/jbr /opt/intellij-idea/jbr.bak
35 | sudo mv build/linux-x86_64-server-release/jdk /opt/intellij-idea/jbr
36 | sudo mv build/linux-x86_64-server-release/support /opt/intellij-idea/support
37 | ```
38 |
--------------------------------------------------------------------------------
/fix-fcitx-window-position.patch:
--------------------------------------------------------------------------------
1 | diff --git a/src/java.desktop/share/classes/javax/swing/JTextArea.java b/src/java.desktop/share/classes/javax/swing/JTextArea.java
2 | index ecc9342f05a..15bb6a953ea 100644
3 | --- a/src/java.desktop/share/classes/javax/swing/JTextArea.java
4 | +++ b/src/java.desktop/share/classes/javax/swing/JTextArea.java
5 | @@ -562,6 +562,15 @@ public class JTextArea extends JTextComponent {
6 | return rowHeight;
7 | }
8 |
9 | + /**
10 | + * fix fcitx position
11 | + *
12 | + * @return font metrics
13 | + */
14 | + public FontMetrics getFontMetrics() {
15 | + return getFontMetrics(getFont());
16 | + }
17 | +
18 | /**
19 | * Returns the number of columns in the TextArea.
20 | *
21 | diff --git a/src/java.desktop/share/classes/javax/swing/JTextField.java b/src/java.desktop/share/classes/javax/swing/JTextField.java
22 | index 3abe09d0565..27e7eab53e6 100644
23 | --- a/src/java.desktop/share/classes/javax/swing/JTextField.java
24 | +++ b/src/java.desktop/share/classes/javax/swing/JTextField.java
25 | @@ -427,6 +427,15 @@ public class JTextField extends JTextComponent implements SwingConstants {
26 | return columnWidth;
27 | }
28 |
29 | + /**
30 | + * fix fcitx position
31 | + *
32 | + * @return font metrics
33 | + */
34 | + public FontMetrics getFontMetrics() {
35 | + return getFontMetrics(getFont());
36 | + }
37 | +
38 | /**
39 | * Returns the preferred size Dimensions
needed for this
40 | * TextField
. If a non-zero number of columns has been
41 | diff --git a/src/java.desktop/share/classes/sun/awt/im/InputContext.java b/src/java.desktop/share/classes/sun/awt/im/InputContext.java
42 | index bb955dc5089..73c829d1ff9 100644
43 | --- a/src/java.desktop/share/classes/sun/awt/im/InputContext.java
44 | +++ b/src/java.desktop/share/classes/sun/awt/im/InputContext.java
45 | @@ -28,6 +28,7 @@ package sun.awt.im;
46 | import java.awt.AWTEvent;
47 | import java.awt.AWTKeyStroke;
48 | import java.awt.Component;
49 | +import java.awt.Cursor;
50 | import java.awt.EventQueue;
51 | import java.awt.Frame;
52 | import java.awt.Rectangle;
53 | @@ -39,6 +40,7 @@ import java.awt.event.FocusEvent;
54 | import java.awt.event.InputEvent;
55 | import java.awt.event.InputMethodEvent;
56 | import java.awt.event.KeyEvent;
57 | +import java.awt.event.MouseEvent;
58 | import java.awt.event.WindowEvent;
59 | import java.awt.event.WindowListener;
60 | import java.awt.im.InputMethodRequests;
61 | @@ -54,6 +56,7 @@ import java.util.prefs.BackingStoreException;
62 | import java.util.prefs.Preferences;
63 | import sun.util.logging.PlatformLogger;
64 | import sun.awt.SunToolkit;
65 | +import sun.awt.X11InputMethod;
66 |
67 | /**
68 | * This InputContext class contains parts of the implementation of
69 | @@ -249,13 +252,25 @@ public class InputContext extends java.awt.im.InputContext
70 | focusLost((Component) event.getSource(), ((FocusEvent) event).isTemporary());
71 | break;
72 |
73 | + case MouseEvent.MOUSE_RELEASED:
74 | + if(checkTextCursor((Component)event.getSource())) {
75 | + transCaretPositionToXIM((Component)event.getSource());
76 | + break;
77 | + }
78 | +
79 | case KeyEvent.KEY_PRESSED:
80 | - if (checkInputMethodSelectionKey((KeyEvent)event)) {
81 | + if (event instanceof KeyEvent && checkInputMethodSelectionKey((KeyEvent)event)) {
82 | // pop up the input method selection menu
83 | InputMethodManager.getInstance().notifyChangeRequestByHotKey((Component)event.getSource());
84 | break;
85 | }
86 |
87 | + case KeyEvent.KEY_RELEASED:
88 | + if (event instanceof KeyEvent && checkDirectionKey((KeyEvent)event)) {
89 | + transCaretPositionToXIM((Component) event.getSource());
90 | + break;
91 | + }
92 | +
93 | // fall through
94 |
95 | default:
96 | @@ -360,6 +375,62 @@ public class InputContext extends java.awt.im.InputContext
97 | }
98 | }
99 |
100 | + /**
101 | + * fix fcitx position
102 | + */
103 | + private void transCaretPositionToXIM(Component source) {
104 | + synchronized (source.getTreeLock()) {
105 | + synchronized (this) {
106 | + if ("sun.awt.im.CompositionArea".equals(source.getClass().getName())) {
107 | + // no special handling for this one
108 | + } else if (getComponentWindow(source) instanceof InputMethodWindow) {
109 | + // no special handling for this one either
110 | + } else {
111 | + if (!source.isDisplayable()) {
112 | + // Component is being disposed
113 | + return;
114 | + }
115 | + currentClientComponent = source;
116 | + }
117 | + awtFocussedComponent = source;
118 | + if (inputMethod != null && inputMethod instanceof X11InputMethod) {
119 | + ((X11InputMethod)inputMethod).setXICTextCursorPosition(source);
120 | + }
121 | + InputMethodContext inputContext = ((InputMethodContext)this);
122 | + if (!inputContext.isCompositionAreaVisible()) {
123 | + InputMethodRequests req = source.getInputMethodRequests();
124 | + if (req != null && inputContext.useBelowTheSpotInput()) {
125 | + inputContext.setCompositionAreaUndecorated(true);
126 | + } else {
127 | + inputContext.setCompositionAreaUndecorated(false);
128 | + }
129 | + }
130 | + // restores the composition area if it was set to invisible
131 | + // when focus got lost
132 | + if (compositionAreaHidden == true) {
133 | + ((InputMethodContext)this).setCompositionAreaVisible(true);
134 | + compositionAreaHidden = false;
135 | + }
136 | + }
137 | + }
138 | + }
139 | +
140 | + /**
141 | + * fix fcitx position
142 | + */
143 | + private boolean checkDirectionKey(KeyEvent event) {
144 | + return true;
145 | + }
146 | +
147 | + /**
148 | + * fix fcitx position
149 | + */
150 | + private boolean checkTextCursor(Component source) {
151 | + if(source.getCursor().getType()==Cursor.TEXT_CURSOR)
152 | + return true;
153 | + return false;
154 | + }
155 | +
156 | /**
157 | * Activates the current input method of this input context, and grabs
158 | * the composition area for use by this input context.
159 | diff --git a/src/java.desktop/unix/classes/sun/awt/X11/XInputMethod.java b/src/java.desktop/unix/classes/sun/awt/X11/XInputMethod.java
160 | index 4244812ceb0..08480ed33c8 100644
161 | --- a/src/java.desktop/unix/classes/sun/awt/X11/XInputMethod.java
162 | +++ b/src/java.desktop/unix/classes/sun/awt/X11/XInputMethod.java
163 | @@ -28,9 +28,23 @@ package sun.awt.X11;
164 | import java.awt.AWTException;
165 | import java.awt.Component;
166 | import java.awt.Container;
167 | +import java.awt.geom.Point2D;
168 | +import java.awt.geom.AffineTransform;
169 | +import java.awt.FontMetrics;
170 | +import java.awt.Point;
171 | import java.awt.Rectangle;
172 | import java.awt.im.spi.InputMethodContext;
173 | import java.awt.peer.ComponentPeer;
174 | +import java.awt.GraphicsConfiguration;
175 | +import java.awt.GraphicsEnvironment;
176 | +
177 | +import java.lang.reflect.Field;
178 | +import java.lang.reflect.InvocationTargetException;
179 | +import java.lang.reflect.Method;
180 | +
181 | +import javax.swing.JTextArea;
182 | +import javax.swing.JTextField;
183 | +import javax.swing.text.JTextComponent;
184 |
185 | import sun.awt.AWTAccessor;
186 | import sun.awt.X11InputMethod;
187 | @@ -86,21 +100,107 @@ public class XInputMethod extends X11InputMethod {
188 |
189 | private static volatile long xicFocus = 0;
190 |
191 | + public void setXICTextCursorOffXY(ComponentPeer peer) {
192 | + if (peer == null) {
193 | + return;
194 | + }
195 | + xicFocus = ((XComponentPeer)peer).getContentWindow();
196 | + int[] result = getOffXYRelateToFrame(peer ,true);
197 | + setXICFocusNative(((XComponentPeer)peer).getContentWindow(),true,true,result);
198 | + // setXICTextCursorOffXYNative(((XComponentPeer)peer).getContentWindow(),result);
199 | + }
200 | +
201 | protected void setXICFocus(ComponentPeer peer,
202 | boolean value, boolean active) {
203 | if (peer == null) {
204 | return;
205 | }
206 | xicFocus = ((XComponentPeer)peer).getContentWindow();
207 | + int[] result = getOffXYRelateToFrame(peer, value);
208 | setXICFocusNative(((XComponentPeer)peer).getContentWindow(),
209 | value,
210 | - active);
211 | + active,
212 | + result);
213 | }
214 |
215 | public static long getXICFocus() {
216 | return xicFocus;
217 | }
218 |
219 | + protected int[] getOffXYRelateToFrame(ComponentPeer peer, boolean value) {
220 | + int[] result = null;
221 | + if(value && this.awtFocussedComponent!=null && this.awtFocussedComponent instanceof JTextComponent){
222 | + try {
223 | + Method method_getFontMetrics = null;
224 | + Method method_getEditor = null;
225 | + FontMetrics font_metrics = null;
226 | + Object editor = null;
227 | + int font_height = 0;
228 | + int caret_x = 0;
229 | + int caret_y = 0;
230 | + if(this.awtFocussedComponent instanceof JTextArea || this.awtFocussedComponent instanceof JTextField){
231 | + method_getFontMetrics = this.awtFocussedComponent.getClass().getMethod("getFontMetrics");
232 | + font_metrics = (FontMetrics)method_getFontMetrics.invoke(this.awtFocussedComponent);
233 | + font_height = font_metrics.getHeight();
234 | + JTextComponent jc = (JTextComponent)this.awtFocussedComponent;
235 | + if( jc.getCaret().getMagicCaretPosition() != null) {
236 | + caret_x = jc.getCaret().getMagicCaretPosition().x;
237 | + caret_y = jc.getCaret().getMagicCaretPosition().y;
238 | + }
239 | + }else {
240 | + method_getEditor = this.awtFocussedComponent.getClass().getMethod("getEditor");
241 | + editor = method_getEditor.invoke(this.awtFocussedComponent);
242 | + method_getFontMetrics = editor.getClass().getMethod("getFontMetrics",int.class);
243 | + font_metrics = (FontMetrics)method_getFontMetrics.invoke(editor, 0);
244 | + font_height = font_metrics.getHeight();
245 | + Method getCaretLocations = editor.getClass().getMethod("getCaretLocations", boolean.class);
246 | + Object[] locations = (Object[])getCaretLocations.invoke(editor, false);
247 | + Field point = locations[0].getClass().getField("myPoint");
248 | +
249 | + Object o = point.get(locations[0]);
250 | + if (o instanceof Point2D.Double) {
251 | + Point2D.Double pd = (Point2D.Double)point.get(locations[0]);
252 | + caret_x = (int)pd.x;
253 | + caret_y = (int)pd.y;
254 | + } else if (o instanceof Point) {
255 | + System.out.println("o instanceof Point = true");
256 | + Point pd = (Point) point.get(locations[0]);
257 | + caret_x = pd.x;
258 | + caret_y = pd.y;
259 | + }
260 | + }
261 | +
262 | + Method method_getLocationOnScreen = this.awtFocussedComponent.getClass().getMethod("getLocationOnScreen");
263 | + Point point = (Point)method_getLocationOnScreen.invoke(this.awtFocussedComponent);
264 | +
265 | + Method method_getNativeContainer = Component.class.getDeclaredMethod("getNativeContainer");
266 | + method_getNativeContainer.setAccessible(true);
267 | + Container c = (Container)method_getNativeContainer.invoke(awtFocussedComponent);
268 | + if(c!=null) {
269 | + GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
270 | + AffineTransform transform = gc.getDefaultTransform();
271 | + int x = (int) (transform.getScaleX() * (point.x - peer.getLocationOnScreen().x + caret_x));
272 | + int y = (int) (transform.getScaleY() * (point.y - peer.getLocationOnScreen().y + font_height + caret_y));
273 | + result = new int[]{x, y};
274 | + }
275 | + return result;
276 | + } catch (IllegalAccessException e) {
277 | + e.printStackTrace();
278 | + } catch (IllegalArgumentException e) {
279 | + e.printStackTrace();
280 | + } catch (InvocationTargetException e) {
281 | + e.printStackTrace();
282 | + } catch (NoSuchMethodException e) {
283 | + e.printStackTrace();
284 | + } catch (SecurityException e) {
285 | + e.printStackTrace();
286 | + } catch(NoSuchFieldException e) {
287 | + e.printStackTrace();
288 | + }
289 | + }
290 | + return result;
291 | + }
292 | +
293 | /* XAWT_HACK FIX ME!
294 | do NOT call client code!
295 | */
296 | @@ -253,6 +353,6 @@ public class XInputMethod extends X11InputMethod {
297 | private native boolean recreateXICNative(long window, long px11data, int ctxid);
298 | private native int releaseXICNative(long px11data);
299 | private native void setXICFocusNative(long window,
300 | - boolean value, boolean active);
301 | + boolean value, boolean active, int[] offxy);
302 | private native void adjustStatusWindow(long window);
303 | }
304 | diff --git a/src/java.desktop/unix/classes/sun/awt/X11InputMethod.java b/src/java.desktop/unix/classes/sun/awt/X11InputMethod.java
305 | index d9bed439688..f09697d5a9c 100644
306 | --- a/src/java.desktop/unix/classes/sun/awt/X11InputMethod.java
307 | +++ b/src/java.desktop/unix/classes/sun/awt/X11InputMethod.java
308 | @@ -33,6 +33,7 @@ import java.awt.AWTException;
309 | import java.awt.event.InputMethodEvent;
310 | import java.awt.font.TextAttribute;
311 | import java.awt.font.TextHitInfo;
312 | +import java.awt.Component;
313 | import java.awt.peer.ComponentPeer;
314 | import java.text.AttributedString;
315 | import java.util.Map;
316 | @@ -57,6 +58,11 @@ public abstract class X11InputMethod extends X11InputMethodBase {
317 | super();
318 | }
319 |
320 | + /**
321 | + * fix fcitx position
322 | + */
323 | + public abstract void setXICTextCursorOffXY(ComponentPeer peer);
324 | +
325 | /**
326 | * Reset the composition state to the current composition state.
327 | */
328 | @@ -380,6 +386,30 @@ public abstract class X11InputMethod extends X11InputMethodBase {
329 | }
330 | }
331 |
332 | + /**
333 | + * fix fcitx position
334 | + */
335 | + public synchronized void setXICTextCursorPosition(Component component) {
336 | + if (component == null) {
337 | + return;
338 | + }
339 | + if (isActive) {
340 | + // deactivate/activate are being suppressed during a focus change -
341 | + // this may happen when an input method window is made visible
342 | + // boolean ac = haveActiveClient(); already set true in awt_InputMethod.c
343 | + setXICTextCursorOffXY(getPeer(awtFocussedComponent));
344 | +
345 | + }
346 | + awtFocussedComponent = component;
347 | + // ComponentPeer lastXICFocussedComponentPeer = null;
348 | + // if (lastXICFocussedComponent != null) {
349 | + // lastXICFocussedComponentPeer = getPeer(lastXICFocussedComponent);
350 | + // }
351 | + ComponentPeer awtFocussedComponentPeer = getPeer(awtFocussedComponent);
352 | + if(awtFocussedComponent !=null )
353 | + setXICTextCursorOffXY(awtFocussedComponentPeer);
354 | + }
355 | +
356 | protected abstract boolean recreateXIC(int ctxid);
357 | protected abstract int releaseXIC();
358 | private static native boolean recreateX11InputMethod();
359 | diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c b/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c
360 | index e63741f583e..c9fa427a1f8 100644
361 | --- a/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c
362 | +++ b/src/java.desktop/unix/native/libawt_xawt/awt/awt_InputMethod.c
363 | @@ -434,13 +434,17 @@ setXICFocus(XIC ic, unsigned short req)
364 | * Sets the focus window to the given XIC.
365 | */
366 | static void
367 | -setXICWindowFocus(XIC ic, Window w)
368 | +setXICWindowFocus(XIC ic, Window w, int arr[2])
369 | {
370 | if (ic == NULL) {
371 | (void)fprintf(stderr, "Couldn't find X Input Context\n");
372 | return;
373 | }
374 | - (void) XSetICValues(ic, XNFocusWindow, w, NULL);
375 | + XPoint spot;
376 | + spot.x = arr[0];
377 | + spot.y = arr[1];
378 | + XVaNestedList xy = (XVaNestedList)XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
379 | + (void) XSetICValues(ic, XNFocusWindow, w, XNPreeditAttributes, xy, NULL);
380 | }
381 |
382 | /*
383 | @@ -1519,7 +1523,8 @@ Java_sun_awt_X11_XInputMethod_setXICFocusNative(JNIEnv *env,
384 | jobject this,
385 | jlong w,
386 | jboolean req,
387 | - jboolean active)
388 | + jboolean active,
389 | + jintArray arr)
390 | {
391 | X11InputMethodData *pX11IMData;
392 | AWT_LOCK();
393 | @@ -1540,7 +1545,16 @@ Java_sun_awt_X11_XInputMethod_setXICFocusNative(JNIEnv *env,
394 | * On Solaris2.6, setXICWindowFocus() has to be invoked
395 | * before setting focus.
396 | */
397 | - setXICWindowFocus(pX11IMData->current_ic, w);
398 | + int positions[2] = {100,50};
399 | + if (arr) {
400 | + int length = (*env)->GetArrayLength(env,arr);
401 | + int *addArr = (*env)->GetIntArrayElements(env, arr, NULL);
402 | + for (int i= 0; i < length; ++i) {
403 | + positions[i] = *(addArr + i);
404 | + }
405 | + (*env)->ReleaseIntArrayElements(env, arr, addArr, 0);
406 | + }
407 | + setXICWindowFocus(pX11IMData->current_ic, w, positions);
408 | setXICFocus(pX11IMData->current_ic, req);
409 | currentX11InputMethodInstance = pX11IMData->x11inputmethod;
410 | currentFocusWindow = w;
411 |
--------------------------------------------------------------------------------