xsetroot.c revision b8f63ae3
1/*
2 *
3Copyright 1987, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24 */
25
26/*
27 * xsetroot.c 	MIT Project Athena, X Window System root window
28 *		parameter setting utility.  This program will set
29 *		various parameters of the X root window.
30 *
31 *  Author:	Mark Lillibridge, MIT Project Athena
32 *		11-Jun-87
33 */
34
35#ifdef HAVE_CONFIG_H
36# include "config.h"
37#endif
38
39#include <X11/Xlib.h>
40#include <X11/Xutil.h>
41#include <X11/Xatom.h>
42#include <X11/Xmu/CurUtil.h>
43#include <X11/Xcursor/Xcursor.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include "X11/bitmaps/gray"
48
49#define Dynamic 1
50
51static char *program_name;
52static Display *dpy;
53static int screen;
54static Window root;
55static char *fore_color = NULL;
56static char *back_color = NULL;
57static int reverse = 0;
58static int save_colors = 0;
59static int unsave_past = 0;
60static Pixmap save_pixmap = (Pixmap)None;
61
62static void FixupState(void);
63static void SetBackgroundToBitmap(Pixmap bitmap,
64				  unsigned int width, unsigned int height);
65static Cursor CreateCursorFromFiles(char *cursor_file, char *mask_file);
66static Cursor CreateCursorFromName(char *name);
67static Pixmap MakeModulaBitmap(int mod_x, int mod_y);
68static XColor NameToXColor(char *name, unsigned long pixel);
69static unsigned long NameToPixel(char *name, unsigned long pixel);
70static Pixmap ReadBitmapFile(char *filename, unsigned int *width, unsigned int *height, int *x_hot, int *y_hot);
71
72static void _X_NORETURN _X_COLD
73usage(const char *errmsg)
74{
75    if (errmsg != NULL)
76	fprintf (stderr, "%s: %s\n\n", program_name, errmsg);
77
78    fprintf(stderr, "Usage: %s [options]\n%s\n", program_name,
79            "  where options are:\n"
80            "  -help                           Print this help\n"
81            "  -version                        Print a version message\n"
82            "  -def, -default                  Reset unspecified attributes\n"
83            "  -cursor <cursorfile> <maskfile> Pointer cursor used when outside of any windows\n"
84            "  -cursor_name <cursorfontname>   Use a pointer cursor from the cursor font\n"
85            "  -xcf <cursorfile> <cursorsize>  Load a pointer cursor from an Xcursor file\n"
86            "  -bitmap <filename>              Use the bitmap specified in the file\n"
87            "  -mod <x> <y>                    Use a plaid-like grid pattern on screen\n"
88            "  -gray, -grey                    Make the entire background grey\n"
89            "  -bg,  -background <color>       Set background color\n"
90            "  -fg,  -foreground <color>       Set foreground color\n"
91            "  -rv,  -reverse                  Swap foreground and background colors\n"
92            "  -solid <color>                  Set the background of the root window\n"
93            "  -name <string>                  Set the name of the root window\n"
94            "  -d,   -display <display>        Specifies the server to connect to\n"
95            );
96    exit(1);
97    /*NOTREACHED*/
98}
99
100
101int
102main(int argc, char *argv[])
103{
104    int excl = 0;
105    int nonexcl = 0;
106    int restore_defaults = 0;
107    char *display_name = NULL;
108    char *name = NULL;
109    char *cursor_file = NULL;
110    char *cursor_mask = NULL;
111    char *cursor_name = NULL;
112    char *solid_color = NULL;
113    char *xcf = NULL;
114    int xcf_size = 32;
115    Cursor cursor;
116    int gray = 0;
117    char *bitmap_file = NULL;
118    int mod_x = 0;
119    int mod_y = 0;
120    register int i;
121    unsigned int ww, hh;
122    Pixmap bitmap;
123
124    program_name=argv[0];
125
126    for (i = 1; i < argc; i++) {
127	if (!strcmp ("-display", argv[i]) || !strcmp ("-d", argv[i])) {
128	    if (++i>=argc) usage ("-display requires an argument");
129	    display_name = argv[i];
130	    continue;
131	}
132	if (!strcmp("-help", argv[i])) {
133	    usage(NULL);
134	}
135	if (!strcmp("-version", argv[i])) {
136            printf("%s\n", PACKAGE_STRING);
137            exit(0);
138	}
139	if (!strcmp("-def", argv[i]) || !strcmp("-default", argv[i])) {
140	    restore_defaults = 1;
141	    continue;
142	}
143	if (!strcmp("-name", argv[i])) {
144	    if (++i>=argc) usage("-name requires an argument");
145	    name = argv[i];
146	    nonexcl++;
147	    continue;
148	}
149	if (!strcmp("-cursor", argv[i])) {
150	    if (++i>=argc)
151		usage("missing cursorfile & maskfile arguments for -cursor");
152	    cursor_file = argv[i];
153	    if (++i>=argc)
154		usage("missing maskfile argument for -cursor");
155	    cursor_mask = argv[i];
156	    nonexcl++;
157	    continue;
158	}
159	if (!strcmp("-cursor_name", argv[i])) {
160	    if (++i>=argc) usage("-cursor_name requires an argument");
161	    cursor_name = argv[i];
162	    nonexcl++;
163	    continue;
164	}
165	if (!strcmp("-xcf", argv[i])) {
166	    if (++i>=argc)
167		usage("missing cursorfile & cursorsize arguments for -xcf");
168	    xcf = argv[i];
169	    if (++i>=argc)
170		usage("missing cursorsize argument for -xcf");
171	    xcf_size = atoi(argv[i]);
172	    if (xcf_size <= 0)
173		xcf_size = 32;
174	    nonexcl++;
175	    continue;
176	}
177	if (!strcmp("-fg",argv[i]) || !strcmp("-foreground",argv[i])) {
178	    if (++i>=argc) usage("-foreground requires an argument");
179	    fore_color = argv[i];
180	    continue;
181	}
182	if (!strcmp("-bg",argv[i]) || !strcmp("-background",argv[i])) {
183	    if (++i>=argc) usage("-background requires an argument");
184	    back_color = argv[i];
185	    continue;
186	}
187	if (!strcmp("-solid", argv[i])) {
188	    if (++i>=argc) usage("-solid requires an argument");
189	    solid_color = argv[i];
190	    excl++;
191	    continue;
192	}
193	if (!strcmp("-gray", argv[i]) || !strcmp("-grey", argv[i])) {
194	    gray = 1;
195	    excl++;
196	    continue;
197	}
198	if (!strcmp("-bitmap", argv[i])) {
199	    if (++i>=argc) usage("-bitmap requires an argument");
200	    bitmap_file = argv[i];
201	    excl++;
202	    continue;
203	}
204	if (!strcmp("-mod", argv[i])) {
205	    if (++i>=argc) usage("missing x & y arguments for -mod");
206	    mod_x = atoi(argv[i]);
207	    if (mod_x <= 0) mod_x = 1;
208	    if (++i>=argc) usage("missing y argument for -mod");
209	    mod_y = atoi(argv[i]);
210	    if (mod_y <= 0) mod_y = 1;
211	    excl++;
212	    continue;
213	}
214	if (!strcmp("-rv",argv[i]) || !strcmp("-reverse",argv[i])) {
215	    reverse = 1;
216	    continue;
217	}
218	fprintf(stderr, "%s: unrecognized argument '%s'\n",
219		program_name, argv[i]);
220	usage(NULL);
221    }
222
223    /* Check for multiple use of exclusive options */
224    if (excl > 1) {
225	fprintf(stderr, "%s: choose only one of {solid, gray, bitmap, mod}\n",
226		program_name);
227	usage(NULL);
228    }
229
230    dpy = XOpenDisplay(display_name);
231    if (!dpy) {
232	fprintf(stderr, "%s:  unable to open display '%s'\n",
233		program_name, XDisplayName (display_name));
234	exit (2);
235    }
236    screen = DefaultScreen(dpy);
237    root = RootWindow(dpy, screen);
238
239    /* If there are no arguments then restore defaults. */
240    if (!excl && !nonexcl)
241	restore_defaults = 1;
242
243    /* Handle a cursor file */
244    if (cursor_file) {
245	cursor = CreateCursorFromFiles(cursor_file, cursor_mask);
246	XDefineCursor(dpy, root, cursor);
247	XFreeCursor(dpy, cursor);
248    }
249
250    if (cursor_name) {
251	cursor = CreateCursorFromName (cursor_name);
252	if (cursor)
253	{
254	    XDefineCursor (dpy, root, cursor);
255	    XFreeCursor (dpy, cursor);
256	}
257    }
258    if (xcf) {
259	XcursorImages *images = XcursorFilenameLoadImages(xcf, xcf_size);
260	if (!images) {
261	    fprintf(stderr, "Invalid cursor file \"%s\"\n", xcf);
262	} else {
263	    cursor = XcursorImagesLoadCursor(dpy, images);
264	    if (cursor)
265	    {
266		XDefineCursor (dpy, root, cursor);
267		XFreeCursor (dpy, cursor);
268	    }
269	}
270    }
271    /* Handle -gray and -grey options */
272    if (gray) {
273	bitmap = XCreateBitmapFromData(dpy, root, gray_bits,
274				       gray_width, gray_height);
275	SetBackgroundToBitmap(bitmap, gray_width, gray_height);
276    }
277
278    /* Handle -solid option */
279    if (solid_color) {
280	XSetWindowBackground(dpy, root, NameToPixel(solid_color,
281						    BlackPixel(dpy, screen)));
282	XClearWindow(dpy, root);
283	unsave_past = 1;
284    }
285
286    /* Handle -bitmap option */
287    if (bitmap_file) {
288	bitmap = ReadBitmapFile(bitmap_file, &ww, &hh, (int *)NULL, (int *)NULL);
289	SetBackgroundToBitmap(bitmap, ww, hh);
290    }
291
292    /* Handle set background to a modula pattern */
293    if (mod_x) {
294	bitmap = MakeModulaBitmap(mod_x, mod_y);
295	SetBackgroundToBitmap(bitmap, 16, 16);
296    }
297
298    /* Handle set name */
299    if (name)
300	XStoreName(dpy, root, name);
301
302    /* Handle restore defaults */
303    if (restore_defaults) {
304	if (!cursor_file)
305	    XUndefineCursor(dpy, root);
306	if (!excl) {
307	    XSetWindowBackgroundPixmap(dpy, root, (Pixmap) None);
308	    XClearWindow(dpy, root);
309	    unsave_past = 1;
310	}
311    }
312
313    FixupState();
314    XCloseDisplay(dpy);
315    exit (0);
316}
317
318
319/* Free past incarnation if needed, and retain state if needed. */
320static void
321FixupState(void)
322{
323    Atom prop, type;
324    int format;
325    unsigned long length, after;
326    unsigned char *data;
327
328    if (!(DefaultVisual(dpy, screen)->class & Dynamic))
329	unsave_past = 0;
330    if (!unsave_past && !save_colors)
331	return;
332    prop = XInternAtom(dpy, "_XSETROOT_ID", False);
333    if (unsave_past) {
334	if (XGetWindowProperty(dpy, root, prop, 0L, 1L, True, AnyPropertyType,
335		       &type, &format, &length, &after, &data) != Success)
336	    fprintf(stderr,
337		    "%s: warning: cannot get _XSETROOT_ID property from root window\n",
338		    program_name);
339	else if ((type == XA_PIXMAP) && (format == 32) &&
340		 (length == 1) && (after == 0))
341	    XKillClient(dpy, *((Pixmap *)data));
342	else if (type != None)
343	    fprintf(stderr, "%s: warning: _XSETROOT_ID property is garbage\n",
344		    program_name);
345    }
346    if (save_colors) {
347	if (!save_pixmap)
348	    save_pixmap = XCreatePixmap(dpy, root, 1, 1, 1);
349	XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, PropModeReplace,
350			(unsigned char *) &save_pixmap, 1);
351	XSetCloseDownMode(dpy, RetainPermanent);
352    }
353}
354
355/*
356 * SetBackgroundToBitmap: Set the root window background to a caller supplied
357 *                        bitmap.
358 */
359static void
360SetBackgroundToBitmap(Pixmap bitmap, unsigned int width, unsigned int height)
361{
362    Pixmap pix;
363    GC gc;
364    XGCValues gc_init;
365
366    gc_init.foreground = NameToPixel(fore_color, BlackPixel(dpy, screen));
367    gc_init.background = NameToPixel(back_color, WhitePixel(dpy, screen));
368    if (reverse) {
369	unsigned long temp=gc_init.foreground;
370	gc_init.foreground=gc_init.background;
371	gc_init.background=temp;
372    }
373    gc = XCreateGC(dpy, root, GCForeground|GCBackground, &gc_init);
374    pix = XCreatePixmap(dpy, root, width, height,
375			(unsigned int)DefaultDepth(dpy, screen));
376    XCopyPlane(dpy, bitmap, pix, gc, 0, 0, width, height, 0, 0, (unsigned long)1);
377    XSetWindowBackgroundPixmap(dpy, root, pix);
378    XFreeGC(dpy, gc);
379    XFreePixmap(dpy, bitmap);
380    if (save_colors)
381	save_pixmap = pix;
382    else
383	XFreePixmap(dpy, pix);
384    XClearWindow(dpy, root);
385    unsave_past = 1;
386}
387
388
389/*
390 * CreateCursorFromFiles: make a cursor of the right colors from two bitmap
391 *                        files.
392 */
393#define BITMAP_HOT_DEFAULT 8
394
395static Cursor
396CreateCursorFromFiles(char *cursor_file, char *mask_file)
397{
398    Pixmap cursor_bitmap, mask_bitmap;
399    unsigned int width, height, ww, hh;
400    int x_hot, y_hot;
401    Cursor cursor;
402    XColor fg, bg, temp;
403
404    fg = NameToXColor(fore_color, BlackPixel(dpy, screen));
405    bg = NameToXColor(back_color, WhitePixel(dpy, screen));
406    if (reverse) {
407	temp = fg; fg = bg; bg = temp;
408    }
409
410    cursor_bitmap = ReadBitmapFile(cursor_file, &width, &height, &x_hot, &y_hot);
411    mask_bitmap = ReadBitmapFile(mask_file, &ww, &hh, (int *)NULL, (int *)NULL);
412
413    if (width != ww || height != hh) {
414	fprintf(stderr,
415"%s: dimensions of cursor bitmap and cursor mask bitmap are different\n",
416		program_name);
417	exit(1);
418	/*NOTREACHED*/
419    }
420
421    if ((x_hot == -1) && (y_hot == -1)) {
422	x_hot = BITMAP_HOT_DEFAULT;
423	y_hot = BITMAP_HOT_DEFAULT;
424    }
425    if ((x_hot < 0) || (x_hot >= width) ||
426	(y_hot < 0) || (y_hot >= height)) {
427	fprintf(stderr, "%s: hotspot is outside cursor bounds\n", program_name);
428	exit(1);
429	/*NOTREACHED*/
430    }
431
432    cursor = XCreatePixmapCursor(dpy, cursor_bitmap, mask_bitmap, &fg, &bg,
433				 (unsigned int)x_hot, (unsigned int)y_hot);
434    XFreePixmap(dpy, cursor_bitmap);
435    XFreePixmap(dpy, mask_bitmap);
436
437    return(cursor);
438}
439
440static Cursor
441CreateCursorFromName(char *name)
442{
443    XColor fg, bg, temp;
444    int	    i;
445    Font    fid;
446
447    fg = NameToXColor(fore_color, BlackPixel(dpy, screen));
448    bg = NameToXColor(back_color, WhitePixel(dpy, screen));
449    if (reverse) {
450	temp = fg; fg = bg; bg = temp;
451    }
452    i = XmuCursorNameToIndex (name);
453    if (i == -1)
454	return (Cursor) 0;
455    fid = XLoadFont (dpy, "cursor");
456    if (!fid)
457	return (Cursor) 0;
458    return XCreateGlyphCursor (dpy, fid, fid,
459			       i, i+1, &fg, &bg);
460}
461
462/*
463 * MakeModulaBitmap: Returns a modula bitmap based on an x & y mod.
464 */
465static Pixmap
466MakeModulaBitmap(int mod_x, int mod_y)
467{
468    int i;
469    long pattern_line = 0;
470    char modula_data[16*16/8];
471
472    for (i=16; i--; ) {
473	pattern_line <<=1;
474	if ((i % mod_x) == 0) pattern_line |= 0x0001;
475    }
476    for (i=0; i<16; i++) {
477	if ((i % mod_y) == 0) {
478	    modula_data[i*2] = (char)0xff;
479	    modula_data[i*2+1] = (char)0xff;
480	} else {
481	    modula_data[i*2] = pattern_line & 0xff;
482	    modula_data[i*2+1] = (pattern_line>>8) & 0xff;
483	}
484    }
485
486    return(XCreateBitmapFromData(dpy, root, modula_data, 16, 16));
487}
488
489
490/*
491 * NameToXColor: Convert the name of a color to its Xcolor value.
492 */
493static XColor
494NameToXColor(char *name, unsigned long pixel)
495{
496    XColor c;
497
498    if (!name || !*name) {
499	c.pixel = pixel;
500	XQueryColor(dpy, DefaultColormap(dpy, screen), &c);
501    } else if (!XParseColor(dpy, DefaultColormap(dpy, screen), name, &c)) {
502	fprintf(stderr, "%s: unknown color or bad color format: %s\n",
503			program_name, name);
504	exit(1);
505	/*NOTREACHED*/
506    }
507    return(c);
508}
509
510static unsigned long
511NameToPixel(char *name, unsigned long pixel)
512{
513    XColor ecolor;
514
515    if (!name || !*name)
516	return pixel;
517    if (!XParseColor(dpy,DefaultColormap(dpy,screen),name,&ecolor)) {
518	fprintf(stderr,"%s:  unknown color \"%s\"\n",program_name,name);
519	exit(1);
520	/*NOTREACHED*/
521    }
522    if (!XAllocColor(dpy, DefaultColormap(dpy, screen),&ecolor)) {
523	fprintf(stderr, "%s:  unable to allocate color for \"%s\"\n",
524		program_name, name);
525	exit(1);
526	/*NOTREACHED*/
527    }
528    if ((ecolor.pixel != BlackPixel(dpy, screen)) &&
529	(ecolor.pixel != WhitePixel(dpy, screen)) &&
530	(DefaultVisual(dpy, screen)->class & Dynamic))
531	save_colors = 1;
532    return(ecolor.pixel);
533}
534
535static Pixmap
536ReadBitmapFile(char *filename, unsigned int *width, unsigned int *height,
537	       int *x_hot, int *y_hot)
538{
539    Pixmap bitmap;
540    int status;
541
542    status = XReadBitmapFile(dpy, root, filename, width,
543			     height, &bitmap, x_hot, y_hot);
544    if (status == BitmapSuccess)
545      return(bitmap);
546    else if (status == BitmapOpenFailed)
547	fprintf(stderr, "%s: can't open file: %s\n", program_name, filename);
548    else if (status == BitmapFileInvalid)
549	fprintf(stderr, "%s: bad bitmap format file: %s\n",
550			program_name, filename);
551    else
552	fprintf(stderr, "%s: insufficient memory for bitmap: %s",
553			program_name, filename);
554    exit(1);
555    /*NOTREACHED*/
556}
557