Fix move command parsing.
[fvwm.git] / libs / Graphics.c
blobfa45ef99dafe3bbe30a07926c1eb343fed983a85
1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 /* Graphics.c: misc convenience functions for drawing stuff */
19 /* ---------------------------- included header files ---------------------- */
21 #include "config.h"
23 #include <X11/Xlib.h>
24 #include <stdio.h>
25 #include <math.h>
27 #include "defaults.h"
28 #include "libs/fvwmlib.h"
29 #include "libs/Parse.h"
30 #include "libs/PictureBase.h"
31 #include "libs/PictureUtils.h"
32 #include "libs/PictureGraphics.h"
33 #include "libs/gravity.h"
34 #include "libs/FImage.h"
35 #include "libs/Graphics.h"
37 /* ---------------------------- local definitions -------------------------- */
39 /* Define some standard constants that are not included in the C89 standard */
40 #ifndef M_PI
41 #define M_PI 3.14159265358979323846
42 #endif
43 #ifndef M_PI_2
44 #define M_PI_2 1.57079632679489661923
45 #endif
46 #ifndef M_1_PI
47 #define M_1_PI 0.31830988618379067154
48 #endif
50 /* ---------------------------- local macros ------------------------------- */
52 /* ---------------------------- imports ------------------------------------ */
54 /* ---------------------------- included code files ------------------------ */
56 /* ---------------------------- local types -------------------------------- */
58 /* ---------------------------- forward declarations ----------------------- */
60 /* ---------------------------- local variables ---------------------------- */
62 /* ---------------------------- exported variables (globals) --------------- */
64 /* ---------------------------- local functions ---------------------------- */
66 /* ---------------------------- interface functions ------------------------ */
68 /* Draws the relief pattern around a window
69 * Draws a line_width wide rectangle from (x,y) to (x+w,y+h) i.e w+1 wide,
70 * h+1 high
71 * Draws end points assuming CAP_NOT_LAST style in GC
72 * Draws anti-clockwise in case CAP_BUTT is the style and the end points overlap
73 * Top and bottom lines come out full length, the sides come out 1 pixel less
74 * This is so FvwmBorder windows have a correct bottom edge and the sticky lines
75 * look like just lines
76 * rotation rotate the relief and shadow part
78 void do_relieve_rectangle_with_rotation(
79 Display *dpy, Drawable d, int x, int y, int w, int h,
80 GC ReliefGC, GC ShadowGC, int line_width, Bool use_alternate_shading,
81 int rotation)
83 XSegment* seg;
84 GC shadow_gc, relief_gc;
85 int i,i2;
86 int a;
87 int l;
88 int max_w;
89 int max_h;
91 a = (use_alternate_shading) ? 1 : 0;
92 l = 1 - a;
93 if (w <= 0 || h <= 0)
95 return;
97 /* If line_width is negative, reverse the rotation, which will */
98 /* have the effect of inverting the relief. */
99 if (line_width < 0)
101 line_width = -line_width;
102 rotation = gravity_add_rotations(rotation, ROTATION_180);
104 switch (rotation)
106 case ROTATION_180:
107 case ROTATION_270:
108 rotation = gravity_add_rotations(rotation, ROTATION_180);
109 shadow_gc = ReliefGC;
110 relief_gc = ShadowGC;
111 break;
112 default:
113 shadow_gc = ShadowGC;
114 relief_gc = ReliefGC;
115 break;
117 max_w = min((w + 1) / 2, line_width);
118 max_h = min((h + 1) / 2, line_width);
119 seg = (XSegment*)alloca((sizeof(XSegment) * line_width) * 2);
120 /* from 0 to the lesser of line_width & just over half w */
121 for (i = 0; i < max_w; i++)
123 if (rotation == ROTATION_0)
125 /* left */
126 seg[i].x1 = x+i; seg[i].y1 = y+i+a;
127 seg[i].x2 = x+i; seg[i].y2 = y+h-i+a;
129 else /* ROTATION_90 */
131 /* right */
132 seg[i].x1 = x+w-i; seg[i].y1 = y+h-i-a;
133 seg[i].x2 = x+w-i; seg[i].y2 = y+i+1-a;
136 i2 = i;
137 /* draw top segments */
138 for (i = 0; i < max_h; i++,i2++)
140 seg[i2].x1 = x+w-i-a; seg[i2].y1 = y+i;
141 seg[i2].x2 = x+i+1-a; seg[i2].y2 = y+i;
143 XDrawSegments(dpy, d, relief_gc, seg, i2);
144 /* bottom */
145 for (i = 0; i < max_h; i++)
147 seg[i].x1 = x+i+a+l; seg[i].y1 = y+h-i;
148 seg[i].x2 = x+w-i-1+a; seg[i].y2 = y+h-i;
150 i2 = i;
151 for (i = 0; i < max_w; i++,i2++)
153 if (rotation == ROTATION_0)
155 /* right */
156 seg[i2].x1 = x+w-i; seg[i2].y1 = y+h-i-a;
157 seg[i2].x2 = x+w-i; seg[i2].y2 = y+i+1-a;
159 else /* ROTATION_90 */
161 /* left */
162 seg[i2].x1 = x+i; seg[i2].y1 = y+i+a;
163 seg[i2].x2 = x+i; seg[i2].y2 = y+h-i+a;
166 XDrawSegments(dpy, d, shadow_gc, seg, i2);
168 return;
171 void do_relieve_rectangle(
172 Display *dpy, Drawable d, int x, int y, int w, int h,
173 GC ReliefGC, GC ShadowGC, int line_width, Bool use_alternate_shading)
175 do_relieve_rectangle_with_rotation(
176 dpy, d, x, y, w, h, ReliefGC, ShadowGC, line_width,
177 use_alternate_shading, ROTATION_0);
178 return;
181 /* Creates a pixmap that is a horizontally stretched version of the input
182 * pixmap
184 Pixmap CreateStretchXPixmap(
185 Display *dpy, Pixmap src, int src_width, int src_height, int src_depth,
186 int dest_width, GC gc)
188 int i;
189 Pixmap pixmap;
190 GC my_gc = None;
192 if (src_width < 0 || src_height < 0 || dest_width < 0)
194 return None;
196 pixmap = XCreatePixmap(dpy, src, dest_width, src_height, src_depth);
197 if (pixmap == None)
199 return None;
201 if (gc == None)
203 my_gc = fvwmlib_XCreateGC(dpy, pixmap, 0, 0);
205 for (i = 0; i < dest_width; i++)
207 XCopyArea(
208 dpy, src, pixmap, (gc == None)? my_gc:gc,
209 (i * src_width) / dest_width, 0, 1, src_height, i, 0);
211 if (my_gc)
213 XFreeGC(dpy, my_gc);
215 return pixmap;
219 /* Creates a pixmap that is a vertically stretched version of the input
220 * pixmap
222 Pixmap CreateStretchYPixmap(
223 Display *dpy, Pixmap src, int src_width, int src_height, int src_depth,
224 int dest_height, GC gc)
226 int i;
227 Pixmap pixmap;
228 GC my_gc = None;
230 if (src_height < 0 || src_depth < 0 || dest_height < 0)
232 return None;
234 pixmap = XCreatePixmap(dpy, src, src_width, dest_height, src_depth);
235 if (pixmap == None)
237 return None;
239 if (gc == None)
241 my_gc = fvwmlib_XCreateGC(dpy, pixmap, 0, 0);
243 for (i = 0; i < dest_height; i++)
245 XCopyArea(
246 dpy, src, pixmap, (gc == None)? my_gc:gc,
247 0, (i * src_height) / dest_height, src_width, 1, 0, i);
249 if (my_gc)
251 XFreeGC(dpy, my_gc);
253 return pixmap;
257 /* Creates a pixmap that is a stretched version of the input
258 * pixmap
260 Pixmap CreateStretchPixmap(
261 Display *dpy, Pixmap src, int src_width, int src_height, int src_depth,
262 int dest_width, int dest_height, GC gc)
264 Pixmap pixmap = None;
265 Pixmap temp_pixmap;
266 GC my_gc = None;
268 if (src_width < 0 || src_height < 0 || src_depth < 0 || dest_width < 0)
270 return None;
272 if (gc == None)
274 my_gc = fvwmlib_XCreateGC(dpy, src, 0, 0);
276 temp_pixmap = CreateStretchXPixmap(
277 dpy, src, src_width, src_height, src_depth, dest_width,
278 (gc == None)? my_gc:gc);
279 if (temp_pixmap == None)
281 if (my_gc)
283 XFreeGC(dpy, my_gc);
285 return None;
287 pixmap = CreateStretchYPixmap(
288 dpy, temp_pixmap, dest_width, src_height, src_depth,
289 dest_height, (gc == None)? my_gc:gc);
290 XFreePixmap(dpy, temp_pixmap);
291 if (my_gc)
293 XFreeGC(dpy, my_gc);
295 return pixmap;
298 /* Creates a pixmap that is a tiled version of the input pixmap. Modifies the
299 * sets the fill_style of the GC to FillSolid and the tile to None. */
300 Pixmap CreateTiledPixmap(
301 Display *dpy, Pixmap src, int src_width, int src_height,
302 int dest_width, int dest_height, int depth, GC gc)
304 XGCValues xgcv;
305 Pixmap pixmap;
307 if (src_width < 0 || src_height < 0 ||
308 dest_width < 0 || dest_height < 0)
310 return None;
312 pixmap = XCreatePixmap(dpy, src, dest_width, dest_height, depth);
313 if (pixmap == None)
315 return None;
317 xgcv.fill_style = FillTiled;
318 xgcv.tile = src;
319 xgcv.ts_x_origin = 0;
320 xgcv.ts_y_origin = 0;
321 XChangeGC(
322 dpy, gc, GCFillStyle | GCTile | GCTileStipXOrigin |
323 GCTileStipYOrigin, &xgcv);
324 XFillRectangle(dpy, pixmap, gc, 0, 0, dest_width, dest_height);
325 xgcv.fill_style = FillSolid;
326 XChangeGC(dpy, gc, GCFillStyle, &xgcv);
328 return pixmap;
331 Pixmap CreateRotatedPixmap(
332 Display *dpy, Pixmap src, int src_width, int src_height, int depth,
333 GC gc, int rotation)
335 GC my_gc = None;
336 Pixmap pixmap = None;
337 int dest_width, dest_height, i, j;
338 Bool error = False;
339 FImage *fim = NULL;
340 FImage *src_fim = NULL;
342 if (src_width <= 0 || src_height <= 0)
344 return None;
346 switch(rotation)
348 case ROTATION_90:
349 case ROTATION_270:
350 dest_width = src_height;
351 dest_height = src_width;
352 break;
353 case ROTATION_0:
354 case ROTATION_180:
355 dest_width = src_width;
356 dest_height = src_height;
357 break;
358 default:
359 return None;
360 break;
362 pixmap = XCreatePixmap(dpy, src, dest_width, dest_height, depth);
363 if (pixmap == None)
365 return None;
367 if (gc == None)
369 my_gc = fvwmlib_XCreateGC(dpy, src, 0, 0);
371 if (rotation == ROTATION_0)
373 XCopyArea(
374 dpy, src, pixmap, (gc == None)? my_gc:gc,
375 0, 0, src_width, src_height, 0, 0);
376 goto bail;
379 if (!(src_fim = FGetFImage(
380 dpy, src, Pvisual, depth, 0, 0, src_width, src_height,
381 AllPlanes, ZPixmap)))
383 error = True;
384 goto bail;
386 if (!(fim = FCreateFImage(
387 dpy, Pvisual, depth, ZPixmap, dest_width, dest_height)))
389 error = True;
390 goto bail;
393 for (j = 0; j < src_height; j++)
395 for (i = 0; i < src_width; i++)
397 switch(rotation)
399 case ROTATION_270:
400 XPutPixel(
401 fim->im, j, src_width - i - 1,
402 XGetPixel(src_fim->im, i, j));
403 break;
404 case ROTATION_90:
405 XPutPixel(
406 fim->im, src_height - j - 1, i,
407 XGetPixel(src_fim->im, i, j));
408 break;
409 case ROTATION_180:
410 XPutPixel(
411 fim->im,
412 src_width - i - 1, src_height - j - 1,
413 XGetPixel(src_fim->im, i, j));
414 break;
415 default:
416 break;
420 FPutFImage(dpy, pixmap, gc, fim, 0, 0, 0, 0, dest_width, dest_height);
421 bail:
422 if (error && pixmap)
424 XFreePixmap(dpy,pixmap);
425 pixmap = None;
427 if (fim)
429 FDestroyFImage(dpy, fim);
431 if (src_fim)
433 FDestroyFImage(dpy, src_fim);
435 if (my_gc)
437 XFreeGC(dpy, my_gc);
439 return pixmap;
444 * Returns True if the given type of gradient is supported.
447 Bool IsGradientTypeSupported(char type)
449 switch (toupper(type))
451 case V_GRADIENT:
452 case H_GRADIENT:
453 case B_GRADIENT:
454 case D_GRADIENT:
455 case R_GRADIENT:
456 case Y_GRADIENT:
457 case S_GRADIENT:
458 case C_GRADIENT:
459 return True;
460 default:
461 fprintf(stderr, "%cGradient type is not supported\n",
462 toupper(type));
463 return False;
469 * Allocates a linear color gradient (veliaa@rpi.edu)
472 static
473 XColor *AllocLinearGradient(
474 char *s_from, char *s_to, int npixels, int skip_first_color, int dither)
476 XColor *xcs;
477 XColor from, to, c;
478 float r;
479 float dr;
480 float g;
481 float dg;
482 float b;
483 float db;
484 int i;
485 int got_all = 1;
486 int div;
488 if (npixels < 1)
490 fprintf(stderr,
491 "AllocLinearGradient: Invalid number of pixels: %d\n",
492 npixels);
493 return NULL;
495 if (!s_from || !XParseColor(Pdpy, Pcmap, s_from, &from))
497 fprintf(stderr, "Cannot parse color \"%s\"\n",
498 s_from ? s_from : "<blank>");
499 return NULL;
501 if (!s_to || !XParseColor(Pdpy, Pcmap, s_to, &to))
503 fprintf(stderr, "Cannot parse color \"%s\"\n",
504 s_to ? s_to : "<blank>");
505 return NULL;
508 /* divisor must not be zero, hence this calculation */
509 div = (npixels == 1) ? 1 : npixels - 1;
510 c = from;
511 /* red part and step width */
512 r = from.red;
513 dr = (float)(to.red - from.red);
514 /* green part and step width */
515 g = from.green;
516 dg = (float)(to.green - from.green);
517 /* blue part and step width */
518 b = from.blue;
519 db = (float)(to.blue - from.blue);
520 xcs = (XColor *)safemalloc(sizeof(XColor) * npixels);
521 memset(xcs, 0, sizeof(XColor) * npixels);
522 c.flags = DoRed | DoGreen | DoBlue;
523 for (i = (skip_first_color) ? 1 : 0; i < npixels && div > 0; ++i)
525 c.red = (unsigned short)
526 ((int)(r + dr / (float)div * (float)i + 0.5));
527 c.green = (unsigned short)
528 ((int)(g + dg / (float)div * (float)i + 0.5));
529 c.blue = (unsigned short)
530 ((int)(b + db / (float)div * (float)i + 0.5));
531 if (dither == 0 && !PictureAllocColor(Pdpy, Pcmap, &c, False))
533 got_all = 0;
535 xcs[i] = c;
537 if (!got_all && dither == 0)
539 fprintf(stderr, "Cannot alloc color gradient %s to %s\n",
540 s_from, s_to);
543 return xcs;
548 * Allocates a nonlinear color gradient (veliaa@rpi.edu)
551 static XColor *AllocNonlinearGradient(
552 char *s_colors[], int clen[], int nsegs, int npixels, int dither)
554 XColor *xcs = (XColor *)safemalloc(sizeof(XColor) * npixels);
555 int i;
556 int curpixel = 0;
557 int *seg_end_colors;
558 int seg_sum = 0;
559 float color_sum = 0.0;
561 if (nsegs < 1 || npixels < 2)
563 fprintf(stderr,
564 "Gradients must specify at least one segment and"
565 " two colors\n");
566 free(xcs);
567 return NULL;
569 for (i = 0; i < npixels; i++)
571 xcs[i].pixel = 0;
574 /* get total length of all segments */
575 for (i = 0; i < nsegs; i++)
577 seg_sum += clen[i];
580 /* calculate the index of a segment's las color */
581 seg_end_colors = alloca(nsegs * sizeof(int));
582 if (nsegs == 1)
584 seg_end_colors[0] = npixels - 1;
586 else
588 for (i = 0; i < nsegs; i++)
590 color_sum += (float)(clen[i] * (npixels - 1)) /
591 (float)(seg_sum);
592 seg_end_colors[i] = (int)(color_sum + 0.5);
594 if (seg_end_colors[nsegs - 1] > npixels - 1)
596 fprintf(stderr,
597 "BUG: (AllocNonlinearGradient): "
598 "seg_end_colors[nsegs - 1] (%d)"
599 " > npixels - 1 (%d)."
600 " Gradient drawing aborted\n",
601 seg_end_colors[nsegs - 1], npixels - 1);
602 return NULL;
604 /* take care of rounding errors */
605 seg_end_colors[nsegs - 1] = npixels - 1;
608 for (i = 0; i < nsegs; ++i)
610 XColor *c = NULL;
611 int j;
612 int n;
613 int skip_first_color = (curpixel != 0);
615 if (i == 0)
617 n = seg_end_colors[0] + 1;
619 else
621 n = seg_end_colors[i] - seg_end_colors[i - 1] + 1;
624 if (n > 1)
626 c = AllocLinearGradient(
627 s_colors[i], s_colors[i + 1], n,
628 skip_first_color, dither);
629 if (!c && (n - skip_first_color) != 0)
631 free(xcs);
632 return NULL;
634 for (j = skip_first_color; j < n; ++j)
636 xcs[curpixel + j] = c[j];
638 curpixel += n - 1;
640 if (c)
642 free(c);
643 c = NULL;
645 if (curpixel != seg_end_colors[i])
647 fprintf(stderr,
648 "BUG: (AllocNonlinearGradient): "
649 "nsegs %d, i %d, curpixel %d,"
650 " seg_end_colors[i] = %d,"
651 " npixels %d, n %d\n",
652 nsegs, i, curpixel,
653 seg_end_colors[i],npixels,n);
654 return NULL;
657 return xcs;
660 /* Convenience function. Calls AllocNonLinearGradient to fetch all colors and
661 * then frees the color names and the perc and color_name arrays. */
662 XColor *AllocAllGradientColors(
663 char *color_names[], int perc[], int nsegs, int ncolors, int dither)
665 XColor *xcs = NULL;
666 int i;
668 /* grab the colors */
669 xcs = AllocNonlinearGradient(
670 color_names, perc, nsegs, ncolors, dither);
671 for (i = 0; i <= nsegs; i++)
673 if (color_names[i])
675 free(color_names[i]);
678 free(color_names);
679 free(perc);
680 if (!xcs)
682 fprintf(stderr, "couldn't create gradient\n");
683 return NULL;
686 return xcs;
689 /* groks a gradient string and creates arrays of colors and percentages
690 * returns the number of colors asked for (No. allocated may be less due
691 * to the ColorLimit command). A return of 0 indicates an error
693 int ParseGradient(
694 char *gradient, char **rest, char ***colors_return, int **perc_return,
695 int *nsegs_return)
697 char *item;
698 char *orig;
699 int npixels;
700 char **s_colors;
701 int *perc;
702 int nsegs, i, sum;
703 Bool is_syntax_error = False;
705 /* get the number of colors specified */
706 if (rest)
708 *rest = gradient;
710 orig = gradient;
712 if (GetIntegerArguments(gradient, &gradient, (int *)&npixels, 1) != 1 ||
713 npixels < 2)
715 fprintf(
716 stderr, "ParseGradient: illegal number of colors in"
717 " gradient: '%s'\n", orig);
718 return 0;
721 /* get the starting color or number of segments */
722 gradient = GetNextToken(gradient, &item);
723 if (gradient)
725 gradient = SkipSpaces(gradient, NULL, 0);
727 if (!gradient || !*gradient || !item)
729 fprintf(stderr, "Incomplete gradient style: '%s'\n", orig);
730 if (item)
732 free(item);
734 if (rest)
736 *rest = gradient;
738 return 0;
741 if (GetIntegerArguments(item, NULL, &nsegs, 1) != 1)
743 /* get the end color of a simple gradient */
744 s_colors = (char **)safemalloc(sizeof(char *) * 2);
745 perc = (int *)safemalloc(sizeof(int));
746 nsegs = 1;
747 s_colors[0] = item;
748 gradient = GetNextToken(gradient, &item);
749 s_colors[1] = item;
750 perc[0] = 100;
752 else
754 free(item);
755 /* get a list of colors and percentages */
756 if (nsegs < 1)
757 nsegs = 1;
758 if (nsegs > MAX_GRADIENT_SEGMENTS)
759 nsegs = MAX_GRADIENT_SEGMENTS;
760 s_colors = (char **)safemalloc(sizeof(char *) * (nsegs + 1));
761 perc = (int *)safemalloc(sizeof(int) * nsegs);
762 for (i = 0; !is_syntax_error && i <= nsegs; i++)
764 s_colors[i] = 0;
765 gradient = GetNextToken(gradient, &s_colors[i]);
766 if (i < nsegs)
768 if (GetIntegerArguments(
769 gradient, &gradient, &perc[i], 1)
770 != 1 || perc[i] <= 0)
772 /* illegal size */
773 perc[i] = 0;
777 if (s_colors[nsegs] == NULL)
779 fprintf(
780 stderr, "ParseGradient: too few gradient"
781 " segments: '%s'\n", orig);
782 is_syntax_error = True;
786 /* sanity check */
787 for (i = 0, sum = 0; !is_syntax_error && i < nsegs; ++i)
789 int old_sum = sum;
791 sum += perc[i];
792 if (sum < old_sum)
794 /* integer overflow */
795 fprintf(
796 stderr, "ParseGradient: multi gradient"
797 " overflow: '%s'", orig);
798 is_syntax_error = 1;
799 break;
802 if (is_syntax_error)
804 for (i = 0; i <= nsegs; ++i)
806 if (s_colors[i])
808 free(s_colors[i]);
811 free(s_colors);
812 free(perc);
813 if (rest)
815 *rest = gradient;
817 return 0;
821 /* sensible limits */
822 if (npixels < 2)
823 npixels = 2;
824 if (npixels > MAX_GRADIENT_COLORS)
825 npixels = MAX_GRADIENT_COLORS;
827 /* send data back */
828 *colors_return = s_colors;
829 *perc_return = perc;
830 *nsegs_return = nsegs;
831 if (rest)
832 *rest = gradient;
834 return npixels;
837 /* Calculate the prefered dimensions of a gradient, based on the number of
838 * colors and the gradient type. Returns False if the gradient type is not
839 * supported. */
840 Bool CalculateGradientDimensions(
841 Display *dpy, Drawable d, int ncolors, char type, int dither,
842 int *width_ret, int *height_ret)
844 static int best_width = 0, best_height = 0;
845 int dither_factor = (dither > 0)? 128:1;
847 /* get the best tile size (once) */
848 if (!best_width)
850 if (!XQueryBestTile(
851 dpy, d, 1, 1, (unsigned int*)&best_width,
852 (unsigned int*)&best_height))
854 best_width = 0;
855 best_height = 0;
857 /* this is needed for buggy X servers like XFree 3.3.3.1 */
858 if (!best_width)
859 best_width = 1;
860 if (!best_height)
861 best_height = 1;
864 switch (type) {
865 case H_GRADIENT:
866 *width_ret = ncolors;
867 *height_ret = best_height * dither_factor;
868 break;
869 case V_GRADIENT:
870 *width_ret = best_width * dither_factor;
871 *height_ret = ncolors;
872 break;
873 case D_GRADIENT:
874 case B_GRADIENT:
875 /* diagonal gradients are rendered into a rectangle for which
876 * the width plus the height is equal to ncolors + 1. The
877 * rectangle is square when ncolors is odd and one pixel
878 * taller than wide with even numbers */
879 *width_ret = (ncolors + 1) / 2;
880 *height_ret = ncolors + 1 - *width_ret;
881 break;
882 case S_GRADIENT:
883 /* square gradients have the last color as a single pixel in
884 * the centre */
885 *width_ret = *height_ret = 2 * ncolors - 1;
886 break;
887 case C_GRADIENT:
888 /* circular gradients have the first color as a pixel in each
889 * corner */
890 *width_ret = *height_ret = 2 * ncolors - 1;
891 break;
892 case R_GRADIENT:
893 case Y_GRADIENT:
894 /* swept types need each color to occupy at least one pixel at
895 * the edge. Get the smallest odd number that will provide
896 * enough */
897 for (*width_ret = 1;
898 (double)(*width_ret - 1) * M_PI < (double)ncolors;
899 *width_ret += 2)
901 /* nothing to do here */
903 *height_ret = *width_ret;
904 break;
905 default:
906 fprintf(stderr, "%cGradient not supported\n", type);
907 return False;
909 return True;
912 /* Does the actual drawing of the pixmap. If the in_drawable argument is None,
913 * a new pixmap of the given depth, width and height is created. If it is not
914 * None the gradient is drawn into it. The d_width, d_height, d_x and d_y
915 * describe the traget rectangle within the drawable. */
916 Drawable CreateGradientPixmap(
917 Display *dpy, Drawable d, GC gc, int type, int g_width,
918 int g_height, int ncolors, XColor *xcs, int dither, Pixel **d_pixels,
919 int *d_npixels, Drawable in_drawable, int d_x, int d_y,
920 int d_width, int d_height, XRectangle *rclip)
922 Pixmap pixmap = None;
923 PictureImageColorAllocator *pica = NULL;
924 XColor c;
925 FImage *fim;
926 register int i, j;
927 XGCValues xgcv;
928 Drawable target;
929 int t_x;
930 int t_y;
931 int t_width;
932 int t_height;
933 int ps;
935 if (d_pixels != NULL && *d_pixels != NULL)
937 if (d_npixels != NULL && *d_npixels > 0)
939 PictureFreeColors(
940 dpy, Pcmap, *d_pixels, *d_npixels, 0, False);
942 free(*d_pixels);
943 *d_pixels = NULL;
945 if (d_npixels != NULL)
947 *d_npixels = 0;
949 if (g_height < 0 || g_width < 0 || d_width < 0 || d_height < 0)
950 return None;
952 if (in_drawable == None)
954 /* create a pixmap to use */
955 pixmap = XCreatePixmap(dpy, d, g_width, g_height, Pdepth);
956 if (pixmap == None)
957 return None;
958 target = pixmap;
959 t_x = 0;
960 t_y = 0;
961 t_width = g_width;
962 t_height = g_height;
964 else
966 target = in_drawable;
967 t_x = d_x;
968 t_y = d_y;
969 t_width = d_width;
970 t_height = d_height;
973 fim = FCreateFImage(
974 dpy, Pvisual, Pdepth, ZPixmap, t_width, t_height);
975 if (!fim)
977 fprintf(stderr, "%cGradient couldn't get image\n", type);
978 if (pixmap != None)
979 XFreePixmap(dpy, pixmap);
980 return None;
982 if (dither)
984 pica = PictureOpenImageColorAllocator(
985 dpy, Pcmap, t_width, t_height,
986 False, False, dither, False);
988 ps = t_width * t_height;
989 /* now do the fancy drawing */
990 switch (type)
992 case H_GRADIENT:
994 for (i = 0; i < t_width; i++)
996 int d = i * ncolors / t_width;
997 c = xcs[d];
998 for (j = 0; j < t_height; j++)
1000 if (dither)
1002 c = xcs[d];
1003 PictureAllocColorImage(
1004 dpy, pica, &c, i, j);
1006 XPutPixel(fim->im, i, j, c.pixel);
1010 break;
1011 case V_GRADIENT:
1013 for (j = 0; j < t_height; j++)
1015 int d = j * ncolors / t_height;
1016 c = xcs[d];
1017 for (i = 0; i < t_width; i++)
1019 if (dither)
1021 c = xcs[d];
1022 PictureAllocColorImage(
1023 dpy, pica, &c, i, j);
1025 XPutPixel(fim->im, i, j, c.pixel);
1028 break;
1030 case D_GRADIENT:
1032 register int t_scale = t_width + t_height - 1;
1033 for (i = 0; i < t_width; i++)
1035 for (j = 0; j < t_height; j++)
1037 c = xcs[(i+j) * ncolors / t_scale];
1038 if (dither)
1040 PictureAllocColorImage(
1041 dpy, pica, &c, i, j);
1043 XPutPixel(fim->im, i, j, c.pixel);
1046 break;
1048 case B_GRADIENT:
1050 register int t_scale = t_width + t_height - 1;
1051 for (i = 0; i < t_width; i++)
1053 for (j = 0; j < t_height; j++)
1055 c = xcs[(i + (t_height - j - 1)) * ncolors /
1056 t_scale];
1057 if (dither)
1059 PictureAllocColorImage(
1060 dpy, pica, &c, i, j);
1062 XPutPixel(fim->im, i, j, c.pixel);
1065 break;
1067 case S_GRADIENT:
1069 register int t_scale = t_width * t_height;
1070 register int myncolors = ncolors * 2;
1071 for (i = 0; i < t_width; i++) {
1072 register int pi = min(i, t_width - 1 - i) * t_height;
1073 for (j = 0; j < t_height; j++) {
1074 register int pj =
1075 min(j, t_height - 1 - j) * t_width;
1076 c = xcs[(min(pi, pj) * myncolors - 1) /
1077 t_scale];
1078 if (dither)
1080 PictureAllocColorImage(
1081 dpy, pica, &c, i, j);
1083 XPutPixel(fim->im, i, j, c.pixel);
1087 break;
1088 case C_GRADIENT:
1090 register double t_scale =
1091 (double)(t_width * t_height) / sqrt(8);
1092 for (i = 0; i < t_width; i++)
1094 for (j = 0; j < t_height; j++)
1096 register double x =
1097 (double)((2 * i - t_width) * t_height) /
1098 4.0;
1099 register double y =
1100 (double)((t_height - 2 * j) * t_width) /
1101 4.0;
1102 register double rad = sqrt(x * x + y * y);
1103 c = xcs[(int)((rad * ncolors - 0.5) / t_scale)];
1104 if (dither)
1106 PictureAllocColorImage(
1107 dpy, pica, &c, i, j);
1109 XPutPixel(fim->im, i, j, c.pixel);
1112 break;
1114 case R_GRADIENT:
1116 register int w = t_width - 1;
1117 register int h = t_height - 1;
1118 /* g_width == g_height, both are odd, therefore x can be 0.0 */
1119 for (i = 0; i <= w; i++) {
1120 for (j = 0; j <= h; j++) {
1121 register double x =
1122 (double)((2 * i - w) * h) / 4.0;
1123 register double y =
1124 (double)((h - 2 * j) * w) / 4.0;
1125 /* angle ranges from -pi/2 to +pi/2 */
1126 register double angle;
1127 if (x != 0.0) {
1128 angle = atan(y / x);
1129 } else {
1130 angle = (y < 0) ? - M_PI_2 : M_PI_2;
1132 /* extend to -pi/2 to 3pi/2 */
1133 if (x < 0)
1134 angle += M_PI;
1135 /* move range from -pi/2:3*pi/2 to 0:2*pi */
1136 if (angle < 0.0)
1137 angle += M_PI * 2.0;
1138 /* normalize to gradient */
1139 c = xcs[(int)(angle * M_1_PI * 0.5 * ncolors)];
1140 if (dither)
1142 PictureAllocColorImage(
1143 dpy, pica, &c, i, j);
1145 XPutPixel(fim->im, i, j, c.pixel);
1149 break;
1151 * The Yin Yang gradient style and the following code are:
1152 * Copyright 1999 Sir Boris. (email to sir_boris@bigfoot.com may be
1153 * read by his groom but is not guaranteed to elicit a response)
1154 * No restrictions are placed on this code, as long as the copyright
1155 * notice is preserved.
1157 case Y_GRADIENT:
1159 register int r = t_width * t_height / 4;
1160 for (i = 0; i < t_width; i++) {
1161 for (j = 0; j < t_height; j++) {
1162 register double x =
1163 (double)((2 * i - t_width) * t_height) /
1164 4.0;
1165 register double y =
1166 (double)((t_height - 2 * j) * t_width) /
1167 4.0;
1168 register double rad = sqrt(x * x + y * y);
1169 /* angle ranges from -pi/2 to +pi/2 */
1170 register double angle;
1172 if (x != 0.0) {
1173 angle = atan(y / x);
1174 } else {
1175 angle = (y < 0) ? - M_PI_2 : M_PI_2;
1177 /* extend to -pi/2 to 3pi/2 */
1178 if (x < 0)
1179 angle += M_PI;
1180 /* warp the angle within the yinyang circle */
1181 if (rad <= r) {
1182 angle -= acos(rad / r);
1184 /* move range from -pi/2:3*pi/2 to 0:2*pi */
1185 if (angle < 0.0)
1186 angle += M_PI * 2.0;
1187 /* normalize to gradient */
1188 c = xcs[(int)(angle * M_1_PI * 0.5 * ncolors)];
1189 if (dither)
1191 PictureAllocColorImage(
1192 dpy, pica, &c, i, j);
1194 XPutPixel(fim->im, i, j, c.pixel);
1198 break;
1199 default:
1200 /* placeholder function, just fills the pixmap with the first
1201 * color */
1202 memset(fim->im->data, 0, fim->im->bytes_per_line * t_height);
1203 XAddPixel(fim->im, xcs[0].pixel);
1204 break;
1207 if (dither)
1209 if (d_pixels != NULL && d_npixels != NULL)
1211 PictureCloseImageColorAllocator(
1212 dpy, pica, d_npixels, d_pixels, 0);
1214 else
1216 /* possible color leak */
1220 /* set the gc style */
1221 xgcv.function = GXcopy;
1222 xgcv.plane_mask = AllPlanes;
1223 xgcv.fill_style = FillSolid;
1224 xgcv.clip_mask = None;
1225 XChangeGC(dpy, gc, GCFunction|GCPlaneMask|GCFillStyle|GCClipMask,
1226 &xgcv);
1227 if (rclip)
1229 XSetClipRectangles(dpy, gc, 0, 0, rclip, 1, Unsorted);
1231 /* copy the image to the server */
1232 FPutFImage(dpy, target, gc, fim, 0, 0, t_x, t_y, t_width, t_height);
1233 if (rclip)
1235 XSetClipMask(dpy, gc, None);
1237 FDestroyFImage(dpy, fim);
1238 return target;
1242 /* Create a pixmap from a gradient specifier, width and height are hints
1243 * that are only used for gradients that can be tiled e.g. H or V types
1244 * types are HVDBSCRY for Horizontal, Vertical, Diagonal, Back-diagonal, Square,
1245 * Circular, Radar and Yin/Yang respectively (in order of bloatiness)
1247 Pixmap CreateGradientPixmapFromString(
1248 Display *dpy, Drawable d, GC gc, int type, char *action,
1249 int *width_return, int *height_return,
1250 Pixel **pixels_return, int *nalloc_pixels, int dither)
1252 Pixel *d_pixels = NULL;
1253 int d_npixels = 0;
1254 XColor *xcs = NULL;
1255 int ncolors = 0;
1256 char **colors;
1257 int *perc, nsegs;
1258 Pixmap pixmap = None;
1260 /* set return pixels to NULL in case of premature return */
1261 if (pixels_return)
1262 *pixels_return = NULL;
1263 if (nalloc_pixels)
1264 *nalloc_pixels = 0;
1266 /* translate the gradient string into an array of colors etc */
1267 if (!(ncolors = ParseGradient(action, NULL, &colors, &perc, &nsegs))) {
1268 fprintf(stderr, "Can't parse gradient: '%s'\n", action);
1269 return None;
1271 /* grab the colors */
1272 xcs = AllocAllGradientColors(
1273 colors, perc, nsegs, ncolors, dither);
1274 if (xcs == NULL)
1276 return None;
1279 /* grok the size to create from the type */
1280 type = toupper(type);
1282 if (CalculateGradientDimensions(
1283 dpy, d, ncolors, type, dither, width_return, height_return))
1285 pixmap = CreateGradientPixmap(
1286 dpy, d, gc, type, *width_return, *height_return,
1287 ncolors, xcs, dither, &d_pixels, &d_npixels,
1288 None, 0, 0, 0, 0, NULL);
1291 /* if the caller has not asked for the pixels there is probably a leak
1293 if (PUseDynamicColors)
1295 if (!(pixels_return && nalloc_pixels))
1297 /* if the caller has not asked for the pixels there is
1298 * probably a leak */
1299 fprintf(stderr,
1300 "CreateGradient: potential color leak, losing track"
1301 " of pixels\n");
1302 if (d_pixels != NULL)
1304 free(d_pixels);
1307 else
1309 if (!dither)
1311 Pixel *pixels;
1312 int i;
1314 pixels = (Pixel *)safemalloc(
1315 ncolors * sizeof(Pixel));
1316 for(i=0; i<ncolors; i++)
1318 pixels[i] = xcs[i].pixel;
1320 *pixels_return = pixels;
1321 *nalloc_pixels = ncolors;
1323 else
1325 *pixels_return = d_pixels;
1326 *nalloc_pixels = d_npixels;
1330 else if (d_pixels != NULL)
1332 /* should not happen */
1333 free(d_pixels);
1336 free(xcs);
1338 return pixmap;
1343 * Draws a little Triangle pattern within a window
1346 void DrawTrianglePattern(
1347 Display *dpy, Drawable d, GC ReliefGC, GC ShadowGC, GC FillGC,
1348 int x, int y, int width, int height, int bw, char orientation,
1349 Bool draw_relief, Bool do_fill, Bool is_pressed)
1351 const struct
1353 const char line[3];
1354 const char point[3];
1355 } hi[4] =
1357 { { 1, 0, 0 }, { 1, 1, 0 } }, /* up */
1358 { { 1, 0, 1 }, { 1, 0, 0 } }, /* down */
1359 { { 1, 0, 0 }, { 1, 1, 0 } }, /* left */
1360 { { 1, 0, 1 }, { 1, 1, 0 } } /* right */
1362 XPoint points[4];
1363 GC temp_gc;
1364 int short_side;
1365 int long_side;
1366 int t_width;
1367 int t_height;
1368 int i;
1369 int type;
1371 /* remove border width from target area */
1372 width -= 2 * bw;
1373 height -= 2 * bw;
1374 x += bw;
1375 y += bw;
1376 if (width < 1 || height < 1)
1377 /* nothing to do */
1378 return;
1380 orientation = tolower(orientation);
1381 switch (orientation)
1383 case 'u':
1384 case 'd':
1385 long_side = width;
1386 short_side = height;
1387 type = (orientation == 'd');
1388 break;
1389 case 'l':
1390 case 'r':
1391 long_side = height;
1392 short_side = width;
1393 type = (orientation == 'r') + 2;
1394 break;
1395 default:
1396 /* unknowm orientation */
1397 return;
1400 /* assure the base side has an odd length */
1401 if ((long_side & 0x1) == 0)
1402 long_side--;
1403 /* reduce base length if short sides don't fit */
1404 if (short_side < long_side / 2 + 1)
1405 long_side = 2 * short_side - 1;
1406 else
1407 short_side = long_side / 2 + 1;
1409 if (orientation == 'u' || orientation == 'd')
1411 t_width = long_side;
1412 t_height = short_side;
1414 else
1416 t_width = short_side;
1417 t_height = long_side;
1419 /* find proper x/y coordinate */
1420 x += (width - t_width) / 2;
1421 y += (height - t_height) / 2;
1422 /* decrement width and height for convenience of calculation */
1423 t_width--;
1424 t_height--;
1426 /* get the list of points to draw */
1427 switch (orientation)
1429 case 'u':
1430 y += t_height;
1431 t_height = -t_height;
1432 case 'd':
1433 points[1].x = x + t_width / 2;
1434 points[1].y = y + t_height;
1435 points[2].x = x + t_width;
1436 points[2].y = y;
1437 break;
1438 case 'l':
1439 x += t_width;
1440 t_width = -t_width;
1441 case 'r':
1442 points[1].x = x + t_width;
1443 points[1].y = y + t_height / 2;
1444 points[2].x = x;
1445 points[2].y = y + t_height;
1446 break;
1448 points[0].x = x;
1449 points[0].y = y;
1450 points[3].x = x;
1451 points[3].y = y;
1453 if (do_fill)
1455 /* solid triangle */
1456 XFillPolygon(
1457 dpy, d, FillGC, points, 3, Convex, CoordModeOrigin);
1459 if (draw_relief)
1461 /* relief triangle */
1462 for (i = 0; i < 3; i++)
1464 temp_gc = (is_pressed ^ hi[type].line[i]) ?
1465 ReliefGC : ShadowGC;
1466 XDrawLine(
1467 dpy, d, temp_gc, points[i].x, points[i].y,
1468 points[i+1].x, points[i+1].y);
1470 for (i = 0; i < 3; i++)
1472 temp_gc = (is_pressed ^ hi[type].point[i]) ?
1473 ReliefGC : ShadowGC;
1474 XDrawPoint(dpy, d, temp_gc, points[i].x, points[i].y);
1478 return;
1481 GC fvwmlib_XCreateGC(
1482 Display *display, Drawable drawable, unsigned long valuemask,
1483 XGCValues *values)
1485 GC gc;
1486 Bool f;
1487 XGCValues gcv;
1489 if (!values)
1491 values = &gcv;
1493 f = values->graphics_exposures;
1494 if (!(valuemask & GCGraphicsExposures))
1496 valuemask |= GCGraphicsExposures;
1497 values->graphics_exposures = 0;
1499 gc = XCreateGC(display, drawable, valuemask, values);
1500 values->graphics_exposures = f;
1502 return gc;