.:Публикации:. [www.karlson.ru]

ПРОГРАММИРОВАНИЕ ДЛЯ X Window System С БИБЛИОТЕКОЙ OSF/MOTIF.

Кожекин Н.

Open Software Foundation Motif (эк. OSF/Motif) - это библиотека виджетов, построенная на базе X Toolkit Intrinsics, менеджер окон и набор утилит вспомогательных операций. На данный момент этот пакет стал фактическим стандартом в области программирования для X Window. Стиль startx из Linux - это и есть стиль Motif. Эта библиотека является коммерческой разработкой и вместе с Linux не распространяется. Пользователь Linux может купить эту библиотеку за цену порядка 200 $ США или заказать при помощи ftp mail ее некоммерческую замену - библиотеку Lesstif, пока доступную лишь в версиях для тестирования.

Компиляция Motif программы производится следующей командой cc sourcefile.c -o outputfile.c -lX11 -lXt -lXm. Программа написанная на Motif включает #include и заголовки для всех используемых объектов, причем имя файла включения образуется от имени соответствующего виджета с изменением слова Window на W, а Button на B. Например, если вы используете объект Toggle Button, то вы должны в начале программы написать #include . Все процедуры и классы объектов Motif начинаются с префикса Xm, ресурсы с префикса XmN, классы ресурсов с префикса XmC, типы ресурсов с префикса XmR, а указатели классов объектов с префикса xm. Причем поддерживаемые Xt ресурсы имеют полные аналоги с префиксом Xm. Иерархия виджетов и ресурсов Motif показаны на рисунках 1 и 2. Каждый виджет наследует ресурсы от более высокого класса ресурсов.

               Core
    _____________:_________
    :                      :
 Composite            XmPrimitive
 ___:____                  :
 :       :                 :
Shell Constraint     Primitive Widgets
         :
      XmManager
         :
      Manager Widgets
Рис. 1

                        OBJECT
                          :
                      RECTOBJECT
   _______________________:________
   :                               :
   :                           gadget
   :             __________________:______________
   :             :                 :              :
   :      arrowbuttongadget labelgadget sepatatorgadget
   :             __________________:_______________
   :             :                 :               :
   : cascedebuttongadget pushbuttongadget togglebuttongadget
   :
   :_____________________________CORE
    _______________________________:__________________
    :                                                :
   COMPOSITE                                      primitive
  ____:___            ________________________________:________________
  :       :           :       :      :    :           :      :        :
SHELL CONSTRAINT arrowbutton label list scrollbar separator text textfield
:         :                   :_________________________________________
:     manager                 :            :            :              :
:         :               cascedebutton drawnbutton pushbutton togglebutton
:         |_______________________________________________________
:         |     :           :         :          :      :        :
:bulletinboard drawingarea frame panedwindow  rowcolmn scale mainwindow
:         :_______________________________________               :
:_____________           :           :            :       scrolledwindow
:             :        form    selectionbox    messagebox
WMSHELL OVERRIDESHELL          ______:______
:             :                :            :
VENDORSHELL  menushell      command fileselectionbox
   ___:______________
   :                 :
TOPLEVELSHELL TRANSIENTSHELL
   :                 :
APLICATIONSHELL   dialogshell
Рис. 2. Полная иерархия классов Xt и Motif. Классы Xt написаны большими буквами, классы Xm маленькими.

Core - вершина иерархии Xt intrinsics. Этот суперкласс задает фон, размер, и позицию, ресурсы обычные для всех виджетов.
XmPrimitive - суперкласс примитивных виджетов.
XmManager - суперкласс manager виджетов.
XmGadget - суперкласс гаджетов.
Composite - суперкласс контейнеров виджетов. Имеет два подкласса Shell и Constraint.

Primitive Widgets

