Next: Application Twotic-tac-toe Up: Motif Tutorial Previous: SelectionDialog

Application One, mkill

At this point we can start to build some real Motif applications. These are all going to be fairly simple, but what I'd like to demonstrate is that the knowledge you now have is sufficient to create useful applications. Large involved applications can be found aplenty in Young's book. I will also demonstrate several more widgets in these applications.

This first application is called "mkill". It is extremely simple, and also quite useful, at least for me. When it is executed, it sits on the screen in a small window displaying a single button labeled "Push to kill". When you push the button, the program forks and starts up the "ps" command. It reads the output of "ps" through a pipe, and places it in a selection box. You then click on the item that you wish to kill, and it is killed.

I created this application to solve a simple problem: I HATE to have to type ps, then wait, then look up the process number and then kill that process number. This solves the problem. You still have to wait some, for ps to do it's thing, but then you just point and click.

The connection to ps is made simple here by using a library called "link". This library handles the forking and pipe set-up. I'll include the source for it at the bottom of the file. It is very short and simple.

To try mkill (not to be confused with xkill, a separate program), type in the following code (and the link library) and compile:


        #include <X11/Intrinsic.h>
        #include <Xm/Xm.h>
        #include <Xm/PushB.h>
        #include <Xm/SelectioB.h>
        #include "link.h"

        #define OK      1
        #define CANCEL  2

        Widget toplevel, dialog;
        Boolean first_time=True;

        void dialogCB(Widget w,
                   int client_data,
                   XmAnyCallbackStruct *call_data)
        {
          char *procstr,s[100],num[100];
          XmSelectionBoxCallbackStruct *selection;

          switch (client_data)
          {
                case OK:
                        selection=(XmSelectionBoxCallbackStruct *) call_data;
                        XmStringGetLtoR(selection->value, 
                                XmSTRING_DEFAULT_CHARSET, &procstr);
                        
                        /* extract the process number from the line and
                           kill that process number. */
                        strncpy(num,procstr,5);
                        strcpy(s,"kill -9 ");
                        strcat(s,num);
                        system(s);

                        XtFree(selection);
                        break;
                case CANCEL:
                        break;
          }
          XtUnmanageChild(w);
        }

        void buttonCB(Widget w,
                   caddr_t client_data,
                   XmAnyCallbackStruct *call_data)
        {
          Arg al[10];
          int ac;
          XmString list[100];
          int list_cnt;
          struct link_handle l;
          char s[200];

          /* In the version of Motif I use, there is a bug that causes the
             selection box's list to be unchangeable once it has been loaded.
             This means that you can load it the first time, but after that
             the list cannot be changed to anything else. The only way I have
             found to get around the problem is to destroy the widget each
             time, recreate it, and then reload it with the new list. You might
             try it without recreating the widget each time and see if it works 
             for you. If it does, then you should create the widget once 
             in the main program instead of every time, and you can also 
             remove this destruction code.*/
          if (first_time)
            first_time=False;
          else
            XtDestroyWidget(dialog);

          /* recreate the selection box dialog */
          ac = 0;
          XtSetArg(al[ac],XmNautoUnmanage,False); ac++;
          XtSetArg(al[ac],XmNmustMatch,True); ac++;
          XtSetArg(al[ac], XmNselectionLabelString, XmStringCreateLtoR
                ("Pick a process to kill.", XmSTRING_DEFAULT_CHARSET));  ac++;
          dialog = XmCreateSelectionDialog(toplevel, "dialog", al, ac);
          XtAddCallback (dialog, XmNokCallback, dialogCB, OK);
          XtAddCallback (dialog, XmNcancelCallback, dialogCB, CANCEL);
          XtUnmanageChild(XmSelectionBoxGetChild(dialog,
                XmDIALOG_HELP_BUTTON));

          /* establish a link to ps with the link library, and place
             the strings from it into the selection box. */
          link_open(&l,"ps","-g");
          link_read(&l,s);
          list_cnt=0;
          while (!link_read(&l,s))
          {
            s[strlen(s)-1]='\0'; /* get rid of the '\n' */
            list[list_cnt++]=
                    XmStringCreateLtoR(s, XmSTRING_DEFAULT_CHARSET);
          }
          link_close(&l);

          /* add the list to the selection box */
          ac=0;
          XtSetArg(al[ac], XmNlistItems, list); ac++;
          XtSetArg(al[ac],XmNlistItemCount, list_cnt); ac++;
          XtSetValues(dialog, al, ac);
          XtManageChild(dialog);
        }

        main(int argc, char *argv[])
        {
          Widget button;
          Arg al[10];
          int ac;

          toplevel=XtInitialize(argv[0],"",NULL,0,&argc,argv);

          /* create the "push to kill" button */
          ac=0;
          XtSetArg(al[ac],XmNlabelString, XmStringCreate("Push to kill",
                XmSTRING_DEFAULT_CHARSET)); ac++;
          button=XtCreateManagedWidget("label",xmPushButtonWidgetClass,
                toplevel,al,ac);
          XtAddCallback (button, XmNactivateCallback, buttonCB, NULL);

          XtRealizeWidget(toplevel);
          XtMainLoop();
        }

