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