Arrow button - кнопка с ориентируемой стрелкой.
Label - имеет четыре подкласса PushButton, DrawnButton, CascedeButton и ToggleButton.
Scrollbar - позволяет содержание окна смотреть пользователю по частям, скроллируя их.
Separator - используется для отделения виджетов в окне.
List - допускает выбор из списка текстовых элементов.
Text - полноэкранный текстовый редактор.
TextField - однострочный текстовый редактор.

Gadgets

Есть много случаев в Motif программировании, когда программист хочет использовать примитивный виджет, но не хочет использовать окно. Motif предлагает гаджеты - безоконные виджеты. Три гаджета предложены - ArrowButtonm, Label и Separator, которые ведут как соответствующие виджеты.

Manager Widgets

Frame - представляет эффект трехмерных бордюров для окон.
ScrolledWindow - позволяет скроллирование окну.
MainWindow - типичный toplevel контейнер для виджетов. Может содержать много виджетов, как меню, скроллеры и т.п.
DrawingArea - представляет виджет для изображения графики. Motif здесь не предлагает своих средств, и вы должны использовать XLib функции.
PanedWindow - виджет для объединения разнородных виджет.
Scale - объект масштабного изменения для пользовательского ввода.
RowColmn - широко используемый виджет для расположения виджет в 2D стиле.
BulletinBoard - имеет подклассы
Form - похож на RowColmn.
Dialog - имеется два типа диалогов : MessageBox, для выдачи информации пользователю и SelectionBox, для интерактивной работы с пользователям. Предлагаются также Comand и FileSelectionBox.

Shell Widgets и Constraint Widgets

Все виджеты содержатся в Shell Widget, работа которого заключается в интерфейсе к оконному менеджеру. Aplication Shell - это обычная оболочка для приложений. Override Shell - используется для всплывающих pop-up меню. Transient Shell используется для диалогов. Constraint виджеты используются для организации геометрии набора виджетов.

Все виджеты и их ресурсы подробно описаны в Motif Programming Manual и Motif Reference Manual. Смотрите их для подробностей.

Программа Xcolor.c, приведенная мной в качестве примера иллюстрирует использование пользовательского ввода с мыши в DrawingArea и представляет собой простейший графический редактор. Эта программа позволяет пользователю выбрать цвет и рисовать им различной толщины пунктирные и целые линии, а так же закрашенные и пустые четырехугольники. Пользователю предоставляется так же возможность загрузить картинку из pixmap-файла и очистить экран.

Как мы собираемся достичь этого?

Для нашего виджета (DrawingArea) мы создадим следующую таблицу трансляций : draw_mouse(down) ManagerGadgetArm()
: draw_mouse(up) ManagerGadgetActivate()
: draw_mouse(motion) ManagerGadgetButtonMotion()

Для регистрации нашей процедуры обратного вызова мы используем как обычно функцию XtAppAddActions() и процедуру XtParseTranslationTable(String*) для присваивания таблицы трансляций ресурсу XtNtranslations. Наша процедура обратного вызова (draw_mouse) должна реагировать на события мыши следующим образом: Mouse Down - только запоминаем координаты мыши (x,y) Mouse Up - рисуем наш графический примитив от запомненных координат до полученных сейчас Mouse Motion - рисуем пунктирный силуэт нашего графического примитива.

Картинка нарисованная пользователем должна восстанавливаться если окно было накрыто другим или минимизировано. Для этого мы используем Pixmap - вне экранный Drawable Area. Pixmap создается функцией XCreatePixmap, освобождается функцией XFreePixmap. Скопировать Pixmap в окно или наоборот можно функцией XCopyArea. Каждый раз когда пользователь рисует что-либо в окне, программа сохраняет рисунок в Pixmap. При получении события expose программа копирует содержание Pixmap в свое окно.

Для переключения рисования прямоугольников и прямых, а также заполненности или пустоты рисуемых объектов я использовал кнопки типа Toggle, которые можно создать процедурой XtVaCreateManagedWidget() с указателем xmToggleButtonWidgetClass или XmCreateToggleButton(). Вот несколько важных toggle ресурсов:

