From 3dfcc64d60f5d0a1018c5fe6ebd6f38db678e5bb Mon Sep 17 00:00:00 2001
From: vincentdarley <vincentdarley>
Date: Wed, 16 Oct 2002 11:29:49 +0000
Subject: dead keys first fix for MacOSX

---
 ChangeLog                 |   5 +
 macosx/tkMacOSXKeyEvent.c | 271 +++++++++++++++++++++++++++++++++++++++++-----
 macosx/tkMacOSXKeyboard.c |  43 +-------
 3 files changed, 252 insertions(+), 67 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index dcaf94f..e6ebdfe 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,11 @@
 
 	* macosx/tkMacOSXMenu.c: fix to accelerators shown in menus
 	with non-alphanumeric keys.
+	* macosx/tkMacOSXKeyEvent.c: 
+	* macosx/tkMacOSXKeyboard.c: applied first patch from [Patch
+	#622582] to fix some of [Bug #616988].  Further work is needed
+	in this area, but this will let foreign keyboards at least work
+	reasonably with Tk.
 	
 2002-10-15  Jeff Hobbs  <jeffh@ActiveState.com>
 
diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c
index a8d2711..78a37eb 100644
--- a/macosx/tkMacOSXKeyEvent.c
+++ b/macosx/tkMacOSXKeyEvent.c
@@ -60,7 +60,7 @@ typedef struct {
     Point       global;
     Point       local;
     int         state;
-    char        ch;
+    unsigned char ch;
     UInt32      keyCode;
     UInt32      keyModifiers;
     UInt32      message;  
@@ -69,6 +69,8 @@ typedef struct {
 static Tk_Window gGrabWinPtr = NULL;     /* Current grab window, NULL if no grab. */
 static Tk_Window gKeyboardWinPtr = NULL; /* Current keyboard grab window. */
 
+static UInt32 deadKeyState = 0;
+
 /*
  * Declarations for functions used only in this file.
  */
@@ -78,6 +80,22 @@ static int GenerateKeyEvent _ANSI_ARGS_(( EventKind eKind,
         Window window, 
         UInt32 savedKeyCode,
         UInt32 savedModifiers));
+
+
+static int GetKeyboardLayout (
+	Ptr * resource );
+
+static int DecodeViaUnicodeResource(
+	Ptr uchr,
+	EventKind eKind,
+	const KeyEventData * e,
+	XEvent * event );
+static int DecodeViaKCHRResource(
+	Ptr kchr,
+	const KeyEventData * e,
+	XEvent * event );
+
+
 /*
  *----------------------------------------------------------------------
  *
@@ -232,7 +250,7 @@ int TkMacOSXProcessKeyboardEvent(
  *----------------------------------------------------------------------
  */
 
-int
+static int
 GenerateKeyEvent( EventKind eKind, 
         KeyEventData * e, 
         Window window, 
@@ -241,8 +259,6 @@ GenerateKeyEvent( EventKind eKind,
 {
     Tk_Window tkwin;
     XEvent event;
-    unsigned char byte;
-    char buf[16];
     TkDisplay *dispPtr;
     
     /*
@@ -264,33 +280,18 @@ GenerateKeyEvent( EventKind eKind,
         fprintf(stderr,"tkwin == NULL, %d\n", __LINE__);
         return -1;
     }
-    byte = (e->message&charCodeMask);
-    if (byte == 0) {
-        /* 
-         * Either we have a pure-modifier change, or perhaps
-         * a dead-key (e.g. opt-e) was pressed.  In the former case we do
-         * want to generate an event, in the latter I'm not sure
-         * what to do.
-         */
-        if (eKind == kEventRawKeyModifiersChanged) {
-            /* Drop through to the event code below */
+
+    event.xkey.trans_chars[0] = 0;
+
+    if (0 != e->ch) {
+	Ptr resource = NULL;
+	if (GetKeyboardLayout(&resource)) {
+	    if (0 == DecodeViaUnicodeResource(resource,eKind,e,&event))
+		return 0;
         } else {
-	    /* 
-	     * What shall we do here?  We certainly aren't dealing
-	     * with deadkeys at present.  Is this where they come?
-	     */
+	    if (0 == DecodeViaKCHRResource(resource,e,&event))
             return 0;
         }
-    } else if ((savedKeyCode == 0) &&
-            (Tcl_ExternalToUtf(NULL, TkMacOSXCarbonEncoding, 
-			       (char *) &byte, 1, 0, NULL, 
-                        buf, sizeof(buf), NULL, NULL, NULL) != TCL_OK)) {
-        /*
-         * This event specifies a lead byte.  Wait for the second byte
-         * to come in before sending the XEvent.
-         */
-        fprintf(stderr,"Failed %02x\n", byte);
-        return 0;
     }   
 
     event.xany.send_event = False;
@@ -327,7 +328,7 @@ GenerateKeyEvent( EventKind eKind,
      * 
      * Help needed!
      */
-    event.xkey.keycode = byte |
+    event.xkey.keycode = e->ch |
         ((savedKeyCode & charCodeMask) << 8) |
         ((e->message&keyCodeMask) << 8);
    
@@ -377,6 +378,218 @@ GenerateKeyEvent( EventKind eKind,
 /*
  *----------------------------------------------------------------------
  *
+ * GetKeyboardLayout --
+ *
+ *	Queries the OS for a pointer to a keyboard resource.
+ *
+ *	NB (benny): This function is supposed to work with the
+ *	keyboard layout switch menu that we have in 10.2.  Currently
+ *	the menu is not enabled at all for wish, so I can not really
+ *	test it.  We will probably have to use real TSM-style event
+ *	handling to get all those goodies, but I haven't figured out
+ *	those bits yet.
+ *
+ * Results:
+ *	1 if there is returned a Unicode 'uchr' resource in
+ *	"*resource", 0 if it is a classic 'KCHR' resource.
+ *
+ * Side effects:
+ *	Sets some internal static variables.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+GetKeyboardLayout ( Ptr * resource )
+{
+    static SInt16 lastKeyLayoutID = -1; /* should be safe */
+    static Handle uchrHnd = NULL;
+    static Handle KCHRHnd = NULL;
+
+    SInt16 keyScript;
+    SInt16 keyLayoutID;
+
+    keyScript = GetScriptManagerVariable(smKeyScript);
+    keyLayoutID = GetScriptVariable(keyScript,smScriptKeys);
+
+    if (lastKeyLayoutID != keyLayoutID) {
+	deadKeyState = 0;
+	lastKeyLayoutID = keyLayoutID;
+	uchrHnd = GetResource('uchr',keyLayoutID);
+	if (NULL == uchrHnd) {
+	    KCHRHnd = GetResource('KCHR',keyLayoutID);
+	}
+    }
+
+    if (NULL != uchrHnd) {
+	*resource = *uchrHnd;
+	return 1;
+    } else {
+	*resource = *KCHRHnd;
+	return 0;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DecodeViaUnicodeResource --
+ *
+ *        Given MacOS key event data this function generates the UTF-8
+ *        characters.  It does this using a 'uchr' and the
+ *        UCKeyTranslate API.
+ *
+ *	  NB (benny): This function is not tested at all, because my
+ *	  system does not actually return a 'uchr' resource in
+ *	  GetKeyboardLayout currently.  We probably need to do
+ *	  TSM-style event handling to get keyboard layout switching
+ *	  first.
+ *
+ * Results:
+ *        1 if the data was generated, 0 if we are waiting for another
+ *        byte of a dead-key sequence.
+ *
+ * Side effects:
+ *        Sets the trans_chars array in the XEvent->xkey structure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DecodeViaUnicodeResource(
+	Ptr uchr,
+	EventKind eKind,
+	const KeyEventData * e,
+	XEvent * event )
+{
+    /* input of UCKeyTranslate */
+    unsigned vkey;
+    int action;
+    unsigned modifiers;
+    unsigned long keyboardType;
+
+    /* output of UCKeyTranslate */
+    enum { BUFFER_SIZE = 16 };
+    UniChar unistring[BUFFER_SIZE];
+    UniCharCount actuallength; 
+    OSStatus status;
+
+    /* for converting the result */
+    char utf8buffer[sizeof(event->xkey.trans_chars)+4];
+    int s, d;
+
+    vkey = ((e->message) >> 8) & 0xFF;
+    modifiers = ((e->keyModifiers) >> 8) & 0xFF;
+    keyboardType = LMGetKbdType();
+
+    switch(eKind) {	
+	default: /* keep compilers happy */
+	case kEventRawKeyDown:	 action = kUCKeyActionDown; break;
+	case kEventRawKeyUp:	 action = kUCKeyActionUp; break;
+	case kEventRawKeyRepeat: action = kUCKeyActionAutoKey; break;
+    }
+
+    status = UCKeyTranslate(
+	    (const UCKeyboardLayout *)uchr,
+	    vkey, action, modifiers, keyboardType,
+	    0, &deadKeyState, BUFFER_SIZE, &actuallength, unistring);
+
+    if (0 != deadKeyState)
+	return 0; /* more data later */
+
+    if (noErr != status) {
+	fprintf(stderr,"UCKeyTranslate failed: %d", (int) status);
+	actuallength = 0;
+    }
+    s = 0;
+    d = 0;
+    while (s<actuallength) {
+	int newd = d + Tcl_UniCharToUtf(unistring[s],utf8buffer+d);
+	if (newd > (sizeof(event->xkey.trans_chars)-1)) {
+	    break;
+	}
+	d = newd;
+	++s;
+    }
+    utf8buffer[d] = 0;
+    strcpy(event->xkey.trans_chars, utf8buffer);
+
+    return 1;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DecodeViaKCHRResource --
+ *
+ *        Given MacOS key event data this function generates the UTF-8
+ *        characters.  It does this using a 'KCHR' and the
+ *        KeyTranslate API.
+ *
+ *	  NB (benny): The function is not actually tested with double
+ *	  byte encodings yet.
+ *
+ * Results:
+ *        1 if the data was generated, 0 if we are waiting for another
+ *        byte of a dead-key sequence.
+ *
+ * Side effects:
+ *        Sets the trans_chars array in the XEvent->xkey structure.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+DecodeViaKCHRResource(
+	Ptr kchr,
+	const KeyEventData * e,
+	XEvent * event )
+{
+    /* input and output of KeyTranslate */
+    UInt16 keycode;
+    UInt32 result;
+
+    /* for converting the result */
+    char macbuff[2];
+    char * macstr;
+    int maclen;
+
+    keycode = e->keyCode | e->keyModifiers;
+    result = KeyTranslate(kchr, keycode, &deadKeyState);
+
+    if (0 != deadKeyState)
+	return 0; /* more data later */
+
+    macbuff[0] = (char) (result >> 16);
+    macbuff[1] = (char)  result;
+
+    if (0 != macbuff[0]) {
+	/* if the first byte is valid, the second is too */
+	macstr = macbuff;
+	maclen = 2;
+    } else if (0 != macbuff[1]) {
+	/* only the second is valid */
+	macstr = macbuff+1;
+	maclen = 1;
+    } else {
+	/* no valid bytes at all */
+	macstr = NULL;
+	maclen = 0;
+    }
+
+    if (maclen > 0) {
+	int result = Tcl_ExternalToUtf(
+		NULL, TkMacOSXCarbonEncoding,
+		macstr, maclen, 0, NULL,
+		event->xkey.trans_chars, sizeof(event->xkey.trans_chars),
+		NULL, NULL, NULL);
+    }
+
+    return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * XGrabKeyboard --
  *
  *        Simulates a keyboard grab by setting the focus.
diff --git a/macosx/tkMacOSXKeyboard.c b/macosx/tkMacOSXKeyboard.c
index aff807e..447da04 100644
--- a/macosx/tkMacOSXKeyboard.c
+++ b/macosx/tkMacOSXKeyboard.c
@@ -9,7 +9,7 @@
  * See the file "license.terms" for information on usage and redistribution
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  *
- * RCS: @(#) $Id: tkMacOSXKeyboard.c,v 1.3 2002/09/09 23:52:02 hobbs Exp $
+ * RCS: @(#) $Id: tkMacOSXKeyboard.c,v 1.4 2002/10/16 11:29:50 vincentdarley Exp $
  */
 
 #include "tkInt.h"
@@ -156,7 +156,7 @@ XKeycodeToKeysym(
         return NoSymbol;
     }
     
-    virtualKey = (char) (keycode >> 16);    
+    virtualKey = keycode >> 16;
     c = (keycode) & 0xffff;
     if (c > 255) {
         return NoSymbol;
@@ -166,7 +166,7 @@ XKeycodeToKeysym(
      * When determining what keysym to produce we first check to see if
      * the key is a function key.  We then check to see if the character
      * is another non-printing key.  Finally, we return the key syms
-     * for all ASCI chars.
+     * for all ASCII chars.
      */
     if (c == 0x10) {
 	hPtr = Tcl_FindHashEntry(&vkeyTable, (char *) virtualKey);
@@ -222,42 +222,9 @@ TkpGetString(
     Tcl_DString *dsPtr)		/* Uninitialized or empty string to hold
 				 * result. */
 {
-    register Tcl_HashEntry *hPtr;
-    char string[3];
-    int virtualKey;
-    int c, len;
-
-    if (!initialized) {
-	InitKeyMaps();
-    }
-
+    (void) winPtr; /*unused*/
     Tcl_DStringInit(dsPtr);
-    
-    virtualKey = (char) (eventPtr->xkey.keycode >> 16);    
-    c = (eventPtr->xkey.keycode) & 0xffff;
-    
-    if (c < 256) {
-        string[0] = (char) c;
-        len = 1;
-    } else {
-        string[0] = (char) (c >> 8);
-        string[1] = (char) c;
-        len = 2;
-    }    
-    /*
-     * Just return NULL if the character is a function key or another
-     * non-printing key.
-     */
-    if (c == 0x10 || (eventPtr->xany.send_event == -1)) {
-	len = 0;
-    } else {
-	hPtr = Tcl_FindHashEntry(&keycodeTable, (char *) virtualKey);
-	if (hPtr != NULL) {
-	    len = 0;
-	}
-    }
-    return Tcl_ExternalToUtfDString(TkMacOSXCarbonEncoding, string, 
-				    len, dsPtr);
+    return Tcl_DStringAppend(dsPtr, eventPtr->xkey.trans_chars, -1);
 }
 
 /*
-- 
cgit v0.12