all grads done
[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     g_assert(l->surface.type == Surface_Planar);
276
277     oldp = l->pixmap; /* save to free after changing the visible pixmap */
278     l->pixmap = XCreatePixmap(ob_display, ob_root, x+w, y+h, render_depth);
279     g_assert(l->pixmap != None);
280
281     if (l->xftdraw != NULL)
282         XftDrawDestroy(l->xftdraw);
283     l->xftdraw = XftDrawCreate(ob_display, l->pixmap, render_visual, 
284                                render_colormap);
285     g_assert(l->xftdraw != NULL);
286
287     g_free(l->surface.data.planar.pixel_data);
288     l->surface.data.planar.pixel_data = g_new(pixel32, w * h);
289
290
291     if (l->surface.data.planar.grad == Background_ParentRelative) {
292         sw = l->surface.data.planar.parent->area.width;
293         source = l->surface.data.planar.parent->surface.data.planar.pixel_data
294             + l->surface.data.planar.parentx
295             + sw * l->surface.data.planar.parenty;
296         dest = l->surface.data.planar.pixel_data;
297         for (i = 0; i < h; i++, source += sw, dest += w) {
298             memcpy(dest, source, w * sizeof(pixel32));
299         }
300     }
301     else if (l->surface.data.planar.grad == Background_Solid)
302         gradient_solid(l, x, y, w, h);
303     else gradient_render(&l->surface, w, h);
304
305     for (i = 0; i < l->textures; i++) {
306         tarea = l->texture[i].position;
307         if (l->surface.data.planar.grad != Background_ParentRelative) {
308             if (l->surface.data.planar.relief != Flat) {
309                 switch (l->surface.data.planar.bevel) {
310                 case Bevel1:
311                     tarea.x += 1; tarea.y += 1;
312                     tarea.width -= 2; tarea.height -= 2;
313                     break;
314                 case Bevel2:
315                     tarea.x += 2; tarea.y += 2;
316                     tarea.width -= 4; tarea.height -= 4;
317                     break;
318                 }
319             } else if (l->surface.data.planar.border) {
320                 tarea.x += 1; tarea.y += 1;
321                 tarea.width -= 2; tarea.height -= 2;
322             }
323         }
324
325         switch (l->texture[i].type) {
326         case Text:
327             if (!transferred) {
328                 transferred = 1;
329                 if (l->surface.data.planar.grad != Background_Solid)
330                     pixel32_to_pixmap(l->surface.data.planar.pixel_data, 
331                                       l->pixmap,x,y,w,h);
332             }
333             if (l->xftdraw == NULL) {
334                 l->xftdraw = XftDrawCreate(ob_display, l->pixmap, 
335                                         render_visual, render_colormap);
336             }
337             font_draw(l->xftdraw, &l->texture[i].data.text, 
338                       &tarea);
339         break;
340         case Bitmask:
341             if (!transferred) {
342                 transferred = 1;
343                 if (l->surface.data.planar.grad != Background_Solid)
344                     pixel32_to_pixmap(l->surface.data.planar.pixel_data, 
345                                       l->pixmap,x,y,w,h);
346             }
347             if (l->texture[i].data.mask.color->gc == None)
348                 color_allocate_gc(l->texture[i].data.mask.color);
349             mask_draw(l->pixmap, &l->texture[i].data.mask,
350                       &tarea);
351         break;
352         case RGBA:
353             image_draw(l->surface.data.planar.pixel_data, 
354                        &l->texture[i].data.rgba,
355                        &tarea, &l->area);
356         break;
357         }
358     }
359
360     if (!transferred) {
361         transferred = 1;
362         if (l->surface.data.planar.grad != Background_Solid)
363             pixel32_to_pixmap(l->surface.data.planar.pixel_data, l->pixmap
364                               ,x,y,w,h);
365     }
366
367
368     XSetWindowBackgroundPixmap(ob_display, win, l->pixmap);
369     XClearWindow(ob_display, win);
370     if (oldp != None) XFreePixmap(ob_display, oldp);
371 }
372
373 void render_shutdown(void)
374 {
375 }
376
377 Appearance *appearance_new(SurfaceType type, int numtex)
378 {
379   PlanarSurface *p;
380   Appearance *out;
381
382   out = g_new(Appearance, 1);
383   out->surface.type = type;
384   out->textures = numtex;
385   out->xftdraw = NULL;
386   if (numtex) out->texture = g_new0(Texture, numtex);
387   else out->texture = NULL;
388   out->pixmap = None;
389
390   switch (type) {
391   case Surface_Planar:
392     p = &out->surface.data.planar;
393     p->primary = NULL;
394     p->secondary = NULL;
395     p->border_color = NULL;
396     p->bevel_dark = NULL;
397     p->bevel_light = NULL;
398     p->pixel_data = NULL;
399     break;
400   }
401   return out;
402 }
403
404 Appearance *appearance_copy(Appearance *orig)
405 {
406     PlanarSurface *spo, *spc;
407     Appearance *copy = g_new(Appearance, 1);
408     copy->surface.type = orig->surface.type;
409     switch (orig->surface.type) {
410     case Surface_Planar:
411         spo = &(orig->surface.data.planar);
412         spc = &(copy->surface.data.planar);
413         spc->grad = spo->grad;
414         spc->relief = spo->relief;
415         spc->bevel = spo->bevel;
416         if (spo->primary != NULL)
417             spc->primary = color_new(spo->primary->r,
418                                      spo->primary->g, 
419                                      spo->primary->b);
420         else spc->primary = NULL;
421
422         if (spo->secondary != NULL)
423             spc->secondary = color_new(spo->secondary->r,
424                                        spo->secondary->g,
425                                        spo->secondary->b);
426         else spc->secondary = NULL;
427
428         if (spo->border_color != NULL)
429             spc->border_color = color_new(spo->border_color->r,
430                                           spo->border_color->g,
431                                           spo->border_color->b);
432         else spc->border_color = NULL;
433
434         if (spo->bevel_dark != NULL)
435             spc->bevel_dark = color_new(spo->bevel_dark->r,
436                                         spo->bevel_dark->g,
437                                         spo->bevel_dark->b);
438         else spc->bevel_dark = NULL;
439
440         if (spo->bevel_light != NULL)
441             spc->bevel_light = color_new(spo->bevel_light->r,
442                                          spo->bevel_light->g,
443                                          spo->bevel_light->b);
444         else spc->bevel_light = NULL;
445
446         spc->interlaced = spo->interlaced;
447         spc->border = spo->border;
448         spc->pixel_data = NULL;
449     break;
450     }
451     copy->textures = orig->textures;
452     copy->texture = g_memdup(orig->texture, orig->textures * sizeof(Texture));
453     copy->pixmap = None;
454     copy->xftdraw = NULL;
455     return copy;
456 }
457
458 void appearance_free(Appearance *a)
459 {
460     if (a) {
461         PlanarSurface *p;
462         if (a->pixmap != None) XFreePixmap(ob_display, a->pixmap);
463         if (a->xftdraw != NULL) XftDrawDestroy(a->xftdraw);
464         if (a->textures)
465             g_free(a->texture);
466         if (a->surface.type == Surface_Planar) {
467             p = &a->surface.data.planar;
468             color_free(p->primary);
469             color_free(p->secondary);
470             color_free(p->border_color);
471             color_free(p->bevel_dark);
472             color_free(p->bevel_light);
473             g_free(p->pixel_data);
474         }
475         g_free(a);
476     }
477 }
478
479
480 void pixel32_to_pixmap(pixel32 *in, Pixmap out, int x, int y, int w, int h)
481 {
482     pixel32 *scratch;
483     XImage *im = NULL;
484     im = XCreateImage(ob_display, render_visual, render_depth,
485                       ZPixmap, 0, NULL, w, h, 32, 0);
486     g_assert(im != NULL);
487     im->byte_order = render_endian;
488 /* this malloc is a complete waste of time on normal 32bpp
489    as reduce_depth just sets im->data = data and returns
490 */
491     scratch = g_new(pixel32, im->width * im->height);
492     im->data = (char*) scratch;
493     reduce_depth(in, im);
494     XPutImage(ob_display, out, DefaultGC(ob_display, ob_screen),
495               im, 0, 0, x, y, w, h);
496     im->data = NULL;
497     XDestroyImage(im);
498     g_free(scratch);
499 }
500
501 void appearance_minsize(Appearance *l, int *w, int *h)
502 {
503     int i;
504     int m;
505     *w = *h = 1;
506
507     switch (l->surface.type) {
508     case Surface_Planar:
509         if (l->surface.data.planar.relief != Flat) {
510             switch (l->surface.data.planar.bevel) {
511             case Bevel1:
512                 *w = *h = 2;
513                 break;
514             case Bevel2:
515                 *w = *h = 4;
516                 break;
517             }
518         } else if (l->surface.data.planar.border)
519             *w = *h = 2;
520
521         for (i = 0; i < l->textures; ++i) {
522             switch (l->texture[i].type) {
523             case Bitmask:
524                 *w += l->texture[i].data.mask.mask->w;
525                 *h += l->texture[i].data.mask.mask->h;
526                 break;
527             case Text:
528                 m = font_measure_string(l->texture[i].data.text.font,
529                                         l->texture[i].data.text.string,
530                                         l->texture[i].data.text.shadow,
531                                         l->texture[i].data.text.offset);
532                 *w += m;
533                 m = font_height(l->texture[i].data.text.font,
534                                 l->texture[i].data.text.shadow,
535                                 l->texture[i].data.text.offset);
536                 *h += m;
537                 break;
538             case RGBA:
539                 *w += l->texture[i].data.rgba.width;
540                 *h += l->texture[i].data.rgba.height;
541                 break;
542             case NoTexture:
543                 break;
544             }
545         }
546         break;
547     }
548 }
549
550 gboolean render_pixmap_to_rgba(Pixmap pmap, Pixmap mask,
551                                int *w, int *h, pixel32 **data)
552 {
553     Window xr;
554     int xx, xy;
555     guint pw, ph, mw, mh, xb, xd, i, x, y, di;
556     XImage *xi, *xm = NULL;
557
558     if (!XGetGeometry(ob_display, pmap, &xr, &xx, &xy, &pw, &ph, &xb, &xd))
559         return FALSE;
560     if (mask) {
561         if (!XGetGeometry(ob_display, mask, &xr, &xx, &xy, &mw, &mh, &xb, &xd))
562             return FALSE;
563         if (pw != mw || ph != mh || xd != 1)
564             return FALSE;
565     }
566
567     xi = XGetImage(ob_display, pmap, 0, 0, pw, ph, 0xffffffff, ZPixmap);
568     if (!xi)
569         return FALSE;
570
571     if (mask) {
572         xm = XGetImage(ob_display, mask, 0, 0, mw, mh, 0xffffffff, ZPixmap);
573         if (!xm)
574             return FALSE;
575     }
576
577     *data = g_new(pixel32, pw * ph);
578     increase_depth(*data, xi);
579
580     if (mask) {
581         /* apply transparency from the mask */
582         di = 0;
583         for (i = 0, y = 0; y < ph; ++y) {
584             for (x = 0; x < pw; ++x, ++i) {
585                 if (!((((unsigned)xm->data[di + x / 8]) >> (x % 8)) & 0x1))
586                     (*data)[i] &= ~(0xff << default_alpha_offset);
587             }
588             di += xm->bytes_per_line;
589         }
590     }
591
592     *w = pw;
593     *h = ph;
594
595     return TRUE;
596 }
597
598 #ifdef USE_GL
599 void gl_paint(Window win, Appearance *l)
600 {
601     int err;
602     Window root, child;
603     int i, transferred = 0, sw, b, d;
604     pixel32 *source, *dest;
605     Pixmap oldp;
606     int tempx, tempy, absx, absy, absw, absh;
607     int x = l->area.x;
608     int y = l->area.y;
609     int w = l->area.width;
610     int h = l->area.height;
611     Rect tarea; /* area in which to draw textures */
612     if (w <= 0 || h <= 0 || x+w <= 0 || y+h <= 0) return;
613
614     g_assert(l->surface.type == Surface_Planar);
615
616 printf("making %p, %p, %p current\n", ob_display, win, render_glx_context);
617     err = glXMakeCurrent(ob_display, win, render_glx_context);
618 g_assert(err != 0);
619
620             glMatrixMode(GL_MODELVIEW);
621             glLoadIdentity();
622             glMatrixMode(GL_PROJECTION);
623             glLoadIdentity();
624             glOrtho(0, 1376, 1032, 0, 0, 10);
625     if (XGetGeometry(ob_display, win, &root, &tempx, &tempy,
626                      &absw, &absh,  &b, &d) &&
627         XTranslateCoordinates(ob_display, win, root, tempx, tempy, 
628         &absx, &absy, &child))
629         printf("window at %d, %d (%d,%d)\n", absx, absy, absw, absh);
630     else
631         return;
632
633     glViewport(0, 0, 1376, 1032);
634     glMatrixMode(GL_MODELVIEW);
635     glTranslatef(-absx, 1032-absh-absy, 0);
636     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
637
638
639     if (l->surface.data.planar.grad == Background_ParentRelative) {
640         printf("crap\n");
641     } else
642         render_gl_gradient(&l->surface, absx+x, absy+y, absw, absh);
643
644     glXSwapBuffers(ob_display, win);
645 }
646
647 #endif /* USE_GL */