XmNindicatorOn - установите этот ресурс True, если хотите, чтобы сначала индикатор был нажат (по умолчанию значение этого ресурса False),
XmNindicatorSize - меняет размер индикатора в пикселях,
XmNlabelType - тип наклейки: XmSTRING (строка) или XmPIXMAP (рисунок),
XmNlabelString - строка наклейки,
XmNlabelPixmap - рисунок ненажатой наклейки (структура типа Pixmap),
XmNselectPixmap - Pixmap нажатой наклейки,
XmNselectColour - цвет нажатой кнопки (структура типа Pixel).

Для расположения кнопок в форме используется механизм привязок. Привязки разделены на верхние (top), нижние (bottom), левые (left) и правые (right). Виджет соответственно имеет четыре ресурса типа XmN...Attachment. Чтобы привязать виджет к родительской форме установите этот ресурс значением XmATTACH_FORM. Чтобы привязать виджет к другому виджет используется значение XmATTACH_WIDGET, а ресурс XmNwidget установите соответственно выбранному виджету. Для привязки одного виджета напротив другого установите ресурс привязки значением XmATTACH_OPOSITE_WIDGET. Чтобы привязать виджет какой-либо точке установите ресурс XmN...Attachment значением XmATTACH_POSITION, а ресурс XmN...Position установите соответствующим целым значением.

Для обеспечения пользователем изменения размера линии можно использовать виджет Scale (реализован в примере в листинге). Создается этот виджет процедурой XmCreateScale. Некоторые его ресурсы очень важны: XmNmaximum - наибольшее значение (int),
XmNminimum -наименьшее значение (int, по-умолчанию 0),
XmNorientation - этот ресурс может иметь значения XmHORISONTAL или XmVERTICAL (с очевидной интерпретацией),
XmNtitleString - ресурс типа XmString : название,
XmNDecimalPoints - (по умолчанию 0) количество знаков отделяемых для десятичного представления после целой части,
XmNShowValue - True или False : отображать числом выбранный результат или нет,
XmNValue - начальное значение (int), XmNprocessingDirection - XmMAX_ON_TOP, Xm_MAX_ON_BOTTOM, XmMAX_ON_LEFT или XmMAX_ON_RIGHT : определяет положение максимума (зависит от XmNorintation).

Специальные объекты для диалога с пользователем называются в Motif Dialogs. Диалоги открывают окно с общением и могут принимать некоторый пользовательский ввод. Пользователю обычно предоставляется три кнопки -- Ok, Cancel и Help -- первые две позволяют пользователю согласиться или нет с предложенным и окончить диалог, а третья может предоставлять дополнительную информацию. Не забудьте, что для того чтобы диалог появился в вашей программе вы должны применить операцию manage к соответствующему виджет, а чтобы диалог исчез операцию unmanage. Вот предоставляемые Motif диалоги:

BulletinBoardDialog - это диалог полностью задаваемый программистом: сам по себе он ничего не содержит,
ErrorDialog - извещает пользователя об ошибке в работе программы,
SelectionDialog - предоставляет пользователю выбор из листа опций (кнопки Aplay, Cancel, Help),
FileSelectionDialog - специальный selectiondialog для выбора файлов и директорий (кнопки Ok, Filter, Cancel),
InformationDialog - выводит дополнительную информацию о программе,
PromptDialog - позволяет пользователю ввести строку данных для программы,
QuestionDialog - вопросы типа Yes/No (кнопки соответственно),
WarningDialog - извещает пользователя о проблемах в работе программы,
WorkingDialog - извещает пользователя о работе программы.
Любой диалог можно создать процедурой XmCreate...Dialog(), а программируются они все почти идентично.

Все нижеизложенные принципы программирования (и некоторые другие) в системе Motif подробно иллюстрирует программа из листинга 2 (листинг 1 [makefile] собирает программу [xcolor.c] из листинга 2). Для любой информации по поводу пишите -- (e-mail).

