1 /* -*- Mode: C; tab-width: 4 -*- */
2 /*-
3  * nose --- a little guy with a big nose and a hat wanders around
4  *          spewing out messages
5  */
6 
7 #if 0
8 static const char sccsid[] = "@(#)nose.c	5.00 2000/11/01 xlockmore";
9 
10 #endif
11 
12 /*-
13  * Permission to use, copy, modify, and distribute this software and its
14  * documentation for any purpose and without fee is hereby granted,
15  * provided that the above copyright notice appear in all copies and that
16  * both that copyright notice and this permission notice appear in
17  * supporting documentation.
18  *
19  * This file is provided AS IS with no warranties of any kind.  The author
20  * shall have no liability with respect to the infringement of copyrights,
21  * trade secrets or any patents by this file or any part thereof.  In no
22  * event will the author be liable for any lost revenue or profits or
23  * other special, indirect and consequential damages.
24  *
25  * Revision History:
26  * 01-Nov-2000: Allocation checks
27  * 06-Jun-1997: Compatible with xscreensaver and now colorized (idea from
28  *              xscreensaver but colors are random).
29  * 27-Feb-1996: Added new ModeInfo arg to init and callback hooks.  Removed
30  *              references to onepause, now uses MI_PAUSE(mi) interface.
31  *              Ron Hitchens <ron AT idiom.com>
32  * 10-Oct-1995: A better way of handling fortunes from a file, thanks to
33  *              Jouk Jansen <joukj AT hrem.nano.tudelft.nl>.
34  * 21-Sep-1995: font option added, debugged for multiscreens
35  * 12-Aug-1995: xlock version
36  * 1992: xscreensaver version, noseguy (Jamie Zawinski <jwz AT jwz.org>)
37  * 1990: X11 version, xnlock (Dan Heller <argv@sun.com>)
38  */
39 
40 /*-
41  * xscreensaver, Copyright (c) 1992 Jamie Zawinski <jwz AT jwz.org>
42  *
43  * Permission to use, copy, modify, distribute, and sell this software and its
44  * documentation for any purpose is hereby granted without fee, provided that
45  * the above copyright notice appear in all copies and that both that
46  * copyright notice and this permission notice appear in supporting
47  * documentation.  No representations are made about the suitability of this
48  * software for any purpose.  It is provided "as is" without express or
49  * implied warranty.
50  */
51 
52 #ifdef STANDALONE
53 #define MODE_nose
54 #define DEFAULTS "*delay: 100000 \n" \
55 	"*ncolors: 64 \n" \
56 	"*font: fixed\n" \
57 	"*text: \n" \
58 	"*filename: \n" \
59 	"*fortunefile: \n" \
60 	"*program: \n" \
61 
62 # define free_nose 0
63 # define reshape_nose 0
64 # define nose_handle_event 0
65 #define UNIFORM_COLORS
66 #include "xlockmore.h"		/* in xscreensaver distribution */
67 #else /* STANDALONE */
68 #include "xlock.h"		/* in xlockmore distribution */
69 #include "iostuff.h"
70 #endif /* STANDALONE */
71 
72 #ifdef MODE_nose
73 
74 ENTRYPOINT ModeSpecOpt nose_opts =
75 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
76 
77 #ifdef USE_MODULES
78 ModStruct   nose_description =
79 {"nose", "init_nose", "draw_nose", "release_nose",
80  "refresh_nose", "init_nose", (char *) NULL, &nose_opts,
81  100000, 1, 1, 1, 64, 1.0, "",
82  "Shows a man with a big nose runs around spewing out messages", 0, NULL};
83 
84 #endif
85 
86 #include "bitmaps/nose-hat.xbm"
87 #include "bitmaps/nose-hatd.xbm"
88 #include "bitmaps/nose-facef.xbm"
89 #include "bitmaps/nose-faced.xbm"
90 #include "bitmaps/nose-facel.xbm"
91 #include "bitmaps/nose-facer.xbm"
92 #include "bitmaps/nose-shoef.xbm"
93 #include "bitmaps/nose-shoel.xbm"
94 #include "bitmaps/nose-shoer.xbm"
95 #include "bitmaps/nose-stepl.xbm"
96 #include "bitmaps/nose-stepr.xbm"
97 
98 #define L 0
99 #define R 1
100 #define LSTEP 2
101 #define RSTEP 3
102 #define LF 4
103 #define RF 5
104 #define F 6
105 #define D 7
106 #define PIXMAPS 8
107 #define PIXMAP_SIZE 64
108 #define MOVE 0
109 #define TALK 1
110 #define FREEZE 2
111 
112 #ifdef USE_MB
113 static XFontSet mode_font = None;
getFontHeight(XFontSet f)114 static int getFontHeight(XFontSet f)
115 {
116 	XRectangle ink, log;
117 
118 	if (f == None) {
119 		return 8;
120 	} else {
121 		XmbTextExtents(mode_font, "My", strlen("My"), &ink, &log);
122 		/*XmbTextExtents(mode_font, "M��", strlen("M��"), &ink, &log);*/
123 		return log.height;
124 	}
125 }
getTextWidth(char * string)126 static int getTextWidth(char *string)
127 {
128 	XRectangle ink, logical;
129 
130 	XmbTextExtents(mode_font, string, strlen(string), &ink, &logical);
131 	return logical.width;
132 }
133 extern XFontSet getFontSet(Display * display);
134 #define DrawString(d, p, gc, start, ascent, string, len) (void) XmbDrawString(\
135 	d, p, mode_font, gc, start, ascent, string, len)
136 #else
137 static XFontStruct *mode_font = None;
138 #define getFontHeight(f) ((f == None) ? 8 : (f->ascent + f->descent == 0) ? 16 :  f->ascent + f->descent)
getTextWidth(char * string)139 static int getTextWidth(char *string)
140 {
141 	int width =  XTextWidth(mode_font, string, strlen(string));
142 
143 	if (width == 0 && strlen(string) > 0)
144 		return 7 * strlen(string);
145 	return width;
146 }
147 extern XFontStruct *getFont(Display * display);
148 #define DrawString(d, p, gc, start, ascent, string, len) (void) XDrawString(\
149 	d, p, gc, start, ascent, string, len)
150 #endif
151 extern char *getWords(int screen, int screens);
152 extern int  isRibbon(void);
153 
154 typedef struct {
155 	int         x, y, width, height;
156 } window_rect;
157 
158 typedef struct {
159 	int         xs, ys;
160 	int         width, height;
161 	GC          text_fg_gc, text_bg_gc, noseGC[PIXMAPS];
162 	char       *words;
163 	int         x, y;
164 	int         tinymode;	/* walking or getting passwd */
165 	int         length, dir, lastdir;
166 	int         up;
167 	int         busyLoop;
168 	int         frames;
169 	int         state;
170 	Bool        talking;
171 	window_rect s;
172 	Pixmap      position[PIXMAPS];
173 } nosestruct;
174 
175 static nosestruct *noses = (nosestruct *) NULL;
176 
177 #define LEFT 001
178 #define RIGHT 002
179 #define DOWN 004
180 #define UP 010
181 #define FRONT 020
182 #define X_INCR 3
183 #define Y_INCR 2
184 #define YELLOW (MI_NPIXELS(mi) / 6)
185 
186 #define COPY(d,g,c,p,np,x,y,w,h) XSetForeground(d,g,c);\
187 XSetStipple(d,g,p); XSetTSOrigin(d,g,x,y);\
188 XFillRectangle(d,np,g,x,y,w,h)
189 
190 static void
free_nose_screen(Display * display,nosestruct * np)191 free_nose_screen(Display *display, nosestruct *np)
192 {
193 	int         pix;
194 
195 	if (np->text_fg_gc != None) {
196 		XFreeGC(display, np->text_fg_gc);
197 		np->text_fg_gc = None;
198 	}
199 	if (np->text_bg_gc != None) {
200 		XFreeGC(display, np->text_bg_gc);
201 		np->text_bg_gc = None;
202 	}
203 	for (pix = 0; pix < PIXMAPS; pix++) {
204 		if (np->position[pix] != None) {
205 			XFreePixmap(display, np->position[pix]);
206 	   		np->position[pix] = None;
207 		}
208 		if (np->noseGC[pix] != None) {
209 			XFreeGC(display, np->noseGC[pix]);
210 	   		np->noseGC[pix] = None;
211 		}
212 	}
213 }
214 
215 static Bool
pickClothes(ModeInfo * mi)216 pickClothes(ModeInfo * mi)
217 {
218 	Display    *display = MI_DISPLAY(mi);
219 	Window      window = MI_WINDOW(mi);
220 	GC          gc = MI_GC(mi);
221 	nosestruct *np = &noses[MI_SCREEN(mi)];
222 	XGCValues   gcv;
223 	Pixmap      face_pix, hat_pix, shoe_pix, shoel_pix, shoer_pix;
224 	unsigned long hat_color = (MI_NPIXELS(mi) <= 2) ?
225 	MI_WHITE_PIXEL(mi) : MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
226 	unsigned long face_color = (MI_NPIXELS(mi) <= 2) ?
227 	MI_WHITE_PIXEL(mi) : MI_PIXEL(mi, (YELLOW));	/* Racism? */
228 	unsigned long shoe_color = (MI_NPIXELS(mi) <= 2) ?
229 	MI_WHITE_PIXEL(mi) : MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
230 	int         i;
231 
232 	gcv.graphics_exposures = False;
233 	gcv.foreground = MI_BLACK_PIXEL(mi);
234 	gcv.background = MI_BLACK_PIXEL(mi);
235 	for (i = 0; i < PIXMAPS; i++) {
236 		if ((np->position[i] = XCreatePixmap(display, window,
237 				PIXMAP_SIZE, PIXMAP_SIZE, MI_DEPTH(mi))) == None)
238 			return False;
239 		if ((np->noseGC[i] = XCreateGC(display, np->position[i],
240 				GCForeground | GCBackground | GCGraphicsExposures,
241 				&gcv)) == None)
242 			return False;
243 		XFillRectangle(display, np->position[i], np->noseGC[i],
244 			       0, 0, PIXMAP_SIZE, PIXMAP_SIZE);
245 	}
246 	XSetBackground(display, gc, MI_BLACK_PIXEL(mi));
247 	XSetFillStyle(display, gc, FillStippled);
248 	/* DOWN NOSE GUY */
249 	if ((shoe_pix = XCreateBitmapFromData(display, window,
250 			(char *) nose_shoe_front_bits,
251 			nose_shoe_front_width,
252 			nose_shoe_front_height)) == None) {
253 		return False;
254 	}
255 	COPY(display, gc, shoe_color, shoe_pix, np->position[D],
256 	     (PIXMAP_SIZE - nose_shoe_front_width) / 2,
257 	     nose_hat_height + nose_face_front_height + 3,
258 	     nose_shoe_front_width, nose_shoe_front_height);
259 	if ((face_pix = XCreateBitmapFromData(display, window,
260 			(char *) nose_face_down_bits,
261 			nose_face_down_width,
262 			nose_face_down_height)) == None) {
263 		XFreePixmap(display, shoe_pix);
264 		return False;
265 	}
266 	COPY(display, gc, face_color, face_pix, np->position[D],
267 	  (PIXMAP_SIZE - nose_face_down_width) / 2, nose_hat_down_height + 7,
268 	     nose_face_down_width, nose_face_down_height);
269 	XFreePixmap(display, face_pix);
270 	if ((hat_pix = XCreateBitmapFromData(display, window,
271 			(char *) nose_hat_down_bits,
272 			nose_hat_down_width, nose_hat_down_height)) == None) {
273 		XFreePixmap(display, shoe_pix);
274 		return False;
275 	}
276 	COPY(display, gc, hat_color, hat_pix, np->position[D],
277 	     (PIXMAP_SIZE - nose_hat_down_width) / 2, 7,
278 	     nose_hat_down_width, nose_hat_down_height);
279 	XFreePixmap(display, hat_pix);
280 	if (MI_NPIXELS(mi) <= 2) {
281 		XSetFillStyle(display, gc, FillSolid);
282 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
283 		XFillRectangle(display, np->position[D], gc,
284 			       0, nose_hat_down_height + 6, PIXMAP_SIZE, 1);
285 		XSetFillStyle(display, gc, FillStippled);
286 	}
287 	/* FRONT NOSE GUY */
288 	COPY(display, gc, shoe_color, shoe_pix, np->position[F],
289 	     (PIXMAP_SIZE - nose_shoe_front_width) / 2,
290 	     nose_hat_height + nose_face_front_height + 3,
291 	     nose_shoe_front_width, nose_shoe_front_height);
292 	XFreePixmap(display, shoe_pix);
293 	if ((hat_pix = XCreateBitmapFromData(display, window,
294 			(char *) nose_hat_bits,
295 			nose_hat_width, nose_hat_height)) == None) {
296 		return False;
297 	}
298 	COPY(display, gc, hat_color, hat_pix, np->position[F],
299 	     (PIXMAP_SIZE - nose_hat_width) / 2, 4,
300 	     nose_hat_width, nose_hat_height);
301 	if (MI_NPIXELS(mi) <= 2) {
302 		XSetFillStyle(display, gc, FillSolid);
303 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
304 		XFillRectangle(display, np->position[F], gc,
305 			       0, nose_hat_height + 3, PIXMAP_SIZE, 1);
306 		XSetFillStyle(display, gc, FillStippled);
307 	}
308 	if ((face_pix = XCreateBitmapFromData(display, window,
309 			(char *) nose_face_front_bits,
310 			nose_face_front_width,
311 			nose_face_front_height)) == None) {
312 		XFreePixmap(display, hat_pix);
313 		return False;
314 	}
315 	COPY(display, gc, face_color, face_pix, np->position[F],
316 	     (PIXMAP_SIZE - nose_face_front_width) / 2, nose_hat_height + 1,
317 	     nose_face_front_width, nose_face_front_height);
318 	/* FRONT LEFT NOSE GUY */
319 	if ((shoel_pix = XCreateBitmapFromData(display, window,
320 			(char *) nose_shoe_left_bits,
321 			nose_shoe_left_width,
322 			nose_shoe_left_height)) == None) {
323 		XFreePixmap(display, hat_pix);
324 		XFreePixmap(display, face_pix);
325 		return False;
326 	}
327 	COPY(display, gc, shoe_color, shoel_pix, np->position[LF],
328 	     (PIXMAP_SIZE - nose_shoe_left_width) / 2 - 4,
329 	     nose_hat_height + nose_face_front_height + 3,
330 	     nose_shoe_left_width, nose_shoe_left_height);
331 	COPY(display, gc, hat_color, hat_pix, np->position[LF],
332 	     (PIXMAP_SIZE - nose_hat_width) / 2, 4,
333 	     nose_hat_width, nose_hat_height);
334 	if (MI_NPIXELS(mi) <= 2) {
335 		XSetFillStyle(display, gc, FillSolid);
336 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
337 		XFillRectangle(display, np->position[LF], gc,
338 			       0, nose_hat_height + 3, PIXMAP_SIZE, 1);
339 		XSetFillStyle(display, gc, FillStippled);
340 	}
341 	COPY(display, gc, face_color, face_pix, np->position[LF],
342 	     (PIXMAP_SIZE - nose_face_front_width) / 2, nose_hat_height + 1,
343 	     nose_face_front_width, nose_face_front_height);
344 	/* FRONT RIGHT NOSE GUY */
345 	if ((shoer_pix = XCreateBitmapFromData(display, window,
346 			(char *) nose_shoe_right_bits,
347 			nose_shoe_right_width,
348 			nose_shoe_right_height)) == None) {
349 		XFreePixmap(display, hat_pix);
350 		XFreePixmap(display, shoel_pix);
351 		XFreePixmap(display, face_pix);
352 		return False;
353 	}
354 	COPY(display, gc, shoe_color, shoer_pix, np->position[RF],
355 	     (PIXMAP_SIZE - nose_shoe_right_width) / 2 + 4,
356 	     nose_hat_height + nose_face_front_height + 3,
357 	     nose_shoe_right_width, nose_shoe_right_height);
358 	COPY(display, gc, hat_color, hat_pix, np->position[RF],
359 	     (PIXMAP_SIZE - nose_hat_width) / 2, 4,
360 	     nose_hat_width, nose_hat_height);
361 	if (MI_NPIXELS(mi) <= 2) {
362 		XSetFillStyle(display, gc, FillSolid);
363 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
364 		XFillRectangle(display, np->position[RF], gc,
365 			       0, nose_hat_height + 3, PIXMAP_SIZE, 1);
366 		XSetFillStyle(display, gc, FillStippled);
367 	}
368 	COPY(display, gc, face_color, face_pix, np->position[RF],
369 	     (PIXMAP_SIZE - nose_face_front_width) / 2, nose_hat_height + 1,
370 	     nose_face_front_width, nose_face_front_height);
371 	XFreePixmap(display, face_pix);
372 	/* LEFT NOSE GUY */
373 	COPY(display, gc, shoe_color, shoel_pix, np->position[L],
374 	     (PIXMAP_SIZE - nose_shoe_left_width) / 2 - 4,
375 	     nose_hat_height + nose_face_front_height + 3,
376 	     nose_shoe_left_width, nose_shoe_left_height);
377 	XFreePixmap(display, shoel_pix);
378 	COPY(display, gc, hat_color, hat_pix, np->position[L],
379 	     (PIXMAP_SIZE - nose_hat_width) / 2, 4,
380 	     nose_hat_width, nose_hat_height);
381 	if (MI_NPIXELS(mi) <= 2) {
382 		XSetFillStyle(display, gc, FillSolid);
383 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
384 		XFillRectangle(display, np->position[L], gc,
385 			       0, nose_hat_height + 3, PIXMAP_SIZE, 1);
386 		XSetFillStyle(display, gc, FillStippled);
387 	}
388 	if ((face_pix = XCreateBitmapFromData(display, window,
389 			(char *) nose_face_left_bits,
390 			nose_face_left_width,
391 			nose_face_left_height)) == None) {
392 		XFreePixmap(display, hat_pix);
393 		XFreePixmap(display, shoer_pix);
394 		return False;
395 	}
396 	COPY(display, gc, face_color, face_pix, np->position[L],
397 	   (PIXMAP_SIZE - nose_face_left_width) / 2 - 4, nose_hat_height + 4,
398 	     nose_face_left_width, nose_face_left_height);
399 	/* LEFT NOSE GUY STEPPING */
400 	if ((shoel_pix = XCreateBitmapFromData(display, window,
401 			(char *) nose_step_left_bits,
402 			nose_step_left_width,
403 			nose_step_left_height)) == None) {
404 		XFreePixmap(display, hat_pix);
405 		XFreePixmap(display, face_pix);
406 		XFreePixmap(display, shoer_pix);
407 		return False;
408 	}
409 	COPY(display, gc, shoe_color, shoel_pix, np->position[LSTEP],
410 	     (PIXMAP_SIZE - nose_step_left_width) / 2,
411 	     nose_hat_height + nose_face_front_height - 1,
412 	     nose_step_left_width, nose_step_left_height);
413 	XFreePixmap(display, shoel_pix);
414 	COPY(display, gc, hat_color, hat_pix, np->position[LSTEP],
415 	     (PIXMAP_SIZE - nose_hat_width) / 2, 4,
416 	     nose_hat_width, nose_hat_height);
417 	if (MI_NPIXELS(mi) <= 2) {
418 		XSetFillStyle(display, gc, FillSolid);
419 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
420 		XFillRectangle(display, np->position[LSTEP], gc,
421 			       0, nose_hat_height + 3, PIXMAP_SIZE, 1);
422 		XSetFillStyle(display, gc, FillStippled);
423 	}
424 	COPY(display, gc, face_color, face_pix, np->position[LSTEP],
425 	   (PIXMAP_SIZE - nose_face_left_width) / 2 - 4, nose_hat_height + 4,
426 	     nose_face_left_width, nose_face_left_height);
427 	XFreePixmap(display, face_pix);
428 	/* RIGHT NOSE GUY */
429 	if ((face_pix = XCreateBitmapFromData(display, window,
430 			(char *) nose_face_right_bits,
431 			nose_face_right_width,
432 			nose_face_right_height)) == None) {
433 		XFreePixmap(display, hat_pix);
434 		XFreePixmap(display, shoer_pix);
435 		return False;
436 	}
437 	COPY(display, gc, shoe_color, shoer_pix, np->position[R],
438 	     (PIXMAP_SIZE - nose_shoe_right_width) / 2 + 4,
439 	     nose_hat_height + nose_face_front_height + 3,
440 	     nose_shoe_right_width, nose_shoe_right_height);
441 	XFreePixmap(display, shoer_pix);
442 	COPY(display, gc, hat_color, hat_pix, np->position[R],
443 	     (PIXMAP_SIZE - nose_hat_width) / 2, 4,
444 	     nose_hat_width, nose_hat_height);
445 	if (MI_NPIXELS(mi) <= 2) {
446 		XSetFillStyle(display, gc, FillSolid);
447 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
448 		XFillRectangle(display, np->position[R], gc,
449 			       0, nose_hat_height + 3, PIXMAP_SIZE, 1);
450 		XSetFillStyle(display, gc, FillStippled);
451 	}
452 	COPY(display, gc, face_color, face_pix, np->position[R],
453 	  (PIXMAP_SIZE - nose_face_right_width) / 2 + 4, nose_hat_height + 4,
454 	     nose_face_right_width, nose_face_right_height);
455 	/* RIGHT NOSE GUY STEPPING */
456 	if ((shoer_pix = XCreateBitmapFromData(display, window,
457 			(char *) nose_step_right_bits,
458 			nose_step_right_width,
459 			nose_step_right_height)) == None) {
460 		XFreePixmap(display, face_pix);
461 		XFreePixmap(display, hat_pix);
462 		return False;
463 	}
464 	COPY(display, gc, shoe_color, shoer_pix, np->position[RSTEP],
465 	     (PIXMAP_SIZE - nose_step_right_width) / 2,
466 	     nose_hat_height + nose_face_front_height - 1,
467 	     nose_step_right_width, nose_step_right_height);
468 	XFreePixmap(display, shoer_pix);
469 	COPY(display, gc, hat_color, hat_pix, np->position[RSTEP],
470 	     (PIXMAP_SIZE - nose_hat_width) / 2, 4,
471 	     nose_hat_width, nose_hat_height);
472 	XFreePixmap(display, hat_pix);
473 	if (MI_NPIXELS(mi) <= 2) {
474 		XSetFillStyle(display, gc, FillSolid);
475 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
476 		XFillRectangle(display, np->position[RSTEP], gc,
477 			       0, nose_hat_height + 3, PIXMAP_SIZE, 1);
478 		XSetFillStyle(display, gc, FillStippled);
479 	}
480 	COPY(display, gc, face_color, face_pix, np->position[RSTEP],
481 	  (PIXMAP_SIZE - nose_face_right_width) / 2 + 4, nose_hat_height + 4,
482 	     nose_face_right_width, nose_face_right_height);
483 	XFreePixmap(display, face_pix);
484 	XSetFillStyle(display, gc, FillSolid);
485 	return True;
486 }
487 
488 static void
walk(ModeInfo * mi,register int dir)489 walk(ModeInfo * mi, register int dir)
490 {
491 	Display    *display = MI_DISPLAY(mi);
492 	Window      window = MI_WINDOW(mi);
493 	nosestruct *np = &noses[MI_SCREEN(mi)];
494 	register int incr = 0;
495 
496 	if (dir & (LEFT | RIGHT)) {	/* left/right movement (maybe up/down too) */
497 		np->up = -np->up;	/* bouncing effect (even if hit a wall) */
498 		if (dir & LEFT) {
499 			incr = X_INCR;
500 			np->frames = (np->up < 0) ? L : LSTEP;
501 		} else {
502 			incr = -X_INCR;
503 			np->frames = (np->up < 0) ? R : RSTEP;
504 		}
505 		/* note that maybe neither UP nor DOWN is set! */
506 		if ((dir & UP) && np->y > Y_INCR)
507 			np->y -= Y_INCR;
508 		else if ((dir & DOWN) && np->y < np->height - np->ys)
509 			np->y += Y_INCR;
510 	} else if (dir == UP) {	/* Explicit up/down movement only (no left/right) */
511 		np->y -= Y_INCR;
512 		XCopyArea(display, np->position[F], window, np->noseGC[F],
513 			  0, 0, PIXMAP_SIZE, PIXMAP_SIZE, np->x, np->y);
514 	} else if (dir == DOWN) {
515 		np->y += Y_INCR;
516 		XCopyArea(display, np->position[D], window, np->noseGC[D],
517 			  0, 0, PIXMAP_SIZE, PIXMAP_SIZE, np->x, np->y);
518 	} else if (dir == FRONT && np->frames != F) {
519 		if (np->up > 0)
520 			np->up = -np->up;
521 		if (np->lastdir & LEFT)
522 			np->frames = LF;
523 		else if (np->lastdir & RIGHT)
524 			np->frames = RF;
525 		else
526 			np->frames = F;
527 		XCopyArea(display, np->position[np->frames], window, np->noseGC[np->frames],
528 			  0, 0, PIXMAP_SIZE, PIXMAP_SIZE, np->x, np->y);
529 	}
530 	if (dir & LEFT)
531 		while (--incr >= 0) {
532 			--np->x;
533 			XCopyArea(display, np->position[np->frames], window, np->noseGC[np->frames],
534 				  0, 0, PIXMAP_SIZE, PIXMAP_SIZE, np->x, np->y + np->up);
535 			XFlush(display);
536 	} else if (dir & RIGHT)
537 		while (++incr <= 0) {
538 			++np->x;
539 			XCopyArea(display, np->position[np->frames], window, np->noseGC[np->frames],
540 				  0, 0, PIXMAP_SIZE, PIXMAP_SIZE, np->x, np->y + np->up);
541 			XFlush(display);
542 		}
543 	np->lastdir = dir;
544 }
545 
546 static int
think(ModeInfo * mi)547 think(ModeInfo * mi)
548 {
549 	nosestruct *np = &noses[MI_SCREEN(mi)];
550 
551 	if (LRAND() & 1)
552 		walk(mi, FRONT);
553 	if (LRAND() & 1) {
554 		np->words = getWords(MI_SCREEN(mi), MI_NUM_SCREENS(mi));
555 		return 1;
556 	}
557 	return 0;
558 }
559 
560 #define MAXLINES 40
561 #if 0
562 #define MAXWIDTH BUFSIZ
563 #else
564 #define MAXWIDTH 170
565 #endif
566 
567 /*-
568 Strange but true:
569 On an HP Pa-RISC with gcc MAXLINES * MAXWIDTH should be <=
570 around 6800 (40 * 170) or
571 
572 as: /usr/tmp/cca####.s line#3364 [err#13]
573   (warning) Use of GR3 when frame>=8192 may cause conflict
574 
575 */
576 
577 static void
talk(ModeInfo * mi,Bool force_erase)578 talk(ModeInfo * mi, Bool force_erase)
579 {
580 	Display    *display = MI_DISPLAY(mi);
581 	Window      window = MI_WINDOW(mi);
582 	nosestruct *np = &noses[MI_SCREEN(mi)];
583 	int         width = 0, height = 0, Y, Z, total = 0;
584 	register char *p, *p2;
585 	char        buf[BUFSIZ], args[MAXLINES][MAXWIDTH];
586 
587 	/* clear what we've written */
588 	if (np->talking || force_erase) {
589 		if (!np->talking)
590 			return;
591 		XFillRectangle(display, window, np->text_bg_gc,
592 			np->s.x - 5, np->s.y - 5,
593 			np->s.width + 10, np->s.height + 10);
594 		np->talking = False;
595 		if (!force_erase)
596 			np->state = MOVE;
597 		return;
598 	}
599 	np->talking = True;
600 	walk(mi, FRONT);
601 	p = strncpy(buf, np->words, BUFSIZ - 1);
602 	while (strlen(p) > 0) {
603 		p2 = p + strlen(p) - 1;
604 		if (*p2 == '\n')
605 			*p2 = 0;
606 		else
607 			break;
608 	}
609 	for (height = 0; p && height < MAXLINES; height++) {
610 		int w;
611 		char *p3;
612 
613 		if (!(p2 = (char *) strchr(p, '\n')) || !p2[1]) {
614 			p2 = p + strlen(p);
615 
616 		}
617 		/* p2 now points to the first '\n' */
618 		*p2 = 0;
619 		for (w = 0; w < p2 - p; w++) {
620 			p3 = p + w;
621 			if (*p3 == '\t') /* this should be improved */
622 				*p3 = ' ';
623 		}
624 		(void) strncpy(args[height], p, MAXWIDTH - 1);
625 		args[height][MAXWIDTH - 1] = '\0';
626 		if (mode_font == None && height == 0)
627 			width = 8;
628 		if (mode_font != None && (w =
629 				getTextWidth(args[height])) > width)
630 			width = w;
631 		total += p2 - p;  /* total chars; count to determine reading time */
632 		if (p == p2)
633 			break;
634 		p = p2 + 1;
635 	}
636 	/*
637 	 * Figure out the height and width in imagepixels (height, width) extend the
638 	 * new box by 15 pixels on the sides (30 total) top and bottom.
639 	 */
640 	np->s.width = width + 30;
641 	np->s.height = height * getFontHeight(mode_font) + 30;
642 	if (np->x - np->s.width - 10 < 5)
643 		np->s.x = 5;
644 	else if ((np->s.x = np->x + 32 - (np->s.width + 15) / 2)
645 		 + np->s.width + 15 > np->width - 5)
646 		np->s.x = np->width - 15 - np->s.width;
647 	if (np->y - np->s.height - 10 < 5)
648 		np->s.y = np->y + np->ys + 5;
649 	else
650 		np->s.y = np->y - 5 - np->s.height;
651 
652 	XFillRectangle(display, window, np->text_bg_gc,
653 		       np->s.x, np->s.y, np->s.width, np->s.height);
654 
655 	/* make a box that's 5 pixels thick. Then add a thin box inside it */
656 	XSetLineAttributes(display, np->text_fg_gc, 5, 0, 0, 0);
657 	XDrawRectangle(display, window, np->text_fg_gc,
658 		       np->s.x, np->s.y, np->s.width - 1, np->s.height - 1);
659 	XSetLineAttributes(display, np->text_fg_gc, 0, 0, 0, 0);
660 	XDrawRectangle(display, window, np->text_fg_gc,
661 	      np->s.x + 7, np->s.y + 7, np->s.width - 15, np->s.height - 15);
662 
663 	Y = 15 + getFontHeight(mode_font);
664 
665 	/* now print each string in reverse order (start at bottom of box) */
666 	for (Z = 0; Z < height; Z++) {
667 		DrawString(display, window, np->text_fg_gc,
668 			np->s.x + 15, np->s.y + Y,
669 			args[Z], strlen(args[Z]));
670 		Y += getFontHeight(mode_font);
671 	}
672 	np->busyLoop = (total / 15) * 10;
673 	if (np->busyLoop < 30)
674 		np->busyLoop = 30;
675 	np->state = TALK;
676 }
677 
678 static int
look(ModeInfo * mi)679 look(ModeInfo * mi)
680 {
681 	Display    *display = MI_DISPLAY(mi);
682 	Window      window = MI_WINDOW(mi);
683 
684 	/*GC          gc = MI_GC(mi); */
685 	nosestruct *np = &noses[MI_SCREEN(mi)];
686 	int         i;
687 
688 	if (NRAND(3)) {
689 		i = (LRAND() & 1) ? D : F;
690 		XCopyArea(display, np->position[i], window, np->noseGC[i],
691 			  0, 0, PIXMAP_SIZE, PIXMAP_SIZE, np->x, np->y);
692 		return 3;
693 	}
694 	if (!NRAND(5))
695 		return 0;
696 	if (NRAND(3)) {
697 		i = (LRAND() & 1) ? LF : RF;
698 		XCopyArea(display, np->position[i], window, np->noseGC[i],
699 			  0, 0, PIXMAP_SIZE, PIXMAP_SIZE, np->x, np->y);
700 		return 3;
701 	}
702 	if (!NRAND(5))
703 		return 0;
704 	i = (LRAND() & 1) ? L : R;
705 	XCopyArea(display, np->position[i], window, np->noseGC[i],
706 		  0, 0, PIXMAP_SIZE, PIXMAP_SIZE, np->x, np->y);
707 	return 3;
708 }
709 
710 static void
move(ModeInfo * mi)711 move(ModeInfo * mi)
712 {
713 	nosestruct *np = &noses[MI_SCREEN(mi)];
714 
715 	if (!np->length) {
716 		register int tries = 0;
717 
718 		np->dir = 0;
719 		if ((LRAND() & 1) && think(mi)) {
720 			talk(mi, False);	/* sets timeout to itself */
721 			return;
722 		}
723 		if (!NRAND(3) && (np->busyLoop = look(mi))) {
724 			np->state = MOVE;
725 			return;
726 		}
727 		np->busyLoop = 3 + NRAND(3);
728 		do {
729 			if (!tries)
730 				np->length = np->width / 100 + NRAND(90), tries = 8;
731 			else
732 				tries--;
733 			switch (NRAND(8)) {
734 				case 0:
735 					if (np->x - X_INCR * np->length >= 5)
736 						np->dir = LEFT;
737 					break;
738 				case 1:
739 					if (np->x + X_INCR * np->length <= np->width - np->xs - 6)
740 						np->dir = RIGHT;
741 					break;
742 				case 2:
743 					if (np->y - (Y_INCR * np->length) >= 5)
744 						np->dir = UP;
745 					break;
746 				case 3:
747 					if (np->y + Y_INCR * np->length <= np->height - np->ys - 6)
748 						np->dir = DOWN;
749 					break;
750 				case 4:
751 					if (np->x - X_INCR * np->length >= 5 &&
752 					  np->y - (Y_INCR * np->length) >= 5)
753 						np->dir = (LEFT | UP);
754 					break;
755 				case 5:
756 					if (np->x + X_INCR * np->length <= np->width - np->xs - 6 &&
757 					    np->y - Y_INCR * np->length >= 5)
758 						np->dir = (RIGHT | UP);
759 					break;
760 				case 6:
761 					if (np->x - X_INCR * np->length >= 5 &&
762 					    np->y + Y_INCR * np->length <= np->height - np->ys - 6)
763 						np->dir = (LEFT | DOWN);
764 					break;
765 				case 7:
766 					if (np->x + X_INCR * np->length <= np->width - np->xs - 6 &&
767 					    np->y + Y_INCR * np->length <= np->height - np->ys - 6)
768 						np->dir = (RIGHT | DOWN);
769 					break;
770 				default:
771 					/* No Defaults */
772 					break;
773 			}
774 		} while (!np->dir);
775 	}
776 	walk(mi, np->dir);
777 	--np->length;
778 	np->state = MOVE;
779 }
780 
781 ENTRYPOINT void
init_nose(ModeInfo * mi)782 init_nose(ModeInfo * mi)
783 {
784 	Display    *display = MI_DISPLAY(mi);
785 	Window      window = MI_WINDOW(mi);
786 	GC          gc = MI_GC(mi);
787 	XGCValues   gcv;
788 	nosestruct *np;
789 
790 	MI_INIT(mi, noses);
791 	np = &noses[MI_SCREEN(mi)];
792 
793 	np->width = MI_WIDTH(mi) + 2;
794 	np->height = MI_HEIGHT(mi) + 2;
795 	np->tinymode = (np->width + np->height < 4 * PIXMAP_SIZE);
796 	np->xs = PIXMAP_SIZE;
797 	np->ys = PIXMAP_SIZE;
798 
799 	MI_CLEARWINDOW(mi);
800 
801 	XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
802 
803 	/* do not want any exposure events from XCopyPlane */
804 	XSetGraphicsExposures(display, gc, False);
805 
806 	if (mode_font == None) {
807 #ifdef USE_MB
808 		mode_font = getFontSet(display);
809 #else
810 		mode_font = getFont(display);
811 #endif
812 	}
813 	if (np->noseGC[0] == None)
814 		if (!pickClothes(mi)) {
815 			free_nose_screen(display, np);
816 			return;
817 		}
818 	np->words = getWords(MI_SCREEN(mi), MI_NUM_SCREENS(mi));
819 	if (np->text_fg_gc == None && mode_font != None) {
820 #ifndef USE_MB
821 		gcv.font = mode_font->fid;
822 #endif
823 		gcv.graphics_exposures = False;
824 		gcv.foreground = MI_WHITE_PIXEL(mi);
825 		gcv.background = MI_BLACK_PIXEL(mi);
826 		if ((np->text_fg_gc = XCreateGC(display, window,
827 #ifndef USE_MB
828 				 GCFont |
829 #endif
830 				 GCForeground | GCBackground | GCGraphicsExposures,
831 				 &gcv)) == None) {
832 			free_nose_screen(display, np);
833 			return;
834 		}
835 		gcv.foreground = MI_BLACK_PIXEL(mi);
836 		gcv.background = MI_WHITE_PIXEL(mi);
837 		if ((np->text_bg_gc = XCreateGC(display, window,
838 #ifndef USE_MB
839 				 GCFont |
840 #endif
841 				 GCForeground | GCBackground | GCGraphicsExposures,
842 				 &gcv)) == None) {
843 			free_nose_screen(display, np);
844 			return;
845 		}
846 	}
847 	np->up = 1;
848 	if (np->tinymode) {
849 		int pos = NRAND(PIXMAPS);
850 
851 		np->x = 0;
852 		np->y = 0;
853 		XCopyArea(display, np->position[pos], window, np->noseGC[pos],
854 			  0, 0, PIXMAP_SIZE, PIXMAP_SIZE,
855 			  (np->width - PIXMAP_SIZE) / 2,
856 			  (np->height - PIXMAP_SIZE) / 2);
857 		np->state = FREEZE;
858 	} else {
859 		np->x = np->width / 2;
860 		np->y = np->height / 2;
861 		np->state = MOVE;
862 	}
863 	XFlush(display);
864 }
865 
866 ENTRYPOINT void
draw_nose(ModeInfo * mi)867 draw_nose(ModeInfo * mi)
868 {
869 	nosestruct *np;
870 
871 	if (noses == NULL)
872 		return;
873 	np = &noses[MI_SCREEN(mi)];
874 	if (np->noseGC[0] == None)
875 		return;
876 
877 	MI_IS_DRAWN(mi) = True;
878 	if (np->busyLoop > 0) {
879 		np->busyLoop--;
880 		return;
881 	}
882 	switch (np->state) {
883 		case MOVE:
884 			move(mi);
885 			break;
886 		case TALK:
887 			talk(mi, 0);
888 			break;
889 	}
890 }
891 
892 ENTRYPOINT void
release_nose(ModeInfo * mi)893 release_nose(ModeInfo * mi)
894 {
895 	if (noses != NULL) {
896 		int         screen;
897 
898 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
899 			free_nose_screen(MI_DISPLAY(mi), &noses[screen]);
900 		free(noses);
901 		noses = (nosestruct *) NULL;
902 	}
903 	if (mode_font != None) {
904 #ifdef USE_MB
905 		XFreeFontSet(MI_DISPLAY(mi), mode_font);
906 #else
907 		XFreeFont(MI_DISPLAY(mi), mode_font);
908 #endif
909 		mode_font = None;
910 	}
911 }
912 
913 #ifndef STANDALONE
914 ENTRYPOINT void
refresh_nose(ModeInfo * mi)915 refresh_nose(ModeInfo * mi)
916 {
917 	MI_CLEARWINDOW(mi);
918 }
919 #endif
920 
921 XSCREENSAVER_MODULE ("Nose", nose)
922 
923 #endif /* MODE_nose */
924