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