Листинг 1. makefile
#
#       Makefile для программы XColor -- пример
#                для Motif программистов

CC = gcc

# Если вы хотите компилировать для дебагинга поменяйте
# опции: "-O" на "-g"

CFLAGS               = -O2

XM_LIBS =  -lX11 -lXt -lXm

XColor : Xcolor.o 
        ${CC} ${LDFLAGS} -o $@ Xcolor.c ${XM_LIBS}

clean:
        rm -f core ${OBJS} *.o

# замените в выше представленном тексте восемь
# пробелов на символ табуляции
Листинг 2. xcolor.c
      
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

void ScrubDial(Widget, int);

GC gc;
XGCValues gcv;
Widget draw, top_wid, main_w, widget, menu_bar;
Display *display; 
Colormap cmap;
Pixmap pix_map;
Widget 

String colours[] = { "Black",  "Red", "Green",
                      "Blue", "Grey", "White"};
String activites[] = { "Rectangle" , "Line" };
/* некоторые начальные установки */
long int actived = 0; /* рисование линий */
Bool ifSolid = 0; /* не заштриховывать квадраты,
а линии рисовать сплошными */ 
unsigned int line_width = 1;
long int fill_pixel = 1;
/* сохраняет текущий цвет заливки
(по-умолчанию черный)*/


main(int argc, char *argv[])

