Change to the linux kernel coding style
[wmaker-crm.git] / src / texture.c
1 /*
2 * Window Maker window manager
3 *
4 * Copyright (c) 1997-2003 Alfredo K. Kojima
5 *
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.
10 *
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.
15 *
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.
20 */
21
22 #include "wconfig.h"
23
24 #include <X11/Xlib.h>
25 #include <X11/Xutil.h>
26
27 #ifdef TEXTURE_PLUGIN
28 # ifdef HAVE_DLFCN_H
29 # include <dlfcn.h>
30 # endif
31 #endif
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36
37 #include <wraster.h>
38
39 #include "WindowMaker.h"
40 #include "wcore.h"
41 #include "texture.h"
42 #include "funcs.h"
43
44 extern WPreferences wPreferences;
45
46 static void bevelImage(RImage * image, int relief);
47
48 WTexSolid *wTextureMakeSolid(WScreen * scr, XColor * color)
49 {
50 WTexSolid *texture;
51 int gcm;
52 XGCValues gcv;
53
54 texture = wmalloc(sizeof(WTexture));
55
56 texture->type = WTEX_SOLID;
57 texture->subtype = 0;
58
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;
72
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;
79
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;
86
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;
92 }
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);
99
100 gcm = GCForeground | GCBackground | GCGraphicsExposures;
101 gcv.graphics_exposures = False;
102
103 gcv.background = gcv.foreground = texture->light.pixel;
104 texture->light_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
105
106 gcv.background = gcv.foreground = texture->dim.pixel;
107 texture->dim_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
108
109 gcv.background = gcv.foreground = texture->dark.pixel;
110 texture->dark_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
111
112 gcv.background = gcv.foreground = color->pixel;
113 texture->normal_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
114
115 return texture;
116 }
117
118 static int dummyErrorHandler(Display * foo, XErrorEvent * bar)
119 {
120 #ifdef DEBUG
121 wwarning("your server is buggy. Tell the author if some error related to color occurs");
122 #endif
123 return 0;
124 }
125
126 void wTextureDestroy(WScreen * scr, WTexture * texture)
127 {
128 int i;
129 int count = 0;
130 unsigned long colors[8];
131
132 #ifdef DEBUG
133 if (texture == NULL) {
134 printf("BUG: trying to free NULL texture\n");
135 return;
136 }
137 #endif
138
139 /*
140 * some stupid servers don't like white or black being freed...
141 */
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;
155
156 case WTEX_PIXMAP:
157 RReleaseImage(texture->pixmap.pixmap);
158 break;
159
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]);
165 }
166 wfree(texture->mgradient.colors);
167 break;
168
169 case WTEX_THGRADIENT:
170 case WTEX_TVGRADIENT:
171 case WTEX_TDGRADIENT:
172 RReleaseImage(texture->tgradient.pixmap);
173 break;
174
175 #ifdef TEXTURE_PLUGIN
176 case WTEX_FUNCTION:
177 #ifdef HAVE_DLFCN_H
178 if (texture->function.handle) {
179 dlclose(texture->function.handle);
180 }
181 #endif
182 for (i = 0; i < texture->function.argc; i++) {
183 wfree(texture->function.argv[i]);
184 }
185 wfree(texture->function.argv);
186 break;
187 #endif /* TEXTURE_PLUGIN */
188 }
189 if (CANFREE(texture->any.color.pixel))
190 colors[count++] = texture->any.color.pixel;
191 if (count > 0) {
192 XErrorHandler oldhandler;
193
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);
201 }
202 XFreeGC(dpy, texture->any.gc);
203 wfree(texture);
204 #undef CANFREE
205 }
206
207 WTexGradient *wTextureMakeGradient(WScreen * scr, int style, RColor * from, RColor * to)
208 {
209 WTexGradient *texture;
210 XGCValues gcv;
211
212 texture = wmalloc(sizeof(WTexture));
213 memset(texture, 0, sizeof(WTexture));
214 texture->type = style;
215 texture->subtype = 0;
216
217 texture->color1 = *from;
218 texture->color2 = *to;
219
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;
223
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);
228
229 return texture;
230 }
231
232 WTexIGradient *wTextureMakeIGradient(WScreen * scr, int thickness1, RColor colors1[2],
233 int thickness2, RColor colors2[2])
234 {
235 WTexIGradient *texture;
236 XGCValues gcv;
237 int i;
238
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];
245 }
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;
256 }
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);
261
262 return texture;
263 }
264
265 WTexMGradient *wTextureMakeMGradient(WScreen * scr, int style, RColor ** colors)
266 {
267 WTexMGradient *texture;
268 XGCValues gcv;
269 int i;
270
271 texture = wmalloc(sizeof(WTexture));
272 memset(texture, 0, sizeof(WTexture));
273 texture->type = style;
274 texture->subtype = 0;
275
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);
283
284 texture->colors = colors;
285
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);
290
291 return texture;
292 }
293
294 WTexPixmap *wTextureMakePixmap(WScreen * scr, int style, char *pixmap_file, XColor * color)
295 {
296 WTexPixmap *texture;
297 XGCValues gcv;
298 RImage *image;
299 char *file;
300
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;
305 }
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;
311 }
312 wfree(file);
313
314 texture = wmalloc(sizeof(WTexture));
315 memset(texture, 0, sizeof(WTexture));
316 texture->type = WTEX_PIXMAP;
317 texture->subtype = style;
318
319 texture->normal = *color;
320
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);
325
326 texture->pixmap = image;
327
328 return texture;
329 }
330
331 WTexTGradient *wTextureMakeTGradient(WScreen * scr, int style, RColor * from, RColor * to,
332 char *pixmap_file, int opacity)
333 {
334 WTexTGradient *texture;
335 XGCValues gcv;
336 RImage *image;
337 char *file;
338
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;
343 }
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;
349 }
350 wfree(file);
351
352 texture = wmalloc(sizeof(WTexture));
353 memset(texture, 0, sizeof(WTexture));
354 texture->type = style;
355
356 texture->opacity = opacity;
357
358 texture->color1 = *from;
359 texture->color2 = *to;
360
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;
364
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);
369
370 texture->pixmap = image;
371
372 return texture;
373 }
374
375 #ifdef TEXTURE_PLUGIN
376 WTexFunction *wTextureMakeFunction(WScreen * scr, char *lib, char *func, int argc, char **argv)
377 {
378 XColor fallbackColor;
379 XGCValues gcv;
380 WTexFunction *texture;
381
382 texture = wmalloc(sizeof(WTexture));
383 texture->type = WTEX_FUNCTION;
384 texture->handle = NULL;
385 texture->render = 0;
386 texture->argc = argc;
387 texture->argv = argv;
388
389 fallbackColor.red = 0x8000;
390 fallbackColor.green = 0x8000;
391 fallbackColor.blue = 0x8000;
392
393 gcv.background = gcv.foreground = fallbackColor.pixel;
394 gcv.graphics_exposures = False;
395 texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
396
397 # ifdef HAVE_DLFCN_H
398 /* open the library */
399 texture->handle = dlopen(lib, RTLD_LAZY);
400 if (!texture->handle) {
401 wwarning(_("library \"%s\" cound not be opened."), lib);
402 wfree(argv);
403 wfree(texture);
404 return NULL;
405 }
406
407 /* find the function */
408 texture->render = dlsym(texture->handle, func);
409 if (!texture->render) {
410 wwarning(_("function \"%s\" not found in library \"%s\""), func, lib);
411 wfree(argv);
412 dlclose(texture->handle);
413 wfree(texture);
414 return NULL;
415 }
416 # else
417 wwarning(_("function textures not supported on this system, sorry."));
418 # endif
419
420 /* success! */
421 return texture;
422 }
423 #endif /* TEXTURE_PLUGIN */
424
425 RImage *wTextureRenderImage(WTexture * texture, int width, int height, int relief)
426 {
427 RImage *image = NULL;
428 RColor color1;
429 int d;
430 int subtype;
431
432 switch (texture->any.type) {
433 case WTEX_SOLID:
434 image = RCreateImage(width, height, False);
435
436 color1.red = texture->solid.normal.red >> 8;
437 color1.green = texture->solid.normal.green >> 8;
438 color1.blue = texture->solid.normal.blue >> 8;
439 color1.alpha = 255;
440
441 RClearImage(image, &color1);
442 break;
443
444 case WTEX_PIXMAP:
445 if (texture->pixmap.subtype == WTP_TILE) {
446 image = RMakeTiledImage(texture->pixmap.pixmap, width, height);
447 } else if (texture->pixmap.subtype == WTP_CENTER) {
448 color1.red = texture->pixmap.normal.red >> 8;
449 color1.green = texture->pixmap.normal.green >> 8;
450 color1.blue = texture->pixmap.normal.blue >> 8;
451 color1.alpha = 255;
452 image = RMakeCenteredImage(texture->pixmap.pixmap, width, height, &color1);
453 } else {
454 image = RScaleImage(texture->pixmap.pixmap, width, height);
455 }
456 break;
457
458 case WTEX_IGRADIENT:
459 image = RRenderInterwovenGradient(width, height,
460 texture->igradient.colors1,
461 texture->igradient.thickness1,
462 texture->igradient.colors2, texture->igradient.thickness2);
463 break;
464
465 case WTEX_HGRADIENT:
466 subtype = RGRD_HORIZONTAL;
467 goto render_gradient;
468
469 case WTEX_VGRADIENT:
470 subtype = RGRD_VERTICAL;
471 goto render_gradient;
472
473 case WTEX_DGRADIENT:
474 subtype = RGRD_DIAGONAL;
475 render_gradient:
476
477 image = RRenderGradient(width, height, &texture->gradient.color1,
478 &texture->gradient.color2, subtype);
479 break;
480
481 case WTEX_MHGRADIENT:
482 subtype = RGRD_HORIZONTAL;
483 goto render_mgradient;
484
485 case WTEX_MVGRADIENT:
486 subtype = RGRD_VERTICAL;
487 goto render_mgradient;
488
489 case WTEX_MDGRADIENT:
490 subtype = RGRD_DIAGONAL;
491 render_mgradient:
492 image = RRenderMultiGradient(width, height, &(texture->mgradient.colors[1]), subtype);
493 break;
494
495 case WTEX_THGRADIENT:
496 subtype = RGRD_HORIZONTAL;
497 goto render_tgradient;
498
499 case WTEX_TVGRADIENT:
500 subtype = RGRD_VERTICAL;
501 goto render_tgradient;
502
503 case WTEX_TDGRADIENT:
504 subtype = RGRD_DIAGONAL;
505 render_tgradient:
506 {
507 RImage *grad;
508
509 image = RMakeTiledImage(texture->tgradient.pixmap, width, height);
510 if (!image)
511 break;
512
513 grad = RRenderGradient(width, height, &texture->tgradient.color1,
514 &texture->tgradient.color2, subtype);
515 if (!grad) {
516 RReleaseImage(image);
517 image = NULL;
518 break;
519 }
520
521 RCombineImagesWithOpaqueness(image, grad, texture->tgradient.opacity);
522 RReleaseImage(grad);
523 }
524 break;
525
526 #ifdef TEXTURE_PLUGIN
527 case WTEX_FUNCTION:
528 #ifdef HAVE_DLFCN_H
529 if (texture->function.render) {
530 image = texture->function.render(texture->function.argc, texture->function.argv,
531 width, height, relief);
532 }
533 #endif
534 if (!image) {
535 RErrorCode = RERR_INTERNAL;
536 }
537 break;
538 #endif /* TEXTURE_PLUGIN */
539
540 default:
541 puts("ERROR in wTextureRenderImage()");
542 image = NULL;
543 break;
544 }
545
546 if (!image) {
547 RColor gray;
548
549 wwarning(_("could not render texture: %s"), RMessageForError(RErrorCode));
550
551 image = RCreateImage(width, height, False);
552 if (image == NULL) {
553 wwarning(_("could not allocate image buffer"));
554 return NULL;
555 }
556
557 gray.red = 190;
558 gray.green = 190;
559 gray.blue = 190;
560 gray.alpha = 255;
561 RClearImage(image, &gray);
562 }
563
564 /* render bevel */
565
566 switch (relief) {
567 case WREL_ICON:
568 d = RBEV_RAISED3;
569 break;
570
571 case WREL_RAISED:
572 d = RBEV_RAISED2;
573 break;
574
575 case WREL_SUNKEN:
576 d = RBEV_SUNKEN;
577 break;
578
579 case WREL_FLAT:
580 d = 0;
581 break;
582
583 case WREL_MENUENTRY:
584 d = -WREL_MENUENTRY;
585 break;
586
587 default:
588 d = 0;
589 }
590
591 if (d > 0) {
592 RBevelImage(image, d);
593 } else if (d < 0) {
594 bevelImage(image, -d);
595 }
596
597 return image;
598 }
599
600 static void bevelImage(RImage * image, int relief)
601 {
602 int width = image->width;
603 int height = image->height;
604 RColor color;
605
606 switch (relief) {
607 case WREL_MENUENTRY:
608 color.red = color.green = color.blue = 80;
609 color.alpha = 0;
610 /**/ ROperateLine(image, RAddOperation, 1, 0, width - 2, 0, &color);
611 /**/ ROperateLine(image, RAddOperation, 0, 0, 0, height - 1, &color);
612
613 color.red = color.green = color.blue = 40;
614 color.alpha = 0;
615 ROperateLine(image, RSubtractOperation, width - 1, 0, width - 1, height - 1, &color);
616
617 /**/ ROperateLine(image, RSubtractOperation, 1, height - 2, width - 2, height - 2, &color);
618
619 color.red = color.green = color.blue = 0;
620 color.alpha = 255;
621 RDrawLine(image, 0, height - 1, width - 1, height - 1, &color);
622 /**/ break;
623
624 }
625 }
626
627 void wDrawBevel(Drawable d, unsigned width, unsigned height, WTexSolid * texture, int relief)
628 {
629 GC light, dim, dark;
630 XSegment segs[4];
631
632 if (relief == WREL_FLAT)
633 return;
634
635 light = texture->light_gc;
636 dim = texture->dim_gc;
637 dark = texture->dark_gc;
638 switch (relief) {
639 case WREL_FLAT:
640 return;
641 case WREL_MENUENTRY:
642 case WREL_RAISED:
643 case WREL_ICON:
644 segs[0].x1 = 1;
645 segs[0].x2 = width - 2;
646 segs[0].y2 = segs[0].y1 = height - 2;
647 segs[1].x1 = width - 2;
648 segs[1].y1 = 1;
649 segs[1].x2 = width - 2;
650 segs[1].y2 = height - 2;
651 XDrawSegments(dpy, d, dim, segs, 2);
652 segs[0].x1 = 0;
653 segs[0].x2 = width - 1;
654 segs[0].y2 = segs[0].y1 = height - 1;
655 segs[1].x1 = segs[1].x2 = width - 1;
656 segs[1].y1 = 0;
657 segs[1].y2 = height - 1;
658 XDrawSegments(dpy, d, dark, segs, 2);
659 segs[0].x1 = segs[0].y1 = segs[0].y2 = 0;
660 segs[0].x2 = width - 2;
661 segs[1].x1 = segs[1].y1 = 0;
662 segs[1].x2 = 0;
663 segs[1].y2 = height - 2;
664 XDrawSegments(dpy, d, light, segs, 2);
665 if (relief == WREL_ICON) {
666 segs[0].x1 = segs[0].y1 = segs[0].y2 = 1;
667 segs[0].x2 = width - 2;
668 segs[1].x1 = segs[1].y1 = 1;
669 segs[1].x2 = 1;
670 segs[1].y2 = height - 2;
671 XDrawSegments(dpy, d, light, segs, 2);
672 }
673 break;
674 #ifdef unused
675 case WREL_SUNKEN:
676 segs[0].x1 = 0;
677 segs[0].x2 = width - 1;
678 segs[0].y2 = segs[0].y1 = 0;
679 segs[1].x1 = segs[1].x2 = 0;
680 segs[1].y1 = 0;
681 segs[1].y2 = height - 1;
682 XDrawSegments(dpy, d, dark, segs, 2);
683
684 segs[0].x1 = 0;
685 segs[0].y1 = segs[0].y2 = height - 1;
686 segs[0].x2 = width - 1;
687 segs[1].x2 = segs[1].x1 = width - 1;
688 segs[1].y1 = 1;
689 segs[1].y2 = height - 1;
690 XDrawSegments(dpy, d, light, segs, 2);
691 break;
692 #endif
693 }
694 }