1 /*--------------------------------*-C-*---------------------------------*
2  * File:	xpm.c
3  *----------------------------------------------------------------------*
4  * $Id: xpm.c,v 1.49 2001/07/27 06:38:37 gcw 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
rxvt_scale_pixmap(rxvt_t * r,const char * geom)54 rxvt_scale_pixmap(rxvt_t *r, 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(r, 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
rxvt_resize_pixmap(rxvt_t * r)148 rxvt_resize_pixmap(rxvt_t *r)
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
rxvt_pixmap_incr(unsigned int * wh,unsigned int * xy,float * incr,float * p,unsigned int widthheight,unsigned int xpmwidthheight)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
rxvt_set_bgPixmap(rxvt_t * r,const char * file)313 rxvt_set_bgPixmap(rxvt_t *r, 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(r);
358     return r->h->bgPixmap.pixmap;
359 }
360 
361 #endif				/* XPM_BACKGROUND */
362