{   Widget quit, clear, colour, scale, Buttons,
    file_select;
    XtAppContext app;
    XmString  clears, colurss, red, green, blue,
    black, grey, white;
    XtActionsRec actions;
    void clear_call(), colour_call(), scale_pop_up(),
         restore_pixmap(), draw_mouse(), brush_call(),
         activites_call(), quit_pop_up(),
         buttons_pop_up(), select_pop_up();
   
    String translations =
": draw_mouse(motion)
               ManagerGadgetButtonMotion() \n\
: draw_mouse(down) ManagerGadgetArm() \n\
: draw_mouse(up) ManagerGadgetActivate()";
    
    top_wid = XtVaAppInitialize(&app, "Draw", NULL, 0,
        &argc, argv, NULL,
        XmNwidth,  500,
        XmNheight, 500,
        NULL);

   main_w = XtVaCreateManagedWidget("main_window",
        xmMainWindowWidgetClass,   top_wid,
        XmNwidth, 500,
        XmNheight, 500,
        NULL);
    
    /* Создание простейшей панели меню */
    clears = XmStringCreateSimple("Clear");
    colourss = XmStringCreateSimple("Colour");
    
    menu_bar = XmVaCreateSimpleMenuBar(main_w,
        "main_list", XmVaCASCADEBUTTON, clears, 'C',
        XmVaCASCADEBUTTON, colourss, 'o',
       NULL);

    XtManageChild(menu_bar);

   /* Первое меню -- clear ; его процедура
    обратного вызова clear_call() */

    XmVaCreateSimplePulldownMenu(menu_bar,
        "clear_menu", 0, clear_call, XmVaPUSHBUTTON,
         clears, 'C', NULL, NULL, NULL);
    XmStringFree(clears);

     /* создание colour pull down menu */

    black = XmStringCreateSimple(colours[0]);
    red = XmStringCreateSimple(colours[1]);
    green = XmStringCreateSimple(colours[2]);
    blue = XmStringCreateSimple(colours[3]);
    grey = XmStringCreateSimple(colours[4]);
    white = XmStringCreateSimple(colours[5]);

    colour = XmVaCreateSimplePulldownMenu(menu_bar,
        "edit_menu", 1, colour_call,
        XmVaRADIOBUTTON, black, 'k', NULL, NULL,
        XmVaRADIOBUTTON, red, 'R', NULL, NULL,
        XmVaRADIOBUTTON, green, 'G', NULL, NULL,
        XmVaRADIOBUTTON, blue, 'B', NULL, NULL,
        XmVaRADIOBUTTON, grey, 'e', NULL, NULL,
        XmVaRADIOBUTTON, white, 'W', NULL, NULL,
        XmNradioBehavior, True,
        XmNradioAlwaysOne, True, NULL);

    XmStringFree(black);
    XmStringFree(red);
    XmStringFree(green);
    XmStringFree(blue);
    XmStringFree(grey);
    XmStringFree(white);
    /* установить по молчанию выбранным черный цвет */
    if ( widget = XtNameToWidget(colour,"button_0")) 
    XtVaSetValues(widget, XmNset, True, NULL);

    /* Создаем DrawingArea виджет. */
    /* создаем новые процедуры действия */

    actions.string = "draw_mouse";
    actions.proc = draw_mouse;
    XtAppAddActions(app, &actions, 1);

    draw = XtVaCreateWidget("draw",
     xmDrawingAreaWidgetClass, main_w,
     XmNtranslations,
     XtParseTranslationTable(translations),
     XmNbackground,
     WhitePixelOfScreen(XtScreen(main_w)),
     NULL);
    
    XtAddEventHandler( draw, ExposureMask, False,
            (XtEventHandler) restore_pixmap, NULL); 
    cmap = DefaultColormapOfScreen(XtScreen(draw));
    display = XtDisplay(draw);

     /* устанавливаем DrawingArea как
       "work area" главного окна */
    XtVaSetValues(main_w,
        XmNmenuBar,    menu_bar,
        XmNworkWindow, draw,
        NULL);

   /* Создаем графический контекст -- GC. Ассоциируем
   его с ресурсом XmNuserData виджета DrawingArea. */
    gcv.foreground = BlackPixelOfScreen(XtScreen(draw));
    gc = XCreateGC(XtDisplay(draw),
       RootWindowOfScreen(XtScreen(draw)),
        GCForeground, &gcv);

    XtManageChild(draw);
    
    scale =XtVaCreateManagedWidget("Scale",
    xmCascadeButtonWidgetClass, menu_bar,
    XmNmnemonic, 'S', NULL);
    XtAddCallback(scale, XmNactivateCallback,
    scale_pop_up,"Select the size of lines you draw.");
    quit = XtVaCreateManagedWidget("Quit",
    xmCascadeButtonWidgetClass, menu_bar, XmNmnemonic,
    'Q', NULL);
    XtAddCallback(quit, XmNactivateCallback,
    quit_pop_up, "Are you sure you want to quit?");
    Buttons = XtVaCreateManagedWidget("Buttons",
    xmCascadeButtonWidgetClass, menu_bar, XmNmnemonic,
    'B', NULL);
    XtAddCallback(Buttons, XmNactivateCallback,
    buttons_pop_up, NULL);
    file_select =  XtVaCreateManagedWidget("File",
               xmCascadeButtonWidgetClass, menu_bar,
               XmNmnemonic, 'F', NULL);
    XtAddCallback(file_select, XmNactivateCallback,
                              select_pop_up, NULL); 
    XtRealizeWidget(top_wid);
     
   pix_map = XCreatePixmap(display,
    RootWindowOfScreen(XtScreen(draw)), 500, 500, 8);
   /* 8 - цветной экран, изменить на 1
    если черно-белый*/
   clear_call;
   XtAppMainLoop(app);
  }

/* POP UPS */

