change how the widgets' _dirty flag works so that all inheritence levels of the widge...
[mikachu/openbox.git] / util / bsetroot.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // bsetroot.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh at debian.org>
4 // Copyright (c) 1997 - 2000, 2002 Brad Hughes <bhughes at trolltech.com>
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #ifdef HAVE_STDLIB_H
30 #  include <stdlib.h>
31 #endif // HAVE_STDLIB_H
32
33 #ifdef HAVE_STRING_H
34 #  include <string.h>
35 #endif // HAVE_STRING_H
36
37 #ifdef    HAVE_STDIO_H
38 #  include <stdio.h>
39 #endif // HAVE_STDIO_H
40
41 #ifdef    HAVE_CTYPE_H
42 # include <ctype.h>
43 #endif // HAVE_CTYPE_H
44 }
45
46 #include "gccache.hh"
47 #include "texture.hh"
48 #include "util.hh"
49 #include "bsetroot.hh"
50
51 bsetroot::bsetroot(int argc, char **argv, char *dpy_name)
52   : BaseDisplay(argv[0], dpy_name) {
53
54   bool mod = False, sol = False, grd = False;
55   int mod_x = 0, mod_y = 0;
56
57   for (int i = 1; i < argc; i++) {
58     if (! strcmp("-help", argv[i])) {
59       usage();
60     } else if ((! strcmp("-fg", argv[i])) ||
61                (! strcmp("-foreground", argv[i])) ||
62                (! strcmp("-from", argv[i]))) {
63       if ((++i) >= argc) usage(1);
64
65       fore = argv[i];
66     } else if ((! strcmp("-bg", argv[i])) ||
67                (! strcmp("-background", argv[i])) ||
68                (! strcmp("-to", argv[i]))) {
69       if ((++i) >= argc) usage(1);
70
71       back = argv[i];
72     } else if (! strcmp("-solid", argv[i])) {
73       if ((++i) >= argc) usage(1);
74
75       fore = argv[i];
76       sol = True;
77     } else if (! strcmp("-mod", argv[i])) {
78       if ((++i) >= argc) usage();
79
80       mod_x = atoi(argv[i]);
81
82       if ((++i) >= argc) usage();
83
84       mod_y = atoi(argv[i]);
85
86       if (mod_x < 1) mod_x = 1;
87       if (mod_y < 1) mod_y = 1;
88
89       mod = True;
90     } else if (! strcmp("-gradient", argv[i])) {
91       if ((++i) >= argc) usage();
92
93       grad = argv[i];
94       grd = True;
95     } else if (! strcmp("-display", argv[i])) {
96       // -display passed through tests ealier... we just skip it now
97       i++;
98     } else {
99       usage();
100     }
101   }
102
103   if ((mod + sol + grd) != True) {
104     fprintf(stderr,
105             "%s: error: must specify one of: -solid, -mod, -gradient\n",
106             getApplicationName());
107
108     usage(2);
109   }
110
111   img_ctrl = new BImageControl*[getNumberOfScreens()];
112   for (unsigned int s = 0; s < getNumberOfScreens(); ++s)
113     img_ctrl[s] = new BImageControl(this, getScreenInfo(s), True);
114
115   if (sol && ! fore.empty())
116     solid();
117   else if (mod && mod_x && mod_y && ! (fore.empty() || back.empty()))
118     modula(mod_x, mod_y);
119   else if (grd && ! (grad.empty() || fore.empty() || back.empty()))
120     gradient();
121   else usage();
122 }
123
124
125 bsetroot::~bsetroot(void) {
126   XSetCloseDownMode(getXDisplay(), RetainPermanent);
127
128   XKillClient(getXDisplay(), AllTemporary);
129
130   std::for_each(img_ctrl, img_ctrl + getNumberOfScreens(), PointerAssassin());
131
132   delete [] img_ctrl;
133 }
134
135
136 // adapted from wmsetbg
137 void bsetroot::setPixmapProperty(int screen, Pixmap pixmap) {
138   static Atom rootpmap_id = None, esetroot_id = None;
139   Atom type;
140   int format;
141   unsigned long length, after;
142   unsigned char *data;
143   const ScreenInfo *screen_info = getScreenInfo(screen);
144
145   if (rootpmap_id == None) {
146     rootpmap_id = XInternAtom(getXDisplay(), "_XROOTPMAP_ID", False);
147     esetroot_id = XInternAtom(getXDisplay(), "ESETROOT_PMAP_ID", False);
148   }
149
150   XGrabServer(getXDisplay());
151
152   /* Clear out the old pixmap */
153   XGetWindowProperty(getXDisplay(), screen_info->getRootWindow(),
154                      rootpmap_id, 0L, 1L, False, AnyPropertyType,
155                      &type, &format, &length, &after, &data);
156
157   if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
158     unsigned char* data_esetroot = 0;
159     XGetWindowProperty(getXDisplay(), screen_info->getRootWindow(),
160                        esetroot_id, 0L, 1L, False, AnyPropertyType,
161                        &type, &format, &length, &after, &data_esetroot);
162     if (data && data_esetroot && *((Pixmap *) data)) {
163       XKillClient(getXDisplay(), *((Pixmap *) data));
164       XSync(getXDisplay(), False);
165       XFree(data_esetroot);
166     }
167     XFree(data);
168   }
169
170   if (pixmap) {
171     XChangeProperty(getXDisplay(), screen_info->getRootWindow(),
172                     rootpmap_id, XA_PIXMAP, 32, PropModeReplace,
173                     (unsigned char *) &pixmap, 1);
174     XChangeProperty(getXDisplay(), screen_info->getRootWindow(),
175                     esetroot_id, XA_PIXMAP, 32, PropModeReplace,
176                     (unsigned char *) &pixmap, 1);
177   } else {
178     XDeleteProperty(getXDisplay(), screen_info->getRootWindow(),
179                     rootpmap_id);
180     XDeleteProperty(getXDisplay(), screen_info->getRootWindow(),
181                     esetroot_id);
182   }
183
184   XUngrabServer(getXDisplay());
185   XFlush(getXDisplay());
186 }
187
188
189 // adapted from wmsetbg
190 Pixmap bsetroot::duplicatePixmap(int screen, Pixmap pixmap,
191                                  int width, int height) {
192   XSync(getXDisplay(), False);
193
194   Pixmap copyP = XCreatePixmap(getXDisplay(),
195                                getScreenInfo(screen)->getRootWindow(),
196                                width, height,
197                                DefaultDepth(getXDisplay(), screen));
198   XCopyArea(getXDisplay(), pixmap, copyP, DefaultGC(getXDisplay(), screen),
199             0, 0, width, height, 0, 0);
200   XSync(getXDisplay(), False);
201
202   return copyP;
203 }
204
205
206 void bsetroot::solid(void) {
207   for (unsigned int screen = 0; screen < getNumberOfScreens(); screen++) {
208     BColor c(fore, this, screen);
209     const ScreenInfo *screen_info = getScreenInfo(screen);
210
211     XSetWindowBackground(getXDisplay(), screen_info->getRootWindow(),
212                          c.pixel());
213     XClearWindow(getXDisplay(), screen_info->getRootWindow());
214
215     Pixmap pixmap = XCreatePixmap(getXDisplay(),
216                                   screen_info->getRootWindow(),
217                                   8, 8, DefaultDepth(getXDisplay(), screen));
218     BPen pen(c);
219     XFillRectangle(getXDisplay(), pixmap, pen.gc(), 0, 0, 8, 8);
220
221     setPixmapProperty(screen, duplicatePixmap(screen, pixmap, 8, 8));
222
223     XFreePixmap(getXDisplay(), pixmap);
224   }
225 }
226
227
228 void bsetroot::modula(int x, int y) {
229   char data[32];
230   long pattern;
231
232   unsigned int screen, i;
233
234   for (pattern = 0, screen = 0; screen < getNumberOfScreens(); screen++) {
235     for (i = 0; i < 16; i++) {
236       pattern <<= 1;
237       if ((i % x) == 0)
238         pattern |= 0x0001;
239     }
240
241     for (i = 0; i < 16; i++) {
242       if ((i %  y) == 0) {
243         data[(i * 2)] = static_cast<char>(0xff);
244         data[(i * 2) + 1] = static_cast<char>(0xff);
245       } else {
246         data[(i * 2)] = pattern & 0xff;
247         data[(i * 2) + 1] = (pattern >> 8) & 0xff;
248       }
249     }
250
251     BColor f(fore, this, screen), b(back, this, screen);
252     GC gc;
253     Pixmap bitmap;
254     const ScreenInfo *screen_info = getScreenInfo(screen);
255
256     bitmap =
257       XCreateBitmapFromData(getXDisplay(),
258                             screen_info->getRootWindow(), data,
259                             16, 16);
260
261     XGCValues gcv;
262     gcv.foreground = f.pixel();
263     gcv.background = b.pixel();
264
265     gc = XCreateGC(getXDisplay(), screen_info->getRootWindow(),
266                    GCForeground | GCBackground, &gcv);
267
268     Pixmap pixmap = XCreatePixmap(getXDisplay(),
269                                   screen_info->getRootWindow(),
270                                   16, 16, screen_info->getDepth());
271
272     XCopyPlane(getXDisplay(), bitmap, pixmap, gc,
273                0, 0, 16, 16, 0, 0, 1l);
274     XSetWindowBackgroundPixmap(getXDisplay(),
275                                screen_info->getRootWindow(),
276                                pixmap);
277     XClearWindow(getXDisplay(), screen_info->getRootWindow());
278
279     setPixmapProperty(screen,
280                       duplicatePixmap(screen, pixmap, 16, 16));
281
282     XFreeGC(getXDisplay(), gc);
283     XFreePixmap(getXDisplay(), bitmap);
284
285     if (! (screen_info->getVisual()->c_class & 1))
286       XFreePixmap(getXDisplay(), pixmap);
287   }
288 }
289
290
291 void bsetroot::gradient(void) {
292   /*
293     we have to be sure that neither raised nor sunken is specified otherwise
294     odd looking borders appear.  So we convert to lowercase then look for
295     'raised' or 'sunken' in the description and erase them.  To be paranoid
296     the search is done in a loop.
297   */
298   std::string descr;
299   descr.reserve(grad.size());
300
301   std::string::const_iterator it = grad.begin(), end = grad.end();
302   for (; it != end; ++it)
303     descr += tolower(*it);
304
305   std::string::size_type pos;
306   while ((pos = descr.find("raised")) != std::string::npos)
307     descr.erase(pos, 6); // 6 is strlen raised
308
309   while ((pos = descr.find("sunken")) != std::string::npos)
310     descr.erase(pos, 6);
311
312   // now add on 'flat' to prevent the bevels from being added
313   descr += "flat";
314
315   for (unsigned int screen = 0; screen < getNumberOfScreens(); screen++) {
316     BTexture texture(descr, this, screen, img_ctrl[screen]);
317     const ScreenInfo *screen_info = getScreenInfo(screen);
318
319     texture.setColor(BColor(fore, this, screen));
320     texture.setColorTo(BColor(back, this, screen));
321
322     Pixmap pixmap =
323       img_ctrl[screen]->renderImage(screen_info->getWidth(),
324                                     screen_info->getHeight(),
325                                     texture);
326
327     XSetWindowBackgroundPixmap(getXDisplay(),
328                                screen_info->getRootWindow(),
329                                pixmap);
330     XClearWindow(getXDisplay(), screen_info->getRootWindow());
331
332     setPixmapProperty(screen,
333                       duplicatePixmap(screen, pixmap,
334                                       screen_info->getWidth(),
335                                       screen_info->getHeight()));
336
337     if (! (screen_info->getVisual()->c_class & 1)) {
338       img_ctrl[screen]->removeImage(pixmap);
339     }
340   }
341 }
342
343
344 void bsetroot::usage(int exit_code) {
345     fprintf(stderr,
346             "%s 2.0\n\n"
347             "Copyright (c) 1997-2000, 2002 Bradley T Hughes\n"
348             "Copyright (c) 2001-2002 Sean 'Shaleh' Perry\n\n"
349             "  -display <string>        use display connection\n"
350             "  -mod <x> <y>             modula pattern\n"
351             "  -foreground, -fg <color> modula foreground color\n"
352             "  -background, -bg <color> modula background color\n\n"
353             "  -gradient <texture>      gradient texture\n"
354             "  -from <color>            gradient start color\n"
355             "  -to <color>              gradient end color\n\n"
356             "  -solid <color>           solid color\n\n"
357             "  -help                    print this help text and exit\n",
358             getApplicationName());
359
360     exit(exit_code);
361 }
362
363 int main(int argc, char **argv) {
364   char *display_name = (char *) 0;
365
366   for (int i = 1; i < argc; i++) {
367     if (! strcmp(argv[i], "-display")) {
368       // check for -display option
369
370       if ((++i) >= argc) {
371         fprintf(stderr, "error: '-display' requires an argument\n");
372
373         ::exit(1);
374       }
375
376       display_name = argv[i];
377     }
378   }
379
380   bsetroot app(argc, argv, display_name);
381
382   return 0;
383 }