sync with the 2.0 branch
[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 "../src/i18n.hh"
47 #include "../src/GCCache.hh"
48 #include "../src/Texture.hh"
49 #include "../src/Util.hh"
50 #include "bsetroot.hh"
51
52
53 I18n i18n;
54
55 bsetroot::bsetroot(int argc, char **argv, char *dpy_name)
56   : BaseDisplay(argv[0], dpy_name) {
57
58   bool mod = False, sol = False, grd = False;
59   int mod_x = 0, mod_y = 0;
60
61   for (int i = 1; i < argc; i++) {
62     if (! strcmp("-help", argv[i])) {
63       usage();
64     } else if ((! strcmp("-fg", argv[i])) ||
65                (! strcmp("-foreground", argv[i])) ||
66                (! strcmp("-from", argv[i]))) {
67       if ((++i) >= argc) usage(1);
68
69       fore = argv[i];
70     } else if ((! strcmp("-bg", argv[i])) ||
71                (! strcmp("-background", argv[i])) ||
72                (! strcmp("-to", argv[i]))) {
73       if ((++i) >= argc) usage(1);
74
75       back = argv[i];
76     } else if (! strcmp("-solid", argv[i])) {
77       if ((++i) >= argc) usage(1);
78
79       fore = argv[i];
80       sol = True;
81     } else if (! strcmp("-mod", argv[i])) {
82       if ((++i) >= argc) usage();
83
84       mod_x = atoi(argv[i]);
85
86       if ((++i) >= argc) usage();
87
88       mod_y = atoi(argv[i]);
89
90       if (mod_x < 1) mod_x = 1;
91       if (mod_y < 1) mod_y = 1;
92
93       mod = True;
94     } else if (! strcmp("-gradient", argv[i])) {
95       if ((++i) >= argc) usage();
96
97       grad = argv[i];
98       grd = True;
99     } else if (! strcmp("-display", argv[i])) {
100       // -display passed through tests ealier... we just skip it now
101       i++;
102     } else {
103       usage();
104     }
105   }
106
107   if ((mod + sol + grd) != True) {
108     fprintf(stderr,
109             i18n(bsetrootSet, bsetrootMustSpecify,
110                  "%s: error: must specify one of: -solid, -mod, -gradient\n"),
111             getApplicationName());
112
113     usage(2);
114   }
115
116   img_ctrl = new BImageControl*[getNumberOfScreens()];
117   for (unsigned int s = 0; s < getNumberOfScreens(); ++s)
118     img_ctrl[s] = new BImageControl(this, getScreenInfo(s), True);
119
120   if (sol && ! fore.empty())
121     solid();
122   else if (mod && mod_x && mod_y && ! (fore.empty() || back.empty()))
123     modula(mod_x, mod_y);
124   else if (grd && ! (grad.empty() || fore.empty() || back.empty()))
125     gradient();
126   else usage();
127 }
128
129
130 bsetroot::~bsetroot(void) {
131   XSetCloseDownMode(getXDisplay(), RetainPermanent);
132
133   XKillClient(getXDisplay(), AllTemporary);
134
135   std::for_each(img_ctrl, img_ctrl + getNumberOfScreens(), PointerAssassin());
136
137   delete [] img_ctrl;
138 }
139
140
141 // adapted from wmsetbg
142 void bsetroot::setPixmapProperty(int screen, Pixmap pixmap) {
143   static Atom rootpmap_id = None, esetroot_id = None;
144   Atom type;
145   int format;
146   unsigned long length, after;
147   unsigned char *data;
148   const ScreenInfo *screen_info = getScreenInfo(screen);
149
150   if (rootpmap_id == None) {
151     rootpmap_id = XInternAtom(getXDisplay(), "_XROOTPMAP_ID", False);
152     esetroot_id = XInternAtom(getXDisplay(), "ESETROOT_PMAP_ID", False);
153   }
154
155   XGrabServer(getXDisplay());
156
157   /* Clear out the old pixmap */
158   XGetWindowProperty(getXDisplay(), screen_info->getRootWindow(),
159                      rootpmap_id, 0L, 1L, False, AnyPropertyType,
160                      &type, &format, &length, &after, &data);
161
162   if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
163     unsigned char* data_esetroot = 0;
164     XGetWindowProperty(getXDisplay(), screen_info->getRootWindow(),
165                        esetroot_id, 0L, 1L, False, AnyPropertyType,
166                        &type, &format, &length, &after, &data_esetroot);
167     if (data && data_esetroot && *((Pixmap *) data)) {
168       XKillClient(getXDisplay(), *((Pixmap *) data));
169       XSync(getXDisplay(), False);
170       XFree(data_esetroot);
171     }
172     XFree(data);
173   }
174
175   if (pixmap) {
176     XChangeProperty(getXDisplay(), screen_info->getRootWindow(),
177                     rootpmap_id, XA_PIXMAP, 32, PropModeReplace,
178                     (unsigned char *) &pixmap, 1);
179     XChangeProperty(getXDisplay(), screen_info->getRootWindow(),
180                     esetroot_id, XA_PIXMAP, 32, PropModeReplace,
181                     (unsigned char *) &pixmap, 1);
182   } else {
183     XDeleteProperty(getXDisplay(), screen_info->getRootWindow(),
184                     rootpmap_id);
185     XDeleteProperty(getXDisplay(), screen_info->getRootWindow(),
186                     esetroot_id);
187   }
188
189   XUngrabServer(getXDisplay());
190   XFlush(getXDisplay());
191 }
192
193
194 // adapted from wmsetbg
195 Pixmap bsetroot::duplicatePixmap(int screen, Pixmap pixmap,
196                                  int width, int height) {
197   XSync(getXDisplay(), False);
198
199   Pixmap copyP = XCreatePixmap(getXDisplay(),
200                                getScreenInfo(screen)->getRootWindow(),
201                                width, height,
202                                DefaultDepth(getXDisplay(), screen));
203   XCopyArea(getXDisplay(), pixmap, copyP, DefaultGC(getXDisplay(), screen),
204             0, 0, width, height, 0, 0);
205   XSync(getXDisplay(), False);
206
207   return copyP;
208 }
209
210
211 void bsetroot::solid(void) {
212   for (unsigned int screen = 0; screen < getNumberOfScreens(); screen++) {
213     BColor c(fore, this, screen);
214     const ScreenInfo *screen_info = getScreenInfo(screen);
215
216     XSetWindowBackground(getXDisplay(), screen_info->getRootWindow(),
217                          c.pixel());
218     XClearWindow(getXDisplay(), screen_info->getRootWindow());
219
220     Pixmap pixmap = XCreatePixmap(getXDisplay(),
221                                   screen_info->getRootWindow(),
222                                   8, 8, DefaultDepth(getXDisplay(), screen));
223     BPen pen(c);
224     XFillRectangle(getXDisplay(), pixmap, pen.gc(), 0, 0, 8, 8);
225
226     setPixmapProperty(screen, duplicatePixmap(screen, pixmap, 8, 8));
227
228     XFreePixmap(getXDisplay(), pixmap);
229   }
230 }
231
232
233 void bsetroot::modula(int x, int y) {
234   char data[32];
235   long pattern;
236
237   unsigned int screen, i;
238
239   for (pattern = 0, screen = 0; screen < getNumberOfScreens(); screen++) {
240     for (i = 0; i < 16; i++) {
241       pattern <<= 1;
242       if ((i % x) == 0)
243         pattern |= 0x0001;
244     }
245
246     for (i = 0; i < 16; i++) {
247       if ((i %  y) == 0) {
248         data[(i * 2)] = static_cast<char>(0xff);
249         data[(i * 2) + 1] = static_cast<char>(0xff);
250       } else {
251         data[(i * 2)] = pattern & 0xff;
252         data[(i * 2) + 1] = (pattern >> 8) & 0xff;
253       }
254     }
255
256     BColor f(fore, this, screen), b(back, this, screen);
257     GC gc;
258     Pixmap bitmap;
259     const ScreenInfo *screen_info = getScreenInfo(screen);
260
261     bitmap =
262       XCreateBitmapFromData(getXDisplay(),
263                             screen_info->getRootWindow(), data,
264                             16, 16);
265
266     XGCValues gcv;
267     gcv.foreground = f.pixel();
268     gcv.background = b.pixel();
269
270     gc = XCreateGC(getXDisplay(), screen_info->getRootWindow(),
271                    GCForeground | GCBackground, &gcv);
272
273     Pixmap pixmap = XCreatePixmap(getXDisplay(),
274                                   screen_info->getRootWindow(),
275                                   16, 16, screen_info->getDepth());
276
277     XCopyPlane(getXDisplay(), bitmap, pixmap, gc,
278                0, 0, 16, 16, 0, 0, 1l);
279     XSetWindowBackgroundPixmap(getXDisplay(),
280                                screen_info->getRootWindow(),
281                                pixmap);
282     XClearWindow(getXDisplay(), screen_info->getRootWindow());
283
284     setPixmapProperty(screen,
285                       duplicatePixmap(screen, pixmap, 16, 16));
286
287     XFreeGC(getXDisplay(), gc);
288     XFreePixmap(getXDisplay(), bitmap);
289
290     if (! (screen_info->getVisual()->c_class & 1))
291       XFreePixmap(getXDisplay(), pixmap);
292   }
293 }
294
295
296 void bsetroot::gradient(void) {
297   /*
298     we have to be sure that neither raised nor sunken is specified otherwise
299     odd looking borders appear.  So we convert to lowercase then look for
300     'raised' or 'sunken' in the description and erase them.  To be paranoid
301     the search is done in a loop.
302   */
303   std::string descr;
304   descr.reserve(grad.size());
305
306   std::string::const_iterator it = grad.begin(), end = grad.end();
307   for (; it != end; ++it)
308     descr += tolower(*it);
309
310   std::string::size_type pos;
311   while ((pos = descr.find("raised")) != std::string::npos)
312     descr.erase(pos, 6); // 6 is strlen raised
313
314   while ((pos = descr.find("sunken")) != std::string::npos)
315     descr.erase(pos, 6);
316
317   // now add on 'flat' to prevent the bevels from being added
318   descr += "flat";
319
320   for (unsigned int screen = 0; screen < getNumberOfScreens(); screen++) {
321     BTexture texture(descr, this, screen, img_ctrl[screen]);
322     const ScreenInfo *screen_info = getScreenInfo(screen);
323
324     texture.setColor(BColor(fore, this, screen));
325     texture.setColorTo(BColor(back, this, screen));
326
327     Pixmap pixmap =
328       img_ctrl[screen]->renderImage(screen_info->getWidth(),
329                                     screen_info->getHeight(),
330                                     texture);
331
332     XSetWindowBackgroundPixmap(getXDisplay(),
333                                screen_info->getRootWindow(),
334                                pixmap);
335     XClearWindow(getXDisplay(), screen_info->getRootWindow());
336
337     setPixmapProperty(screen,
338                       duplicatePixmap(screen, pixmap,
339                                       screen_info->getWidth(),
340                                       screen_info->getHeight()));
341
342     if (! (screen_info->getVisual()->c_class & 1)) {
343       img_ctrl[screen]->removeImage(pixmap);
344     }
345   }
346 }
347
348
349 void bsetroot::usage(int exit_code) {
350     fprintf(stderr,
351             i18n(bsetrootSet, bsetrootUsage,
352                  "%s 2.0\n\n"
353                  "Copyright (c) 1997-2000, 2002 Bradley T Hughes\n"
354                  "Copyright (c) 2001-2002 Sean 'Shaleh' Perry\n\n"
355                  "  -display <string>        use display connection\n"
356                  "  -mod <x> <y>             modula pattern\n"
357                  "  -foreground, -fg <color> modula foreground color\n"
358                  "  -background, -bg <color> modula background color\n\n"
359                  "  -gradient <texture>      gradient texture\n"
360                  "  -from <color>            gradient start color\n"
361                  "  -to <color>              gradient end color\n\n"
362                  "  -solid <color>           solid color\n\n"
363                  "  -help                    print this help text and exit\n"),
364             getApplicationName());
365
366     exit(exit_code);
367 }
368
369 int main(int argc, char **argv) {
370   char *display_name = (char *) 0;
371
372   i18n.openCatalog("openbox.cat");
373
374   for (int i = 1; i < argc; i++) {
375     if (! strcmp(argv[i], "-display")) {
376       // check for -display option
377
378       if ((++i) >= argc) {
379         fprintf(stderr, i18n(mainSet, mainDISPLAYRequiresArg,
380                              "error: '-display' requires an argument\n"));
381
382         ::exit(1);
383       }
384
385       display_name = argv[i];
386     }
387   }
388
389   bsetroot app(argc, argv, display_name);
390
391   return 0;
392 }