void scale_pop_up(Widget cascade_button, char *text,
                  XmPushButtonCallbackStruct *cbs)
{   Widget dialog, a_scale;
    XmString title[2];
    void scale_cbk();
    Arg args[2];
    
    title[0] = XmStringCreateSimple(text);
    XtSetArg(args[0], XmNmessageString, title[0]);
    XtSetArg(args[1], XmNdefaultButtonType, XmDIALOG_OK_BUTTON);
    
    dialog = XmCreateInformationDialog(cascade_button, "prompt",
                                        args, 2);
    
    XmStringFree(title[0]);
    ScrubDial(dialog, XmDIALOG_HELP_BUTTON);
    ScrubDial(dialog, XmDIALOG_CANCEL_BUTTON);
    XtManageChild(dialog);
        
    title[1] = XmStringCreateSimple("Size");
    a_scale = XtVaCreateManagedWidget("scale",
            xmScaleWidgetClass, dialog,
            XmNtitleString,     title[1],
            XmNorientation,     XmHORIZONTAL,
            XmNvalue,           line_width,
            XmNmaximum,         11,
            XmNminimum,         1,
            XmNdecimalPoints,   0,
            XmNshowValue,       True,
            XmNwidth,           200, 
            XmNheight,          100,
            NULL);
    XtAddCallback(a_scale, XmNvalueChangedCallback,
    scale_cbk, NULL);
    XmStringFree(title[1]);
    
    XtPopup(XtParent(dialog), XtGrabNone);

}

void quit_pop_up(Widget cascade_button, char *text,
                  XmPushButtonCallbackStruct *cbs)
{   Widget dialog;
    XmString xm_string;
    void quit_call();
    Arg args[2];
    
    xm_string = XmStringCreateSimple(text);
    XtSetArg(args[0], XmNmessageString, xm_string);
    XtSetArg(args[1], XmNdefaultButtonType,
    XmDIALOG_CANCEL_BUTTON);
    
    dialog = XmCreateWarningDialog(cascade_button,
             "quit", args, 2);
    
    XmStringFree(xm_string);
    ScrubDial(dialog, XmDIALOG_HELP_BUTTON);
    XtAddCallback(dialog, XmNokCallback,
     quit_call, NULL);
    XtManageChild(dialog);

    XtPopup(XtParent(dialog), XtGrabNone);

}

void buttons_pop_up(Widget cascade_button, char *text,
                  XmPushButtonCallbackStruct *cbs)
{   Widget dialog, form, brush, action;
    XmString xm_string;
    void brush_call(), action_call();
       
    dialog = XmCreateBulletinBoardDialog(cascade_button,
             "buttons", NULL, 0);
    XtManageChild(dialog);
    
    form = XtVaCreateWidget("form", xmFormWidgetClass,
           dialog, XmNfractionBase, 3, NULL);
    brush = XtVaCreateManagedWidget("Solid/Empty",
           xmToggleButtonWidgetClass,
           form, XmNtopAttachment, XmATTACH_FORM,
           XmNleftAttachment, XmATTACH_FORM, NULL);
    XtAddCallback(brush, XmNvalueChangedCallback,
          brush_call, NULL);
    
    action = XtVaCreateManagedWidget(
     "Lines/Rectangles", xmToggleButtonWidgetClass,
     form, XmNtopAttachment, XmATTACH_WIDGET,
     XmNtopWidget, brush, XmNleftAttachment,
     XmATTACH_FORM, XmNbottomAttachment,
     XmATTACH_FORM, NULL);
    XtAddCallback(action, XmNvalueChangedCallback,
      action_call, NULL);
    
    XtManageChild(form);
    XtPopup(XtParent(dialog), XtGrabNone);
}

void select_pop_up(Widget cascade_button, char *text,
XmPushButtonCallbackStruct *cbs)

{   Widget dialog, remove;
    void select_activate(), cancel();
    XmString mask;
    Arg args[1];

    /* Create the FileSelectionDialog */
    mask  = XmStringCreateSimple("*.*");
    XtSetArg(args[0], XmNdirMask, mask);


    dialog = XmCreateFileSelectionDialog(cascade_button,
           "select", args, 1);
    XtAddCallback(dialog, XmNokCallback,
                  select_activate, NULL);
    XtAddCallback(dialog, XmNcancelCallback,
                  cancel, NULL);

    remove = XmSelectionBoxGetChild(dialog,
           XmDIALOG_HELP_BUTTON);

    XtUnmanageChild(remove); /* убираем кнопку HELP*/

    XtManageChild(dialog);
    XtPopup(XtParent(dialog), XtGrabNone);
}

