Search code examples
cmultithreadingx11toolkitmotif

Multithread or Motif widget issue while trying to append text to a Xm Text Widget


I am trying to use a TextWidget as a log, where programmatically text is added. By using XmTextInsert, XtVaSetValuesand XmTextShowPosition I can append text to a Text Widget, however I have noticed that the text is updated only if the Widget has the focus or when you roll the mouse pointer into the Text Widget area. Otherwise it seems that the Widget is updated only around 5/6 seconds, so you see appear several lines at once.

I can not determine if it is a Motif related issue or a pure X11/Xt/Xm multithread issue, but clearly the attached code is missing something relevant.

As example, this source code only has the TextWidget, and the update works fine, even when the focus is in another application, every second you see a new TEST word added to the Text Widget:

#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/Text.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void displayMessage(char *textLineBuffer);
void createWindow(int argc, char *argv[]);
void *logText(void *arg);

/* X11 RELATED VARIABLES */
XtAppContext    context;

Widget          toplevel;
Widget          busForm;
Widget          textWidget;
pthread_mutex_t textWidgetMutex;
long            textWidgetLength = 0;

int main(int argc, char *argv[]) {

        pthread_t       thread;

        /* INITIALIZE MULTITHREAD X11 ENVIRONMENT */
        XInitThreads();

        /* CREATE X WINDOW */
        createWindow(argc, argv);

        /* GET NEW THREAD TO PROCESS QUEUES */
        /* PARENTS HANDLES X11 */
        pthread_create(&thread, NULL, logText, NULL);

        /* BEGIN X11 APPLICATION */
        XtAppMainLoop(context);

}

void *logText(void *arg) {

        /* READ CLIENT QUEUE MESSAGES */
        while(1) {
                sleep(1);
                displayMessage("TEST\n");
        }

}

