Maximus: Tiled Maximization
[wmaker-crm.git] / src / texture.c
blob2029b2f9ae28256e6e71060043d9388a924f5f12
1 /*
2 * Window Maker window manager
4 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 * USA.
22 #include "wconfig.h"
24 #include <X11/Xlib.h>
25 #include <X11/Xutil.h>
27 #ifdef TEXTURE_PLUGIN
28 # ifdef HAVE_DLFCN_H
29 # include <dlfcn.h>
30 # endif
31 #endif
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
37 #include <wraster.h>
39 #include "WindowMaker.h"
40 #include "wcore.h"
41 #include "texture.h"
42 #include "funcs.h"
44 extern WPreferences wPreferences;
46 static void bevelImage(RImage * image, int relief);
48 WTexSolid *wTextureMakeSolid(WScreen * scr, XColor * color)
50 WTexSolid *texture;
51 int gcm;
52 XGCValues gcv;
54 texture = wmalloc(sizeof(WTexture));
56 texture->type = WTEX_SOLID;
57 texture->subtype = 0;
59 XAllocColor(dpy, scr->w_colormap, color);
60 texture->normal = *color;
61 if (color->red == 0 && color->blue == 0 && color->green == 0) {
62 texture->light.red = 0xb6da;
63 texture->light.green = 0xb6da;
64 texture->light.blue = 0xb6da;
65 texture->dim.red = 0x6185;
66 texture->dim.green = 0x6185;
67 texture->dim.blue = 0x6185;
68 } else {
69 RColor rgb;
70 RHSVColor hsv, hsv2;
71 int v;
73 rgb.red = color->red >> 8;
74 rgb.green = color->green >> 8;
75 rgb.blue = color->blue >> 8;
76 RRGBtoHSV(&rgb, &hsv);
77 RHSVtoRGB(&hsv, &rgb);
78 hsv2 = hsv;
80 v = hsv.value * 16 / 10;
81 hsv.value = (v > 255 ? 255 : v);
82 RHSVtoRGB(&hsv, &rgb);
83 texture->light.red = rgb.red << 8;
84 texture->light.green = rgb.green << 8;
85 texture->light.blue = rgb.blue << 8;
87 hsv2.value = hsv2.value / 2;
88 RHSVtoRGB(&hsv2, &rgb);
89 texture->dim.red = rgb.red << 8;
90 texture->dim.green = rgb.green << 8;
91 texture->dim.blue = rgb.blue << 8;
93 texture->dark.red = 0;
94 texture->dark.green = 0;
95 texture->dark.blue = 0;
96 XAllocColor(dpy, scr->w_colormap, &texture->light);
97 XAllocColor(dpy, scr->w_colormap, &texture->dim);
98 XAllocColor(dpy, scr->w_colormap, &texture->dark);
100 gcm = GCForeground | GCBackground | GCGraphicsExposures;
101 gcv.graphics_exposures = False;
103 gcv.background = gcv.foreground = texture->light.pixel;
104 texture->light_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
106 gcv.background = gcv.foreground = texture->dim.pixel;
107 texture->dim_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
109 gcv.background = gcv.foreground = texture->dark.pixel;
110 texture->dark_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
112 gcv.background = gcv.foreground = color->pixel;
113 texture->normal_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
115 return texture;
118 static int dummyErrorHandler(Display * foo, XErrorEvent * bar)
120 #ifdef DEBUG
121 wwarning("your server is buggy. Tell the author if some error related to color occurs");
122 #endif
123 return 0;
126 void wTextureDestroy(WScreen * scr, WTexture * texture)
128 int i;
129 int count = 0;
130 unsigned long colors[8];
132 #ifdef DEBUG
133 if (texture == NULL) {
134 printf("BUG: trying to free NULL texture\n");
135 return;
137 #endif
140 * some stupid servers don't like white or black being freed...
142 #define CANFREE(c) (c!=scr->black_pixel && c!=scr->white_pixel && c!=0)
143 switch (texture->any.type) {
144 case WTEX_SOLID:
145 XFreeGC(dpy, texture->solid.light_gc);
146 XFreeGC(dpy, texture->solid.dark_gc);
147 XFreeGC(dpy, texture->solid.dim_gc);
148 if (CANFREE(texture->solid.light.pixel))
149 colors[count++] = texture->solid.light.pixel;
150 if (CANFREE(texture->solid.dim.pixel))
151 colors[count++] = texture->solid.dim.pixel;
152 if (CANFREE(texture->solid.dark.pixel))
153 colors[count++] = texture->solid.dark.pixel;
154 break;
156 case WTEX_PIXMAP:
157 RReleaseImage(texture->pixmap.pixmap);
158 break;
160 case WTEX_MHGRADIENT:
161 case WTEX_MVGRADIENT:
162 case WTEX_MDGRADIENT:
163 for (i = 0; texture->mgradient.colors[i] != NULL; i++) {
164 wfree(texture->mgradient.colors[i]);
166 wfree(texture->mgradient.colors);
167 break;
169 case WTEX_THGRADIENT:
170 case WTEX_TVGRADIENT:
171 case WTEX_TDGRADIENT:
172 RReleaseImage(texture->tgradient.pixmap);
173 break;
175 #ifdef TEXTURE_PLUGIN
176 case WTEX_FUNCTION:
177 #ifdef HAVE_DLFCN_H
178 if (texture->function.handle) {
179 dlclose(texture->function.handle);
181 #endif
182 for (i = 0; i < texture->function.argc; i++) {
183 wfree(texture->function.argv[i]);
185 wfree(texture->function.argv);
186 break;
187 #endif /* TEXTURE_PLUGIN */
189 if (CANFREE(texture->any.color.pixel))
190 colors[count++] = texture->any.color.pixel;
191 if (count > 0) {
192 XErrorHandler oldhandler;
194 /* ignore error from buggy servers that don't know how
195 * to do reference counting for colors. */
196 XSync(dpy, 0);
197 oldhandler = XSetErrorHandler(dummyErrorHandler);
198 XFreeColors(dpy, scr->w_colormap, colors, count, 0);
199 XSync(dpy, 0);
200 XSetErrorHandler(oldhandler);
202 XFreeGC(dpy, texture->any.gc);
203 wfree(texture);
204 #undef CANFREE
207 WTexGradient *wTextureMakeGradient(WScreen * scr, int style, RColor * from, RColor * to)
209 WTexGradient *texture;
210 XGCValues gcv;
212 texture = wmalloc(sizeof(WTexture));
213 memset(texture, 0, sizeof(WTexture));
214 texture->type = style;
215 texture->subtype = 0;
217 texture->color1 = *from;
218 texture->color2 = *to;
220 texture->normal.red = (from->red + to->red) << 7;
221 texture->normal.green = (from->green + to->green) << 7;
222 texture->normal.blue = (from->blue + to->blue) << 7;
224 XAllocColor(dpy, scr->w_colormap, &texture->normal);
225 gcv.background = gcv.foreground = texture->normal.pixel;
226 gcv.graphics_exposures = False;
227 texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
229 return texture;
232 WTexIGradient *wTextureMakeIGradient(WScreen * scr, int thickness1, RColor colors1[2],
233 int thickness2, RColor colors2[2])
235 WTexIGradient *texture;
236 XGCValues gcv;
237 int i;
239 texture = wmalloc(sizeof(WTexture));
240 memset(texture, 0, sizeof(WTexture));
241 texture->type = WTEX_IGRADIENT;
242 for (i = 0; i < 2; i++) {
243 texture->colors1[i] = colors1[i];
244 texture->colors2[i] = colors2[i];
246 texture->thickness1 = thickness1;
247 texture->thickness2 = thickness2;
248 if (thickness1 >= thickness2) {
249 texture->normal.red = (colors1[0].red + colors1[1].red) << 7;
250 texture->normal.green = (colors1[0].green + colors1[1].green) << 7;
251 texture->normal.blue = (colors1[0].blue + colors1[1].blue) << 7;
252 } else {
253 texture->normal.red = (colors2[0].red + colors2[1].red) << 7;
254 texture->normal.green = (colors2[0].green + colors2[1].green) << 7;
255 texture->normal.blue = (colors2[0].blue + colors2[1].blue) << 7;
257 XAllocColor(dpy, scr->w_colormap, &texture->normal);
258 gcv.background = gcv.foreground = texture->normal.pixel;
259 gcv.graphics_exposures = False;
260 texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
262 return texture;
265 WTexMGradient *wTextureMakeMGradient(WScreen * scr, int style, RColor ** colors)
267 WTexMGradient *texture;
268 XGCValues gcv;
269 int i;
271 texture = wmalloc(sizeof(WTexture));
272 memset(texture, 0, sizeof(WTexture));
273 texture->type = style;
274 texture->subtype = 0;
276 i = 0;
277 while (colors[i] != NULL)
278 i++;
279 i--;
280 texture->normal.red = (colors[0]->red << 8);
281 texture->normal.green = (colors[0]->green << 8);
282 texture->normal.blue = (colors[0]->blue << 8);
284 texture->colors = colors;
286 XAllocColor(dpy, scr->w_colormap, &texture->normal);
287 gcv.background = gcv.foreground = texture->normal.pixel;
288 gcv.graphics_exposures = False;
289 texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
291 return texture;
294 WTexPixmap *wTextureMakePixmap(WScreen * scr, int style, char *pixmap_file, XColor * color)
296 WTexPixmap *texture;
297 XGCValues gcv;
298 RImage *image;
299 char *file;
301 file = FindImage(wPreferences.pixmap_path, pixmap_file);
302 if (!file) {
303 wwarning(_("image file \"%s\" used as texture could not be found."), pixmap_file);
304 return NULL;
306 image = RLoadImage(scr->rcontext, file, 0);
307 if (!image) {
308 wwarning(_("could not load texture pixmap \"%s\":%s"), file, RMessageForError(RErrorCode));
309 wfree(file);
310 return NULL;
312 wfree(file);
314 texture = wmalloc(sizeof(WTexture));
315 memset(texture, 0, sizeof(WTexture));
316 texture->type = WTEX_PIXMAP;
317 texture->subtype = style;
319 texture->normal = *color;
321 XAllocColor(dpy, scr->w_colormap, &texture->normal);
322 gcv.background = gcv.foreground = texture->normal.pixel;
323 gcv.graphics_exposures = False;
324 texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
326 texture->pixmap = image;
328 return texture;
331 WTexTGradient *wTextureMakeTGradient(WScreen * scr, int style, RColor * from, RColor * to,
332 char *pixmap_file, int opacity)
334 WTexTGradient *texture;
335 XGCValues gcv;
336 RImage *image;
337 char *file;
339 file = FindImage(wPreferences.pixmap_path, pixmap_file);
340 if (!file) {
341 wwarning(_("image file \"%s\" used as texture could not be found."), pixmap_file);
342 return NULL;
344 image = RLoadImage(scr->rcontext, file, 0);
345 if (!image) {
346 wwarning(_("could not load texture pixmap \"%s\":%s"), file, RMessageForError(RErrorCode));
347 wfree(file);
348 return NULL;
350 wfree(file);
352 texture = wmalloc(sizeof(WTexture));
353 memset(texture, 0, sizeof(WTexture));
354 texture->type = style;
356 texture->opacity = opacity;
358 texture->color1 = *from;
359 texture->color2 = *to;
361 texture->normal.red = (from->red + to->red) << 7;
362 texture->normal.green = (from->green + to->green) << 7;
363 texture->normal.blue = (from->blue + to->blue) << 7;
365 XAllocColor(dpy, scr->w_colormap, &texture->normal);
366 gcv.background = gcv.foreground = texture->normal.pixel;
367 gcv.graphics_exposures = False;
368 texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
370 texture->pixmap = image;
372 return texture;
375 #ifdef TEXTURE_PLUGIN
376 WTexFunction *wTextureMakeFunction(WScreen * scr, char *lib, char *func, int argc, char **argv)
378 XColor fallbackColor;
379 XGCValues gcv;
380 WTexFunction *texture;
382 texture = wmalloc(sizeof(WTexture));
383 memset(&fallbackColor, 0, sizeof(fallbackColor));
385 texture->type = WTEX_FUNCTION;
386 texture->handle = NULL;
387 texture->render = 0;
388 texture->argc = argc;
389 texture->argv = argv;
391 fallbackColor.red = 0x8000;
392 fallbackColor.green = 0x8000;
393 fallbackColor.blue = 0x8000;
395 gcv.background = gcv.foreground = fallbackColor.pixel;
396 gcv.graphics_exposures = False;
397 texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
399 # ifdef HAVE_DLFCN_H
400 /* open the library */
401 texture->handle = dlopen(lib, RTLD_LAZY);
402 if (!texture->handle) {
403 wwarning(_("library \"%s\" cound not be opened."), lib);
404 wfree(argv);
405 wfree(texture);
406 return NULL;
409 /* find the function */
410 texture->render = dlsym(texture->handle, func);
411 if (!texture->render) {
412 wwarning(_("function \"%s\" not found in library \"%s\""), func, lib);
413 wfree(argv);
414 dlclose(texture->handle);
415 wfree(texture);
416 return NULL;
418 # else
419 wwarning(_("function textures not supported on this system, sorry."));
420 # endif
422 /* success! */
423 return texture;
425 #endif /* TEXTURE_PLUGIN */
427 RImage *wTextureRenderImage(WTexture * texture, int width, int height, int relief)
429 RImage *image = NULL;
430 RColor color1;
431 int d;
432 int subtype;
434 switch (texture->any.type) {
435 case WTEX_SOLID:
436 image = RCreateImage(width, height, False);
438 color1.red = texture->solid.normal.red >> 8;
439 color1.green = texture->solid.normal.green >> 8;
440 color1.blue = texture->solid.normal.blue >> 8;
441 color1.alpha = 255;
443 RClearImage(image, &color1);
444 break;
446 case WTEX_PIXMAP:
447 if (texture->pixmap.subtype == WTP_TILE) {
448 image = RMakeTiledImage(texture->pixmap.pixmap, width, height);
449 } else if (texture->pixmap.subtype == WTP_CENTER) {
450 color1.red = texture->pixmap.normal.red >> 8;
451 color1.green = texture->pixmap.normal.green >> 8;
452 color1.blue = texture->pixmap.normal.blue >> 8;
453 color1.alpha = 255;
454 image = RMakeCenteredImage(texture->pixmap.pixmap, width, height, &color1);
455 } else {
456 image = RScaleImage(texture->pixmap.pixmap, width, height);
458 break;
460 case WTEX_IGRADIENT:
461 image = RRenderInterwovenGradient(width, height,
462 texture->igradient.colors1,
463 texture->igradient.thickness1,
464 texture->igradient.colors2, texture->igradient.thickness2);
465 break;
467 case WTEX_HGRADIENT:
468 subtype = RGRD_HORIZONTAL;
469 goto render_gradient;
471 case WTEX_VGRADIENT:
472 subtype = RGRD_VERTICAL;
473 goto render_gradient;
475 case WTEX_DGRADIENT:
476 subtype = RGRD_DIAGONAL;
477 render_gradient:
479 image = RRenderGradient(width, height, &texture->gradient.color1,
480 &texture->gradient.color2, subtype);
481 break;
483 case WTEX_MHGRADIENT:
484 subtype = RGRD_HORIZONTAL;
485 goto render_mgradient;
487 case WTEX_MVGRADIENT:
488 subtype = RGRD_VERTICAL;
489 goto render_mgradient;
491 case WTEX_MDGRADIENT:
492 subtype = RGRD_DIAGONAL;
493 render_mgradient:
494 image = RRenderMultiGradient(width, height, &(texture->mgradient.colors[1]), subtype);
495 break;
497 case WTEX_THGRADIENT:
498 subtype = RGRD_HORIZONTAL;
499 goto render_tgradient;
501 case WTEX_TVGRADIENT:
502 subtype = RGRD_VERTICAL;
503 goto render_tgradient;
505 case WTEX_TDGRADIENT:
506 subtype = RGRD_DIAGONAL;
507 render_tgradient:
509 RImage *grad;
511 image = RMakeTiledImage(texture->tgradient.pixmap, width, height);
512 if (!image)
513 break;
515 grad = RRenderGradient(width, height, &texture->tgradient.color1,
516 &texture->tgradient.color2, subtype);
517 if (!grad) {
518 RReleaseImage(image);
519 image = NULL;
520 break;
523 RCombineImagesWithOpaqueness(image, grad, texture->tgradient.opacity);
524 RReleaseImage(grad);
526 break;
528 #ifdef TEXTURE_PLUGIN
529 case WTEX_FUNCTION:
530 #ifdef HAVE_DLFCN_H
531 if (texture->function.render) {
532 image = texture->function.render(texture->function.argc, texture->function.argv,
533 width, height, relief);
535 #endif
536 if (!image) {
537 RErrorCode = RERR_INTERNAL;
539 break;
540 #endif /* TEXTURE_PLUGIN */
542 default:
543 puts("ERROR in wTextureRenderImage()");
544 image = NULL;
545 break;
548 if (!image) {
549 RColor gray;
551 wwarning(_("could not render texture: %s"), RMessageForError(RErrorCode));
553 image = RCreateImage(width, height, False);
554 if (image == NULL) {
555 wwarning(_("could not allocate image buffer"));
556 return NULL;
559 gray.red = 190;
560 gray.green = 190;
561 gray.blue = 190;
562 gray.alpha = 255;
563 RClearImage(image, &gray);
566 /* render bevel */
568 switch (relief) {
569 case WREL_ICON:
570 d = RBEV_RAISED3;
571 break;
573 case WREL_RAISED:
574 d = RBEV_RAISED2;
575 break;
577 case WREL_SUNKEN:
578 d = RBEV_SUNKEN;
579 break;
581 case WREL_FLAT:
582 d = 0;
583 break;
585 case WREL_MENUENTRY:
586 d = -WREL_MENUENTRY;
587 break;
589 default:
590 d = 0;
593 if (d > 0) {
594 RBevelImage(image, d);
595 } else if (d < 0) {
596 bevelImage(image, -d);
599 return image;
602 static void bevelImage(RImage * image, int relief)
604 int width = image->width;
605 int height = image->height;
606 RColor color;
608 switch (relief) {
609 case WREL_MENUENTRY:
610 color.red = color.green = color.blue = 80;
611 color.alpha = 0;
612 /**/ ROperateLine(image, RAddOperation, 1, 0, width - 2, 0, &color);
613 /**/ ROperateLine(image, RAddOperation, 0, 0, 0, height - 1, &color);
615 color.red = color.green = color.blue = 40;
616 color.alpha = 0;
617 ROperateLine(image, RSubtractOperation, width - 1, 0, width - 1, height - 1, &color);
619 /**/ ROperateLine(image, RSubtractOperation, 1, height - 2, width - 2, height - 2, &color);
621 color.red = color.green = color.blue = 0;
622 color.alpha = 255;
623 RDrawLine(image, 0, height - 1, width - 1, height - 1, &color);
624 /**/ break;
629 void wDrawBevel(Drawable d, unsigned width, unsigned height, WTexSolid * texture, int relief)
631 GC light, dim, dark;
632 XSegment segs[4];
634 if (relief == WREL_FLAT)
635 return;
637 light = texture->light_gc;
638 dim = texture->dim_gc;
639 dark = texture->dark_gc;
640 switch (relief) {
641 case WREL_FLAT:
642 return;
643 case WREL_MENUENTRY:
644 case WREL_RAISED:
645 case WREL_ICON:
646 segs[0].x1 = 1;
647 segs[0].x2 = width - 2;
648 segs[0].y2 = segs[0].y1 = height - 2;
649 segs[1].x1 = width - 2;
650 segs[1].y1 = 1;
651 segs[1].x2 = width - 2;
652 segs[1].y2 = height - 2;
653 XDrawSegments(dpy, d, dim, segs, 2);
654 segs[0].x1 = 0;
655 segs[0].x2 = width - 1;
656 segs[0].y2 = segs[0].y1 = height - 1;
657 segs[1].x1 = segs[1].x2 = width - 1;
658 segs[1].y1 = 0;
659 segs[1].y2 = height - 1;
660 XDrawSegments(dpy, d, dark, segs, 2);
661 segs[0].x1 = segs[0].y1 = segs[0].y2 = 0;
662 segs[0].x2 = width - 2;
663 segs[1].x1 = segs[1].y1 = 0;
664 segs[1].x2 = 0;
665 segs[1].y2 = height - 2;
666 XDrawSegments(dpy, d, light, segs, 2);
667 if (relief == WREL_ICON) {
668 segs[0].x1 = segs[0].y1 = segs[0].y2 = 1;
669 segs[0].x2 = width - 2;
670 segs[1].x1 = segs[1].y1 = 1;
671 segs[1].x2 = 1;
672 segs[1].y2 = height - 2;
673 XDrawSegments(dpy, d, light, segs, 2);
675 break;
676 #ifdef unused
677 case WREL_SUNKEN:
678 segs[0].x1 = 0;
679 segs[0].x2 = width - 1;
680 segs[0].y2 = segs[0].y1 = 0;
681 segs[1].x1 = segs[1].x2 = 0;
682 segs[1].y1 = 0;
683 segs[1].y2 = height - 1;
684 XDrawSegments(dpy, d, dark, segs, 2);
686 segs[0].x1 = 0;
687 segs[0].y1 = segs[0].y2 = height - 1;
688 segs[0].x2 = width - 1;
689 segs[1].x2 = segs[1].x1 = width - 1;
690 segs[1].y1 = 1;
691 segs[1].y2 = height - 1;
692 XDrawSegments(dpy, d, light, segs, 2);
693 break;
694 #endif