void select_activate(Widget widget, caddr_t
     client_data, XmFileSelectionBoxCallbackStruct
     *selection)

{   
    FILE *fp, *fopen();
    char *filename, line[200];
    void error();

    XmStringGetLtoR(selection->value,
         XmSTRING_DEFAULT_CHARSET, &filename);

    if ( (fp = fopen(filename,"r")) == NULL)
      error(widget, "CANNOT OPEN FILE", filename);
      else
    {  
       pix_map = XmGetPixmap(XtScreen(draw), filename, 
       WhitePixelOfScreen(XtScreen(draw)),
       BlackPixelOfScreen(XtScreen(draw)));
       fclose(fp); XtUnmanageChild(widget);
    }
    
}

void cancel(Widget widget, caddr_t client_data,
XmFileSelectionBoxCallbackStruct *selection)

{ XtUnmanageChild(widget);  /* убираем виджет */ }

void error(Widget widget, char *s1, char * s2)
{
    Widget dialog;
    XmString xm_string;
    void help_cbk();
    Arg args[2];
    
    xm_string = XmStringCreateSimple(s2);
    XtSetArg(args[0], XmNmessageString, xm_string);
    XtSetArg(args[1], XmNdefaultButtonType,
            XmDIALOG_OK_BUTTON);
    
    dialog = XmCreateErrorDialog(widget,
             "error", args, 2);
    XtAddCallback( dialog, XmNhelpCallback,
             help_cbk, s1);   
    XmStringFree(xm_string); 
    ScrubDial(dialog, XmDIALOG_CANCEL_BUTTON);
    
    XtManageChild(dialog);
    XtPopup(XtParent(dialog), XtGrabNone);

}

/* CALL BACKS */

void help_cbk(Widget w, char* S1)
{   Widget dialog;
    XmString title;
    void scale_cbk();
    Arg args[2];
    
    title = XmStringCreateSimple(S1);
    XtSetArg(args[0], XmNmessageString, title);
    XtSetArg(args[1], XmNdefaultButtonType,
            XmDIALOG_OK_BUTTON);
    
    dialog = XmCreateInformationDialog(w, "help",
                                       args, 2);
    
    XmStringFree(title);
    ScrubDial(dialog, XmDIALOG_HELP_BUTTON);
    ScrubDial(dialog, XmDIALOG_CANCEL_BUTTON);
    XtManageChild(dialog);
        
    XtPopup(XtParent(dialog), XtGrabNone);

}
 
void quit_call()
{   
    XFreePixmap(display, pix_map);
    printf("Quitting program\n");
    exit(0);
} 

void clear_call() /* очищает work area*/
{  XClearWindow(display, XtWindow(draw)); 
   XCopyArea( display, XtWindow(draw), pix_map, gc,
               0, 0, 500, 500, 0, 0);
}

void brush_call(Widget widget, XtPointer client_data,
                XmToggleButtonCallbackStruct *state ) 
{ ifSolid = state->set; }

void scale_cbk(Widget widget, int data,
XmScaleCallbackStruct *scale_struct)
{ line_width = scale_struct->value;}

void action_call(Widget widget, XtPointer client_data,
                XmToggleButtonCallbackStruct *state ) 
{ actived = state->set; }

/* вызывается от любых пунктов меню "Colour". */
 
void colour_call(w, item_no)
Widget w;     
int item_no;  
{
    int n =0;
    Arg args[1];

    XColor xcolour, spare; 

    if (XAllocNamedColor(display, cmap,
         colours[item_no], &xcolour, &spare) == 0)
       return;
   
    fill_pixel = xcolour.pixel;
  }


/*  DrawingArea Callback.*/

void  draw_mouse(Widget w, XButtonEvent *event,
               String *args, int *num_args)

