1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* bomb --- temporary screen */
3 
4 #if 0
5 static const char sccsid[] = "@(#)bomb.c	5.00 2000/11/01 xlockmore";
6 #endif
7 
8 /*-
9  * Copyright (c) 1994 by Dave Shield
10  *
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * Revision History:
24  * 01-Nov-2000: Allocation checks
25  * 10-May-1997: Made more compatible with xscreensaver :)
26  * 09-Jan-1995: Assorted defines to control various aspects of bomb mode.
27  *              Uncomment, or otherwise define the appropriate line
28  *              to obtain the relevant behaviour, thanks to Dave Shield
29  *              <D.T.Shield@csc.liv.ac.uk>.
30  * 20-Dec-1994: Time patch for multiprocessor machines (ie. Sun10) thanks to
31  *              Nicolas Pioch <pioch@Email.ENST.Fr>.
32  * 1994:        Written.  Dave Shield  Liverpool Computer Science
33  */
34 
35 /*-
36  * This mode may have limited appeal.  Its good for logging yourself out
37  * if you do not know if your going to be back.  It is no longer to be used
38  * as a way of forcing users in a lab to logout after locking the screen.
39  */
40 
41 
42 #ifdef STANDALONE
43 # define MODE_bomb
44 # define USE_BOMB
45 # define DEFAULTS	"*delay: 1000000 \n" \
46 			"*count: 10 \n" \
47 			"*cycles: 20 \n" \
48 			"*ncolors: 200 \n" \
49 			"*verbose: False \n" \
50 
51 # define free_bomb 0
52 # define reshape_bomb 0
53 # define bomb_handle_event 0
54 # define UNIFORM_COLORS
55 # define BRIGHT_COLORS
56 # include "xlockmore.h"		/* in xscreensaver distribution */
57 #else /* STANDALONE */
58 # include "xlock.h"		/* in xlockmore distribution */
59 #include "iostuff.h"
60 #endif /* STANDALONE */
61 
62 #ifdef MODE_bomb
63 
64 ENTRYPOINT ModeSpecOpt bomb_opts =
65 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
66 
67 #ifdef USE_MODULES
68 const ModStruct bomb_description =
69 {"bomb", "init_bomb", "draw_bomb", "release_bomb",
70  "refresh_bomb", "change_bomb", (char *) NULL, &bomb_opts,
71  100000, 10, 20, 1, 64, 1.0, "",
72  "Shows a bomb and will autologout after a time", 0, NULL};
73 
74 #endif
75 
76 #if defined(__CYGWIN__) || defined(SOLARIS2) || defined(__FreeBSD__)
77 #include <signal.h>
78 #endif
79 #include <sys/signal.h>
80 
81 #if 0
82 #define SIMPLE_COUNTDOWN	/* Display a simple integer countdown,     */
83 #endif
84 			      /*  rather than a "MIN:SEC" format.        */
85 #define COLOUR_CHANGE		/* Display cycles through the colour wheel */
86 			      /*  rather than staying red throughout.    */
87 
88 #ifdef USE_MB
89 #define FULL_COUNT_FONT         "-adobe-courier-bold-r-*-*-34-*-*-*-*-*-iso8859-1"
90 #define ICON_COUNT_FONT         "-misc-fixed-medium-r-normal-*-8-*-*-*-*-*-iso8859-1"
91 #else
92 #define FULL_COUNT_FONT         "-*-*-*-*-*-*-18-*-*-*-*-*-*-*"
93 #define ICON_COUNT_FONT         "-*-*-*-*-*-*-8-*-*-*-*-*-*-*"
94 #endif
95 #define COUNTDOWN       600	/* No. seconds to lock for */
96 #define NDIGITS         4	/* Number of digits in count */
97 
98 #define MAX_DELAY       1000000	/* Max delay between updates */
99 #define NAP_TIME        5	/* Sleep between shutdown attempts */
100 #define RIVET_RADIUS    6	/* Size of detonator 'rivets' */
101 
102 typedef struct {
103 	Bool        painted;
104 	int         width, height;
105 	int         x, y;
106 	XPoint      loc;
107 	int         delta;
108 	int         color;
109 	time_t      countdown;
110 	int         startcountdown;
111 	int         text_width;
112 	int         text_ascent;
113 	int         text_descent;
114 	int         moveok;
115 	GC          gc;
116 } bombstruct;
117 
118 static bombstruct *bombs = NULL;
119 
120 #ifdef USE_MB
121 static XFontSet mode_font = None;
122 /*static int getFontHeight(XFontSet f)
123 {
124 	XRectangle ink, log;
125 
126 	if (f == None) {
127 		return 8;
128 	} else {
129 		XmbTextExtents(mode_font, "My", strlen("My"), &ink, &log);
130 		return log.height;
131 	}
132 }*/
getTextWidth(char * string)133 static int getTextWidth(char *string)
134 {
135 	XRectangle ink, logical;
136 
137 	if (mode_font != None) {
138 		XmbTextExtents(mode_font, string, strlen(string), &ink, &logical);
139 		return logical.width;
140 	}
141 	return 8;
142 }
143 extern XFontSet getFontSet(Display * display);
144 #define DrawString(d, p, gc, start, ascent, string, len) (void) XmbDrawString(\
145 	d, p, mode_font, gc, start, ascent, string, len)
146 #else
147 static XFontStruct *mode_font = None;
148 /*#define getFontHeight(f) ((f == None) ? 8 : f->ascent + f->descent)*/
getTextWidth(char * string)149 static int getTextWidth(char *string)
150 {
151 	int width = XTextWidth(mode_font, string, strlen(string));
152 	if (width == 0)
153 		return 8;
154 	return width;
155 }
156 
157 extern XFontStruct *getFont(Display * display);
158 
159 #define DrawString(d, p, gc, start, ascent, string, len) (void) XDrawString(\
160 	d, p, gc, start, ascent, string, len)
161 #endif
162 
163 static void
free_bomb_screen(Display * display,bombstruct * bp)164 free_bomb_screen(Display *display, bombstruct *bp)
165 {
166 	if (bp == NULL) {
167 		return;
168 	}
169 	if (bp->gc != None) {
170 		XFreeGC(display, bp->gc);
171 		bp->gc = None;
172 	}
173 	bp = NULL;
174 }
175 
176 static void
rivet(ModeInfo * mi,int x,int y)177 rivet(ModeInfo * mi, int x, int y)
178 {
179 	Display *display = MI_DISPLAY(mi);
180 	GC gc = MI_GC(mi);
181 
182 	XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
183 	if (MI_NPIXELS(mi) > 2) {
184 		XDrawArc(display, MI_WINDOW(mi), gc, x, y,
185 			2 * RIVET_RADIUS, 2 * RIVET_RADIUS, 270 * 64, 90 * 64);
186 		XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
187 		XDrawArc(display, MI_WINDOW(mi), gc, x, y,
188 			2 * RIVET_RADIUS, 2 * RIVET_RADIUS, 70 * 64, 130 * 64);
189 	} else
190 		XDrawArc(display, MI_WINDOW(mi), gc, x, y,
191 			2 * RIVET_RADIUS, 2 * RIVET_RADIUS, 0, 360 * 64);
192 }
193 
194 static void
detonator(ModeInfo * mi,int draw)195 detonator(ModeInfo * mi, int draw)
196 {
197 	Display *display = MI_DISPLAY(mi);
198 	GC gc = MI_GC(mi);
199 	bombstruct *bp = &bombs[MI_SCREEN(mi)];
200 	int b_width, b_height;
201 
202 	b_width = bp->width / 2;
203 	b_height = bp->height / 3;
204 	if (draw) {
205 #ifdef SOLARIS2
206 		/*
207 		 * if this is not done the rectangle is sometimes messed up on
208 		 * Solaris2 with 24 bit TrueColor (Ultra2)
209 		 */
210 		XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
211 		XFillRectangle(display, MI_WINDOW(mi), gc,
212 			bp->loc.x, bp->loc.y, b_width, b_height);
213 #endif
214 		if (MI_NPIXELS(mi) > 2)
215 			XSetForeground(MI_DISPLAY(mi), MI_GC(mi),
216 				MI_PIXEL(mi, bp->color));
217 		else
218 			XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
219 		/*XSetForeground(display, gc, allocPixel(display, MI_COLORMAP(mi),
220 			"grey", "white")); */
221 		XFillRectangle(display, MI_WINDOW(mi), gc,
222 			bp->loc.x, bp->loc.y, b_width, b_height);
223 
224 		/*
225 		 *  If a full size screen (and colour),
226 		 *      'rivet' the box to it
227 		 */
228 		if (bp->width > 160 && bp->height > 160) {
229 			rivet(mi, bp->loc.x + RIVET_RADIUS, bp->loc.y + RIVET_RADIUS);
230 			rivet(mi, bp->loc.x + RIVET_RADIUS,
231 				bp->loc.y + b_height - 3 * RIVET_RADIUS);
232 			rivet(mi, bp->loc.x + b_width - 3 * RIVET_RADIUS,
233 				bp->loc.y + RIVET_RADIUS);
234 			rivet(mi, bp->loc.x + b_width - 3 * RIVET_RADIUS,
235 				bp->loc.y + b_height - 3 * RIVET_RADIUS);
236 		}
237 	} else {
238 		XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
239 		XFillRectangle(display, MI_WINDOW(mi), gc,
240 			bp->loc.x, bp->loc.y, b_width, b_height);
241 	}
242 }
243 
244 ENTRYPOINT void
release_bomb(ModeInfo * mi)245 release_bomb(ModeInfo * mi)
246 {
247 	if (bombs != NULL) {
248 		int screen;
249 
250 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
251                         free_bomb_screen(MI_DISPLAY(mi), &bombs[screen]);
252 		free(bombs);
253 		bombs = (bombstruct *) NULL;
254 	}
255 	if (mode_font != None) {
256 #ifdef USE_MB
257 		XFreeFontSet(MI_DISPLAY(mi), mode_font);
258 #else
259 		XFreeFont(MI_DISPLAY(mi), mode_font);
260 #endif
261 		mode_font = None;
262 	}
263 }
264 
265 ENTRYPOINT void
init_bomb(ModeInfo * mi)266 init_bomb(ModeInfo * mi)
267 {
268 	Display *display = MI_DISPLAY(mi);
269 	bombstruct *bp;
270 	char number[NDIGITS + 2];
271 	XGCValues gcv;
272 #ifdef USE_MB
273 	char **miss, *def;
274 	int n_miss;
275 	XRectangle ink, log;
276 #endif
277 
278 	MI_INIT(mi, bombs);
279 	bp = &bombs[MI_SCREEN(mi)];
280 
281 	bp->width = MI_WIDTH(mi);
282 	bp->height = MI_HEIGHT(mi);
283 
284 	free_bomb_screen(display, bp);
285 	if (mode_font != None) {
286 #ifdef USE_MB
287 		XFreeFontSet(display, mode_font);
288 #else
289 		XFreeFont(display, mode_font);
290 #endif
291 		mode_font = None;
292 	}
293 	/* Set up text font */
294 	if (bp->width > 256 && bp->height > 256) {	/* Full screen */
295 #ifdef USE_MB
296 		mode_font = XCreateFontSet(display, FULL_COUNT_FONT, &miss, &n_miss, &def);
297 #else
298 		mode_font = XLoadQueryFont(display, FULL_COUNT_FONT);
299 #endif
300 		bp->delta = getTextWidth ((char *) "I");
301 	} else {		/* icon window */
302 #ifdef USE_MB
303 		mode_font = XCreateFontSet(display, ICON_COUNT_FONT, &miss, &n_miss, &def);
304 #else
305 		mode_font = XLoadQueryFont(display, ICON_COUNT_FONT);
306 #endif
307 		bp->delta = getTextWidth ((char *) "I");
308 	}
309 	if (mode_font == None) {
310 #ifdef USE_MB
311 		mode_font = XCreateFontSet(display, "-*-medium-r-normal--14-*", &miss, &n_miss, &def);
312 #else
313 		mode_font = getFont(display);
314 #endif
315 	}
316 	if (mode_font == None) {
317 		release_bomb(mi);
318 		return;
319 	}
320 	if (bp->gc == None && mode_font != None) {
321 #ifndef USE_MB
322 		gcv.font = mode_font->fid;
323 #endif
324 		gcv.background = MI_BLACK_PIXEL(mi);
325 		gcv.foreground = MI_WHITE_PIXEL(mi);
326 		gcv.graphics_exposures = False;
327 		if ((bp->gc = XCreateGC(display, MI_WINDOW(mi),
328 			GCForeground | GCBackground | GCGraphicsExposures
329 #ifndef USE_MB
330 			| GCFont
331 #endif
332 			, &gcv)) == None) {
333 
334 			free_bomb_screen(display, bp);
335 			return;
336 		}
337 	}
338 #ifdef SIMPLE_COUNTDOWN
339 	(void) sprintf(number, "%0*d", NDIGITS, 0);
340 #else
341 #ifdef USE_MB
342 	(void) sprintf(number, "%.*s:00", NDIGITS - 2, "0000000");
343 #else
344 	(void) sprintf(number, "%.*s:**", NDIGITS - 2, "*******");
345 #endif
346 #endif
347 	/* if (mode_font == None) bp->text_width = 8; */
348 #ifdef USE_MB
349 	XmbTextExtents(mode_font, number, strlen(number), &ink, &log);
350 	bp->text_width = ink.width;
351 	bp->text_ascent = ink.height;
352 	bp->text_descent = 0;
353 #else
354 	bp->text_width = XTextWidth(mode_font, number, NDIGITS + 1);
355 	bp->text_ascent = mode_font->max_bounds.ascent;
356 	bp->text_descent = mode_font->max_bounds.descent;
357 #endif
358 
359 #ifndef STANDALONE
360 	if (MI_DELAY(mi) > MAX_DELAY)
361 		MI_DELAY(mi) = MAX_DELAY;	/* Time cannot move slowly */
362 #endif /* STANDALONE */
363 	if (MI_COUNT(mi) < 1)
364 		bp->startcountdown = 1;		/* Do not want an instantaneous logout */
365 	bp->startcountdown = MI_COUNT(mi);
366 	bp->startcountdown *= 60;
367 #if 0				/* Stricter if uncommented but people do not have to run bomb */
368 	if (bp->startcountdown > COUNTDOWN)
369 		bp->startcountdown = COUNTDOWN;
370 #endif
371 	if (bp->countdown == 0)	/* <--Stricter if uncommented */
372 		bp->countdown = time((time_t *) NULL) + bp->startcountdown;
373 	/* Detonator Primed */
374 
375 #ifdef STANDALONE
376 	MI_CLEARWINDOWCOLORMAPFAST(mi, MI_GC(mi), MI_BLACK_PIXEL(mi));
377 #else
378 	MI_CLEARWINDOW(mi);
379 #endif
380 	bp->painted = False;
381 
382 	/*
383 	 *  Draw the graphics
384 	 *      Very simple - detonator box with countdown
385 	 *
386 	 *  ToDo:  Improve the graphics
387 	 *      (e.g. stick of dynamite, with burning fuse?)
388 	 */
389 	bp->loc.x = NRAND(bp->width / 2);
390 	bp->loc.y = NRAND(bp->height * 3 / 5);
391 	bp->x = bp->loc.x + bp->width / 4 - (bp->text_width / 2);
392 	bp->y = bp->loc.y + bp->height / 5;	/* Central-ish */
393 	if (MI_NPIXELS(mi) > 2)
394 		bp->color = NRAND(MI_NPIXELS(mi));
395 	detonator(mi, 1);
396 	bp->moveok = 0;
397 }
398 
399 extern void logoutUser(Display * display);
400 
401 /*
402  *  Game Over
403  */
404 static void
explode(ModeInfo * mi)405 explode(ModeInfo * mi)
406 {
407 	bombstruct *bp = &bombs[MI_SCREEN(mi)];
408 	char buff[NDIGITS + 2];
409 
410 	/*
411 	 *  ToDo:
412 	 *      Improve the graphics - some sort of explosion?
413 	 *      (Will need to involve the main X event loop)
414 	 */
415 #ifdef USE_MB
416 #ifdef SIMPLE_COUNTDOWN
417 	(void) sprintf(buff, "%.*s", NDIGITS, "000000000");
418 #else
419 	(void) sprintf(buff, "%.*s:00", NDIGITS - 2, "0000000");
420 #endif
421 #else
422 #ifdef SIMPLE_COUNTDOWN
423 	(void) sprintf(buff, "%.*s", NDIGITS, "*********");
424 #else
425 	(void) sprintf(buff, "%.*s:**", NDIGITS - 2, "*******");
426 #endif
427 #endif
428 	XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_PIXEL(mi, 1));
429 	(void) XDrawString(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
430 		bp->x, bp->y, buff, NDIGITS);
431 
432 	(void) fprintf(stderr, "BOOM!!!!\n");
433 #ifndef DEBUG
434 	if (MI_IS_INWINDOW(mi) || MI_IS_INROOT(mi) ||
435 			MI_IS_NOLOCK(mi) || MI_IS_DEBUG(mi))
436 		(void) kill((int) getpid(), SIGTERM);
437 	else if ((int) getuid() == 0) {		/* Do not try to logout root! */
438 		bp->countdown = 0;
439 		init_bomb(mi);
440 	} else
441 		logoutUser(MI_DISPLAY(mi));
442 #else
443 	(void) kill(getpid(), SIGTERM);
444 #endif
445 }
446 
447 ENTRYPOINT void
draw_bomb(ModeInfo * mi)448 draw_bomb(ModeInfo * mi)
449 {
450 	Display *display = MI_DISPLAY(mi);
451 	GC gc = MI_GC(mi);
452 	char number[NDIGITS + 2];
453 	unsigned long crayon;
454 	int countleft;
455 	bombstruct *bp;
456 
457 	if (bombs == NULL)
458 		return;
459 	bp = &bombs[MI_SCREEN(mi)];
460 	if (mode_font == None)
461 		return;
462 
463 	countleft = (int) (bp->countdown - time((time_t *) NULL));
464 	if (countleft <= 0)
465 		explode(mi);	/* Bye, bye.... */
466 	else {
467 		bp->painted = True;
468 #ifdef SIMPLE_COUNTDOWN
469 		(void) sprintf(number, "%0*d", NDIGITS, countleft);
470 #else
471 		(void) sprintf(number, "%0*d:%02d", NDIGITS - 2,
472 			(countleft / 60) % 100, countleft % 60);
473 #endif
474 		/* Blank out the previous number .... */
475 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
476 		XFillRectangle(display, MI_WINDOW(mi), gc,
477 			bp->x - bp->delta,
478 			(bp->y - bp->text_ascent) - bp->delta,
479 			bp->text_width + (2 * bp->delta),
480 			(bp->text_ascent + bp->text_descent) + (2 * bp->delta));
481 
482 		/* ... and count down */
483 		if (MI_NPIXELS(mi) <= 2)
484 			crayon = MI_WHITE_PIXEL(mi);
485 		else
486 #ifdef COLOUR_CHANGE		/* Blue to red */
487 			crayon = MI_PIXEL(mi, countleft * MI_NPIXELS(mi) / bp->startcountdown);
488 #else
489 			crayon = MI_PIXEL(mi, 1);
490 #endif
491 		if (!(countleft % 60)) {
492 			if (bp->moveok) {
493 				detonator(mi, 0);
494 				bp->loc.x = NRAND(bp->width / 2);
495 				bp->loc.y = NRAND(bp->height * 3 / 5);
496 				bp->x = bp->loc.x + bp->width / 4 -
497 					(bp->text_width / 2);
498 				bp->y = bp->loc.y + bp->height / 5;	/* Central-ish */
499 				if (MI_NPIXELS(mi) > 2)
500 					bp->color = NRAND(MI_NPIXELS(mi));
501 				detonator(mi, 1);
502 				bp->moveok = 0;
503 			}
504 		} else {
505 			bp->moveok = 1;
506 		}
507 		XSetForeground(display, bp->gc, crayon);
508 		DrawString(display, MI_WINDOW(mi), bp->gc,
509 			bp->x - getTextWidth((char *) "I") / 2, bp->y,
510 			number, strlen(number));
511 	}
512 }
513 
514 #ifndef STANDALONE
515 ENTRYPOINT void
refresh_bomb(ModeInfo * mi)516 refresh_bomb(ModeInfo * mi)
517 {
518 	bombstruct *bp;
519 
520 	if (bombs == NULL)
521 		return;
522 	bp = &bombs[MI_SCREEN(mi)];
523 	if (mode_font == None)
524 		return;
525 
526 	if (bp->painted) {
527 		MI_CLEARWINDOW(mi);
528 		detonator(mi, 1);
529 	}
530 }
531 
532 ENTRYPOINT void
change_bomb(ModeInfo * mi)533 change_bomb(ModeInfo * mi)
534 {
535 	bombstruct *bp;
536 
537 	if (bombs == NULL)
538 		return;
539 	bp = &bombs[MI_SCREEN(mi)];
540 	if (mode_font == None)
541 		return;
542 
543 	detonator(mi, 0);
544 	bp->painted = False;
545 	bp->loc.x = NRAND(bp->width / 2);
546 	bp->loc.y = NRAND(bp->height * 3 / 5);
547 	bp->x = bp->loc.x + bp->width / 4 - (bp->text_width / 2);
548 	bp->y = bp->loc.y + bp->height / 5;	/* Central-ish */
549 	if (MI_NPIXELS(mi) > 2)
550 		bp->color = NRAND(MI_NPIXELS(mi));
551 	detonator(mi, 1);
552 	bp->moveok = 0;
553 }
554 #endif
555 
556 XSCREENSAVER_MODULE ("Bomb", bomb)
557 
558 #endif  /* MODE_bomb */
559