void createWindow(int argc, char *argv[]){

        Arg             al[10];
        int             ac;
        XmStringCharSet char_set = XmSTRING_DEFAULT_CHARSET;
        char            pidString[50];

        /* CREATE TOP LEVEL */
        sprintf(pidString, "Bus Client %d", getpid());
        toplevel = XtAppInitialize(&context, pidString, NULL, 0, &argc, argv, NULL, NULL, 0);

        /* RESIZE TOP LEVEL */
        ac = 0;
        XtSetArg(al[ac], XmNwidth, 300); ac++;
        XtSetArg(al[ac], XmNheight, 300); ac++;
        XtSetValues(toplevel, al, ac);

        /* CREATE TOP LEVEL FORM MANAGER */
        ac = 0;
        busForm  = XmCreateForm(toplevel, "busForm", al, ac);
        XtManageChild(busForm);

        /* CREATE TEXT WIDGET */
        ac = 0;
        XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
        XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
        XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
        XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
        XtSetArg(al[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
        XtSetArg(al[ac], XmNscrollingPolicy, XmVARIABLE); ac++;
        XtSetArg(al[ac], XmNscrollBarDisplayPolicy, XmSTATIC); ac++;
        XtSetArg(al[ac], XmNscrollHorizontal, FALSE); ac++;
        XtSetArg(al[ac], XmNeditable, FALSE); ac++;
        textWidget = XmCreateScrolledText(busForm, "textWidget", al, ac);
        XtManageChild(textWidget);

        /* DISPLAY TOP LEVEL */
        XtRealizeWidget(toplevel);
}

void displayMessage(char *textLineBuffer) {
        pthread_mutex_lock(&textWidgetMutex);
        XmTextInsert(textWidget, textWidgetLength, textLineBuffer); 
        textWidgetLength += strlen(textLineBuffer);
        XtVaSetValues(textWidget, XmNcursorPosition, textWidgetLength, NULL);
        XmTextShowPosition(textWidget, textWidgetLength);
        pthread_mutex_unlock(&textWidgetMutex);
}

As seen in the output, the message is displayed constantly, even when the windows has no focus:

Test2

However, if you add a button, when the focus in the Motif application in in the Button Widget, the text is not updated until the mouse rolls over the Text Widget. If you click on the Text Widget, then text is constantly updated there as in the previous example.

This is the source code and corresponding example.

#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/Text.h>
#include <Xm/PushB.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <unistd.h>

void displayMessage(char *textLineBuffer);
void createWindow(int argc, char *argv[]);
void sendButtonCB(Widget w, XtPointer  client_data, XtPointer call_data);
void *logText(void *arg);

/* X11 RELATED VARIABLES */
XtAppContext    context;

Widget          toplevel;
Widget          busForm;
Widget          textWidget;
Widget          registerButton;
Widget          sendButton;
pthread_mutex_t textWidgetMutex;
long            textWidgetLength = 0;

/* BUS CLIENT SYS V QUEUE VARIABLES */
int client_queue_id;
key_t client_queue_key;
int client_message_id;

int main(int argc, char *argv[]) {

        pthread_t       thread;

        /* INITIALIZE MULTITHREAD X11 ENVIRONMENT */
        XInitThreads();

        /* CREATE X WINDOW */
        createWindow(argc, argv);

        /* GET NEW THREAD TO PROCESS QUEUES */
        /* PARENTS HANDLES X11 */
        pthread_create(&thread, NULL, logText, NULL);

        /* BEGIN X11 APPLICATION */
        XtAppMainLoop(context);

}

void *logText(void *arg) {

        /* READ CLIENT QUEUE MESSAGES */
        while(1) {
                sleep(1);
                displayMessage("TEST\n");
        }

}

void createWindow(int argc, char *argv[]){

        Arg             al[10];
        int             ac;
        XmStringCharSet char_set = XmSTRING_DEFAULT_CHARSET;
        char            pidString[50];

        /* CREATE TOP LEVEL */
        sprintf(pidString, "Bus Client %d", getpid());
        toplevel = XtAppInitialize(&context, pidString, NULL, 0, &argc, argv, NULL, NULL, 0);

        /* RESIZE TOP LEVEL */
        ac = 0;
        XtSetArg(al[ac], XmNwidth, 300); ac++;
        XtSetArg(al[ac], XmNheight, 300); ac++;
        XtSetValues(toplevel, al, ac);

        /* CREATE TOP LEVEL FORM MANAGER */
        ac = 0;
        busForm  = XmCreateForm(toplevel, "busForm", al, ac);
        XtManageChild(busForm);

        /* CREATE SEND BUTTON WIDGET */
        ac = 0;
        XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
        XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
        XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
        XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++;
        XtSetArg(al[ac], XmNlabelString, XmStringCreateLtoR("Send", char_set)); ac++;
        sendButton = XmCreatePushButton(busForm, "sendButton", al, ac);
        XtManageChild(sendButton);
        XtAddCallback(sendButton, XmNactivateCallback, sendButtonCB, NULL); 

        /* CREATE TEXT WIDGET */
        ac = 0;
        XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
        XtSetArg(al[ac], XmNtopWidget, registerButton); ac++;
        XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
        XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
        XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
        XtSetArg(al[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++;
        XtSetArg(al[ac], XmNscrollingPolicy, XmVARIABLE); ac++;
        XtSetArg(al[ac], XmNscrollBarDisplayPolicy, XmSTATIC); ac++;
        XtSetArg(al[ac], XmNscrollHorizontal, FALSE); ac++;
        XtSetArg(al[ac], XmNeditable, FALSE); ac++;
        textWidget = XmCreateScrolledText(busForm, "textWidget", al, ac);
        XtManageChild(textWidget);

        /* DISPLAY TOP LEVEL */
        XtRealizeWidget(toplevel);
}

void displayMessage(char *textLineBuffer) {
        pthread_mutex_lock(&textWidgetMutex);
        XmTextInsert(textWidget, textWidgetLength, textLineBuffer); 
        textWidgetLength += strlen(textLineBuffer);
        XtVaSetValues(textWidget, XmNcursorPosition, textWidgetLength, NULL);
        XmTextShowPosition(textWidget, textWidgetLength);
        pthread_mutex_unlock(&textWidgetMutex);
}

Motif Test 2


Solution

  • I just found:

    void displayMessage(char *textLineBuffer) {
            pthread_mutex_lock(&textWidgetMutex);
            XmTextInsert(textWidget, textWidgetLength, textLineBuffer); 
            textWidgetLength += strlen(textLineBuffer);
            XmUpdateDisplay(textWidget);
            pthread_mutex_unlock(&textWidgetMutex);
    }
    

    Forces widget to redraw.