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