[comp.windows.x] Problem with multi-key translations in Xt Intrinsics.

markwk@metheny.unx.sas.com (Mark Kernodle) (08/13/90)

 /*
  * Sample application for Xt intrinsics "feature"
  * Author: E. Blair, SAS Institute, Inc. (919) 677-8000 x6956
  *
  * Problem statement: This program demonstrates what I believe to be a problem
  * in the _XtTranslateEvent Xt event handler interpretation of
  * eventObjTbl entries in the XtTranslations state table. This problem was
  * experienced in the DECwindows Xt intrinsics as well as in the Motif
  * intrinsics. The behavior is that non-initial key events associated
  * with a multi-key event sequence are thrown away even though a translation
  * action exists in the table that should receive the event. The 
  * _XtTranslateEvent handler returns for such events because the EventRec
  * state is NULL. For this sample, I would expect the translation to match the
  * explicit sequences given, dispatching all other keys to the "normal-key"
  * action. Instead, KeyPress events for keys a and b are never delivered to 
  * the "normal-key" action.
  */

#include <ctype.h>
#include <Xm/Xm.h>
#include <X11/Shell.h>
#include <X11/Xlib.h>

/* Some statics for widgets */

static Widget toplevel, window_widget;

/*
 * Widget action table and translations
 */
static void MyFocusIn();
static void MyFocusOut();
static void MyPFKey();
static void MyNormalKey();
static XtActionsRec TestActions[] = {
  {"focus-in",		MyFocusIn},
  {"focus-out",		MyFocusOut},
  {"pf-key",		MyPFKey},
  {"normal-key",	MyNormalKey},
  {NULL,		NULL}
};

static XtTranslations TestTLP = NULL;

static char TestTranslations[] = 
	"<FocusIn>: focus-in() \n\
	<FocusOut>: focus-out() \n\
	<Key>KP_F1,<Key>a: pf-key(1) \n\
	<Key>KP_F1,<Key>b: pf-key(2) \n\
	<Key>: normal-key()";


static void ShowKey(event)
XEvent *event;

{
    int keylen,keysym;
    char *modify,*keystr;
    char keyascii[4];

    keylen = XLookupString (event, keyascii, sizeof(keyascii), &keysym, NULL);
    keyascii[keylen] = '\0';
    if (event->xkey.state & ShiftMask)
	modify = "Shift";
    if (event->xkey.state & LockMask)
	modify = "Lock";
    else if (event->xkey.state & ControlMask)
	modify = "Control";
    else if (event->xkey.state & Mod1Mask)
	modify = "Meta";
    else
	modify = "";
    if (isprint(keyascii[0]))
	keystr = keyascii;
    else {
	keystr = XKeysymToString (keysym);
	if (!keystr)
	    keystr = "**NoKeySymString**";
    }
    printf ("KeyString: %s<Key>%s\n",modify,keystr);
    return;
}

static void MyFocusIn (w, event, argv, nargs)
Widget w;
XEvent *event;
String argv[];
int *nargs;

{
    printf ("Focus in window\n");
    return;
}

static void MyFocusOut (w, event, argv, nargs)
Widget w;
XEvent *event;
String argv[];
int *nargs;

{
    printf ("Focus left window\n");
    return;
}

static void MyPFKey(w, event, argv, nargs)
Widget w;
XEvent *event;
int *nargs;
String argv[];

{
    int i;

    for (i=0; i < *nargs; i++)
	printf ("PF key: Arg[%d]: %s\n", i, argv[i]);

    ShowKey(event);
    return;
}

static void MyNormalKey(w, event, argv, nargs)
Widget w;
XEvent *event;
int *nargs;
String argv[];

{
    printf("Normal key event\n");
    ShowKey (event);
    return;
}


static Widget build_main (p)
    Widget p;
{
    Arg al[10];
    int ac = 0;

    XtSetArg (al[ac], XmNx, 100); ac++;
    XtSetArg (al[ac], XmNy, 100); ac++;
    XtSetArg (al[ac], XmNheight, 100); ac++;
    XtSetArg (al[ac], XmNwidth, 300); ac++;
    XtSetArg (al[ac], XmNtranslations, TestTLP); ac++;
    window_widget = XmCreateDrawingArea(p, "Window", al, ac);
    XtManageChild (window_widget);
    return (window_widget);
}

main (argc, argv)
    int argc;
    char *argv[];
{
    Arg args[1];
    
    /*
     * do toolkit initialization
     */

    toplevel = XtInitialize("Simple", "TEST", NULL, 0, &argc, argv);
    if (!toplevel) {
	printf ("Initialize failed\n");
	exit(1);
    }
    XtSetArg (args[0], XtNallowShellResize, TRUE);
    XtSetValues (toplevel, args, 1);

    XtAddActions (TestActions,XtNumber(TestActions));
    TestTLP = XtParseTranslationTable (TestTranslations);
    if (!TestTLP) {
	printf ("Translation parse failed\n");
	exit(1);
    }


    /*
     * now set up the test window
     */

    build_main (toplevel);

    /*
     * and realize the test window
     */

    XtRealizeWidget (toplevel);

    /*
     * main event loop for test window
     */

    XtMainLoop();
}

gabe@hpcvlx.cv.hp.com (Gabe Begeddov) (08/15/90)

   / hpcvlx:comp.windows.x / markwk@metheny.unx.sas.com (Mark Kernodle) /  8:25 am  Aug 13, 1990 /
   
     * intrinsics. The behavior is that non-initial key events associated
     * with a multi-key event sequence are thrown away even though a translation
     * action exists in the table that should receive the event. The 

You are quite right. There are many problems with the translation
manager handling of multiple event left hand sides. This is mostly due
to the fact that a single table is used to do both caching of event
matching information and lookup ordering of both initial and ensueing 
incoming events. We (HP) are in the process of rewriting the translation
manager for much improved data space usage with a byproduct being (hopefully :-)
correct handling of event sequences and table merges (another problem area
in the current implementation). We will be donating this code to MIT in the 
next few months. Depending on timing and release decisions it should be
available by R5 (if not sooner).

Please also send bug reports related to the tm to xbugs@expo.lcs.mit.edu so 
that they can be logged and checked against any proposed implementation changes.

Thanx, 

Gabe Beged-Dov
Interface Technology Operation
Hewlett Packard

swick@ATHENA.MIT.EDU (Ralph Swick) (08/16/90)

    The behavior is that non-initial key events associated
    with a multi-key event sequence are thrown away even though a translation
    action exists in the table that should receive the event.

This is an interesting case of ambiguity in the Xt specification.
To summarize, given the translation specification

  <Key>a,<Key>b	: something()
  <Key>		: else()

then <KeyPress>b events not preceeded by <KeyPress>a wind up
being discarded as a side-effect of the rule that "more specific
events must preceed more general events".  I won't argue with
anyone claiming that this is non-intuitive.  You'd probably
argue (and in theory, I'd probably agree) that 'a' is meant to
be a prefix character ala emacs and that un-prefixed 'b' should
invoke the else() action.  You'd still have to invoke the "more
specific preceed more general" rule to explain why 'a' not
followed by 'b' should go into the bit-bucket, if that's what
you want to have happen.

Such a rule is easy to implement (it's 1 line; I just did it)
but it opens a big compatibility can o'worms.  If it were to
become part of the implementation, we'd have to provide
a compatibility mode for the current behavior.

Fortunately, there's a simple alternative: you can specify

  <Key>a,<Key>b	: something()
  <Key>b	: else()
  <Key>		: else()

to get the behavior in the preceeding paragraph.  Yes, it's
ugly, but it may be the best option available.

-Ralph