Next: Resource Files Up: Drawingresource files, Previous: Drawingresource files,

Drawing

I can distinctly remember my first attempts at drawing under Motif: it seemed like a nightmare. Coming from a Mac environment, where I have seen 10-year-olds making calls into the drawing library, Motif was a little hard to swallow. I still don't know everything about everything, but simple drawing I can do, so I wanted to include it so that you don't go through the same time-wasting swamp I went through. [For a really good description, the Scheifler/Getty/Newman book is the one to get.] Drawing is hard because you've got to go all the way down to the X level.

To draw in X, you have to understand the concept of a "graphics context". A graphics context, or GC, describes how the drawing will be done: what colors will be used, what sort of drawing function (or mode) will be used (eg - copy, xor, invert, etc.), the line width, different styles, etc. You set up a context that specifies how things will be drawn, and then you draw using that context. You can create mutliple contexts if you like.

You do your drawing on a drawing area widget. The process of drawing 1000 random lines and rectangles is demonstrated in the following code (which sucks up CPU time by the way - just a friendly warning):


       #include <X11/Intrinsic.h>
        #include <X11/Xutil.h>

        #include <Xm/Xm.h>
        #include <Xm/DrawingA.h>

        #define size    500

        int rand_seed=10;
        GC gc;
        Widget drawing_area;

        int rand()
        /* from K&R */
        {
          rand_seed = rand_seed * 1103515245 +12345;
          return (unsigned int)(rand_seed / 65536) % 32768;
        }

        void setup_gc()
        /* set up the graphics context. */
        {
          int foreground,background;
          XGCValues vals;
          Arg al[10];
          int ac;

          /* get the current fg and bg colors. */
          ac=0;
          XtSetArg(al[ac], XmNforeground, &foreground); ac++;
          XtSetArg(al[ac], XmNbackground, &background); ac++;
          XtGetValues(drawing_area, al, ac);

          /* create the gc. */
          vals.foreground = foreground;
          vals.background = background;
          vals.function = GXcopy;
          vals.fill_style = FillTiled;
          gc= XtGetGC(drawing_area, GCForeground | GCBackground |
                GCFunction | GCFillStyle, &vals);
        }

        check_points(int *x, int *y, int *x2, int *y2)
        /* swap values if out of order. */
        {
          if(*x2 < *x){ int tmp = *x; *x = *x2; *x2 = tmp;}
          if(*y2 < *y){ int tmp = *y; *y = *y2; *y2 = tmp;}
        }

        void draw_line(Widget w, GC gc, int x, int y, int x2, int y2)
        {
          check_points(&x,&y,&x2,&y2);
          XDrawLine(XtDisplay(w), XtWindow(w), gc, x, y, x2, y2);
        }

        void draw_rectangle(Widget w, GC gc, int x, int y, int x2, int y2)
        {
          check_points(&x, &y, &x2, &y2);
          XDrawRectangle(XtDisplay(w), XtWindow(w), gc, x, y, x2-x, y2-y);
        }

        void draw_text(Widget w, GC gc, int x, int y, char *s)
        {
              XDrawImageString(XtDisplay (w), XtWindow (w), gc,
                                 x, y, s, strlen(s));
        }

        void clear_area(Widget w)
        /* clear the specified drawing widget. */
        {
          XClearArea(XtDisplay(w), XtWindow(w), 0,0,0,0, True);
        }

        void do_drawing()
        /* draw 1000 lines, then 1000 rectangles, all random. */
        {
          int x;

          clear_area(drawing_area);
          for(x=1; x<1000; x++)
            draw_line(drawing_area,gc,rand()%size,rand()%size,
                                rand()%size,rand()%size);
          clear_area(drawing_area);
          for(x=1; x<1000; x++)
            draw_rectangle(drawing_area,gc,rand()%size,rand()%size,
                                rand()%size,rand()%size);
        }

        Boolean the_work_proc(caddr_t client_data)
        /* work proc that is called when event loop is idle. */
        {
          setup_gc();
          do_drawing();
          return False;
        }

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

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

          /* default window size. */
          ac=0;
          XtSetArg(al[ac],XmNheight,size); ac++;
          XtSetArg(al[ac],XmNwidth,size); ac++;
          XtSetValues(toplevel,al,ac);

          /* create a drawing area widget. */
          ac=0;
          drawing_area=XtCreateManagedWidget("drawing_area",
                xmDrawingAreaWidgetClass, toplevel,al,ac);

          /* add in the work proc. */
          XtAddWorkProc(the_work_proc,NULL);

          XtRealizeWidget(toplevel);
          XtMainLoop();
        }

The main procedure sets up the drawing area, and then establishes something new: a "work proc". A work proc is a procedure that is called whenever the event loop is sitting idle - it can be used to do background processing. [Note: work procs ought to do only small amounts of processing to make sure that the event loop gets returned to to process user events. Since there are no user events here, it doesn't really matter.] A work proc is used here because the drawing cannot take place until the drawing area is realized, and it won't be realized until the event loop is started. Therefore, the drawing must occur AFTER the event loop is started, and the only way to do this is with a work proc (or we could use a button that is pushed to do the drawing, but we've already done buttons). Because this work proc ends with "return False", it will be called over and over again - each time it returns, it will be called again.

What can you learn from this code? It should show you how you can set up a simple GC, and then call normal X procedures to get some simple drawing done. X will let you draw all sorts of things: filled rectangles, circles, filled circles, arcs, polygons, etc. You really need to have a good book or some man pages to do all of this however.



Next: Resource Files Up: Drawingresource files, Previous: Drawingresource files,


morbe@enstb.enst-bretagne.fr