#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#include <Xm/MainW.h>
#include <Xm/Form.h>
#include <Xm/Frame.h>
#include <Xm/DrawingA.h>
#include <Xm/PushB.h>
#include <Xm/RowColumn.h>
#include <Xm/MessageB.h>
#include <Xm/Protocols.h>
#include <X11/Xmu/Editres.h>
#include <stdio.h>
#include <stdlib.h>
GC inputGC; /* GC used for final drawing and GC used for drawing current position */
Widget question;
int x1, y1, x2, y2; /* input points */
unsigned short button_pressed = 0; /* input state */
Colormap cmap;
short fill = 0;
XColor red, green, blue, black, white;
XColor *lineFgColor = &black;
XColor *lineBgColor = &white;
XColor *fillFgColor = &red;
XColor *fillBgColor = &blue;
unsigned int line_width = 0;
int line_style = LineSolid;
int cap_style = CapButt;
int join_style = JoinMiter;
typedef enum toolkit {POINT, LINE, RECTANGLE, ELLIPSE} toolkit;
toolkit tool = LINE;
struct drawing {
toolkit tool;
GC gc;
short fill;
XColor fillColor;
int x1, y1, x2, y2;
};
struct drawing *drawings = NULL;
size_t allocated = 1;
size_t ndrawings = 0;
/* Save drawing */
void saveDrawing(Widget w) {
ndrawings++;
if (allocated <= ndrawings) {
drawings = (struct drawing *) realloc(drawings, 2 * allocated * sizeof(struct drawing));
if (drawings != NULL) {
allocated *= 2;
} else {
fprintf(stderr, "Can't allocate more drawings!\n");
exit(1);
}
}
drawings[ndrawings - 1].gc = XCreateGC(XtDisplay(w), XtWindow(w), 0, NULL);
XSetForeground(XtDisplay(w), drawings[ndrawings - 1].gc, lineFgColor->pixel);
XSetBackground(XtDisplay(w), drawings[ndrawings - 1].gc, lineBgColor->pixel);
XCopyGC(XtDisplay(w), inputGC, GCLineWidth | GCLineStyle, drawings[ndrawings - 1].gc);
drawings[ndrawings - 1].tool = tool;
drawings[ndrawings - 1].x1 = x1;
drawings[ndrawings - 1].x2 = x2;
drawings[ndrawings - 1].y1 = y1;
drawings[ndrawings - 1].y2 = y2;
drawings[ndrawings - 1].fill = fill;
drawings[ndrawings - 1].fillColor = *fillFgColor;
drawings[ndrawings].gc = NULL; /* Sentinel */
}
/* Input event handler */
void DrawEH(Widget w, XtPointer client_data, XEvent *event, Boolean *cont) {
Pixel fg, bg;
if (button_pressed) {
if (!inputGC) {
inputGC = XCreateGC(XtDisplay(w), XtWindow(w), 0, NULL);
/*XSetPlaneMask(XtDisplay(w), inputGC, ~0);*/
}
XtVaGetValues(w, XmNforeground, &fg, XmNbackground, &bg, NULL);
XSetForeground(XtDisplay(w), inputGC, bg ^ lineFgColor->pixel);
XSetBackground(XtDisplay(w), inputGC, bg ^ lineBgColor->pixel);
XSetLineAttributes(XtDisplay(w), inputGC, line_width, line_style, cap_style, join_style);
if (button_pressed > 1) {
XSetFunction(XtDisplay(w), inputGC, GXxor);
/* Kreslime znovu ten samy objekt pres sebe, cimz ho smazeme */
switch (tool) {
case LINE:
XDrawLine(XtDisplay(w), XtWindow(w), inputGC, x1, y1, x2, y2);
break;
case RECTANGLE:
if (fill) {
XSetForeground(XtDisplay(w), inputGC, bg ^ fillFgColor->pixel);
XFillRectangle(XtDisplay(w), XtWindow(w), inputGC, (x1 < x2) ? x1 : x2, (y1 < y2) ? y1 : y2, abs(x2 - x1), abs(y2 - y1));
XSetForeground(XtDisplay(w), inputGC, bg ^ lineFgColor->pixel);
}
XDrawRectangle(XtDisplay(w), XtWindow(w), inputGC, (x1 < x2) ? x1 : x2, (y1 < y2) ? y1 : y2, abs(x2 - x1), abs(y2 - y1));
break;
case ELLIPSE:
if (fill) {
XSetForeground(XtDisplay(w), inputGC, bg ^ fillFgColor->pixel);
XFillArc(XtDisplay(w), XtWindow(w), inputGC, x1 - abs(x2 - x1), y1 - abs(y2 - y1), abs(x2 - x1) * 2, abs(y2 - y1) * 2, 0, 360 * 64);
XSetForeground(XtDisplay(w), inputGC, bg ^ lineFgColor->pixel);
}
XDrawArc(XtDisplay(w), XtWindow(w), inputGC, x1 - abs(x2 - x1), y1 - abs(y2 - y1), abs(x2 - x1) * 2, abs(y2 - y1) * 2, 0, 360 * 64);
break;
default:
break;
}
} else {
/* remember first MotionNotify */
button_pressed = 2;
}
x2 = event->xmotion.x;
y2 = event->xmotion.y;
switch (tool) {
case LINE:
XDrawLine(XtDisplay(w), XtWindow(w), inputGC, x1, y1, x2, y2);
break;
case RECTANGLE:
if (fill) {
XSetForeground(XtDisplay(w), inputGC, bg ^ fillFgColor->pixel);
XFillRectangle(XtDisplay(w), XtWindow(w), inputGC, (x1 < x2) ? x1 : x2, (y1 < y2) ? y1 : y2, abs(x2 - x1), abs(y2 - y1));
XSetForeground(XtDisplay(w), inputGC, bg ^ lineFgColor->pixel);
}
XDrawRectangle(XtDisplay(w), XtWindow(w), inputGC, (x1 < x2) ? x1 : x2, (y1 < y2) ? y1 : y2, abs(x2 - x1), abs(y2 - y1));
break;
case ELLIPSE:
if (fill) {
XSetForeground(XtDisplay(w), inputGC, bg ^ fillFgColor->pixel);
XFillArc(XtDisplay(w), XtWindow(w), inputGC, x1 - abs(x2 - x1), y1 - abs(y2 - y1), abs(x2 - x1) * 2, abs(y2 - y1) * 2, 0, 360 * 64);
XSetForeground(XtDisplay(w), inputGC, bg ^ lineFgColor->pixel);
}
XDrawArc(XtDisplay(w), XtWindow(w), inputGC, x1 - abs(x2 - x1), y1 - abs(y2 - y1), abs(x2 - x1) * 2, abs(y2 - y1) * 2, 0, 360 * 64);
break;
default:
saveDrawing(w);
XDrawPoint(XtDisplay(w), XtWindow(w), inputGC, x2, y2);
}
}
}
/* Save callback function */
void SaveCB(Widget w, XtPointer client_data, XtPointer call_data) {
XmDrawingAreaCallbackStruct *d = (XmDrawingAreaCallbackStruct*) call_data;
switch (d->event->type) {
case ButtonPress:
if (d->event->xbutton.button == Button1) {
button_pressed = 1;
x1 = d->event->xbutton.x;
y1 = d->event->xbutton.y;
}
break;
case ButtonRelease:
if (d->event->xbutton.button == Button1) {
saveDrawing(w);
button_pressed = 0;
XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True); /* Force expose */
}
break;
}
}
/* "Expose" callback function */
void ExposeCB(Widget w, XtPointer client_data, XtPointer call_data) {
size_t i = 0;
XGCValues lineColor;
for (; i < ndrawings; i++) {
x1 = drawings[i].x1;
x2 = drawings[i].x2;
y1 = drawings[i].y1;
y2 = drawings[i].y2;
if (drawings[i].fill) {
XGetGCValues(XtDisplay(w), drawings[i].gc, GCForeground, &lineColor);
}
switch (drawings[i].tool) {
case LINE:
XDrawLine(XtDisplay(w), XtWindow(w), drawings[i].gc, x1, y1, x2, y2);
break;
case RECTANGLE:
if (drawings[i].fill) {
XSetForeground(XtDisplay(w), drawings[i].gc, drawings[i].fillColor.pixel);
XFillRectangle(XtDisplay(w), XtWindow(w), drawings[i].gc, (x1 < x2) ? x1 : x2, (y1 < y2) ? y1 : y2, abs(x2 - x1), abs(y2 - y1));
XSetForeground(XtDisplay(w), drawings[i].gc, lineColor.foreground);
}
XDrawRectangle(XtDisplay(w), XtWindow(w), drawings[i].gc, (x1 < x2) ? x1 : x2, (y1 < y2) ? y1 : y2, abs(x2 - x1), abs(y2 - y1));
break;
case ELLIPSE:
if (drawings[i].fill) {
XSetForeground(XtDisplay(w), drawings[i].gc, drawings[i].fillColor.pixel);
XFillArc(XtDisplay(w), XtWindow(w), drawings[i].gc, x1 - abs(x2 - x1), y1 - abs(y2 - y1), abs(x2 - x1) * 2, abs(y2 - y1) * 2, 0, 360 * 64);
XSetForeground(XtDisplay(w), drawings[i].gc, lineColor.foreground);
}
XDrawArc(XtDisplay(w), XtWindow(w), drawings[i].gc, x1 - abs(x2 - x1), y1 - abs(y2 - y1), abs(x2 - x1) * 2, abs(y2 - y1) * 2, 0, 360 * 64);
break;
case POINT:
XDrawPoint(XtDisplay(w), XtWindow(w), drawings[i].gc, x1, y1);
XDrawPoint(XtDisplay(w), XtWindow(w), drawings[i].gc, x2, y2);
break;
default:
fprintf(stderr, "Unknown tool!\n");
}
}
}
/* "Clear" button callback function */
void ClearCB(Widget w, XtPointer client_data, XtPointer call_data) {
Widget wcd = (Widget) client_data;
ndrawings = 0;
XClearWindow(XtDisplay(wcd), XtWindow(wcd));
}
/* Nastaveni nastroje */
void setTool(Widget w, XtPointer newtool, XtPointer call_data) {
tool = (toolkit) newtool;
}
/* Nastaveni barvy popredi */
void setColor(Widget w, XtPointer color, XtPointer call_data) {
unsigned long pixel = (*((XColor**)color))->pixel; /* I can't touch the stars :'( */
if (pixel == black.pixel) {
*((XColor**) color) = &white;
} else if (pixel == white.pixel) {
*((XColor**) color) = &red;
} else if (pixel == red.pixel) {
*((XColor**) color) = &green;
} else if (pixel == green.pixel) {
*((XColor**) color) = &blue;
} else if (pixel == blue.pixel) {
*((XColor**) color) = &black;
}
/*fprintf(stderr, "%u %u %u\n", (*((XColor**)color))->red, (*((XColor**)color))->green, (*((XColor**)color))->blue);*/
XtVaSetValues(w, XtNforeground, (*((XColor**)color))->pixel, NULL);
}
/* Nastaveni tloustky cary */
void setLineWidth(Widget w, XtPointer client_data, XtPointer call_data) {
XmString label;
switch (line_width) {
case 0:
line_width = 3;
label = XmStringCreateSimple("Width 3");
XtVaSetValues(w, XmNlabelString, label, NULL);
break;
case 3:
line_width = 8;
label = XmStringCreateSimple("Width 8");
XtVaSetValues(w, XmNlabelString, label, NULL);
break;
default:
line_width = 0;
label = XmStringCreateSimple("Width 0");
XtVaSetValues(w, XmNlabelString, label, NULL);
}
XmStringFree(label);
}
/* Nastaveni stylu cary */
void setLineStyle(Widget w, XtPointer client_data, XtPointer call_data) {
XmString label;
switch (line_style) {
case LineSolid:
line_style = LineDoubleDash;
label = XmStringCreateSimple("LineDoubleDash");
XtVaSetValues(w, XmNlabelString, label, NULL);
break;
case LineDoubleDash:
line_style = LineSolid;
label = XmStringCreateSimple(" LineSolid ");
XtVaSetValues(w, XmNlabelString, label, NULL);
break;
default:
label = XmStringCreateSimple("");
}
XmStringFree(label);
}
/* Nastaveni vyplnovani */
void setFill(Widget w, XtPointer client_data, XtPointer call_data) {
XmString label;
if (fill) {
fill = 0;
label = XmStringCreateSimple("Transparent");
} else {
fill = 1;
label = XmStringCreateSimple(" Filled ");
}
XtVaSetValues(w, XmNlabelString, label, NULL);
XmStringFree(label);
}
/* "Quit" button callback function */
void quitCB(Widget w, XtPointer client_data, XtPointer call_data) {
XtManageChild(question);
}
/* Konecny callback */
void questionCB(Widget w, XtPointer client_data, XtPointer call_data) {
size_t i = 0;
if ((int)client_data) {
for (; i < allocated; i++) {
if (drawings != NULL && drawings[i].gc != NULL) {
XFreeGC(XtDisplay(w), drawings[i].gc);
} else {
/*fprintf(stderr, "%lu", i);*/
break;
}
}
if (inputGC != NULL) {
XFreeGC(XtDisplay(w), inputGC);
}
exit(0);
}
}
int main(int argc, char **argv) {
XtAppContext app_context;
XmString label;
Atom wm_delete;
Widget topLevel,
mainWin,
frame,
drawArea,
command_window,
toolbox,
pointBtn,
lineBtn,
rectBtn,
ellipseBtn,
colors,
lineFgBtn,
lineBgBtn,
fillFgBtn,
fillBgBtn,
style,
lineSizeBtn,
lineStyleBtn,
fillBtn,
sys,
quitBtn,
clearBtn;
char *fallback_resources[] = {
"*question.dialogTitle: MalĂĄ otĂĄzka",
"*question.messageString: Konec aplikace?",
"*question.okLabelString: Ano",
"*question.cancelLabelString: Ne",
"*question.messageAlignment: XmALIGNMENT_CENTER",
NULL
};
XtSetLanguageProc(NULL, (XtLanguageProc) NULL, NULL); /* Register the default language procedure */
topLevel = XtVaAppInitialize(
&app_context, /* Application context */
"Draw", /* Application class */
NULL, 0, /* command line option list */
&argc, argv, /* command line args */
fallback_resources, /* for missing app-defaults file */
XmNdeleteResponse, XmDO_NOTHING,
NULL); /* terminate varargs list */
mainWin = XtVaCreateManagedWidget(
"mainWin", /* widget name */
xmMainWindowWidgetClass, /* widget class */
topLevel, /* parent widget */
XmNcommandWindowLocation, XmCOMMAND_BELOW_WORKSPACE,
NULL); /* terminate varargs list */
frame = XtVaCreateManagedWidget(
"frame", /* widget name */
xmFrameWidgetClass, /* widget class */
mainWin, /* parent widget */
NULL); /* terminate varargs list */
drawArea = XtVaCreateManagedWidget(
"drawingArea", /* widget name */
xmDrawingAreaWidgetClass, /* widget class */
frame, /* parent widget */
XmNwidth, 320, /* set startup width */
XmNheight, 240, /* set startup height */
NULL); /* terminate varargs list */
command_window = XtVaCreateManagedWidget(
"Command window", /* widget name */
xmRowColumnWidgetClass, /* widget class */
mainWin, /* parent widget */
NULL); /* terminate varargs list */
toolbox = XtVaCreateManagedWidget(
"Toolbox", /* widget name */
xmRowColumnWidgetClass, /* widget class */
command_window, /* parent widget */
XmNentryAlignment, XmALIGNMENT_CENTER, /* alignment */
XmNorientation, XmHORIZONTAL, /* orientation */
XmNpacking, XmPACK_COLUMN, /* packing mode */
NULL); /* terminate varargs list */
colors = XtVaCreateManagedWidget(
"Color picker", /* widget name */
xmRowColumnWidgetClass, /* widget class */
command_window, /* parent widget */
XmNentryAlignment, XmALIGNMENT_CENTER, /* alignment */
XmNorientation, XmHORIZONTAL, /* orientation */
XmNpacking, XmPACK_COLUMN, /* packing mode */
NULL); /* terminate varargs list */
style = XtVaCreateManagedWidget(
"Style", /* widget name */
xmRowColumnWidgetClass, /* widget class */
command_window, /* parent widget */
XmNentryAlignment, XmALIGNMENT_CENTER, /* alignment */
XmNorientation, XmHORIZONTAL, /* orientation */
XmNpacking, XmPACK_COLUMN, /* packing mode */
NULL); /* terminate varargs list */
sys = XtVaCreateManagedWidget(
"System buttons", /* widget name */
xmRowColumnWidgetClass, /* widget class */
command_window, /* parent widget */
XmNentryAlignment, XmALIGNMENT_CENTER, /* alignment */
XmNorientation, XmHORIZONTAL, /* orientation */
XmNpacking, XmPACK_COLUMN, /* packing mode */
NULL); /* terminate varargs list */
clearBtn = XtVaCreateManagedWidget(
"Clear", /* widget name */
xmPushButtonWidgetClass, /* widget class */
sys, /* parent widget */
NULL); /* terminate varargs list */
pointBtn = XtVaCreateManagedWidget(
"Point", /* widget name */
xmPushButtonWidgetClass, /* widget class */
toolbox, /* parent widget */
NULL); /* terminate varargs list */
lineBtn = XtVaCreateManagedWidget(
"Line", /* widget name */
xmPushButtonWidgetClass, /* widget class */
toolbox, /* parent widget */
NULL); /* terminate varargs list */
rectBtn = XtVaCreateManagedWidget(
"Rectangle", /* widget name */
xmPushButtonWidgetClass, /* widget class */
toolbox, /* parent widget */
NULL); /* terminate varargs list */
ellipseBtn = XtVaCreateManagedWidget(
"Ellipse", /* widget name */
xmPushButtonWidgetClass, /* widget class */
toolbox, /* parent widget */
NULL); /* terminate varargs list */
lineFgBtn = XtVaCreateManagedWidget(
"LineFG", /* widget name */
xmPushButtonWidgetClass, /* widget class */
colors, /* parent widget */
NULL); /* terminate varargs list */
lineBgBtn = XtVaCreateManagedWidget(
"LineBG", /* widget name */
xmPushButtonWidgetClass, /* widget class */
colors, /* parent widget */
NULL); /* terminate varargs list */
fillFgBtn = XtVaCreateManagedWidget(
"FillFG", /* widget name */
xmPushButtonWidgetClass, /* widget class */
colors, /* parent widget */
NULL); /* terminate varargs list */
fillBgBtn = XtVaCreateManagedWidget(
"FillBG", /* widget name */
xmPushButtonWidgetClass, /* widget class */
colors, /* parent widget */
NULL); /* terminate varargs list */
label = XmStringCreateSimple("Width 0");
lineSizeBtn = XtVaCreateManagedWidget(
"LineSize", /* widget name */
xmPushButtonWidgetClass, /* widget class */
style, /* parent widget */
XmNlabelString, label,
NULL); /* terminate varargs list */
XmStringFree(label);
label = XmStringCreateSimple(" LineSolid ");
lineStyleBtn = XtVaCreateManagedWidget(
"LineStyle", /* widget name */
xmPushButtonWidgetClass, /* widget class */
style, /* parent widget */
XmNlabelString, label,
NULL); /* terminate varargs list */
XmStringFree(label);
label = XmStringCreateSimple("Transparent");
fillBtn = XtVaCreateManagedWidget(
"Fill", /* widget name */
xmPushButtonWidgetClass, /* widget class */
style, /* parent widget */
XmNlabelString, label,
NULL); /* terminate varargs list */
XmStringFree(label);
quitBtn = XtVaCreateManagedWidget(
"Quit", /* widget name */
xmPushButtonWidgetClass, /* widget class */
sys, /* parent widget */
NULL); /* terminate varargs list */
question = XmCreateQuestionDialog(topLevel, "question", NULL, 0);
XtVaSetValues(question, XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL, NULL);
XtUnmanageChild(XmMessageBoxGetChild(question, XmDIALOG_HELP_BUTTON));
XtAddCallback(question, XmNokCallback, questionCB, (XtPointer) 1);
/*XtAddCallback(question, XmNcancelCallback, questionCB, (XtPointer) 1);*/
wm_delete = XInternAtom(XtDisplay(topLevel), "WM_DELETE_WINDOW", False);
XmAddWMProtocolCallback(topLevel, wm_delete, quitCB, NULL);
XmActivateWMProtocol(topLevel, wm_delete);
XtAddEventHandler(topLevel, 0, True, _XEditResCheckMessages, NULL);
XmMainWindowSetAreas(mainWin, NULL, command_window, NULL, NULL, frame);
cmap = DefaultColormap(XtDisplay(drawArea), DefaultScreen(XtDisplay(drawArea)));
if (
!XAllocNamedColor(XtDisplay(drawArea), cmap, "red", &red, &red) ||
!XAllocNamedColor(XtDisplay(drawArea), cmap, "green", &green, &green) ||
!XAllocNamedColor(XtDisplay(drawArea), cmap, "black", &black, &black) ||
!XAllocNamedColor(XtDisplay(drawArea), cmap, "white", &white, &white) ||
!XAllocNamedColor(XtDisplay(drawArea), cmap, "blue", &blue, &blue)
) {
fprintf(stderr, "Can't alloc color!\n");
return 1;
}
XtVaSetValues(lineFgBtn, XtNforeground, lineFgColor->pixel, NULL);
XtVaSetValues(lineBgBtn, XtNforeground, lineBgColor->pixel, NULL);
XtVaSetValues(fillFgBtn, XtNforeground, fillFgColor->pixel, NULL);
XtVaSetValues(fillBgBtn, XtNforeground, fillBgColor->pixel, NULL);
XtAddCallback(drawArea, XmNinputCallback, SaveCB, drawArea);
XtAddEventHandler(drawArea, ButtonMotionMask, False, DrawEH, NULL);
XtAddCallback(drawArea, XmNexposeCallback, ExposeCB, drawArea);
XtAddCallback(clearBtn, XmNactivateCallback, ClearCB, drawArea);
XtAddCallback(lineFgBtn, XmNactivateCallback, setColor, &lineFgColor);
XtAddCallback(lineBgBtn, XmNactivateCallback, setColor, &lineBgColor);
XtAddCallback(fillFgBtn, XmNactivateCallback, setColor, &fillFgColor);
XtAddCallback(fillBgBtn, XmNactivateCallback, setColor, &fillBgColor);
XtAddCallback(lineSizeBtn, XmNactivateCallback, setLineWidth, NULL);
XtAddCallback(lineStyleBtn, XmNactivateCallback, setLineStyle, NULL);
XtAddCallback(fillBtn, XmNactivateCallback, setFill, NULL);
XtAddCallback(pointBtn, XmNactivateCallback, setTool, (XtPointer)POINT);
XtAddCallback(lineBtn, XmNactivateCallback, setTool, (XtPointer)LINE);
XtAddCallback(rectBtn, XmNactivateCallback, setTool, (XtPointer)RECTANGLE);
XtAddCallback(ellipseBtn, XmNactivateCallback, setTool, (XtPointer)ELLIPSE);
XtAddCallback(quitBtn, XmNactivateCallback, quitCB, NULL);
XtRealizeWidget(topLevel);
XtAppMainLoop(app_context);
return 0;
}