/* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Graphics.c: misc convenience functions for drawing stuff */ #include "config.h" #include "defaults.h" #include "libs/fvwmlib.h" #include "libs/Picture.h" #include #include #include /* Define some standard constants that are not defined on QNX 4.25 */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef M_PI_2 #define M_PI_2 1.57079632679489661923 #endif /* Draws the relief pattern around a window * Draws a line_width wide rectangle from (x,y) to (x+w,y+h) i.e w+1 wide, * h+1 high * Draws end points assuming CAP_NOT_LAST style in GC * Draws anti-clockwise in case CAP_BUTT is the style and the end points overlap * Top and bottom lines come out full length, the sides come out 1 pixel less * This is so FvwmBorder windows have a correct bottom edge and the sticky lines * look like just lines */ static void do_relieve_rectangle( Display *dpy, Drawable d, int x, int y, int w, int h, GC ReliefGC, GC ShadowGC, int line_width, Bool use_alternate_shading) { XSegment* seg; int i; int a = (use_alternate_shading) ? 1 : 0; if (w <= 0 || h <= 0) { if (w < 0 || h < 0) { fprintf(stderr,"RelieveRectangle: called with w = %d, h = %d\n", w, h); } return; } seg = (XSegment*)safemalloc(sizeof(XSegment) * line_width); /* left side, from 0 to the lesser of line_width & just over half w */ for (i = 0; (i < line_width) && (i <= w / 2); i++) { seg[i].x1 = x+i; seg[i].y1 = y+i+a; seg[i].x2 = x+i; seg[i].y2 = y+h-i-1+a; } XDrawSegments(dpy, d, ReliefGC, seg, i); /* bottom */ for (i = 0; (i < line_width) && (i <= h / 2); i++) { seg[i].x1 = x+i+a; seg[i].y1 = y+h-i; seg[i].x2 = x+w-i-1+a; seg[i].y2 = y+h-i; } XDrawSegments(dpy, d, ShadowGC, seg, i); /* right */ for (i = 0; (i < line_width) && (i <= w / 2); i++) { seg[i].x1 = x+w-i; seg[i].y1 = y+h-i-a; seg[i].x2 = x+w-i; seg[i].y2 = y+i+1-a; } XDrawSegments(dpy, d, ShadowGC, seg, i); /* draw top segments */ for (i = 0; (i < line_width) && (i <= h / 2); i++) { seg[i].x1 = x+w-i-a; seg[i].y1 = y+i; seg[i].x2 = x+i+1-a; seg[i].y2 = y+i; } XDrawSegments(dpy, d, ReliefGC, seg, i); free(seg); } void RelieveRectangle(Display *dpy, Drawable d, int x,int y,int w,int h, GC ReliefGC, GC ShadowGC, int line_width) { do_relieve_rectangle( dpy, d, x, y, w, h, ReliefGC, ShadowGC, line_width, False); } void RelieveRectangle2(Display *dpy, Drawable d, int x,int y,int w,int h, GC ReliefGC, GC ShadowGC, int line_width) { do_relieve_rectangle( dpy, d, x, y, w, h, ReliefGC, ShadowGC, line_width, True); } /* Creates a pixmap that is a horizontally stretched version of the input * pixmap */ Pixmap CreateStretchXPixmap(Display *dpy, Pixmap src, int src_width, int src_height, int src_depth, int dest_width, GC gc) { int i; Pixmap pixmap; if (src_width < 0 || src_height < 0 || dest_width < 0) return None; pixmap = XCreatePixmap(dpy, src, dest_width, src_height, src_depth); if (pixmap) for (i = 0; i < dest_width; i++) XCopyArea(dpy, src, pixmap, gc, (i * src_width) / dest_width, 0, 1, src_height, i, 0); return pixmap; } /* Creates a pixmap that is a vertically stretched version of the input * pixmap */ Pixmap CreateStretchYPixmap(Display *dpy, Pixmap src, int src_width, int src_height, int src_depth, int dest_height, GC gc) { int i; Pixmap pixmap; if (src_height < 0 || src_depth < 0 || dest_height < 0) return None; pixmap = XCreatePixmap(dpy, src, src_width, dest_height, src_depth); if (pixmap) for (i = 0; i < dest_height; i++) XCopyArea(dpy, src, pixmap, gc, 0, (i * src_height) / dest_height, src_width, 1, 0, i); return pixmap; } /* Creates a pixmap that is a stretched version of the input * pixmap */ Pixmap CreateStretchPixmap(Display *dpy, Pixmap src, int src_width, int src_height, int src_depth, int dest_width, int dest_height, GC gc) { Pixmap pixmap = None; Pixmap temp_pixmap; if (src_width < 0 || src_height < 0 || src_depth < 0 || dest_width < 0) return None; temp_pixmap = CreateStretchXPixmap(dpy, src, src_width, src_height, src_depth, dest_width, gc); if (temp_pixmap) { pixmap = CreateStretchYPixmap(dpy, temp_pixmap, dest_width, src_height, src_depth, dest_height, gc); XFreePixmap(dpy, temp_pixmap); } return pixmap; } /* Creates a pixmap that is a tiled version of the input pixmap. */ Pixmap CreateTiledPixmap(Display *dpy, Pixmap src, int src_width, int src_height, int dest_width, int dest_height, int depth, GC gc) { int x; int y; Pixmap pixmap; if (src_width < 0 || src_height < 0 || dest_width < 0 || dest_height < 0) return None; pixmap = XCreatePixmap(dpy, src, dest_width, dest_height, depth); if (pixmap) { for (y = 0; y < dest_height; y += src_height) { for (x = 0; x < dest_width; x += src_width) { XCopyArea(dpy, src, pixmap, gc, 0, 0, src_width, src_height, x, y); } } } return pixmap; } /**************************************************************************** * * Returns True if the given type of gradient is supported. * ****************************************************************************/ Bool IsGradientTypeSupported(char type) { switch (toupper(type)) { case V_GRADIENT: case H_GRADIENT: case B_GRADIENT: case D_GRADIENT: case R_GRADIENT: case Y_GRADIENT: case S_GRADIENT: case C_GRADIENT: return True; default: fprintf(stderr, "%cGradient type is not supported\n", toupper(type)); return False; } } /**************************************************************************** * * Allocates a linear color gradient (veliaa@rpi.edu) * ****************************************************************************/ Pixel *AllocLinearGradient( char *s_from, char *s_to, int npixels, int skip_first_color) { Pixel *pixels; XColor from, to, c; float r; float dr; float g; float dg; float b; float db; int i; int got_all = 1; int div; if (npixels < 1) { fprintf(stderr, "AllocLinearGradient: Invalid number of pixels: %d\n", npixels); return NULL; } if (!s_from || !XParseColor(Pdpy, Pcmap, s_from, &from)) { fprintf(stderr, "Cannot parse color \"%s\"\n", s_from ? s_from : ""); return NULL; } if (!s_to || !XParseColor(Pdpy, Pcmap, s_to, &to)) { fprintf(stderr, "Cannot parse color \"%s\"\n", s_to ? s_to : ""); return NULL; } /* divisor must not be zero, hence this calculation */ div = (npixels == 1) ? 1 : npixels - 1; c = from; /* red part and step width */ r = from.red; dr = (float)(to.red - from.red); /* green part and step width */ g = from.green; dg = (float)(to.green - from.green); /* blue part and step width */ b = from.blue; db = (float)(to.blue - from.blue); pixels = (Pixel *)safemalloc(sizeof(Pixel) * npixels); memset(pixels, 0, sizeof(Pixel) * npixels); c.flags = DoRed | DoGreen | DoBlue; for (i = (skip_first_color) ? 1 : 0; i < npixels && div > 0; ++i) { c.red = (unsigned short)((int)(r + dr / (float)div * (float)i + 0.5)); c.green = (unsigned short)((int)(g + dg / (float)div * (float)i + 0.5)); c.blue = (unsigned short)((int)(b + db / (float)div * (float)i + 0.5)); if (!XAllocColor(Pdpy, Pcmap, &c)) { got_all = 0; } pixels[i] = c.pixel; } if (!got_all) { fprintf(stderr, "Cannot alloc color gradient %s to %s\n", s_from, s_to); } return pixels; } /**************************************************************************** * * Allocates a nonlinear color gradient (veliaa@rpi.edu) * ****************************************************************************/ Pixel *AllocNonlinearGradient( char *s_colors[], int clen[], int nsegs, int npixels) { Pixel *pixels = (Pixel *)safemalloc(sizeof(Pixel) * npixels); int i; int curpixel = 0; int *seg_end_colors; int seg_sum = 0; float color_sum = 0.0; if (nsegs < 1 || npixels < 2) { fprintf(stderr, "Gradients must specify at least one segment and two colors\n"); free(pixels); return NULL; } for (i = 0; i < npixels; i++) pixels[i] = 0; /* get total length of all segments */ for (i = 0; i < nsegs; i++) seg_sum += clen[i]; /* calculate the index of a segment's las color */ seg_end_colors = alloca(nsegs * sizeof(int)); if (nsegs == 1) { seg_end_colors[0] = npixels - 1; } else { for (i = 0; i < nsegs; i++) { color_sum += (float)(clen[i] * (npixels - 1)) / (float)(seg_sum); seg_end_colors[i] = (int)(color_sum + 0.5); } if (seg_end_colors[nsegs - 1] > npixels - 1) { fprintf(stderr, "BUG: (AllocNonlinearGradient): " "seg_end_colors[nsegs - 1] (%d) > npixels - 1 (%d)." " Gradient drawing aborted\n", seg_end_colors[nsegs - 1], npixels - 1); return NULL; } /* take care of rounding errors */ seg_end_colors[nsegs - 1] = npixels - 1; } for (i = 0; i < nsegs; ++i) { Pixel *p = NULL; int j; int n; int skip_first_color = (curpixel != 0); if (i == 0) n = seg_end_colors[0] + 1; else n = seg_end_colors[i] - seg_end_colors[i - 1] + 1; if (n > 1) { p = AllocLinearGradient( s_colors[i], s_colors[i + 1], n, skip_first_color); if (!p && (n - skip_first_color) != 0) { free(pixels); return NULL; } for (j = skip_first_color; j < n; ++j) pixels[curpixel + j] = p[j]; curpixel += n - 1; } if (p) { free(p); p = NULL; } if (curpixel != seg_end_colors[i]) { fprintf(stderr, "BUG: (AllocNonlinearGradient): " "nsegs %d, i %d, curpixel %d, seg_end_colors[i] = %d," " npixels %d, n %d\n", nsegs, i, curpixel, seg_end_colors[i],npixels,n); return NULL; } } return pixels; } /* Convenience function. Calls AllocNonLinearGradient to fetch all colors and * then frees the color names and the perc and color_name arrays. */ Pixel *AllocAllGradientColors(char *color_names[], int perc[], int nsegs, int ncolors) { Pixel *pixels = None; int i; /* grab the colors */ pixels = AllocNonlinearGradient(color_names, perc, nsegs, ncolors); for (i = 0; i <= nsegs; i++) { if (color_names[i]) free(color_names[i]); } free(color_names); free(perc); if (!pixels) { fprintf(stderr, "couldn't create gradient\n"); return None; } return pixels; } /* groks a gradient string and creates arrays of colors and percentages * returns the number of colors asked for (No. allocated may be less due * to the ColorLimit command). A return of 0 indicates an error */ unsigned int ParseGradient(char *gradient, char **rest, char ***colors_return, int **perc_return, int *nsegs_return) { char *item; char *orig; unsigned int npixels; char **s_colors; int *perc; int nsegs, i, sum; Bool is_syntax_error = False; /* get the number of colors specified */ if (rest) *rest = gradient; orig = gradient; if (GetIntegerArguments(gradient, &gradient, (int *)&npixels, 1) != 1 || npixels < 2) { fprintf( stderr, "ParseGradient: illegal number of colors in gradient: '%s'\n", orig); return 0; } /* get the starting color or number of segments */ gradient = GetNextToken(gradient, &item); if (gradient) { gradient = SkipSpaces(gradient, NULL, 0); } if (!gradient || !*gradient || !item) { fprintf(stderr, "Incomplete gradient style: '%s'\n", orig); if (item) free(item); if (rest) *rest = gradient; return 0; } if (GetIntegerArguments(item, NULL, &nsegs, 1) != 1) { /* get the end color of a simple gradient */ s_colors = (char **)safemalloc(sizeof(char *) * 2); perc = (int *)safemalloc(sizeof(int)); nsegs = 1; s_colors[0] = item; gradient = GetNextToken(gradient, &item); s_colors[1] = item; perc[0] = 100; } else { free(item); /* get a list of colors and percentages */ if (nsegs < 1) nsegs = 1; if (nsegs > MAX_GRADIENT_SEGMENTS) nsegs = MAX_GRADIENT_SEGMENTS; s_colors = (char **)safemalloc(sizeof(char *) * (nsegs + 1)); perc = (int *)safemalloc(sizeof(int) * nsegs); for (i = 0; !is_syntax_error && i <= nsegs; i++) { s_colors[i] = 0; gradient = GetNextToken(gradient, &s_colors[i]); if (i < nsegs) { if (GetIntegerArguments(gradient, &gradient, &perc[i], 1) != 1 || perc[i] <= 0) { /* illegal size */ perc[i] = 0; } } } if (s_colors[nsegs] == NULL) { fprintf( stderr, "ParseGradient: too few gradient segments: '%s'\n", orig); is_syntax_error = True; } } /* sanity check */ for (i = 0, sum = 0; !is_syntax_error && i < nsegs; ++i) { int old_sum = sum; sum += perc[i]; if (sum < old_sum) { /* integer overflow */ fprintf( stderr, "ParseGradient: multi gradient overflow: '%s'", orig); is_syntax_error = 1; break; } } if (is_syntax_error) { for (i = 0; i <= nsegs; ++i) { if (s_colors[i]) { free(s_colors[i]); } } free(s_colors); free(perc); if (rest) { *rest = gradient; } return 0; } /* sensible limits */ if (npixels < 2) npixels = 2; if (npixels > MAX_GRADIENT_COLORS) npixels = MAX_GRADIENT_COLORS; /* send data back */ *colors_return = s_colors; *perc_return = perc; *nsegs_return = nsegs; if (rest) *rest = gradient; return npixels; } /* Calculate the prefered dimensions of a gradient, based on the number of * colors and the gradient type. Returns False if the gradient type is not * supported. */ Bool CalculateGradientDimensions( Display *dpy, Drawable d, int ncolors, char type, unsigned int *width_ret, unsigned int *height_ret) { static unsigned int best_width = 0, best_height = 0; /* get the best tile size (once) */ if (!best_width) { if (!XQueryBestTile(dpy, d, 1, 1, &best_width, &best_height)) { best_width = 0; best_height = 0; } /* this is needed for buggy X servers like XFree 3.3.3.1 */ if (!best_width) best_width = 1; if (!best_height) best_height = 1; } switch (type) { case H_GRADIENT: *width_ret = ncolors; *height_ret = best_height; break; case V_GRADIENT: *width_ret = best_width; *height_ret = ncolors; break; case D_GRADIENT: case B_GRADIENT: /* diagonal gradients are rendered into a rectangle for which the * width plus the height is equal to ncolors + 1. The rectangle is square * when ncolors is odd and one pixel taller than wide with even numbers */ *width_ret = (ncolors + 1) / 2; *height_ret = ncolors + 1 - *width_ret; break; case S_GRADIENT: /* square gradients have the last color as a single pixel in the centre */ *width_ret = *height_ret = 2 * ncolors - 1; break; case C_GRADIENT: /* circular gradients have the first color as a pixel in each corner */ *width_ret = *height_ret = 2 * ncolors - 1; break; case R_GRADIENT: case Y_GRADIENT: /* swept types need each color to occupy at least one pixel at the edge */ /* get the smallest odd number that will provide enough */ for (*width_ret = 1; (double)(*width_ret - 1) * M_PI < (double)ncolors; *width_ret += 2) ; *height_ret = *width_ret; break; default: fprintf(stderr, "%cGradient not supported\n", type); return False; } return True; } /* Does the actual drawing of the pixmap. If the in_drawable argument is None, * a new pixmap of the given depth, width and height is created. If it is not * None the gradient is drawn into it. The d_width, d_height, d_x and d_y * describe the traget rectangle within the drawable. */ Drawable CreateGradientPixmap( Display *dpy, Drawable d, GC gc, int type, int g_width, int g_height, int ncolors, Pixel *pixels, Drawable in_drawable, int d_x, int d_y, int d_width, int d_height, XRectangle *rclip) { Pixmap pixmap = None; XImage *image; register int i, j; XGCValues xgcv; Drawable target; int t_x; int t_y; int t_width; int t_height; if (g_height < 0 || g_width < 0 || d_width < 0 || d_height < 0) return None; if (in_drawable == None) { /* create a pixmap to use */ pixmap = XCreatePixmap(dpy, d, g_width, g_height, Pdepth); if (pixmap == None) return None; target = pixmap; t_x = 0; t_y = 0; t_width = g_width; t_height = g_height; } else { target = in_drawable; t_x = d_x; t_y = d_y; t_width = d_width; t_height = d_height; } /* create an XImage structure */ image = XCreateImage(dpy, Pvisual, Pdepth, ZPixmap, 0, 0, t_width, t_height, Pdepth > 16 ? 32 : (Pdepth > 8 ? 16 : 8), 0); if (!image) { fprintf(stderr, "%cGradient couldn't get image\n", type); if (pixmap != None) XFreePixmap(dpy, pixmap); return None; } /* create space for drawing the image locally */ image->data = safemalloc(image->bytes_per_line * t_height); /* now do the fancy drawing */ switch (type) { case H_GRADIENT: { for (i = 0; i < t_width; i++) { register Pixel p = pixels[i * ncolors / t_width]; for (j = 0; j < t_height; j++) XPutPixel(image, i, j, p); } } break; case V_GRADIENT: { for (j = 0; j < t_height; j++) { register Pixel p = pixels[j * ncolors / t_height]; for (i = 0; i < t_width; i++) XPutPixel(image, i, j, p); } break; } case D_GRADIENT: { register int t_scale = t_width + t_height - 1; for (i = 0; i < t_width; i++) { for (j = 0; j < t_height; j++) XPutPixel(image, i, j, pixels[(i + j) * ncolors / t_scale]); } break; } case B_GRADIENT: { register int t_scale = t_width + t_height - 1; for (i = 0; i < t_width; i++) { for (j = 0; j < t_height; j++) XPutPixel(image, i, j, pixels[(i + (t_height - j - 1)) * ncolors / t_scale]); } break; } case S_GRADIENT: { register int t_scale = t_width * t_height; register int myncolors = ncolors * 2; for (i = 0; i < t_width; i++) { register int pi = min(i, t_width - 1 - i) * t_height; for (j = 0; j < t_height; j++) { register int pj = min(j, t_height - 1 - j) * t_width; XPutPixel(image, i, j, pixels[(min(pi, pj) * myncolors - 1) / t_scale]); } } } break; case C_GRADIENT: { register double t_scale = (double)(t_width * t_height) / sqrt(8); for (i = 0; i < t_width; i++) for (j = 0; j < t_height; j++) { register double x = (double)((2 * i - t_width) * t_height) / 4.0; register double y = (double)((t_height - 2 * j) * t_width) / 4.0; register double rad = sqrt(x * x + y * y); XPutPixel(image, i, j, pixels[(int)((rad * ncolors - 0.5) / t_scale)]); } break; } case R_GRADIENT: { register int w = t_width - 1; register int h = t_height - 1; /* g_width == g_height, both are odd, therefore x can be 0.0 */ for (i = 0; i <= w; i++) { for (j = 0; j <= h; j++) { register double x = (double)((2 * i - w) * h) / 4.0; register double y = (double)((h - 2 * j) * w) / 4.0; /* angle ranges from -pi/2 to +pi/2 */ register double angle; if (x != 0.0) { angle = atan(y / x); } else { angle = (y < 0) ? - M_PI_2 : M_PI_2; } /* extend to -pi/2 to 3pi/2 */ if (x < 0) angle += M_PI; /* move range from -pi/2:3*pi/2 to 0:2*pi */ if (angle < 0.0) angle += M_PI * 2.0; /* normalize to gradient */ XPutPixel(image,i,j, pixels[(int)(angle * M_1_PI * 0.5 * ncolors)]); } } } break; /* ************************************************************************* * The Yin Yang gradient style and the following code are: * Copyright 1999 Sir Boris. (email to sir_boris@bigfoot.com may be read by * his groom but is not guaranteed to elicit a response) * No restrictions are placed on this code, * as long as the copyright notice is preserved. * ************************************************************************/ case Y_GRADIENT: { register int r = t_width * t_height / 4; for (i = 0; i < t_width; i++) { for (j = 0; j < t_height; j++) { register double x = (double)((2 * i - t_width) * t_height) / 4.0; register double y = (double)((t_height - 2 * j) * t_width) / 4.0; register double rad = sqrt(x * x + y * y); /* angle ranges from -pi/2 to +pi/2 */ register double angle; if (x != 0.0) { angle = atan(y / x); } else { angle = (y < 0) ? - M_PI_2 : M_PI_2; } /* extend to -pi/2 to 3pi/2 */ if (x < 0) angle += M_PI; /* warp the angle within the yinyang circle */ if (rad <= r) { angle -= acos(rad / r); } /* move range from -pi/2:3*pi/2 to 0:2*pi */ if (angle < 0.0) angle += M_PI * 2.0; /* normalize to gradient */ XPutPixel(image,i,j, pixels[(int)(angle * M_1_PI * 0.5 * ncolors)]); } } } break; default: /* placeholder function, just fills the pixmap with the first color */ memset(image->data, 0, image->bytes_per_line * g_width); XAddPixel(image, pixels[0]); break; } /* set the gc style */ xgcv.function = GXcopy; xgcv.plane_mask = AllPlanes; xgcv.fill_style = FillSolid; xgcv.clip_mask = None; XChangeGC(dpy, gc, GCFunction|GCPlaneMask|GCFillStyle|GCClipMask, &xgcv); if (rclip) { XSetClipRectangles(dpy, gc, 0, 0, rclip, 1, Unsorted); } /* copy the image to the server */ XPutImage(dpy, target, gc, image, 0, 0, t_x, t_y, t_width, t_height); if (rclip) { XSetClipMask(dpy, gc, None); } XDestroyImage(image); return target; } /* Create a pixmap from a gradient specifier, width and height are hints * that are only used for gradients that can be tiled e.g. H or V types * types are HVDBSCRY for Horizontal, Vertical, Diagonal, Back-diagonal, Square, * Circular, Radar and Yin/Yang respectively (in order of bloatiness) */ Pixmap CreateGradientPixmapFromString(Display *dpy, Drawable d, GC gc, int type, char *action, unsigned int *width_return, unsigned int *height_return, Pixel **pixels_return, int *nalloc_pixels) { Pixel *pixels; unsigned int ncolors = 0; char **colors; int *perc, nsegs; Pixmap pixmap = None; /* set return pixels to NULL in case of premature return */ if (pixels_return) *pixels_return = NULL; if (nalloc_pixels) *nalloc_pixels = 0; /* translate the gradient string into an array of colors etc */ if (!(ncolors = ParseGradient(action, NULL, &colors, &perc, &nsegs))) { fprintf(stderr, "Can't parse gradient: '%s'\n", action); return None; } /* grab the colors */ pixels = AllocAllGradientColors(colors, perc, nsegs, ncolors); if (pixels == None) return None; /* grok the size to create from the type */ type = toupper(type); if (CalculateGradientDimensions( dpy, d, ncolors, type, width_return, height_return)) { pixmap = CreateGradientPixmap( dpy, d, gc, type, *width_return, *height_return, ncolors, pixels, None, 0, 0, 0, 0, NULL); } /* if the caller has not asked for the pixels there is probably a leak */ if (!pixels_return) { fprintf(stderr, "CreateGradient: potential color leak, losing track of pixels\n"); free(pixels); } else { *pixels_return = pixels; } if (nalloc_pixels) *nalloc_pixels = ncolors; return pixmap; } /**************************************************************************** * * Draws a little Triangle pattern within a window * ****************************************************************************/ void DrawTrianglePattern( Display *dpy, Drawable d, GC ReliefGC, GC ShadowGC, GC FillGC, int x, int y, int width, int height, int bw, char orientation, Bool draw_relief, Bool do_fill, Bool is_pressed) { const struct { const char line[3]; const char point[3]; } hi[4] = { { { 1, 0, 0 }, { 1, 1, 0 } }, /* up */ { { 1, 0, 1 }, { 1, 0, 0 } }, /* down */ { { 1, 0, 0 }, { 1, 1, 0 } }, /* left */ { { 1, 0, 1 }, { 1, 1, 0 } } /* right */ }; XPoint points[4]; GC temp_gc; int short_side; int long_side; int t_width; int t_height; int i; int type; /* remove border width from target area */ width -= 2 * bw; height -= 2 * bw; x += bw; y += bw; if (width < 1 || height < 1) /* nothing to do */ return; orientation = tolower(orientation); switch (orientation) { case 'u': case 'd': long_side = width; short_side = height; type = (orientation == 'd'); break; case 'l': case 'r': long_side = height; short_side = width; type = (orientation == 'r') + 2; break; default: /* unknowm orientation */ return; } /* assure the base side has an odd length */ if ((long_side & 0x1) == 0) long_side--; /* reduce base length if short sides don't fit */ if (short_side < long_side / 2 + 1) long_side = 2 * short_side - 1; else short_side = long_side / 2 + 1; if (orientation == 'u' || orientation == 'd') { t_width = long_side; t_height = short_side; } else { t_width = short_side; t_height = long_side; } /* find proper x/y coordinate */ x += (width - t_width) / 2; y += (height - t_height) / 2; /* decrement width and height for convenience of calculation */ t_width--; t_height--; /* get the list of points to draw */ switch (orientation) { case 'u': y += t_height; t_height = -t_height; case 'd': points[1].x = x + t_width / 2; points[1].y = y + t_height; points[2].x = x + t_width; points[2].y = y; break; case 'l': x += t_width; t_width = -t_width; case 'r': points[1].x = x + t_width; points[1].y = y + t_height / 2; points[2].x = x; points[2].y = y + t_height; break; } points[0].x = x; points[0].y = y; points[3].x = x; points[3].y = y; if (do_fill) { /* solid triangle */ XFillPolygon(dpy, d, FillGC, points, 3, Convex, CoordModeOrigin); } if (draw_relief) { /* relief triangle */ for (i = 0; i < 3; i++) { temp_gc = (is_pressed ^ hi[type].line[i]) ? ReliefGC : ShadowGC; XDrawLine(dpy, d, temp_gc, points[i].x, points[i].y, points[i+1].x, points[i+1].y); } for (i = 0; i < 3; i++) { temp_gc = (is_pressed ^ hi[type].point[i]) ? ReliefGC : ShadowGC; XDrawPoint(dpy, d, temp_gc, points[i].x, points[i].y); } } return; } GC fvwmlib_XCreateGC( Display *display, Drawable drawable, unsigned long valuemask, XGCValues *values) { GC gc; Bool f; XGCValues gcv; if (!values) values = &gcv; f = values->graphics_exposures; if (!(valuemask & GCGraphicsExposures)) { valuemask |= GCGraphicsExposures; values->graphics_exposures = 0; } gc = XCreateGC(display, drawable, valuemask, values); values->graphics_exposures = f; return gc; }