remove non-planar surfaces
[mikachu/openbox.git] / render / render.c
1 #include <X11/Xlib.h>
2 #include <X11/Xutil.h>
3
4 #ifdef USE_GL
5 # include <GL/glx.h>
6 #endif
7
8 #include <glib.h>
9 #include "render.h"
10 #include "gradient.h"
11 #include "font.h"
12 #include "mask.h"
13 #include "color.h"
14 #include "image.h"
15 #include "theme.h"
16 #include "kernel/openbox.h"
17
18 #ifdef HAVE_STDLIB_H
19 #  include <stdlib.h>
20 #endif
21
22 int render_depth;
23 XVisualInfo render_visual_info;
24
25 Visual *render_visual;
26 Colormap render_colormap;
27
28 int render_red_offset = 0, render_green_offset = 0, render_blue_offset = 0;
29 int render_red_shift, render_green_shift, render_blue_shift;
30 int render_red_mask, render_green_mask, render_blue_mask;
31
32
33 #ifdef USE_GL
34
35 GLXContext render_glx_context;
36
37 int render_glx_rating(XVisualInfo *v)
38 {
39     int er;
40     int rating = 0;
41     int val;
42     printf("evaluating visual %d\n", v->visualid);
43     glXGetConfig(ob_display, v, GLX_BUFFER_SIZE, &val);
44     printf("buffer size %d\n", val);
45
46     switch (val) {
47     case 32:
48         rating += 300;
49     break;
50     case 24:
51         rating += 200;
52     break;
53     case 16:
54         rating += 100;
55     break;
56     }
57
58     glXGetConfig(ob_display, v, GLX_LEVEL, &val);
59     printf("level %d\n", val);
60     if (val != 0)
61         rating = -10000;
62
63     glXGetConfig(ob_display, v, GLX_DEPTH_SIZE, &val);
64     printf("depth size %d\n", val);
65     switch (val) {
66     case 32:
67         rating += 30;
68     break;
69     case 24:
70         rating += 20;
71     break;
72     case 16:
73         rating += 10;
74     break;
75     case 0:
76         rating -= 10000;
77     }
78
79     glXGetConfig(ob_display, v, GLX_DOUBLEBUFFER, &val);
80     printf("double buffer %d\n", val);
81     if (val)
82         rating++;
83     return rating;
84 }
85 #endif /* USE_GL */
86
87 void render_startup(void)
88 {
89     int count, i = 0, val, best = 0, rate = 0, temp;
90     XVisualInfo vimatch, *vilist;
91     paint = x_paint;
92
93     render_depth = DefaultDepth(ob_display, ob_screen);
94     render_visual = DefaultVisual(ob_display, ob_screen);
95     render_colormap = DefaultColormap(ob_display, ob_screen);
96
97 #ifdef USE_GL
98     vimatch.screen = ob_screen;
99     vimatch.class = TrueColor;
100     vilist = XGetVisualInfo(ob_display, VisualScreenMask | VisualClassMask,
101                             &vimatch, &count);
102
103     if (vilist) {
104         printf("looking for a GL visualin %d visuals\n", count);
105         for (i = 0; i < count; i++) {
106             glXGetConfig(ob_display, &vilist[i], GLX_USE_GL, &val);
107             if (val) {
108                 temp = render_glx_rating(&vilist[i]);
109                 if (temp > rate) {
110                     best = i;
111                     rate = temp;
112                 }
113             }
114         }
115     }
116     if (rate > 0) {
117         printf("picked visual %d with rating %d\n", best, rate);
118         render_depth = vilist[best].depth;
119         render_visual = vilist[best].visual;
120         render_colormap = XCreateColormap(ob_display, ob_root, 
121                                           render_visual, AllocNone);
122         render_visual_info = vilist[best];
123         render_glx_context = glXCreateContext(ob_display, &render_visual_info,
124                                               NULL, True);
125         if (render_glx_context == NULL)
126             printf("sadness\n");
127         else {
128             paint = gl_paint;
129         }
130     }
131 #endif /*USE_GL*/
132
133
134   switch (render_visual->class) {
135   case TrueColor:
136     truecolor_startup();
137     break;
138   case PseudoColor:
139   case StaticColor:
140   case GrayScale:
141   case StaticGray:
142     pseudocolor_startup();
143     break;
144   default:
145     g_critical("unsupported visual class.\n");
146     exit(EXIT_FAILURE);
147
148   }
149 }
150
151 void truecolor_startup(void)
152 {
153   unsigned long red_mask, green_mask, blue_mask;
154   XImage *timage = NULL;
155
156   timage = XCreateImage(ob_display, render_visual, render_depth,
157                         ZPixmap, 0, NULL, 1, 1, 32, 0);
158   g_assert(timage != NULL);
159   /* find the offsets for each color in the visual's masks */
160   render_red_mask = red_mask = timage->red_mask;
161   render_green_mask = green_mask = timage->green_mask;
162   render_blue_mask = blue_mask = timage->blue_mask;
163
164   render_red_offset = 0;
165   render_green_offset = 0;
166   render_blue_offset = 0;
167
168   while (! (red_mask & 1))   { render_red_offset++;   red_mask   >>= 1; }
169   while (! (green_mask & 1)) { render_green_offset++; green_mask >>= 1; }
170   while (! (blue_mask & 1))  { render_blue_offset++;  blue_mask  >>= 1; }
171
172   render_red_shift = render_green_shift = render_blue_shift = 8;
173   while (red_mask)   { red_mask   >>= 1; render_red_shift--;   }
174   while (green_mask) { green_mask >>= 1; render_green_shift--; }
175   while (blue_mask)  { blue_mask  >>= 1; render_blue_shift--;  }
176   XFree(timage);
177 }
178
179 void pseudocolor_startup(void)
180 {
181   XColor icolors[256];
182   int tr, tg, tb, n, r, g, b, i, incolors, ii;
183   unsigned long dev;
184   int cpc, _ncolors;
185   g_message("Initializing PseudoColor RenderControl\n");
186
187   /* determine the number of colors and the bits-per-color */
188   pseudo_bpc = 2; /* XXX THIS SHOULD BE A USER OPTION */
189   g_assert(pseudo_bpc >= 1);
190   _ncolors = pseudo_ncolors();
191
192   if (_ncolors > 1 << render_depth) {
193     g_warning("PseudoRenderControl: Invalid colormap size. Resizing.\n");
194     pseudo_bpc = 1 << (render_depth/3) >> 3;
195     _ncolors = 1 << (pseudo_bpc * 3);
196   }
197
198   /* build a color cube */
199   pseudo_colors = malloc(_ncolors * sizeof(XColor));
200   cpc = 1 << pseudo_bpc; /* colors per channel */
201
202   for (n = 0, r = 0; r < cpc; r++)
203     for (g = 0; g < cpc; g++)
204       for (b = 0; b < cpc; b++, n++) {
205         tr = (int)(((float)(r)/(float)(cpc-1)) * 0xFF);
206         tg = (int)(((float)(g)/(float)(cpc-1)) * 0xFF);
207         tb = (int)(((float)(b)/(float)(cpc-1)) * 0xFF);
208         pseudo_colors[n].red = tr | tr << 8;
209         pseudo_colors[n].green = tg | tg << 8;
210         pseudo_colors[n].blue = tb | tb << 8;
211         pseudo_colors[n].flags = DoRed|DoGreen|DoBlue; /* used to track 
212                                                           allocation */
213       }
214
215   /* allocate the colors */
216   for (i = 0; i < _ncolors; i++)
217     if (!XAllocColor(ob_display, render_colormap, &pseudo_colors[i]))
218       pseudo_colors[i].flags = 0; /* mark it as unallocated */
219
220   /* try allocate any colors that failed allocation above */
221
222   /* get the allocated values from the X server (only the first 256 XXX why!?)
223    */
224   incolors = (((1 << render_depth) > 256) ? 256 : (1 << render_depth));
225   for (i = 0; i < incolors; i++)
226     icolors[i].pixel = i;
227   XQueryColors(ob_display, render_colormap, icolors, incolors);
228
229   /* try match unallocated ones */
230   for (i = 0; i < _ncolors; i++) {
231     if (!pseudo_colors[i].flags) { /* if it wasn't allocated... */
232       unsigned long closest = 0xffffffff, close = 0;
233       for (ii = 0; ii < incolors; ii++) {
234         /* find deviations */
235         r = (pseudo_colors[i].red - icolors[ii].red) & 0xff;
236         g = (pseudo_colors[i].green - icolors[ii].green) & 0xff;
237         b = (pseudo_colors[i].blue - icolors[ii].blue) & 0xff;
238         /* find a weighted absolute deviation */
239         dev = (r * r) + (g * g) + (b * b);
240
241         if (dev < closest) {
242           closest = dev;
243           close = ii;
244         }
245       }
246
247       pseudo_colors[i].red = icolors[close].red;
248       pseudo_colors[i].green = icolors[close].green;
249       pseudo_colors[i].blue = icolors[close].blue;
250       pseudo_colors[i].pixel = icolors[close].pixel;
251
252       /* try alloc this closest color, it had better succeed! */
253       if (XAllocColor(ob_display, render_colormap, &pseudo_colors[i]))
254         pseudo_colors[i].flags = DoRed|DoGreen|DoBlue; /* mark as alloced */
255       else
256         g_assert(FALSE); /* wtf has gone wrong, its already alloced for
257                             chissake! */
258     }
259   }
260 }
261
262 void x_paint(Window win, Appearance *l)
263 {
264     int i, transferred = 0, sw;
265     pixel32 *source, *dest;
266     Pixmap oldp;
267     int x = l->area.x;
268     int y = l->area.y;
269     int w = l->area.width;
270     int h = l->area.height;
271     Rect tarea; /* area in which to draw textures */
272
273     if (w <= 0 || h <= 0 || x+w <= 0 || y+h <= 0) return;
274
275     oldp = l->pixmap; /* save to free after changing the visible pixmap */
276     l->pixmap = XCreatePixmap(ob_display, ob_root, x+w, y+h, render_depth);
277     g_assert(l->pixmap != None);
278
279     if (l->xftdraw != NULL)
280         XftDrawDestroy(l->xftdraw);
281     l->xftdraw = XftDrawCreate(ob_display, l->pixmap, render_visual, 
282                                render_colormap);
283     g_assert(l->xftdraw != NULL);
284
285     g_free(l->surface.pixel_data);
286     l->surface.pixel_data = g_new(pixel32, w * h);
287
288
289     if (l->surface.grad == Background_ParentRelative) {
290         sw = l->surface.parent->area.width;
291         source = l->surface.parent->surface.pixel_data
292             + l->surface.parentx
293             + sw * l->surface.parenty;
294         dest = l->surface.pixel_data;
295         for (i = 0; i < h; i++, source += sw, dest += w) {
296             memcpy(dest, source, w * sizeof(pixel32));
297         }
298     }
299     else if (l->surface.grad == Background_Solid)
300         gradient_solid(l, x, y, w, h);
301     else gradient_render(&l->surface, w, h);
302
303     for (i = 0; i < l->textures; i++) {
304         tarea = l->texture[i].position;
305         if (l->surface.grad != Background_ParentRelative) {
306             if (l->surface.relief != Flat) {
307                 switch (l->surface.bevel) {
308                 case Bevel1:
309                     tarea.x += 1; tarea.y += 1;
310                     tarea.width -= 2; tarea.height -= 2;
311                     break;
312                 case Bevel2:
313                     tarea.x += 2; tarea.y += 2;
314                     tarea.width -= 4; tarea.height -= 4;
315                     break;
316                 }
317             } else if (l->surface.border) {
318                 tarea.x += 1; tarea.y += 1;
319                 tarea.width -= 2; tarea.height -= 2;
320             }
321         }
322
323         switch (l->texture[i].type) {
324         case Text:
325             if (!transferred) {
326                 transferred = 1;
327                 if (l->surface.grad != Background_Solid)
328                     pixel32_to_pixmap(l->surface.pixel_data, 
329                                       l->pixmap,x,y,w,h);
330             }
331             if (l->xftdraw == NULL) {
332                 l->xftdraw = XftDrawCreate(ob_display, l->pixmap, 
333                                         render_visual, render_colormap);
334             }
335             font_draw(l->xftdraw, &l->texture[i].data.text, 
336                       &tarea);
337         break;
338         case Bitmask:
339             if (!transferred) {
340                 transferred = 1;
341                 if (l->surface.grad != Background_Solid)
342                     pixel32_to_pixmap(l->surface.pixel_data, 
343                                       l->pixmap,x,y,w,h);
344             }
345             if (l->texture[i].data.mask.color->gc == None)
346                 color_allocate_gc(l->texture[i].data.mask.color);
347             mask_draw(l->pixmap, &l->texture[i].data.mask,
348                       &tarea);
349         break;
350         case RGBA:
351             image_draw(l->surface.pixel_data, 
352                        &l->texture[i].data.rgba,
353                        &tarea, &l->area);
354         break;
355         }
356     }
357
358     if (!transferred) {
359         transferred = 1;
360         if (l->surface.grad != Background_Solid)
361             pixel32_to_pixmap(l->surface.pixel_data, l->pixmap
362                               ,x,y,w,h);
363     }
364
365
366     XSetWindowBackgroundPixmap(ob_display, win, l->pixmap);
367     XClearWindow(ob_display, win);
368     if (oldp != None) XFreePixmap(ob_display, oldp);
369 }
370
371 void render_shutdown(void)
372 {
373 }
374
375 Appearance *appearance_new(int numtex)
376 {
377   Surface *p;
378   Appearance *out;
379
380   out = g_new(Appearance, 1);
381   out->textures = numtex;
382   out->xftdraw = NULL;
383   if (numtex) out->texture = g_new0(Texture, numtex);
384   else out->texture = NULL;
385   out->pixmap = None;
386
387   p = &out->surface;
388   p->primary = NULL;
389   p->secondary = NULL;
390   p->border_color = NULL;
391   p->bevel_dark = NULL;
392   p->bevel_light = NULL;
393   p->pixel_data = NULL;
394   return out;
395 }
396
397 Appearance *appearance_copy(Appearance *orig)
398 {
399     Surface *spo, *spc;
400     Appearance *copy = g_new(Appearance, 1);
401
402     spo = &(orig->surface);
403     spc = &(copy->surface);
404     spc->grad = spo->grad;
405     spc->relief = spo->relief;
406     spc->bevel = spo->bevel;
407     if (spo->primary != NULL)
408         spc->primary = color_new(spo->primary->r,
409                                  spo->primary->g, 
410                                  spo->primary->b);
411     else spc->primary = NULL;
412
413     if (spo->secondary != NULL)
414         spc->secondary = color_new(spo->secondary->r,
415                                    spo->secondary->g,
416                                    spo->secondary->b);
417     else spc->secondary = NULL;
418
419     if (spo->border_color != NULL)
420         spc->border_color = color_new(spo->border_color->r,
421                                       spo->border_color->g,
422                                       spo->border_color->b);
423     else spc->border_color = NULL;
424
425     if (spo->bevel_dark != NULL)
426         spc->bevel_dark = color_new(spo->bevel_dark->r,
427                                     spo->bevel_dark->g,
428                                     spo->bevel_dark->b);
429     else spc->bevel_dark = NULL;
430
431     if (spo->bevel_light != NULL)
432         spc->bevel_light = color_new(spo->bevel_light->r,
433                                      spo->bevel_light->g,
434                                      spo->bevel_light->b);
435     else spc->bevel_light = NULL;
436
437     spc->interlaced = spo->interlaced;
438     spc->border = spo->border;
439     spc->pixel_data = NULL;
440
441     copy->textures = orig->textures;
442     copy->texture = g_memdup(orig->texture, orig->textures * sizeof(Texture));
443     copy->pixmap = None;
444     copy->xftdraw = NULL;
445     return copy;
446 }
447
448 void appearance_free(Appearance *a)
449 {
450     if (a) {
451         Surface *p;
452         if (a->pixmap != None) XFreePixmap(ob_display, a->pixmap);
453         if (a->xftdraw != NULL) XftDrawDestroy(a->xftdraw);
454         if (a->textures)
455             g_free(a->texture);
456         p = &a->surface;
457         color_free(p->primary);
458         color_free(p->secondary);
459         color_free(p->border_color);
460         color_free(p->bevel_dark);
461         color_free(p->bevel_light);
462         g_free(p->pixel_data);
463
464         g_free(a);
465     }
466 }
467
468
469 void pixel32_to_pixmap(pixel32 *in, Pixmap out, int x, int y, int w, int h)
470 {
471     pixel32 *scratch;
472     XImage *im = NULL;
473     im = XCreateImage(ob_display, render_visual, render_depth,
474                       ZPixmap, 0, NULL, w, h, 32, 0);
475     g_assert(im != NULL);
476     im->byte_order = render_endian;
477 /* this malloc is a complete waste of time on normal 32bpp
478    as reduce_depth just sets im->data = data and returns
479 */
480     scratch = g_new(pixel32, im->width * im->height);
481     im->data = (char*) scratch;
482     reduce_depth(in, im);
483     XPutImage(ob_display, out, DefaultGC(ob_display, ob_screen),
484               im, 0, 0, x, y, w, h);
485     im->data = NULL;
486     XDestroyImage(im);
487     g_free(scratch);
488 }
489
490 void appearance_minsize(Appearance *l, int *w, int *h)
491 {
492     int i;
493     int m;
494     *w = *h = 1;
495
496     if (l->surface.relief != Flat) {
497         switch (l->surface.bevel) {
498         case Bevel1:
499             *w = *h = 2;
500             break;
501         case Bevel2:
502             *w = *h = 4;
503             break;
504         }
505     } else if (l->surface.border)
506         *w = *h = 2;
507
508     for (i = 0; i < l->textures; ++i) {
509         switch (l->texture[i].type) {
510         case Bitmask:
511             *w += l->texture[i].data.mask.mask->w;
512             *h += l->texture[i].data.mask.mask->h;
513             break;
514         case Text:
515             m = font_measure_string(l->texture[i].data.text.font,
516                                     l->texture[i].data.text.string,
517                                     l->texture[i].data.text.shadow,
518                                     l->texture[i].data.text.offset);
519             *w += m;
520             m = font_height(l->texture[i].data.text.font,
521                             l->texture[i].data.text.shadow,
522                             l->texture[i].data.text.offset);
523             *h += m;
524             break;
525         case RGBA:
526             *w += l->texture[i].data.rgba.width;
527             *h += l->texture[i].data.rgba.height;
528             break;
529         case NoTexture:
530             break;
531         }
532     }
533 }
534
535 gboolean render_pixmap_to_rgba(Pixmap pmap, Pixmap mask,
536                                int *w, int *h, pixel32 **data)
537 {
538     Window xr;
539     int xx, xy;
540     guint pw, ph, mw, mh, xb, xd, i, x, y, di;
541     XImage *xi, *xm = NULL;
542
543     if (!XGetGeometry(ob_display, pmap, &xr, &xx, &xy, &pw, &ph, &xb, &xd))
544         return FALSE;
545     if (mask) {
546         if (!XGetGeometry(ob_display, mask, &xr, &xx, &xy, &mw, &mh, &xb, &xd))
547             return FALSE;
548         if (pw != mw || ph != mh || xd != 1)
549             return FALSE;
550     }
551
552     xi = XGetImage(ob_display, pmap, 0, 0, pw, ph, 0xffffffff, ZPixmap);
553     if (!xi)
554         return FALSE;
555
556     if (mask) {
557         xm = XGetImage(ob_display, mask, 0, 0, mw, mh, 0xffffffff, ZPixmap);
558         if (!xm)
559             return FALSE;
560     }
561
562     *data = g_new(pixel32, pw * ph);
563     increase_depth(*data, xi);
564
565     if (mask) {
566         /* apply transparency from the mask */
567         di = 0;
568         for (i = 0, y = 0; y < ph; ++y) {
569             for (x = 0; x < pw; ++x, ++i) {
570                 if (!((((unsigned)xm->data[di + x / 8]) >> (x % 8)) & 0x1))
571                     (*data)[i] &= ~(0xff << default_alpha_offset);
572             }
573             di += xm->bytes_per_line;
574         }
575     }
576
577     *w = pw;
578     *h = ph;
579
580     return TRUE;
581 }
582
583 #ifdef USE_GL
584 void gl_paint(Window win, Appearance *l)
585 {
586     int err;
587     Window root, child;
588     int i, transferred = 0, sw, b, d;
589     pixel32 *source, *dest;
590     Pixmap oldp;
591     int tempx, tempy, absx, absy, absw, absh;
592     int x = l->area.x;
593     int y = l->area.y;
594     int w = l->area.width;
595     int h = l->area.height;
596     Rect tarea; /* area in which to draw textures */
597     if (w <= 0 || h <= 0 || x+w <= 0 || y+h <= 0) return;
598
599     g_assert(l->surface.type == Surface_Planar);
600
601 printf("making %p, %p, %p current\n", ob_display, win, render_glx_context);
602     err = glXMakeCurrent(ob_display, win, render_glx_context);
603 g_assert(err != 0);
604
605             glMatrixMode(GL_MODELVIEW);
606             glLoadIdentity();
607             glMatrixMode(GL_PROJECTION);
608             glLoadIdentity();
609             glOrtho(0, 1376, 1032, 0, 0, 10);
610     if (XGetGeometry(ob_display, win, &root, &tempx, &tempy,
611                      &absw, &absh,  &b, &d) &&
612         XTranslateCoordinates(ob_display, win, root, tempx, tempy, 
613         &absx, &absy, &child))
614         printf("window at %d, %d (%d,%d)\n", absx, absy, absw, absh);
615     else
616         return;
617
618     glViewport(0, 0, 1376, 1032);
619     glMatrixMode(GL_MODELVIEW);
620     glTranslatef(-absx, 1032-absh-absy, 0);
621     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
622
623
624     if (l->surface.grad == Background_ParentRelative) {
625         printf("crap\n");
626     } else
627         render_gl_gradient(&l->surface, absx+x, absy+y, absw, absh);
628
629     glXSwapBuffers(ob_display, win);
630 }
631
632 #endif /* USE_GL */