/* * Copyright (c) 2002 Alban G. Hertroys * * libDockapp example - Usage of action rectangles * * Some clickable and draggable areas for you to play * with. * * There's a bit much in here... */ /* The cursor font - stamdard cursor glyphs. */ #include /* Required because we don't use a pixmap for the shape (using a DASetShape * variation). Instead we use the XLib call "XShapeCombineRegion". * Window shapes are an extension (since X11R5). */ #include #include /* already includes Xlib, Xresources, XPM, stdlib and stdio */ /* * Type definitions */ /* I like to keep my graphic contexts (GCs) together */ struct Colors { GC white; /* foreground color from X-resource, or default */ GC black; /* background color, idem */ GC lightGray; /* Some GC's we'll have to define for colors */ GC darkGray; GC slider; /* draw-color when not highlighted, * dark-color when highlighted */ GC sliderLight; /* draw-color when highlighted */ GC sliderDark; /* dark-color when not highlighted */ }; /* * Global variables */ Pixmap pixmap; /* pixmap pixmap */ DARect *buttonDown = NULL; struct Colors *colors; /* our colors */ DAActionRect **actionRects; float sliderPos = 0.7; int mouseIn = 0; Cursor pointer; /* * Prototypes for local functions */ struct Colors* setGCs(Drawable d); unsigned long adjustColor(unsigned long color, signed int adjustment); /* drawing routines */ void createBtn(DARect rect); void drawRaisedFrame(DARect rect); void drawSunkenFrame(DARect rect); void createSquare(DARect rect); void drawSquare(DARect rect, int button); void createSlider(DARect rect); void drawSlider(DARect rect); void setPointerColor(GC); /* utility functions */ DAActionRect setRectAction(DARect rect, DARectCallback action); /* event handlers functions */ void destroy(void); void buttonPress(int button, int state, int x, int y); void buttonRelease(int button, int state, int x, int y); void mouseMove(int x, int y); void mouseEnter(void); void mouseLeave(void); /* what to do for a specific event for every 'item' in the dockapp */ /* Button that can be pressed "down" and jumps back "up" again */ void btnDown(int x, int y, DARect rect, void *data); void btnUp(int x, int y, DARect rect, void *data); void btnLeave(int x, int y, DARect rect, void *data); /* Square that tells which button was pressed (number) */ void squareDown(int x, int y, DARect rect, void *data); /* A draggable slider that highlights when the mouse is over it */ void sliderDown(int x, int y, DARect rect, void *data); void sliderUp(int x, int y, DARect rect, void *data); void sliderMove(int x, int y, DARect rect, void *data); void sliderEnter(int x, int y, DARect rect, void *data); void sliderLeave(int x, int y, DARect rect, void *data); /* * M A I N */ int main(int argc, char **argv) { /* define the event handlers for the events */ DACallbacks eventCallbacks = { destroy, /* destroy */ buttonPress, /* buttonPress */ buttonRelease, /* buttonRelease */ mouseMove, /* motion (mouse) */ mouseEnter, /* mouse enters window */ mouseLeave, /* mouse leaves window */ NULL /* timeout */ }; /* define regions (x, y, width, height) that need event-handling */ Region clipRegion = XCreateRegion(); DARect btn = {0, 0, 16, 16}, square = {0, 25, 22, 22}, slider = {24, 0, 23, 48}; /* define what to do if an event occurs in a rectangle */ DAActionRect *buttonPressRects, *buttonReleaseRects, *mouseMoveRects, *mouseEnterRects, *mouseLeaveRects; buttonPressRects = malloc(3 * sizeof(DAActionRect)); buttonPressRects[0] = setRectAction(btn, btnDown); buttonPressRects[1] = setRectAction(square, squareDown); buttonPressRects[2] = setRectAction(slider, sliderDown); buttonReleaseRects = malloc(2 * sizeof(DAActionRect)); buttonReleaseRects[0] = setRectAction(btn, btnUp); buttonReleaseRects[1] = setRectAction(slider, sliderUp); mouseMoveRects = malloc(sizeof(DAActionRect)); mouseMoveRects[0] = setRectAction(slider, sliderMove); mouseEnterRects = malloc(sizeof(DAActionRect)); mouseEnterRects[0] = setRectAction(slider, sliderEnter); mouseLeaveRects = malloc(2 * sizeof(DAActionRect)); mouseLeaveRects[0] = setRectAction(btn, btnLeave); mouseLeaveRects[1] = setRectAction(slider, sliderLeave); /* XXX: make action rectangles available outside main() * ...libDockapp should be able to do this... (reminder) */ actionRects = malloc(6 * sizeof(DAActionRect*)); actionRects[0] = buttonPressRects; actionRects[1] = buttonReleaseRects; actionRects[2] = mouseMoveRects; actionRects[3] = mouseEnterRects; actionRects[4] = mouseLeaveRects; actionRects[5] = NULL; /* provide standard command-line options */ DAParseArguments( argc, argv, /* Where the options come from */ NULL, 0, /* Our list with options - none as you can see */ "This is the help text for the rectangle example of how to " "use libDockapp.\n", "Rectangle example version 1.0"); /* Tell libdockapp what version we expect it to be, so that you can use * older programs with newer versions of libdockapp with less risc for * compatibility problems. */ DASetExpectedVersion(20030126); /* Initialize a dockapp */ DAInitialize( "", /* Use default display */ "daRectangleExample", /* WM_CLASS hint; don't use chars in [.?*: ] */ 48, 48, /* geometry of dockapp internals */ argc, argv /* (needed internally) */ ); /* Create a pixmap to draw on, and to display */ pixmap = DAMakePixmap(); /* size == dockapp geometry (48,48) */ colors = setGCs(pixmap); XFillRectangle(DADisplay, pixmap, DAClearGC, 0, 0, 48, 48); XClearWindow(DADisplay, DAWindow); /* Make a "Region" from the shapes we have */ XUnionRectWithRegion(&btn, clipRegion, clipRegion); XUnionRectWithRegion(&square, clipRegion, clipRegion); XUnionRectWithRegion(&slider, clipRegion, clipRegion); /* Make this region a window shape mask */ XShapeCombineRegion(DADisplay, DAWindow, ShapeBounding, 0, 0, clipRegion, ShapeSet); /* We don't need the region anymore (it is copied by XShapeCombineRegion). * XXX: That's not certain, it is not documented. XSetRegion does so, * though, so it is a likely assumption that it does copy. */ XDestroyRegion(clipRegion); /* The cursor we want to use. * Specify 'None' for the default, * or one from X11/cursorfont.h */ pointer = XCreateFontCursor(DADisplay, XC_based_arrow_up); XDefineCursor(DADisplay, DAWindow, pointer); /* a square with an image that changes when clicked (A button). */ createBtn(btn); /* a square that shows the number of the mouse-button pressed on click. */ createSquare(square); /* a slider a using two dashed line GC's. */ createSlider(slider); /* Tell libdockapp this is the pixmap that we want to show */ DASetPixmap(pixmap); /* Process events every 100ms */ DASetTimeout(100); /* set event callbacks */ DASetCallbacks(&eventCallbacks); /* Display the pixmap we said it to show */ DAShow(); /* Process events and keep the dockapp running */ DAEventLoop(); return 0; } /* Create our GC's to draw colored lines and such */ struct Colors* setGCs(Drawable d) { struct Colors *colors; XGCValues gcv; unsigned long origColor; char dashList[2] = {3, 1}; colors = malloc(sizeof(struct Colors)); if (colors == NULL) return NULL; /* Get the GC-values of the default GC */ XGetGCValues(DADisplay, DAGC, GCForeground|GCBackground|GCGraphicsExposures, &gcv); origColor = gcv.foreground; /* don't send expose events */ gcv.graphics_exposures = False; /* GC for white color */ gcv.foreground = WhitePixel(DADisplay, DefaultScreen(DADisplay)); colors->white = XCreateGC(DADisplay, d, GCForeground|GCGraphicsExposures, &gcv); /* GC for dark blue color */ #if 1 gcv.foreground = DAGetColor("navy"); #else gcv.foreground = 0; #endif colors->black = XCreateGC(DADisplay, d, GCForeground|GCGraphicsExposures, &gcv); /* GC for light borders */ gcv.foreground = DAGetColor("lightGray"); colors->lightGray = XCreateGC(DADisplay, d, GCForeground|GCGraphicsExposures, &gcv); /* GC for dark borders (note re-use of gcv-values) */ gcv.foreground = DAGetColor("#222222"); colors->darkGray = XCreateGC(DADisplay, d, GCForeground|GCGraphicsExposures, &gcv); /* GC for the un-/highlighted colors and dashed line of the slider */ gcv.foreground = origColor; gcv.line_width = 9; gcv.line_style = LineOnOffDash; colors->slider = XCreateGC(DADisplay, d, GCForeground|GCBackground|GCGraphicsExposures| GCLineWidth|GCLineStyle, &gcv); XSetDashes(DADisplay, colors->slider, 1, dashList, 2); /* light slider GC */ gcv.foreground = adjustColor(origColor, +0x40); colors->sliderLight = XCreateGC(DADisplay, d, GCForeground|GCBackground|GCGraphicsExposures| GCLineWidth|GCLineStyle, &gcv); XSetDashes(DADisplay, colors->sliderLight, 1, dashList, 2); /* dark slider GC */ gcv.foreground = adjustColor(origColor, -0x40); colors->sliderDark = XCreateGC(DADisplay, d, GCForeground|GCBackground|GCGraphicsExposures| GCLineWidth|GCLineStyle, &gcv); XSetDashes(DADisplay, colors->sliderDark, 1, dashList, 2); return colors; } /* Make a (GC) color lighter or darker */ unsigned long adjustColor(unsigned long color, signed int adjustment) { signed long r, g, b; r = color >> 16; g = (color - (r << 16)) >> 8; b = (color - (g << 8) - (r << 16)); r += adjustment; g += adjustment; b += adjustment; if (r > 0xff) r = 0xff; if (g > 0xff) g = 0xff; if (b > 0xff) b = 0xff; if (r < 0) r = 0; if (g < 0) g = 0; if (b < 0) b = 0; return ((unsigned short)r << 16) + ((unsigned short)g << 8) + (unsigned short)b; } void setPointerColor(GC color) { XGCValues gcv; XColor fcolor, bcolor; XGetGCValues(DADisplay, color, GCForeground, &gcv); fcolor.pixel = gcv.foreground; fcolor.flags = DoRed|DoGreen|DoBlue; bcolor.red = 0; bcolor.green = 0; bcolor.blue = 0; XRecolorCursor(DADisplay, pointer, &fcolor, &bcolor); } /* event handlers functions */ void destroy(void){} void buttonPress(int button, int state, int x, int y) { int *data = malloc(sizeof(int*)); *data = button; DAProcessActionRects(x, y, actionRects[0], 3, (void*)data); free(data); } void buttonRelease(int button, int state, int x, int y) { DAProcessActionRects(x, y, actionRects[1], 2, NULL); } void mouseMove(int x, int y) { DAProcessActionRects(x, y, actionRects[2], 1, NULL); } void mouseEnter(void) { mouseIn = 1; drawSlider(actionRects[3][0].rect); } void mouseLeave(void) { mouseIn = 0; /* mouse pointer left the dockapp window */ DAProcessActionRects(0, 0, actionRects[4], 2, NULL); /* if the button is still depressed, make it go up again. */ /* TODO: Use data in actionRects[4] here instead of below check */ if (buttonDown != NULL) { btnUp(0, 0, *buttonDown, NULL); } drawSlider(actionRects[4][1].rect); } /* what to do for a specific event for every 'item' in the dockapp */ /* Button that can be pressed "down" and jumps back "up" again */ void btnDown(int x, int y, DARect rect, void *data) { buttonDown = ▭ drawSunkenFrame(rect); } void btnUp(int x, int y, DARect rect, void *data) { buttonDown = NULL; drawRaisedFrame(rect); } void btnLeave(int x, int y, DARect rect, void *data) { if (buttonDown == NULL) return; drawRaisedFrame(rect); } /* Square that tells which button was pressed (number) */ void squareDown(int x, int y, DARect rect, void *data) { int button; if (data) { int *tmp = (int*)data; button = *tmp; } else button = 0; drawSquare(rect, button); } /* A draggable slider that highlights when the mouse is over it */ void sliderDown(int x, int y, DARect rect, void *data) { buttonDown = ▭ setPointerColor(colors->sliderDark); } void sliderUp(int x, int y, DARect rect, void *data) { buttonDown = NULL; setPointerColor(colors->black); } void sliderMove(int x, int y, DARect rect, void *data) { if (buttonDown == NULL /* || rect.x != buttonDown->x || rect.y != buttonDown->y || rect.width != buttonDown->width || rect.height != buttonDown->height */) { return; } sliderPos = (float)(rect.height - y)/(float)rect.height; if (sliderPos > 1.0) sliderPos = 1.0; if (sliderPos < 0.0) sliderPos = 0.0; drawSlider(rect); } void sliderEnter(int x, int y, DARect rect, void *data) {} void sliderLeave(int x, int y, DARect rect, void *data) {} /* * Drawing functions */ void createBtn(DARect rect) { /* fill square excluding borders */ XFillRectangle(DADisplay, pixmap, colors->lightGray, rect.x +1, rect.y +1, rect.width -2, rect.height -2); drawRaisedFrame(rect); } void drawRaisedFrame(DARect rect) { /* left border */ XDrawLine(DADisplay, pixmap, colors->white, rect.x, rect.y, rect.x, rect.y + rect.height -2); /* top border */ XDrawLine(DADisplay, pixmap, colors->white, rect.x +1, rect.y, rect.width -1, rect.y); /* bottom border */ XDrawLine(DADisplay, pixmap, colors->darkGray, rect.x, rect.y + rect.height -1, rect.x + rect.width -1, rect.y + rect.height -1); /* right border */ XDrawLine(DADisplay, pixmap, colors->darkGray, rect.x + rect.width -1, rect.y +1, rect.x + rect.width -1, rect.y + rect.height -2); DASetPixmap(pixmap); } void drawSunkenFrame(DARect rect) { /* left border */ XDrawLine(DADisplay, pixmap, colors->darkGray, rect.x, rect.y, rect.x, rect.y + rect.height -2); /* top border */ XDrawLine(DADisplay, pixmap, colors->darkGray, rect.x +1, rect.y, rect.width -1, rect.y); /* bottom border */ XDrawLine(DADisplay, pixmap, colors->white, rect.x, rect.y + rect.height -1, rect.x + rect.width -1, rect.y + rect.height -1); /* right border */ XDrawLine(DADisplay, pixmap, colors->white, rect.x + rect.width -1, rect.y +1, rect.x + rect.width -1, rect.y + rect.height -2); DASetPixmap(pixmap); } void createSquare(DARect rect) { /* fill square excluding borders */ XFillRectangle(DADisplay, pixmap, colors->black, rect.x +1, rect.y +1, rect.width -2, rect.height -2); XDrawRectangle(DADisplay, pixmap, colors->lightGray, rect.x, rect.y, rect.width -1, rect.height -1); drawSquare(rect, 0); } void drawSquare(DARect rect, int button) { char label[3]; XFillRectangle(DADisplay, pixmap, colors->black, rect.x +1, rect.y +1, rect.width -2, rect.height -2); snprintf(label, 3, "%2d", button); XDrawString(DADisplay, pixmap, colors->white, rect.x +3, rect.y + rect.height -5, label, 2); DASetPixmap(pixmap); } void createSlider(DARect rect) { /* fill square excluding borders */ XFillRectangle(DADisplay, pixmap, colors->black, rect.x +1, rect.y +1, rect.width -2, rect.height -2); drawSunkenFrame(rect); drawSlider(rect); } void drawSlider(DARect rect) { GC highColor, lowColor; /* determine colors to use */ if (mouseIn) { highColor = colors->sliderLight; lowColor = colors->slider; } else { highColor = colors->slider; lowColor = colors->sliderDark; } /* Draw two lines from bottom to sliderPos fraction of height */ if (sliderPos < 1.0) { XDrawLine(DADisplay, pixmap, highColor, rect.x + 6, rect.y + rect.height -2, rect.x + 6, rect.y + (1.0 - sliderPos) * (rect.height -2)); XDrawLine(DADisplay, pixmap, highColor, rect.x + 17, rect.y + rect.height -2, rect.x + 17, rect.y + (1.0 - sliderPos) * (rect.height -2)); } if (sliderPos > 0.0) { XDrawLine(DADisplay, pixmap, lowColor, rect.x + 6, rect.y +1, rect.x + 6, rect.y + (1.0 - sliderPos) * (rect.height -2)); XDrawLine(DADisplay, pixmap, lowColor, rect.x + 17, rect.y +1, rect.x + 17, rect.y + (1.0 - sliderPos) * (rect.height -2)); } DASetPixmap(pixmap); } DAActionRect setRectAction(DARect rect, DARectCallback action) { DAActionRect ar; ar.rect = rect; ar.action = action; return ar; }