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    unsigned int ww, hh;
121    Pixmap bitmap;
122
123    program_name=argv[0];
124
125    for (int i = 1; i < argc; i++) {
126	if (!strcmp ("-display", argv[i]) || !strcmp ("-d", argv[i])) {
127	    if (++i>=argc) usage ("-display requires an argument");
128	    display_name = argv[i];
129	    continue;
130	}
131	if (!strcmp("-help", argv[i])) {
132	    usage(NULL);
133	}
134	if (!strcmp("-version", argv[i])) {
135            printf("%s\n", PACKAGE_STRING);
136            exit(0);
137	}
138	if (!strcmp("-def", argv[i]) || !strcmp("-default", argv[i])) {
139	    restore_defaults = 1;
140	    continue;
141	}
142	if (!strcmp("-name", argv[i])) {
143	    if (++i>=argc) usage("-name requires an argument");
144	    name = argv[i];
145	    nonexcl++;
146	    continue;
147	}
148	if (!strcmp("-cursor", argv[i])) {
149	    if (++i>=argc)
150		usage("missing cursorfile & maskfile arguments for -cursor");
151	    cursor_file = argv[i];
152	    if (++i>=argc)
153		usage("missing maskfile argument for -cursor");
154	    cursor_mask = argv[i];
155	    nonexcl++;
156	    continue;
157	}
158	if (!strcmp("-cursor_name", argv[i])) {
159	    if (++i>=argc) usage("-cursor_name requires an argument");
160	    cursor_name = argv[i];
161	    nonexcl++;
162	    continue;
163	}
164	if (!strcmp("-xcf", argv[i])) {
165	    if (++i>=argc)
166		usage("missing cursorfile & cursorsize arguments for -xcf");
167	    xcf = argv[i];
168	    if (++i>=argc)
169		usage("missing cursorsize argument for -xcf");
170	    xcf_size = atoi(argv[i]);
171	    if (xcf_size <= 0)
172		xcf_size = 32;
173	    nonexcl++;
174	    continue;
175	}
176	if (!strcmp("-fg",argv[i]) || !strcmp("-foreground",argv[i])) {
177	    if (++i>=argc) usage("-foreground requires an argument");
178	    fore_color = argv[i];
179	    continue;
180	}
181	if (!strcmp("-bg",argv[i]) || !strcmp("-background",argv[i])) {
182	    if (++i>=argc) usage("-background requires an argument");
183	    back_color = argv[i];
184	    continue;
185	}
186	if (!strcmp("-solid", argv[i])) {
187	    if (++i>=argc) usage("-solid requires an argument");
188	    solid_color = argv[i];
189	    excl++;
190	    continue;
191	}
192	if (!strcmp("-gray", argv[i]) || !strcmp("-grey", argv[i])) {
193	    gray = 1;
194	    excl++;
195	    continue;
196	}
197	if (!strcmp("-bitmap", argv[i])) {
198	    if (++i>=argc) usage("-bitmap requires an argument");
199	    bitmap_file = argv[i];
200	    excl++;
201	    continue;
202	}
203	if (!strcmp("-mod", argv[i])) {
204	    if (++i>=argc) usage("missing x & y arguments for -mod");
205	    mod_x = atoi(argv[i]);
206	    if (mod_x <= 0) mod_x = 1;
207	    if (++i>=argc) usage("missing y argument for -mod");
208	    mod_y = atoi(argv[i]);
209	    if (mod_y <= 0) mod_y = 1;
210	    excl++;
211	    continue;
212	}
213	if (!strcmp("-rv",argv[i]) || !strcmp("-reverse",argv[i])) {
214	    reverse = 1;
215	    continue;
216	}
217	fprintf(stderr, "%s: unrecognized argument '%s'\n",
218		program_name, argv[i]);
219	usage(NULL);
220    }
221
222    /* Check for multiple use of exclusive options */
223    if (excl > 1) {
224	fprintf(stderr, "%s: choose only one of {solid, gray, bitmap, mod}\n",
225		program_name);
226	usage(NULL);
227    }
228
229    dpy = XOpenDisplay(display_name);
230    if (!dpy) {
231	fprintf(stderr, "%s:  unable to open display '%s'\n",
232		program_name, XDisplayName (display_name));
233	exit (2);
234    }
235    screen = DefaultScreen(dpy);
236    root = RootWindow(dpy, screen);
237
238    /* If there are no arguments then restore defaults. */
239    if (!excl && !nonexcl)
240	restore_defaults = 1;
241
242    /* Handle a cursor file */
243    if (cursor_file) {
244	cursor = CreateCursorFromFiles(cursor_file, cursor_mask);
245	XDefineCursor(dpy, root, cursor);
246	XFreeCursor(dpy, cursor);
247    }
248
249    if (cursor_name) {
250	cursor = CreateCursorFromName (cursor_name);
251	if (cursor)
252	{
253	    XDefineCursor (dpy, root, cursor);
254	    XFreeCursor (dpy, cursor);
255	}
256    }
257    if (xcf) {
258	XcursorImages *images = XcursorFilenameLoadImages(xcf, xcf_size);
259	if (!images) {
260	    fprintf(stderr, "Invalid cursor file \"%s\"\n", xcf);
261	} else {
262	    cursor = XcursorImagesLoadCursor(dpy, images);
263	    if (cursor)
264	    {
265		XDefineCursor (dpy, root, cursor);
266		XFreeCursor (dpy, cursor);
267	    }
268	}
269    }
270    /* Handle -gray and -grey options */
271    if (gray) {
272	bitmap = XCreateBitmapFromData(dpy, root, gray_bits,
273				       gray_width, gray_height);
274	SetBackgroundToBitmap(bitmap, gray_width, gray_height);
275    }
276
277    /* Handle -solid option */
278    if (solid_color) {
279	XSetWindowBackground(dpy, root, NameToPixel(solid_color,
280						    BlackPixel(dpy, screen)));
281	XClearWindow(dpy, root);
282	unsave_past = 1;
283    }
284
285    /* Handle -bitmap option */
286    if (bitmap_file) {
287	bitmap = ReadBitmapFile(bitmap_file, &ww, &hh, (int *)NULL, (int *)NULL);
288	SetBackgroundToBitmap(bitmap, ww, hh);
289    }
290
291    /* Handle set background to a modula pattern */
292    if (mod_x) {
293	bitmap = MakeModulaBitmap(mod_x, mod_y);
294	SetBackgroundToBitmap(bitmap, 16, 16);
295    }
296
297    /* Handle set name */
298    if (name)
299	XStoreName(dpy, root, name);
300
301    /* Handle restore defaults */
302    if (restore_defaults) {
303	if (!cursor_file)
304	    XUndefineCursor(dpy, root);
305	if (!excl) {
306	    XSetWindowBackgroundPixmap(dpy, root, (Pixmap) None);
307	    XClearWindow(dpy, root);
308	    unsave_past = 1;
309	}
310    }
311
312    FixupState();
313    XCloseDisplay(dpy);
314    exit (0);
315}
316
317
318/* Free past incarnation if needed, and retain state if needed. */
319static void
320FixupState(void)
321{
322    Atom prop, type;
323    int format;
324    unsigned long length, after;
325    unsigned char *data;
326
327    if (!(DefaultVisual(dpy, screen)->class & Dynamic))
328	unsave_past = 0;
329    if (!unsave_past && !save_colors)
330	return;
331    prop = XInternAtom(dpy, "_XSETROOT_ID", False);
332    if (unsave_past) {
333	if (XGetWindowProperty(dpy, root, prop, 0L, 1L, True, AnyPropertyType,
334		       &type, &format, &length, &after, &data) != Success)
335	    fprintf(stderr,
336		    "%s: warning: cannot get _XSETROOT_ID property from root window\n",
337		    program_name);
338	else if ((type == XA_PIXMAP) && (format == 32) &&
339		 (length == 1) && (after == 0))
340	    XKillClient(dpy, *((Pixmap *)data));
341	else if (type != None)
342	    fprintf(stderr, "%s: warning: _XSETROOT_ID property is garbage\n",
343		    program_name);
344    }
345    if (save_colors) {
346	if (!save_pixmap)
347	    save_pixmap = XCreatePixmap(dpy, root, 1, 1, 1);
348	XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, PropModeReplace,
349			(unsigned char *) &save_pixmap, 1);
350	XSetCloseDownMode(dpy, RetainPermanent);
351    }
352}
353
354/*
355 * SetBackgroundToBitmap: Set the root window background to a caller supplied
356 *                        bitmap.
357 */
358static void
359SetBackgroundToBitmap(Pixmap bitmap, unsigned int width, unsigned int height)
360{
361    Pixmap pix;
362    GC gc;
363    XGCValues gc_init;
364
365    gc_init.foreground = NameToPixel(fore_color, BlackPixel(dpy, screen));
366    gc_init.background = NameToPixel(back_color, WhitePixel(dpy, screen));
367    if (reverse) {
368	unsigned long temp=gc_init.foreground;
369	gc_init.foreground=gc_init.background;
370	gc_init.background=temp;
371    }
372    gc = XCreateGC(dpy, root, GCForeground|GCBackground, &gc_init);
373    pix = XCreatePixmap(dpy, root, width, height,
374			(unsigned int)DefaultDepth(dpy, screen));
375    XCopyPlane(dpy, bitmap, pix, gc, 0, 0, width, height, 0, 0, (unsigned long)1);
376    XSetWindowBackgroundPixmap(dpy, root, pix);
377    XFreeGC(dpy, gc);
378    XFreePixmap(dpy, bitmap);
379    if (save_colors)
380	save_pixmap = pix;
381    else
382	XFreePixmap(dpy, pix);
383    XClearWindow(dpy, root);
384    unsave_past = 1;
385}
386
387
388/*
389 * CreateCursorFromFiles: make a cursor of the right colors from two bitmap
390 *                        files.
391 */
392#define BITMAP_HOT_DEFAULT 8
393
394static Cursor
395CreateCursorFromFiles(char *cursor_file, char *mask_file)
396{
397    Pixmap cursor_bitmap, mask_bitmap;
398    unsigned int width, height, ww, hh;
399    int x_hot, y_hot;
400    Cursor cursor;
401    XColor fg, bg;
402
403    fg = NameToXColor(fore_color, BlackPixel(dpy, screen));
404    bg = NameToXColor(back_color, WhitePixel(dpy, screen));
405    if (reverse) {
406	XColor temp = fg;
407	fg = bg;
408	bg = temp;
409    }
410
411    cursor_bitmap = ReadBitmapFile(cursor_file, &width, &height, &x_hot, &y_hot);
412    mask_bitmap = ReadBitmapFile(mask_file, &ww, &hh, (int *)NULL, (int *)NULL);
413
414    if (width != ww || height != hh) {
415	fprintf(stderr,
416"%s: dimensions of cursor bitmap and cursor mask bitmap are different\n",
417		program_name);
418	exit(1);
419	/*NOTREACHED*/
420    }
421
422    if ((x_hot == -1) && (y_hot == -1)) {
423	x_hot = BITMAP_HOT_DEFAULT;
424	y_hot = BITMAP_HOT_DEFAULT;
425    }
426    if ((x_hot < 0) || ((unsigned int)x_hot >= width) ||
427	(y_hot < 0) || ((unsigned int)y_hot >= height)) {
428	fprintf(stderr, "%s: hotspot is outside cursor bounds\n", program_name);
429	exit(1);
430	/*NOTREACHED*/
431    }
432
433    cursor = XCreatePixmapCursor(dpy, cursor_bitmap, mask_bitmap, &fg, &bg,
434				 (unsigned int)x_hot, (unsigned int)y_hot);
435    XFreePixmap(dpy, cursor_bitmap);
436    XFreePixmap(dpy, mask_bitmap);
437
438    return(cursor);
439}
440
441static Cursor
442CreateCursorFromName(char *name)
443{
444    XColor fg, bg;
445    int	    i;
446    Font    fid;
447
448    fg = NameToXColor(fore_color, BlackPixel(dpy, screen));
449    bg = NameToXColor(back_color, WhitePixel(dpy, screen));
450    if (reverse) {
451	XColor temp = fg;
452	fg = bg;
453	bg = temp;
454    }
455    i = XmuCursorNameToIndex (name);
456    if (i == -1)
457	return (Cursor) 0;
458    fid = XLoadFont (dpy, "cursor");
459    if (!fid)
460	return (Cursor) 0;
461    return XCreateGlyphCursor (dpy, fid, fid,
462			       i, i+1, &fg, &bg);
463}
464
465/*
466 * MakeModulaBitmap: Returns a modula bitmap based on an x & y mod.
467 */
468static Pixmap
469MakeModulaBitmap(int mod_x, int mod_y)
470{
471    long pattern_line = 0;
472    char modula_data[16*16/8];
473
474    for (int i = 16; i--; ) {
475	pattern_line <<=1;
476	if ((i % mod_x) == 0) pattern_line |= 0x0001;
477    }
478    for (int i = 0; i < 16; i++) {
479	if ((i % mod_y) == 0) {
480	    modula_data[i*2] = (char)0xff;
481	    modula_data[i*2+1] = (char)0xff;
482	} else {
483	    modula_data[i*2] = pattern_line & 0xff;
484	    modula_data[i*2+1] = (pattern_line>>8) & 0xff;
485	}
486    }
487
488    return(XCreateBitmapFromData(dpy, root, modula_data, 16, 16));
489}
490
491
492/*
493 * NameToXColor: Convert the name of a color to its Xcolor value.
494 */
495static XColor
496NameToXColor(char *name, unsigned long pixel)
497{
498    XColor c;
499
500    if (!name || !*name) {
501	c.pixel = pixel;
502	XQueryColor(dpy, DefaultColormap(dpy, screen), &c);
503    } else if (!XParseColor(dpy, DefaultColormap(dpy, screen), name, &c)) {
504	fprintf(stderr, "%s: unknown color or bad color format: %s\n",
505			program_name, name);
506	exit(1);
507	/*NOTREACHED*/
508    }
509    return(c);
510}
511
512static unsigned long
513NameToPixel(char *name, unsigned long pixel)
514{
515    XColor ecolor;
516
517    if (!name || !*name)
518	return pixel;
519    if (!XParseColor(dpy,DefaultColormap(dpy,screen),name,&ecolor)) {
520	fprintf(stderr,"%s:  unknown color \"%s\"\n",program_name,name);
521	exit(1);
522	/*NOTREACHED*/
523    }
524    if (!XAllocColor(dpy, DefaultColormap(dpy, screen),&ecolor)) {
525	fprintf(stderr, "%s:  unable to allocate color for \"%s\"\n",
526		program_name, name);
527	exit(1);
528	/*NOTREACHED*/
529    }
530    if ((ecolor.pixel != BlackPixel(dpy, screen)) &&
531	(ecolor.pixel != WhitePixel(dpy, screen)) &&
532	(DefaultVisual(dpy, screen)->class & Dynamic))
533	save_colors = 1;
534    return(ecolor.pixel);
535}
536
537static Pixmap
538ReadBitmapFile(char *filename, unsigned int *width, unsigned int *height,
539	       int *x_hot, int *y_hot)
540{
541    Pixmap bitmap;
542    int status;
543
544    status = XReadBitmapFile(dpy, root, filename, width,
545			     height, &bitmap, x_hot, y_hot);
546    if (status == BitmapSuccess)
547      return(bitmap);
548    else if (status == BitmapOpenFailed)
549	fprintf(stderr, "%s: can't open file: %s\n", program_name, filename);
550    else if (status == BitmapFileInvalid)
551	fprintf(stderr, "%s: bad bitmap format file: %s\n",
552			program_name, filename);
553    else
554	fprintf(stderr, "%s: insufficient memory for bitmap: %s",
555			program_name, filename);
556    exit(1);
557    /*NOTREACHED*/
558}
559