1 /*
2  *	Spider
3  *
4  *	(c) Copyright 1989, Donald R. Woods and Sun Microsystems, Inc.
5  *	(c) Copyright 1990, David Lemke and Network Computing Devices Inc.
6  *
7  *	See copyright.h for the terms of the copyright.
8  *
9  *	@(#)events.c	2.6	91/05/09
10  *
11  */
12 
13 /*
14  * Spider event handlers
15  */
16 
17 #include	"defs.h"
18 #include	"globals.h"
19 
20 extern int	cheat_count;
21 
22 static	CardPtr		current_card = CARDNULL;
23 static	CardList	current_list = CARDLISTNULL;
24 
25 static CardList		coords_to_list();
26 static CardPtr		coords_to_card();
27 static Bool		hit_deck();
28 Bool			write_confirmer();
29 Bool			newgame_confirmer();
30 #ifdef XVIEW
31 extern void		show_play();
32 #else
33 extern Bool		show_play_events();
34 #endif
35 #ifdef	KITLESS
36 static void		handle_table_event();
37 static void		redraw_table();
38 static void		button_press();
39 static void		button_release();
40 static void		key_press();
41 static void		resize_event();
42 static void		do_expand();
43 static void		handle_message_event();
44 #endif /* KITLESS */
45 
46 Bool	usebell = False;
47 
48 #ifdef KITLESS
49 /*
50  * main event handling loop
51  */
event_loop()52 event_loop()
53 {
54 XEvent	xev;
55 
56 	while (1)	{
57 		XNextEvent(dpy, &xev);
58 		if (xev.xany.window == table)	{
59 			handle_table_event(&xev);
60 		} else if (xev.xany.window == message_win)	{
61 			handle_message_event(&xev);
62 		}
63 		if (restart)	{
64 			shuffle_cards();
65 		}
66 	}
67 }
68 
69 /*
70  * event on table
71  */
72 static void
handle_table_event(xev)73 handle_table_event(xev)
74 XEvent	*xev;
75 {
76 	if (xev->xany.type == Expose)	{
77 		redraw_table((XExposeEvent *)xev);
78 	} else if (xev->xany.type == ButtonPress)	{
79 		button_press((XButtonPressedEvent *)xev);
80 	} else if (xev->xany.type == ButtonRelease)	{
81 		button_release((XButtonReleasedEvent *)xev);
82 		current_list = CARDLISTNULL;
83 	} else if (xev->xany.type == KeyPress)	{
84 		key_press((XKeyPressedEvent *)xev);
85 	} else if (xev->xany.type == ConfigureNotify)	{
86 		resize_event((XConfigureEvent *)xev);
87 	}
88 }
89 
90 static void
handle_message_event(xev)91 handle_message_event(xev)
92 XEvent	*xev;
93 {
94 	if (xev->xany.type == Expose)	{
95 		show_message(NULL);
96 	}
97 }
98 
99 /* KITLESS doesn't bother to check -- just clobbers existing file */
100 Bool
write_confirmer()101 write_confirmer()
102 {
103 	return True;
104 }
105 
106 Bool
newgame_confirmer()107 newgame_confirmer()
108 {
109 	return True;
110 }
111 #endif /* KITLESS */
112 
113 
114 #ifndef KITLESS
115 void
116 #else /* KITLESS */
117 static void
118 #endif /* KITLESS */
redraw_table(xev)119 redraw_table(xev)
120 XExposeEvent	*xev;
121 {
122 static Bool	last_was_zero = True;	/* so at init time it paints */
123 
124 	/*
125 	 * this mess is to optimize the painting.   a complex exposure
126 	 * area could cause a card to have several damage areas.  since
127 	 * the entire card is painted when damaged, the following keeps
128 	 * track of whether its been painted in the latest flurry of
129 	 * exposures, and prevents it being painted multiple times.
130 	 */
131 	if (last_was_zero)	{
132 		draw_count++;
133 		last_was_zero = False;
134 	}
135 	if (xev->count == 0)
136 		last_was_zero = True;
137 
138 	redraw_deck(xev->x, xev->y, xev->width, xev->height);
139 	redraw_card_piles(xev->x, xev->y, xev->width, xev->height);
140 	redraw_card_stacks(xev->x, xev->y, xev->width, xev->height);
141 }
142 
143 /*
144  * ignore y when getting list
145  */
146 static CardList
coords_to_list(x,y)147 coords_to_list(x, y)
148 int	x, y;
149 {
150 int	i;
151 
152 	if (y < STACK_LOC_Y)	{
153 		for (i = 0; i < NUM_PILES; i++)	{
154 			if ((x >= PILE_LOC_X(piles[i]->place)) &&
155 			    (x <= (PILE_LOC_X(piles[i]->place) + CARD_WIDTH)))
156 				return (piles[i]);
157 		}
158 		return (CARDLISTNULL);
159 	} else	{
160 		for (i = 0; i < NUM_STACKS; i++)	{
161 			if ((x >= STACK_LOC_X(stack[i]->place)) &&
162 			    (x <= (STACK_LOC_X(stack[i]->place) + CARD_WIDTH)))
163 				return (stack[i]);
164 		}
165 		return (CARDLISTNULL);
166 	}
167 }
168 
169 static CardPtr
coords_to_card(x,y)170 coords_to_card(x, y)
171 int	x, y;
172 {
173 CardList	list;
174 CardPtr	tmp;
175 
176 	list = coords_to_list(x, y);
177 	if (list == CARDLISTNULL || IS_PILE(list))
178 		return (CARDNULL);
179 	tmp = list->cards;
180 	if (tmp == CARDNULL)
181 		return CARDNULL;
182 	while (tmp)	{
183 		if (tmp->next)	{
184 			if ((y <= tmp->next->y) && (y >= tmp->y))	{
185 				return (tmp);
186 			}
187 		} else	{
188 			if ((y <= (tmp->y + CARD_HEIGHT)) &&
189 			    (y >= tmp->y))	{
190 				return (tmp);
191 			}
192 		}
193 		tmp = tmp->next;
194 	}
195 	return CARDNULL;
196 }
197 
198 static Bool
hit_deck(x,y)199 hit_deck(x, y)
200 int	x, y;
201 {
202 	return ((x <= (deck->x + CARD_WIDTH)) && (x >= deck->x) &&
203 	    (y <= (deck->y + CARD_HEIGHT)) && (y >= deck->y));
204 }
205 
206 #ifndef KITLESS
207 void
208 #else	/* KITLESS */
209 static void
210 #endif	/* KITLESS */
button_press(xev)211 button_press(xev)
212 XButtonPressedEvent	*xev;
213 {
214 
215 	if (hit_deck(xev->x, xev->y))	{
216 		current_list = deck;
217 		return;
218 	}
219 	current_card = coords_to_card(xev->x, xev->y);
220 	if (xev->button == Button2)	{
221 		if (current_card == CARDNULL)
222 			return;
223 		/* ignore facedown cards */
224 		if (current_card->type != Faceup)	{
225 			current_card = CARDNULL;
226 			current_list = coords_to_list(xev->x, xev->y);
227 			return;
228 		}
229 #ifdef DEBUG
230 		if (xev->state & ShiftMask)	{
231 			current_list = coords_to_list(xev->x, xev->y);
232 			return;
233 		}
234 #endif	/* DEBUG */
235 		if (!can_move(current_card))	{
236 			card_message("Can't move", current_card);
237 			spider_bell(dpy, 0);
238 			current_card = CARDNULL;
239 		}
240 	} else	{
241 		current_card = CARDNULL;
242 	}
243 	current_list = coords_to_list(xev->x, xev->y);
244 	if (IS_PILE(current_list))	{
245 		if (current_list->cards)	{
246 			show_message("Can't move removed cards.");
247 			spider_bell(dpy, 0);
248 		} else	{
249 			show_full_suits();
250 		}
251 		current_list = CARDLISTNULL;
252 	} else if (current_list && current_list->cards == CARDNULL)	{
253 		show_message("No cards to move.");
254 		current_list = CARDLISTNULL;
255 	}
256 }
257 
258 #ifndef KITLESS
259 void
260 #else	/* KITLESS */
261 static void
262 #endif 	/* KITLESS */
button_release(xev)263 button_release(xev)
264 XButtonReleasedEvent	*xev;
265 {
266 CardList	list_hit;
267 CardPtr		tmp;
268 
269 	if (current_list == CARDLISTNULL)
270 		return;
271 
272 	if (hit_deck(xev->x, xev->y))	{
273 		if (current_list == deck)	{
274 			if (deal_number == 0)	{
275 				deal_cards();
276 			} else	{
277 				deal_next_hand(True);
278 			}
279 		} else	{	/* no dropping on deck */
280 			show_message("Can't move cards to deck");
281 			spider_bell(dpy, 0);
282 		}
283 		return;
284 	}
285 
286 	list_hit = coords_to_list(xev->x, xev->y);
287 	if (list_hit == CARDLISTNULL)
288 		return;
289 
290 	if (current_card)	{
291 #ifdef DEBUG
292 		if (xev->state & ShiftMask)	{
293 			move_to_list(current_card, list_hit, True);
294 			current_card = CARDNULL;
295 			return;
296 		}
297 #endif
298 		if (list_hit == current_list)	{
299 			best_list_move(list_hit, current_card);
300 			return;
301 		}
302 		if ((IS_PILE(list_hit)))	{
303 			if((current_card->rank == King) &&
304 			    (can_move(current_card)) &&
305 			    (last_card(current_card->list)->rank == Ace)) {
306 				move_to_pile(current_card);
307 			} else	{
308 				card_message("Can't remove", current_card);
309 				spider_bell(dpy, 0);
310 			}
311 			current_card = CARDNULL;
312 			return;
313 		}
314 		if (can_move_to(current_card, list_hit))	{
315 			move_to_list(current_card, list_hit, True);
316 		} else	{
317 			card2_message("Can't move", current_card, "to",
318 				last_card(list_hit));
319 			spider_bell(dpy, 0);
320 		}
321 		current_card = CARDNULL;
322 	/* try best move if the mouse wasn't moved */
323 	} else if (list_hit == current_list)	{
324 		best_list_move(list_hit, CARDNULL);
325 	} else	{
326 		if (IS_PILE(list_hit))	{
327 			tmp = last_card(current_list);
328 			if (tmp->rank == Ace)	{
329 				while (tmp && can_move(tmp))	{
330 					if (tmp->rank == King)	{
331 						move_to_pile(tmp);
332 						return;
333 					}
334 					tmp = tmp->prev;
335 				}
336 			}
337 			card_message("Can't remove", tmp);
338 			spider_bell(dpy, 0);
339 			return;
340 		}
341 		tmp = current_list->cards;
342 		while (tmp)	{
343 			if (can_move(tmp))	{
344 				if (can_move_to(tmp, list_hit))	{
345 					move_to_list(tmp, list_hit, True);
346 					return;
347 				}
348 			}
349 			tmp = tmp->next;
350 		}
351 		if (tmp)
352 			card_message("Can't move", tmp);
353 		else
354 			show_message("No movable cards");
355 		spider_bell(dpy, 0);
356 	}
357 }
358 
359 #ifndef KITLESS
360 void
361 #else	/* KITLESS */
362 static void
363 #endif	/* KITLESS */
key_press(xev)364 key_press(xev)
365 XKeyPressedEvent	*xev;
366 {
367 char	str[32];
368 char	buf[512];
369 char	*fname;
370 int	num;
371 #ifdef KITLESS
372 #define	get_name_field(x)	get_selection(x)
373 #else
374 extern char	*get_name_field();
375 #endif /* KITLESS */
376 
377 	num = XLookupString(xev, str, 32, NULL, NULL);
378 	if (num == 0)
379 		return;
380 	switch (str[0])	{
381 	case	'f':		/* find card */
382 	case	'F':
383 		fname = get_name_field();
384 		if (fname == NULL || strlen(fname) == 0) {
385 			show_message("Selection is unusable or unobtainable.");
386 		} else	{
387 			locate(fname);
388 		}
389 		break;
390 	case	'l':
391 	case	'L':
392 		if ((fname = get_name_field()) == NULL)	{
393 			show_message("Selection is unusable or unobtainable.");
394 		} else	{
395 			read_file_or_selection(fname);
396 		}
397 		/* force everything to redraw */
398 		force_redraw();
399 		break;
400 	case	'w':
401 	case	'W':
402 		/* write to selection */
403 		if ((fname = get_name_field()) == NULL)	{
404 			show_message("Selection is unusable or unobtainable.");
405 		} else	{
406 			write_file(fname, write_confirmer);
407 		}
408 		break;
409 	case	's':
410 	case	'S':
411 		/* score */
412 		(void)sprintf(buf, "Current position scores %d out of 1000.",
413 			compute_score());
414 		show_message(buf);
415 		break;
416 	case	'a':
417 	case	'A':
418 		/* play again */
419 		(void)replay();
420 		init_cache();	/* reset move cache */
421 		break;
422 	case	'r':
423 	case	'R':
424 		/* show move log */
425 #ifdef XVIEW
426 		show_play();
427 #else
428 		show_play(0, 0, show_play_events, delay);
429 #endif
430 		break;
431 	case	'n':
432 	case	'N':
433 		/* start over */
434 		if (newgame_confirmer())	{
435 			restart = True;
436 			clear_message();
437 		}
438 		break;
439 	case	'e':
440 	case	'E':
441 		do_expand();
442 		break;
443 	case	'd':
444 	case	'D':
445 		if (deal_number == 0)	{
446 			deal_cards();
447 		} else	{
448 			deal_next_hand(True);
449 		}
450 		/* deal next hand */
451 		break;
452 	case	'u':
453 	case	'U':
454 		undo();
455 		break;
456 	case	'v':
457 	case	'V':
458 		print_version();
459 		break;
460 	case	'Q':
461 		/* quit */
462 		exit(0);
463 	case	'#':
464 		if (deal_number == 0)	{
465 			show_message("Haven't dealt yet.");
466 		} else if (deal_number == 1)	{
467 			sprintf(buf, "Initial deal; cheat count: %d",
468 					cheat_count);
469 			show_message(buf);
470 		} else	{
471 			sprintf(buf, "Deal number %d of 5; cheat count: %d",
472 				deal_number - 1, cheat_count);
473 			show_message(buf);
474 		}
475 		break;
476 	case	'?':
477 		if (deal_number == 0)	{
478 			show_message("Haven't dealt yet.");
479 		} else	{
480 			advise_best_move();
481 		}
482 		break;
483 	default:
484 		str[num] = '\0';	/* NULL terminate it */
485 		(void) sprintf(buf, "Unknown command: '%s'", str);
486 		show_message(buf);
487 		break;
488 	}
489 }
490 
491 #ifdef KITLESS
492 static void
resize_event(xev)493 resize_event(xev)
494 XConfigureEvent	*xev;
495 {
496 int	i;
497 
498 	table_height = xev->height;
499 	table_width = xev->width;
500 
501 	/* adjust message window */
502 	XMoveResizeWindow(dpy, message_win, 0, (table_height - 2 * TABLE_BW -
503 		(message_font->ascent + message_font->descent)),
504 		(table_width - 2 * TABLE_BW),
505 		(message_font->ascent + message_font->descent));
506 
507 	/* fix stacks */
508 	for (i = 0; i < NUM_STACKS; i++)	{
509 		if (stack[i])
510 			recompute_list_deltas(stack[i]);
511 	}
512 	/* exposure will repaint them */
513 }
514 #endif	/* KITLESS */
515 
516 void
print_version()517 print_version()
518 {
519 char	buf[256];
520 
521 	(void)sprintf(buf, "Spider version %s, last built: %s", version,
522 							build_date);
523 	show_message(buf);
524 }
525 
526 
527 static CardList
get_list()528 get_list()
529 {
530 XEvent	event;
531 CardList	list = CARDLISTNULL;
532 
533 	if (XGrabPointer(dpy, table, False,
534 			ButtonPressMask | ButtonReleaseMask,
535 			GrabModeAsync, GrabModeAsync, table, None,
536 			CurrentTime) != GrabSuccess)	{
537 		show_message("Unable to grab pointer.");
538 		return CARDLISTNULL;
539 	}
540 	while (1)	{
541 		XNextEvent(dpy, &event);
542 
543 		switch (event.type)	{
544 		case	ButtonRelease:
545 			if (event.xbutton.window == table)	{
546 				list = coords_to_list(event.xbutton.x,
547 					event.xbutton.y);
548 			}
549 			XUngrabPointer(dpy, CurrentTime);
550 			return list;
551 		default:
552 			break;
553 		}
554 	}
555 }
556 
557 #ifdef KITLESS
558 static void
559 #else	/* KITLESS */
560 void
561 #endif	/* KITLESS */
do_expand()562 do_expand()
563 {
564 CardList	list;
565 
566 	show_message("Click over the column whose contents you want to see.");
567 #ifdef	XAW
568 	flush_message();
569 #endif	/* XAW */
570 	list = get_list();
571 	if (list && !IS_PILE(list))	{
572 		expand(list);
573 	} else	{
574 		show_message("That wasn't over a column!");
575 	}
576 }
577 
578 #ifdef KITLESS
579 Bool
show_play_events()580 show_play_events()
581 {
582 XEvent	xev;
583 
584 	while (XPending(dpy))	{
585 		XNextEvent(dpy, &xev);
586 
587 		/* any key or button will stop it */
588 		switch(xev.type)	{
589 			default:
590 				if (xev.xany.window == table)   {
591 					handle_table_event(&xev);
592 				} else if (xev.xany.window == message_win) {
593 					handle_message_event(&xev);
594 				}
595 				break;
596 
597 			case	KeyPress:
598 			case	KeyRelease:
599 			case	ButtonPress:
600 			case	ButtonRelease:
601 				return False;
602 		}
603 	}
604 	return True;
605 }
606 #endif	/* KITLESS */
607 
spider_bell(d,level)608 spider_bell(d, level)
609 Display	*d;
610 int	level;
611 {
612 	if (usebell)
613 		XBell(d, level);
614 }
615