If you look at this code, you will see that it is exactly the same code as that used in the selection box demo in tutorial 6. Two things have been added. First, in the buttonCB routine, the link library is used to create a pipe to ps and to read the output of ps. This output is then given to the selection box. Then in dialogCB, the selected string is read out of call_data, the process number is extracted, and a system call is made to kill the process.

If you have problems when you try to run this, see if the command "ps g" works at the unix command line. You may want to change the "g" parameter to something else, or even make it just "" to solve the problem.

The link library header file is given below:



        /* Link module, v1.0, 5/4/90 Marshall Brain */

        /* This module allows a program to form links to other seperately
           executing programs and communicate with them. Links can be
           opened and closed, and the program using this unit can 
           write to and read from the other program over the link. */
        /* This module will not work with all programs. If the program
           does anything weird with stdout, or if it fails to flush
           stdout correctly, then this module will fail. If you are creating
           a stand-alone program that you wish to link to another program
           with this library, then you MUST make sure that stdout is
           flushed correctly. Either call "fflush(stdout)" after every
           printf, or call "setbuf(stdout,NULL)" at the beginning of the
           program to eliminate buffering.                         */

        #include <stdio.h>
        #include <strings.h>
        #include <signal.h>

        struct link_handle
        {
          int pipefd1[2],pipefd2[2];
          int pid;
          FILE *fpin,*fpout;
        };

        extern link_open(struct link_handle *l, char name[], char param[]);
        /* open a link to another program named name, passing a param
           to the program if desired. This routine will execute name
           in parallel and you can start communicating with it with
           link_read and link_write.*/

        extern link_close(struct link_handle *l);
        /* Close the link to a program that has terminated. Use link_kill
           if the program needs to be terminated as well.*/

        extern int link_read(struct link_handle *l,char s[]);
        /* read from the program started with link_open.*/

        extern link_write_char(struct link_handle *l,char c);
        /* write a char, without a newline, to the program.*/
          
        extern link_write(struct link_handle *l,char s[]);
        /* write a string to the program, with a newline.*/

        extern link_kill(struct link_handle *l);
        /*kill the program and close the link. If the program has terminated
           on its own use link_close instead.*/

The link library code file is given below:


        /* Link module, v1.0, 5/4/90 Marshall Brain */

        #include "link.h"

        link_open(struct link_handle *l, char name[],char param[])
        {
          pipe(l->pipefd1);
          pipe(l->pipefd2);
          if((l->pid=fork())==0)        /*child*/
          {
            close(l->pipefd1[0]);
            close(1);
            dup(l->pipefd1[1]);
            close(2);           /*2 new lines*/
            dup(l->pipefd1[1]);
            close(l->pipefd2[1]);
            close(0);
            dup(l->pipefd2[0]);
            execlp(name,name,param,(char*)0);
          }
          else
          {
            l->fpin=fdopen(l->pipefd1[0],"r");
            l->fpout=fdopen(l->pipefd2[1],"w");
            close(l->pipefd1[1]); 
            close(l->pipefd2[0]); 
           }
        }

        link_close(struct link_handle *l)
        {
          wait((union wait*)0);
          close(l->pipefd1[1]);
          close(l->pipefd2[0]);
          fclose(l->fpin);
          fclose(l->fpout);
          l->pid=0;
        }

        int link_read(struct link_handle *l,char s[])
        {
          int eof_flag;
          
          if (fgets(s,100,l->fpin)==NULL)
            eof_flag=1;
          else
            eof_flag=0;
          return(eof_flag);
        }

        link_write_char(struct link_handle *l,char c)
        {
          fprintf(l->fpout,"%c",c);
          fflush(l->fpout);
        }
          
        link_write(struct link_handle *l,char s[])
        {
          fprintf(l->fpout,"%s\n",s);
          fflush(l->fpout);
        }

        link_kill(struct link_handle *l)
        {
          kill(l->pid,SIGKILL);
          link_close(l);
        }



Next: Application Twotic-tac-toe Up: Motif Tutorial Previous: SelectionDialog


morbe@enstb.enst-bretagne.fr