{   static Position x, y, last_x, last_y;
    Position width, height;
    int line_style;
    int cap_style = CapRound;
    int join_style = JoinRound;

    
        if (strcmp(args[0], "down") == 0)
          {  /* фиксируем начальную точку */
            x = event->x;
            y = event->y;
          }
       else
         if (strcmp(args[0], "motion") == 0)
          {  line_style = LineOnOffDash;
             XSetLineAttributes(event->display, gc,
              1, line_style, cap_style, join_style);

             gcv.foreground
               = WhitePixelOfScreen(XtScreen(w));

             XSetForeground(event->display, gc,
                            gcv.foreground);

             XSetFunction(event->display,
                          gc, GXinvert);
             
            if (actived == 1) {
            XDrawLine(event->display,
            event->window, gc, x, y, last_x, y);
            XDrawLine(event->display, event->window,
               gc, last_x, y, last_x, last_y);
            XDrawLine(event->display, event->window,
               gc, last_x, last_y, x, last_y);
            XDrawLine(event->display, event->window,
               gc, x, last_y, x, y); } else 
            XDrawLine(event->display, event->window,
               gc, x,y, last_x, last_y); 
            XCopyArea( event->display,
                event->window, pix_map, gc,
                0, 0, 500, 500, 0, 0);

            gcv.foreground
              = BlackPixelOfScreen(XtScreen(w));
            XSetForeground(event->display, gc,
                   gcv.foreground);

            if (actived == 1) 
          {XDrawLine(event->display, event->window,
                   gc, x, y, event->x, y);
            XDrawLine(event->display, event->window,
            gc, event->x, y, event->x, event->y);
            XDrawLine(event->display, event->window,
            gc, event->x, event->y, x, event->y);
            XDrawLine(event->display, event->window,
            gc, x, event->y, x, y);}
            else 
                  {XDrawLine(event->display,
                        event->window, gc,
                      x, y, event->x, event->y);}}

        else
         if (strcmp(args[0], "up") == 0)
          { /* рисуем полную линию */

       XSetFunction(event->display, gc, GXcopy);

          if (ifSolid == 0 ) line_style = LineSolid;
             else line_style = LineOnOffDash;
          if (actived == 1 ) line_style = LineSolid;
           /* устанавливаем атрибуты линии */

             XSetLineAttributes(event->display, gc,
       line_width, line_style, cap_style, join_style);
       XSetForeground(event->display, gc, fill_pixel);

       if (actived == 1 ) {
       XDrawLine(event->display, event->window, gc,
                  x, y, event->x, y);
      XDrawLine(event->display, event->window, gc,
           event->x, y, event->x, event->y);
      XDrawLine(event->display, event->window, gc,
           event->x, event->y, x, event->y);
      XDrawLine(event->display, event->window, gc,
           x, event->y, x, y);} else
    {XDrawLine(event->display, event->window, gc,
           x, y, event->x, event->y); } 

    if ((event->y) > y) height = event->y - y;
    else { height = y; y = event->y -y; }
    if ((event->x) > x) width = event->x - x;
    else { width = x; x = event->x -x; } 
    if (actived == 1)
    { if (ifSolid == 0) 
     { XFillRectangle(event->display, event->window,
      gc, x, y, width, height); } else
     { XDrawRectangle(event->display, event->window,
        gc, x, y, width, height); }
            }
    XCopyArea( event->display, event->window, pix_map, gc,
            0, 0, 500, 500, 0, 0);
       }
        last_x = event->x;
        last_y = event->y;
  }

/* восстановление содержания drawingarea */
void restore_pixmap(Widget drawidget,
 XtPointer pUserData, XEvent *userEvent,
 Boolean *pbContinue)
{
  XCopyArea(XtDisplay(drawidget), pix_map, 
  (XtWindow(drawidget)), gc, 0, 0, 500, 500, 0, 0);
}

/* исключение DialButton из диалога */

void ScrubDial(Widget wid, int dial)
{    Widget remove;
     remove = XmMessageBoxGetChild(wid, dial);
     XtUnmanageChild(remove);
} 

...домик на крыше...,поиск,гостевая книга,cv. Be free, use Linux!