JDK-4083691 : Key mapping in TextAreas & TextFields on Solaris
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.1,1.2.0
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: solaris_2.5.1,windows_nt
  • CPU: x86,sparc
  • Submitted: 1997-10-03
  • Updated: 2000-01-28
  • Resolved: 1998-11-09
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other Other
1.1.8 1.1.8Fixed 1.2.2Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
I got the following bug report.  I've included the text of
the report more or less as sent.
-------------------------------------------------------------
Forward:
we fixed a problem with the French keyboard layout, and when I discovered
the problem, it also turned out to be the cause of two other problems:

  Keypad '+' was coming up as '='
  Caps lock was not working properly.

>   In ...src/unix/sun/canvas.c XK_KP_3 is being mapped to the character
>'3'. Somewhere Java detects if a French keyboard is being used. If so,
>it maps keypad 3 (i. e. XK_KP_3) to the character comma (or single quote?)
>which shares the key with the integer 3. (In American keyboards the key
>for the integer 3 is shared by the pound # character)
>   So we have a key mapping confusion in Java for the French keyboard.



Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: 1.1.8 FIXED IN: 1.1.8 1.2.2 INTEGRATED IN: 1.1.8 1.2.2 VERIFIED IN: 1.1.8
14-06-2004

EVALUATION We need a French keyboard for further evaluation. mike.bronson@eng 1998-09-24 A French keyboard is not required for this one. Simply do "export LANG=fr" and then run the testcase (or any Java TextField/Area), and press "+" on the Keypad and you'll the "=" appears in the textfield instead. The bug happens on 1.1.7 and 1.2, only on Solaris. matthew.chapman@eng 1998-10-07 In fact, this bug is not specific to the French locale. In the default (English) locale the "+" on the keyboard still gives a "=", and the "*" key gives a "8" when NumLock is on. matthew.chapman@eng 1998-10-08 This bug verified as fixed in jdk118f via regression testing. However, the test fails when running under JavaTest2.0 which means the test is not up to spec. The test has no pass/fail buttons yet requires user interaction. john.s.lee@Eng 1999-01-25 When the regression test was written it was thought it was applicable to only a French locale. This is why the "ignore" keyword was used in the header. As this test is actually potentially useful for any locale, I will replace the ignore so the test will be run by the harness and pass/fail buttons will be displayed. stuart.lawrence@eng 1999-01-25 Note that there is a report of this bug filed against win32. This is probably a different bug which was fixed separately some time ago. I tested this bug against 1.2.2-G on WinNT 4.0 SP3, and the bug did not exist there. eric.hawkes@eng 1999-02-16 Ported 1.1.8 fix to 1.2.2. Added regression test that can be run in the harness. See "Suggested Fix" section. eric.hawkes@eng 1999-02-16
16-02-1999

SUGGESTED FIX In a TextField or TextArea, key events are converted into Java events, and then back into X key events. The problem occurs in translation back to X key events in a function called modify_Event in canvas.c. This change also fixes the Caps Lock problem (defect 2767), and makes it so keypad + gives + in text fields instead of =. The offending bit of code is default: if (keyChar < 256) { keysym = keyChar; } else { keysym = getX11KeySym(keyCode); } And this is why it causes a problem: On the French keyboard: Normal '3' should give ' Normal '4' should give " Normal '3' shifted should give 3 Normal '4' shifted should give 4 Keypad '3' should give 3 Keypad '4' should give 4 These are all correct, except for: Keypad '3' gives ' Keypad '4' gives " On the French keyboard: Normal '3' gets converted to 0x27 (' character) with keycode 0. Keypad '3' gets converted to 0x33 ('3' character) with keycode 0x63. This is correct, but when translated back, the Keypad '3' value is converted wrongly. Because keyChar == 0x33, the above piece of code uses that as the keysym value, and then when converted back into a key code, X thinks that this '3' character came from the normal '3' key. Thus, X thinks the normal '3' key was pressed, and you get the ' character in the text field, which is incorrect. What SHOULD happen is if we have a non-zero key code, this should be used. I have changed the code to use the key code if one is available. This change means I can remove some other conditions in the same area of code, so this bit of code is a little simpler. NUMLOCK This introduces another slight problem. Before this code change, keypad keys were translated back into numbers, whether numlock was on or not. With this code change, these keys get translated into some other codes (don't know what), and people might notice the difference. I am going to sort of 'hard code' these keys to be numlocked so that they will be converted back into numbers so the behaviour will not seem to have changed. I do not believe that this keyboard translation code is perfect, but it is better than it was. Any old TextArea or TextField will do to test this bug, but here's a little program which you can use if you need it: import java.awt.*; import java.io.*; import java.util.*; import java.text.*; class Editor extends Frame { TextArea text; MenuBar menubar; Menu file_menu, help; FileDialog file_dialog; File file; public Editor(String title) { super(title); // Add a menu bar menubar = new MenuBar(); this.setMenuBar(menubar); // Add File menu file_menu = new Menu("File", true); file_menu.add("New"); file_menu.add("Open"); file_menu.add("Save"); file_menu.add("Quit"); menubar.add(file_menu); // Add Help menu help = new Menu("Help"); help.add("About"); menubar.add(help); menubar.setHelpMenu(help); // Create file dialog file_dialog = new FileDialog(this, "Create/Open File", FileDialog.LOAD); // Add text area text = new TextArea("", 25, 80, TextArea.SCROLLBARS_BOTH); this.add(text); } public boolean handleEvent(Event e){ switch(e.id) { case Event.ACTION_EVENT: if (e.target instanceof MenuItem) { if (((String)e.arg).equals("Quit")) { System.exit(0); } else if (((String)e.arg).equals("New")) { file_dialog.pack(); file_dialog.show(); String file_name = file_dialog.getFile(); file = new File(file_name); try { FileOutputStream os = new FileOutputStream(file); os.close(); } catch(IOException ex) {} return true; } else if (((String)e.arg).equals("Open")) { file_dialog.pack(); file_dialog.show(); String file_name = file_dialog.getFile(); file = new File(file_name); if (file.canRead()) { byte buffer[] = new byte[(int)file.length()]; try { FileInputStream is = new FileInputStream(file); is.read(buffer); is.close(); } catch (IOException ex) {} System.out.println("input length: " + buffer.length); text.setText(new String(buffer)); } return true; } else if (((String)e.arg).equals("Save")) { if (file.canWrite()) { // NOTE: This is a hack to remove extra CRs inserted by TextArea // This could be done more efficiently! String s = text.getText(); byte buffer[] = s.getBytes(); try { FileOutputStream os = new FileOutputStream(file); os.write(buffer); os.write('\n'); os.close(); } catch(IOException ex) {} } return true; } } // MenuItem } // switch // If not handled, return false return false; } public static void main(String args[]) { Editor e = new Editor("Editor"); e.pack(); e.show(); } } The END key I have also changed the order in which the entries for .._VK_END appear in the translation table in canvas.c, because they caused the END key to be translated to X key code XK_R13, which is not correct. I believe that all this XK_R13 business is probably not necessary in our code, as I think it's a workaround for a bug in Solaris, but I'm not going to worry about that now. DELTA: src/unix/sun/canvas.c (a114 1.4) *** 1.3 Tue Sep 30 14:09:03 1997 --- 1.4 Tue Sep 30 14:09:05 1997 *************** *** 114,121 **** --- 114,126 ---- { java_awt_event_KeyEvent_VK_PAGE_DOWN, XK_Page_Down, FALSE }, { java_awt_event_KeyEvent_VK_PAGE_DOWN, XK_R15, FALSE }, { java_awt_event_KeyEvent_VK_PAGE_DOWN, XK_Next, FALSE }, + #if defined(IBM) /*ibm.3102*/ + { java_awt_event_KeyEvent_VK_END, XK_End, FALSE }, /*ibm.3102*/ + { java_awt_event_KeyEvent_VK_END, XK_R13, FALSE }, /*ibm.3102*/ + #else /*ibm.3102*/ { java_awt_event_KeyEvent_VK_END, XK_R13, FALSE }, { java_awt_event_KeyEvent_VK_END, XK_End, FALSE }, + #endif /*ibm.3102*/ { java_awt_event_KeyEvent_VK_HOME, XK_Home, FALSE }, { java_awt_event_KeyEvent_VK_HOME, XK_R7, FALSE }, *************** *** 493,498 **** --- 498,512 ---- if (xevent->type != KeyPress && xevent->type != KeyRelease) return; + #if defined(IBM) /*ibm.3102*/ + /* ibm.3102: If there is a Java keyCode, then use that to translate back + * to an X keycode, rather than assuming that keysym can be taken from + * keyChar. */ + if (keyCode == 0) /*ibm.3102*/ + keysym = keyChar; /*ibm.3102*/ + else /*ibm.3102*/ + keysym = getX11KeySym(keyCode); /*ibm.3102*/ + #else /*ibm.3102*/ switch(keyCode) { case java_awt_event_KeyEvent_VK_ENTER: case java_awt_event_KeyEvent_VK_BACK_SPACE: *************** *** 514,519 **** --- 528,534 ---- } break; } + #endif /*ibm.3102*/ if (keysym != 0) { if (modifiers & java_awt_event_InputEvent_CTRL_MASK) { switch (keysym + 64) { *************** *** 540,545 **** --- 555,569 ---- } xevent->xkey.keycode = XKeysymToKeycode(awt_display, keysym); } + + #if defined(IBM) /*ibm.3102*/ + /* ibm.3102: If the key is a keypad key, then hard-code the numlock + * state. This makes the keypad behave a lot more like it did before + * the above ibm.3102 change was made (except without getting the + * wrong characters appearing). */ + if (keysym >= 0xffaa && keysym <= 0xffbd) + xevent->xkey.state |= awt_NumLockMask; + #endif if (keysym >= 'A' && keysym <= 'Z') { xevent->xkey.state |= ShiftMask; DELTA: src/unix/sun/canvas.c (a112 1.4) *** 1.3 Tue Sep 30 14:09:06 1997 --- 1.4 Tue Sep 30 14:09:07 1997 *************** *** 114,121 **** --- 114,126 ---- { java_awt_event_KeyEvent_VK_PAGE_DOWN, XK_Page_Down, FALSE }, { java_awt_event_KeyEvent_VK_PAGE_DOWN, XK_R15, FALSE }, { java_awt_event_KeyEvent_VK_PAGE_DOWN, XK_Next, FALSE }, + #if defined(IBM) /*ibm.3102*/ + { java_awt_event_KeyEvent_VK_END, XK_End, FALSE }, /*ibm.3102*/ + { java_awt_event_KeyEvent_VK_END, XK_R13, FALSE }, /*ibm.3102*/ + #else /*ibm.3102*/ { java_awt_event_KeyEvent_VK_END, XK_R13, FALSE }, { java_awt_event_KeyEvent_VK_END, XK_End, FALSE }, + #endif /*ibm.3102*/ { java_awt_event_KeyEvent_VK_HOME, XK_Home, FALSE }, { java_awt_event_KeyEvent_VK_HOME, XK_R7, FALSE }, *************** *** 498,503 **** --- 503,517 ---- if (xevent->type != KeyPress && xevent->type != KeyRelease) return; + #if defined(IBM) /*ibm.3102*/ + /* ibm.3102: If there is a Java keyCode, then use that to translate back + * to an X keycode, rather than assuming that keysym can be taken from + * keyChar. */ + if (keyCode == 0) /*ibm.3102*/ + keysym = keyChar; /*ibm.3102*/ + else /*ibm.3102*/ + keysym = getX11KeySym(keyCode); /*ibm.3102*/ + #else /*ibm.3102*/ switch(keyCode) { case java_awt_event_KeyEvent_VK_ENTER: case java_awt_event_KeyEvent_VK_BACK_SPACE: *************** *** 519,524 **** --- 533,539 ---- } break; } + #endif /*ibm.3102*/ if (keysym != 0) { if (modifiers & java_awt_event_InputEvent_CTRL_MASK) { switch (keysym + 64) { *************** *** 545,550 **** --- 560,574 ---- } xevent->xkey.keycode = XKeysymToKeycode(awt_display, keysym); } + + #if defined(IBM) /*ibm.3102*/ + /* ibm.3102: If the key is a keypad key, then hard-code the numlock + * state. This makes the keypad behave a lot more like it did before + * the above ibm.3102 change was made (except without getting the + * wrong characters appearing). */ + if (keysym >= 0xffaa && keysym <= 0xffbd) + xevent->xkey.state |= awt_NumLockMask; + #endif if (keysym >= 'A' && keysym <= 'Z') { xevent->xkey.state |= ShiftMask; ---- The above fix is rather excessive. I think it's safer just to deal with the two broken keys ("+" and "*") in the switch statement. The above change actually breaks on Solaris because of the code to handle the XK_kana_fullstop key, so I'll go with the simple fix. It works okay in both the French and default locales. This fix is required on JDK 1.2. Diffs: (diff src/solaris/sun/canvas.c 1.101 1.102) 508a509,510 > case java_awt_event_KeyEvent_VK_ADD: > case java_awt_event_KeyEvent_VK_MULTIPLY: Testcase: import java.applet.*; import java.awt.*; public class KeyMapping extends Applet { public void init() { TextArea ta = new TextArea("", 8, 40); add(ta); } } <HTML> <BODY> Type the following keys inside the text area, with NumLock on: Keypad '+' should give + Keypad '*' should give * <APPLET code="KeyMapping.class" width=400 height=150> </APPLET> </BODY> </HTML> matthew.chapman@eng 1998-10-08 ========================================================= Ported the 1.1.8 fix to 1.2.2 svdiffs 1.131 1.132 canvas.c 587a588,589 > case java_awt_event_KeyEvent_VK_ADD: > case java_awt_event_KeyEvent_VK_MULTIPLY: Regression test is at /test/java/awt/event/KeyEvent/NumpadTest eric.hawkes@eng 1999-02-16
16-02-1999