Impossible to pass a Motif widget array as client_data in XtAddCallback?

178 Views Asked by At

Greets all, what I've got is a much simplified snippet of a real program that has to update the label strings in 45 million Motif label widgets. Simplified version: I create two label widgets and set their label strings to "text." I place the two widgets into a standard C array and pass it as client_data to a callback function using XtAddCallback. The callback is invoked by a pushbutton and it's supposed to change the label strings from "text" to "different text," but it only changes the string in the first label widget. I'm assuming that for whatever reason the client_data (an XtPointer) that's supposed to have the two widget array actually only has the first widget. I realize full well that I could access the label widgets from the parent widget that's passed in as the first parameter in XtAddCallback, then use XtNameToWidget to get the labels, but that would entail 45 million calls to XtNameToWidget, which I'd rather avoid. I also realize I could put the all the label widgets into a global array, and then index over the array to change the label strings, but globals are also something I'd rather avoid. Another thing that's weird, if there is no second widget I would have thought that calling XtSetValues on it would crash the program, but it doesn't. So, if anyone's interested in taking a look at this (doubtful), here's the simplified snippet, if you name it arrays.c, it compiles (at least on Linux) with gcc -lXm -lXt -lX11 -o arrays arrays.c - Oh and for reasons that defy logical explanation, I'm being forced to use Motif-2.1.32

/* Impossible to pass a widget array using XtAddCallback? */

#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <stdio.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>

void pass_array_cb(Widget, XtPointer, XtPointer);

int main(int argc, char * argv[])
{
    XtAppContext app_context;
    XmString text;

    Widget toplevel;
    Widget form, button, label_01, label_02;
    Arg av[10];
    int ac = 0;

    toplevel = XtOpenApplication(      
        &app_context,
        "arrays",
        NULL,
        0,
        &argc,
        argv,
        NULL,
        applicationShellWidgetClass,
        av,
        ac);

    form = XtVaCreateManagedWidget("form",
        xmFormWidgetClass, toplevel,
        XmNheight,         100,
        XmNwidth,          200,
        NULL);

    text = XmStringCreateLocalized((char *) "text");

    label_01 = XtVaCreateManagedWidget("label_01",
        xmLabelWidgetClass, form,
        XmNwidth,           60,
        XmNheight,          20,
        XmNlabelString,     text,
        XmNtopAttachment,   XmATTACH_WIDGET,
        XmNtopWidget,       toplevel,
        NULL);

    label_02 = XtVaCreateManagedWidget("label_02",
        xmLabelWidgetClass, form,
        XmNwidth,           60,
        XmNheight,          20,
        XmNlabelString,     text,
        XmNtopAttachment,   XmATTACH_WIDGET,
        XmNtopWidget,       label_01,
        NULL);

    XmStringFree(text);

    Widget label_widgets[2] = { label_01, label_02 };

    button = XtVaCreateManagedWidget("button",
        xmPushButtonWidgetClass, form,
        XmNwidth,                60,
        XmNheight,               20,
        XmNtopAttachment,        XmATTACH_WIDGET,
        XmNtopWidget,            label_02,
        NULL);

    XtAddCallback(button, XmNactivateCallback, pass_array_cb, *label_widgets);

    XtRealizeWidget   (toplevel);
    XtAppMainLoop     (app_context);

    return 0;
}

void pass_array_cb(Widget w, XtPointer client_data, XtPointer call_data)
{
    Widget (*label_widgets)[2] = (Widget (*)[2])client_data;
    XmString text = XmStringCreateLocalized((char *)"different text");
    int i;
    for(i = 0; i < 2; i++)
    {
        printf("in the callback, widget index is %d\n", i);
        XtVaSetValues(*label_widgets[i], XmNlabelString, text, NULL);
    }
    XmStringFree(text);
}
1

There are 1 best solutions below

3
n. m. could be an AI On
XtAddCallback(button, XmNactivateCallback, pass_array_cb, *label_widgets);

*label_widgets is exactly the same as label_widgets[0] which in your case is exactly the same as label_01. You are passing label_01 as your callback argument. It has type Widget.

Widget (*label_widgets)[2] = (Widget (*)[2])client_data;

label_widgets is now a pointer to a 2-element array of Widget, which is very different from Widget you have passed as the argument.

XtVaSetValues(*label_widgets[i], XmNlabelString, text, NULL);

*label_widgets[i] is the same as label_widgets[i][0]. Since label_widgets is a pointer (see above), it is taken as a pointer to the first element of an array. An array of what? Of two-element arrays or Widget. So it takes the ith element of that array, which happens to be a 2-element array of Widget, and takes the 0ths widget of that. Needles to say, there is no array of arrays of Widget anywhere, so this code just wanders off to the La-La Land.

You want this:

Pass a pointer to the first element of label_widgets. Look up array-to-pointer decay for more info.

XtAddCallback(button, XmNactivateCallback, pass_array_cb, label_widgets);

Receive a pointer to the first element of an array of Widget.

Widget* label_widgets = (Widget*)client_data;

Index into the aforementioned array using a pointer to its first element (all array indexing works this way).

XtVaSetValues(label_widgets[i], XmNlabelString, text, NULL);