*** empty log message ***
[dana/urxvt.git] / src / xpm.C
1 /*--------------------------------*-C-*---------------------------------*
2  * File:        xpm.C
3  *----------------------------------------------------------------------*
4  *
5  * All portions of code are copyright by their respective author/s.
6  * Copyright (c) 1997      Carsten Haitzler <raster@zip.com.au>
7  * Copyright (c) 1997,1998 Oezguer Kesim <kesim@math.fu-berlin.de>
8  * Copyright (c) 1998-2001 Geoff Wing <gcw@pobox.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  *---------------------------------------------------------------------*/
24
25 #include "../config.h"          /* NECESSARY */
26 #include "rxvt.h"               /* NECESSARY */
27 #include "xpm.intpro"           /* PROTOS for internal routines */
28
29 #ifdef XPM_BACKGROUND
30
31 /*
32  * These GEOM strings indicate absolute size/position:
33  * @ `WxH+X+Y'
34  * @ `WxH+X'    -> Y = X
35  * @ `WxH'      -> Y = X = 50
36  * @ `W+X+Y'    -> H = W
37  * @ `W+X'      -> H = W, Y = X
38  * @ `W'        -> H = W, X = Y = 50
39  * @ `0xH'      -> H *= H/100, X = Y = 50 (W unchanged)
40  * @ `Wx0'      -> W *= W/100, X = Y = 50 (H unchanged)
41  * @ `=+X+Y'    -> (H, W unchanged)
42  * @ `=+X'      -> Y = X (H, W unchanged)
43  *
44  * These GEOM strings adjust position relative to current position:
45  * @ `+X+Y'
46  * @ `+X'       -> Y = X
47  *
48  * And this GEOM string is for querying current scale/position:
49  * @ `?'
50  */
51 int
52 rxvt_term::scale_pixmap (const char *geom)
53 {
54   int             flags, changed = 0;
55   int             x = 0, y = 0;
56   unsigned int    w = 0, h = 0;
57   unsigned int    n;
58   char           *p, *str;
59   bgPixmap_t     *bgpixmap = & (bgPixmap);
60
61 #define MAXLEN_GEOM             sizeof("[1000x1000+1000+1000]")
62
63   if (geom == NULL)
64     return 0;
65   str = (char *)rxvt_malloc (MAXLEN_GEOM + 1);
66   if (!strcmp (geom, "?"))
67     {
68       sprintf (str, "[%dx%d+%d+%d]",    /* can't presume snprintf () ! */
69               min (bgpixmap->w, 9999), min (bgpixmap->h, 9999),
70               min (bgpixmap->x, 9999), min (bgpixmap->y, 9999));
71       process_xterm_seq (XTerm_title, str, CHAR_ST);
72       free (str);
73       return 0;
74     }
75
76   if ((p = strchr (geom, ';')) == NULL)
77     p = strchr (geom, '\0');
78   n = (p - geom);
79   if (n <= MAXLEN_GEOM)
80     {
81       strncpy (str, geom, n);
82       str[n] = '\0';
83
84       flags = XParseGeometry (str, &x, &y, &w, &h);
85       if (!flags)
86         {
87           flags |= WidthValue;
88           w = 0;
89         }                       /* default is tile */
90       if (flags & WidthValue)
91         {
92           if (! (flags & XValue))
93             x = 50;
94           if (! (flags & HeightValue))
95             h = w;
96           if (w && !h)
97             {
98               w = (bgpixmap->w * w) / 100;
99               h = bgpixmap->h;
100             }
101           else if (h && !w)
102             {
103               w = bgpixmap->w;
104               h = (bgpixmap->h * h) / 100;
105             }
106           if (w > 1000)
107             w = 1000;
108           if (h > 1000)
109             h = 1000;
110           if (bgpixmap->w != (short)w)
111             {
112               bgpixmap->w = (short)w;
113               changed++;
114             }
115           if (bgpixmap->h != (short)h)
116             {
117               bgpixmap->h = (short)h;
118               changed++;
119             }
120         }
121       if (! (flags & YValue))
122         {
123           if (flags & XNegative)
124             flags |= YNegative;
125           y = x;
126         }
127
128       if (! (flags & WidthValue) && geom[0] != '=')
129         {
130           x += bgpixmap->x;
131           y += bgpixmap->y;
132         }
133       else
134         {
135           if (flags & XNegative)
136             x += 100;
137           if (flags & YNegative)
138             y += 100;
139         }
140       MIN_IT (x, 100);
141       MIN_IT (y, 100);
142       MAX_IT (x, 0);
143       MAX_IT (y, 0);
144       if (bgpixmap->x != x)
145         {
146           bgpixmap->x = x;
147           changed++;
148         }
149       if (bgpixmap->y != y)
150         {
151           bgpixmap->y = y;
152           changed++;
153         }
154     }
155   free (str);
156   return changed;
157 }
158
159 void
160 rxvt_term::resize_pixmap ()
161 {
162   XGCValues       gcvalue;
163   GC              gc;
164   unsigned int    width = TermWin_TotalWidth ();
165   unsigned int    height = TermWin_TotalHeight ();
166   dDisp;
167
168   if (TermWin.pixmap != None)
169     XFreePixmap (disp, TermWin.pixmap);
170
171   if (bgPixmap.pixmap == None)
172     { /* So be it: I'm not using pixmaps */
173       TermWin.pixmap = None;
174
175       if (!(options & Opt_transparent) || !am_transparent)
176         XSetWindowBackground (disp, TermWin.vt,
177                               pix_colors[Color_bg]);
178
179       return;
180     }
181
182   gcvalue.foreground = pix_colors[Color_bg];
183   gc = XCreateGC (disp, TermWin.vt, GCForeground, &gcvalue);
184
185   if (bgPixmap.pixmap != None)
186     {   /* we have a specified pixmap */
187       unsigned int    w = bgPixmap.w, h = bgPixmap.h,
188                       x = bgPixmap.x, y = bgPixmap.y;
189       unsigned int    xpmh = xpmAttr.height,
190                       xpmw = xpmAttr.width;
191
192       /*
193        * don't zoom pixmap too much nor expand really small pixmaps
194        */
195       if (w > 1000 || h > 1000)
196         w = 1;
197       else if (width > (10 * xpmw)
198                || height > (10 * xpmh))
199         w = 0;          /* tile */
200
201       if (w == 0)
202         {
203           /* basic X tiling - let the X server do it */
204           TermWin.pixmap = XCreatePixmap (disp, TermWin.vt,
205                                          xpmw, xpmh,
206                                          (unsigned int)display->depth);
207           XCopyArea (disp, bgPixmap.pixmap, TermWin.pixmap, gc,
208                     0, 0, xpmw, xpmh, 0, 0);
209         }
210       else
211         {
212           float           incr, p;
213           Pixmap          tmp;
214
215           TermWin.pixmap = XCreatePixmap (disp, TermWin.vt,
216                                          width, height,
217                                          (unsigned int)display->depth);
218           /*
219            * horizontal scaling
220            */
221           rxvt_pixmap_incr (&w, &x, &incr, &p, width, xpmw);
222
223           tmp = XCreatePixmap (disp, TermWin.vt,
224                               width, xpmh, (unsigned int)display->depth);
225           XFillRectangle (disp, tmp, gc, 0, 0, width,
226                          xpmh);
227
228           for ( /*nil */ ; x < w; x++, p += incr)
229             {
230               if (p >= xpmw)
231                 p = 0;
232               /* copy one column from the original pixmap to the tmp pixmap */
233               XCopyArea (disp, bgPixmap.pixmap, tmp, gc,
234                         (int)p, 0, 1, xpmh, (int)x, 0);
235             }
236
237           /*
238            * vertical scaling
239            */
240           rxvt_pixmap_incr (&h, &y, &incr, &p, height, xpmh);
241
242           if (y > 0)
243             XFillRectangle (disp, TermWin.pixmap, gc, 0, 0, width, y);
244
245           if (h < height)
246             XFillRectangle (disp, TermWin.pixmap, gc, 0, (int)h, width, height - h + 1);
247
248           for ( /*nil */ ; y < h; y++, p += incr)
249             {
250               if (p >= xpmh)
251                 p = 0;
252
253               /* copy one row from the tmp pixmap to the main pixmap */
254               XCopyArea (disp, tmp, TermWin.pixmap, gc,
255                         0, (int)p, width, 1, 0, (int)y);
256             }
257
258           XFreePixmap (disp, tmp);
259         }
260     }
261
262   XSetWindowBackgroundPixmap (disp, TermWin.vt, TermWin.pixmap);
263   XFreeGC (disp, gc);
264   am_transparent = 0;
265 }
266
267 /*
268  * Calculate tiling sizes and increments
269  * At start, p == 0, incr == xpmwidthheight
270  */
271 /* INTPROTO */
272 static void
273 rxvt_pixmap_incr (unsigned int *wh, unsigned int *xy, float *incr, float *p, unsigned int widthheight, unsigned int xpmwidthheight)
274 {
275   unsigned int    cwh, cxy;
276   float           cincr, cp;
277
278   cp = 0;
279   cincr = (float)xpmwidthheight;
280   cxy = *xy;
281   cwh = *wh;
282   if (cwh == 1)
283     {   /* display one image, no horizontal/vertical scaling */
284       cincr = (float)widthheight;
285       if (xpmwidthheight <= widthheight)
286         {
287           cwh = xpmwidthheight;
288           cxy = (cxy * (widthheight - cwh)) / 100;      /* beware! order */
289           cwh += cxy;
290         }
291       else
292         {
293           cxy = 0;
294           cwh = widthheight;
295         }
296     }
297   else if (cwh < 10)
298     {   /* fit WH images across/down screen */
299       cincr *= cwh;
300       cxy = 0;
301       cwh = widthheight;
302     }
303   else
304     {
305       cincr *= 100.0 / cwh;
306       if (cwh < 100)
307         {       /* contract */
308           float           pos;
309
310           cwh = (cwh * widthheight) / 100;
311           pos = (float)cxy / 100 * widthheight - (cwh / 2);
312
313           cxy = (widthheight - cwh);
314           if (pos <= 0)
315             cxy = 0;
316           else if (pos < cxy)
317             cxy = (int) pos;
318           cwh += cxy;
319         }
320       else
321         {       /* expand */
322           if (cxy > 0)
323             {   /* position */
324               float           pos;
325
326               pos = (float)cxy / 100 * xpmwidthheight - (cincr / 2);
327               cp = xpmwidthheight - cincr;
328               if (pos <= 0)
329                 cp = 0;
330               else if (pos < cp)
331                 cp = pos;
332             }
333           cxy = 0;
334           cwh = widthheight;
335         }
336     }
337   cincr /= widthheight;
338   *wh = cwh;
339   *xy = cxy;
340   *incr = cincr;
341   *p = cp;
342 }
343
344 Pixmap
345 rxvt_term::set_bgPixmap (const char *file)
346 {
347   char *f;
348
349   assert (file != NULL);
350
351   if (bgPixmap.pixmap != None)
352     {
353       XFreePixmap (display->display, bgPixmap.pixmap);
354       bgPixmap.pixmap = None;
355     }
356
357   XSetWindowBackground (display->display, TermWin.vt, pix_colors[Color_bg]);
358
359   if (*file != '\0')
360     {
361       /*      XWindowAttributes attr; */
362
363       /*
364        * we already have the required attributes
365        */
366       /*      XGetWindowAttributes (display->display, TermWin.vt, &attr); */
367
368       xpmAttr.closeness = 30000;
369       xpmAttr.colormap = display->cmap;
370       xpmAttr.visual = display->visual;
371       xpmAttr.depth = display->depth;
372       xpmAttr.valuemask = (XpmCloseness | XpmColormap | XpmVisual |
373                            XpmDepth | XpmSize | XpmReturnPixels);
374
375       /* search environment variables here too */
376       f = (char *)rxvt_File_find (file, ".xpm", rs[Rs_path]);
377       if (f == NULL
378           || XpmReadFileToPixmap (display->display, display->root, f,
379                                  &bgPixmap.pixmap, NULL,
380                                  &xpmAttr))
381         {
382           char *p;
383
384           /* semi-colon delimited */
385           if ((p = strchr (file, ';')) == NULL)
386             p = strchr (file, '\0');
387
388           rxvt_warn ("couldn't load XPM file \"%.*s\", ignoring.\n", (p - file), file);
389         }
390
391       free (f);
392     }
393
394   resize_pixmap ();
395   return bgPixmap.pixmap;
396 }
397
398 #endif                          /* XPM_BACKGROUND */