1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* solitaire --- Shows a pointless game, used in the Mandarin Candidate */
3 
4 #if 0
5 static const char sccsid[] = "@(#)solitaire.cc	5.00 2000/11/01 xlockmore";
6 
7 #endif
8 
9 /* Copyright (c) D. Bagley, 1999. */
10 
11 /*
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * This module is based on Eric Lassauge's text3d and Timothy Budd's
25  * C++ program from 1990 contained in the book "An Introduction to
26  * Object Oriented Programming"  Addison-Wesley Publishing  1991.
27  *
28  * Albert H. Morehead and Geoffrey Mott-Smith, The Complete Book of
29  * Solitaire and Patience Games, Grosset & Dunlap, New York, 1949.
30  *
31  * After I started this I found out about some nice related sites:
32  *   http://www.delorite.com/store/ace
33  *   http://wildsav.idv.uni-linz.ac.at/mfx/psol-cardsets/index.html
34  *
35  * Revision History:
36  * 01-Nov-2000: Allocation checks
37  * 17-Jan-2000: autoplay, trackmouse, resizing, release
38  * 10-Dec-1999: wanted to do some C++, may end up using GL but not for now.
39  *
40  */
41 
42 #ifdef STANDALONE
43 #define MODE_solitaire
44 #define DEFAULTS "*delay: 2000000 \n" \
45 	"*ncolors: 64 \n" \
46 	"*mouse: False \n"
47 
48 # define reshape_solitaire 0
49 # define free_solitaire 0
50 # define solitaire_handle_event 0
51 #define UNIFORM_COLORS
52 extern "C"
53 {
54   #include "xlockmore.h"		/* from the xscreensaver distribution */
55 }
56 #else				/* !STANDALONE */
57 #include "xlock.h"		/* in xlockmore distribution */
58 #endif				/* !STANDALONE */
59 
60 #ifdef MODE_solitaire
61 
62 /*  Methods overridden in subclasses of class cardPile
63            Suit Alternate Deck Discard Deal
64 initialize         X       X
65 cleanup                                 X
66 addCard            X       X      X
67 display            X
68 select             X       X      X
69 canTake     X      X
70  */
71 
72 #define DEF_TRACKMOUSE  "False"
73 
74 static Bool trackmouse;
75 
76 #define MAXCOLORS 2
77 #define MAXSUITS 4
78 #define MAXRANK 13
79 #define MAXALTERNATEPILES 7
80 #define MAXPILES (MAXALTERNATEPILES+MAXSUITS+2)
81 #define OVERLAP (0.25)
82 
83 enum Colors {col_black, col_red};
84 enum Suits {spade, diamond, club, heart};
85 
86 #ifdef FR
87 // Ace => As, Jack => Valet,
88 // Queen = Reine (Dame), King = Roi, Joker = Joker
89 static const char *RankNames[] = {"", "A",
90 	"2", "3", "4", "5", "6", "7", "8", "9", "10",
91 	"V", "D", "R"};
92 #else
93 #ifdef NL
94 // Ace => Aas, Jack => Boer (Farmer),
95 // Queen = Vrouw (Lady), King = Heer (Gentleman), Joker = Joker
96 static const char *RankNames[] = {"", "A",
97 	"2", "3", "4", "5", "6", "7", "8", "9", "10",
98 	"B", "V", "H"};
99 #else
100 #ifdef RU
101 // Some may be approximations to Cyrillic
102 static const char *RankNames[] = {"", "T",
103 	"2", "3", "4", "5", "6", "7", "8", "9", "10",
104 	"B", "Q", "K"};
105 #else
106 static const char *RankNames[] = {"", "A",
107 	"2", "3", "4", "5", "6", "7", "8", "9", "10",
108 	"J", "Q", "K"};
109 #endif
110 #endif
111 #endif
112 
int_round(double x)113 static inline int int_round(double x) { return int(x + 0.5); }
114 
115 #ifdef DOFONT
116 // Does this really add anything?
117 extern XFontStruct *getFont(Display * display);
118 static XFontStruct *mode_font = None;
119 static int  char_width[256];
120 
121 static int
font_width(XFontStruct * font,char ch)122 font_width(XFontStruct * font, char ch)
123 {
124 	int         dummy;
125 	XCharStruct xcs;
126 
127 	(void) XTextExtents(font, &ch, 1, &dummy, &dummy, &dummy, &xcs);
128 	return xcs.width;
129 }
130 #endif
131 
132 static void
drawSpade(ModeInfo * mi,int x,int y,int width,int height)133 drawSpade(ModeInfo * mi, int x, int y, int width, int height)
134 {
135 	Display    *display = MI_DISPLAY(mi);
136 	Window      window = MI_WINDOW(mi);
137 	GC          gc = MI_GC(mi);
138 	XPoint      polygon[4];
139 
140 	polygon[0].x =  x + width / 2;
141 	polygon[0].y =  y;
142 	polygon[1].x =  short(float(-width) / 3.0);
143 	polygon[1].y =  height / 3;
144 	polygon[2].x =  short(float(width) / 3.0);
145 	polygon[2].y =  height / 3;
146 	polygon[3].x =  short(float(width) / 3.0) + 1;
147 	polygon[3].y =  -height / 3;
148 	XFillPolygon(display, window, gc,
149 		polygon, 4, Convex, CoordModePrevious);
150 	XFillArc(display, window, gc,
151 		x, y + height / 3, width / 2, height / 2, 0, 23040);
152 	XFillArc(display, window, gc,
153 		x + width / 2, y + height / 3, width / 2, height / 2, 0, 23040);
154 	polygon[0].x =  x + width / 2;
155 	polygon[0].y =  y + height / 2;
156 	polygon[1].x =  -width / 8;
157 	polygon[1].y =  height / 2;
158 	polygon[2].x =  width / 4;
159 	polygon[2].y =  0;
160 	XFillPolygon(display, window, gc,
161 		polygon, 3, Convex, CoordModePrevious);
162 }
163 
164 static void
drawDiamond(ModeInfo * mi,int x,int y,int width,int height)165 drawDiamond(ModeInfo * mi, int x, int y, int width, int height)
166 {
167 	Display    *display = MI_DISPLAY(mi);
168 	Window      window = MI_WINDOW(mi);
169 	GC          gc = MI_GC(mi);
170 	XPoint      polygon[4];
171 
172 	polygon[0].x =  x + width / 2;
173 	polygon[0].y =  y;
174 	polygon[1].x =  -width / 2;
175 	polygon[1].y =  height / 2;
176 	polygon[2].x =  width / 2;
177 	polygon[2].y =  height / 2;
178 	polygon[3].x =  width / 2;
179 	polygon[3].y =  -height / 2;
180 	XFillPolygon(display, window, gc,
181 		polygon, 4, Convex, CoordModePrevious);
182 }
183 
184 static void
drawClub(ModeInfo * mi,int x,int y,int width,int height)185 drawClub(ModeInfo * mi, int x, int y, int width, int height)
186 {
187 	Display    *display = MI_DISPLAY(mi);
188 	Window      window = MI_WINDOW(mi);
189 	GC          gc = MI_GC(mi);
190 	XPoint      polygon[3];
191 
192 	XFillArc(display, window, gc,
193 		x, y + short(float(height) / 3.6), width / 2, height / 2, 0, 23040);
194 	XFillArc(display, window, gc,
195 		x + width / 2, y + short(float(height) / 3.6), width / 2, height / 2, 0, 23040);
196 	XFillArc(display, window, gc,
197 		x + width / 4, y, width / 2, height / 2, 0, 23040);
198 	polygon[0].x =  x + width / 2;
199 	polygon[0].y =  y + height / 2 - 2;
200 	polygon[1].x =  -width / 8 - 1;
201 	polygon[1].y =  height / 2 + 2;
202 	polygon[2].x =  width / 4;
203 	polygon[2].y =  0;
204 	XFillPolygon(display, window, gc,
205 		polygon, 3, Convex, CoordModePrevious);
206 }
207 
208 static void
drawHeart(ModeInfo * mi,int x,int y,int width,int height)209 drawHeart(ModeInfo * mi, int x, int y, int width, int height)
210 {
211 	Display    *display = MI_DISPLAY(mi);
212 	Window      window = MI_WINDOW(mi);
213 	GC          gc = MI_GC(mi);
214 	XPoint      polygon[4];
215 
216 	XFillArc(display, window, gc,
217 		x + 1, y, width / 2, height / 2, 0, 23040);
218 	XFillArc(display, window, gc,
219 		x + width / 2 - 1, y, width / 2, height / 2, 0, 23040);
220 	polygon[0].x =  x + width / 2;
221 	polygon[0].y =  y + short(float(height) / 4.1);
222 	polygon[1].x =  short(float(-width) / 2.7);
223 	polygon[1].y =  short(float(height) / 4.1);
224 	polygon[2].x =  short(float(width) / 2.7);
225 	polygon[2].y =  short(float(height) / 2.05);
226 	polygon[3].x =  short(float(width) / 2.7);
227 	polygon[3].y =  short(float(-height) / 2.05);
228 	XFillPolygon(display, window, gc,
229 		polygon, 4, Convex, CoordModePrevious);
230 }
231 
232 void
drawSuit(ModeInfo * mi,Suits suit,int x,int y,int width,int height)233 drawSuit(ModeInfo * mi, Suits suit, int x, int y, int width, int height)
234 {
235 	switch (suit) {
236 	case spade:
237 		drawSpade(mi, x, y, width, height);
238 		break;
239 	case diamond:
240 		drawDiamond(mi, x, y, width, height);
241 		break;
242 	case club:
243 		drawClub(mi, x, y, width, height);
244 		break;
245 	case heart:
246 		drawHeart(mi, x, y, width, height);
247 		break;
248 	}
249 }
250 
251 //card.h
252 class Card
253 {
254 private:
255 	Suits suit;
256 	int rank;
257 public:
258 	Card(Suits suit, int rank);
259 	Suits whichSuit();
260 	int whichRank();
261 	Colors whichColor();
262 };
Card(Suits a_suit,int a_rank)263 inline Card::Card(Suits a_suit, int a_rank) { this->suit = a_suit; this->rank = a_rank;}
whichSuit()264 inline Suits Card::whichSuit() { return this->suit;}
whichRank()265 inline int Card::whichRank() { return this->rank;}
whichColor()266 inline Colors Card::whichColor() { return ((whichSuit() % MAXCOLORS) ? col_red : col_black);}
267 
268 
269 //cardview.h
270 class CardView
271 {
272 private:
273 	ModeInfo *mi;
274 	Card * card;
275 	Bool faceUp;
276 	int locationX, locationY;
277 public:
278 	CardView(ModeInfo * mi, Card * card);
279 	CardView(ModeInfo *mi, Suits suit, int rank);
280 	Card * thisCard();
281 	void draw(int pileCount);
282 	void erase();
283 	Bool isFaceUp();
284 	void flip();
285 	Bool includes(int, int); // mouse interaction
286 	int x();
287 	int y();
288 	int width();
289 	int height();
290 	void moveTo(int, int);
291 };
292 
thisCard()293 inline Card* CardView::thisCard() { return card; }
isFaceUp()294 inline Bool CardView::isFaceUp() { return faceUp; }
flip()295 inline void CardView::flip() { faceUp = !faceUp; }
x()296 inline int CardView::x() { return locationX; }
y()297 inline int CardView::y() { return locationY; }
moveTo(int lx,int ly)298 inline void CardView::moveTo(int lx, int ly) { locationX = lx; locationY = ly; }
299 
300 //cardview.cc
CardView(ModeInfo * a_mi,Card * a_card)301 CardView::CardView(ModeInfo * a_mi, Card * a_card)
302 {
303 	this->mi = a_mi;
304 	this->card = a_card;
305 	faceUp = False;
306 	locationX = locationY = 0;
307 }
308 
CardView(ModeInfo * a_mi,Suits suit,int rank)309 CardView::CardView(ModeInfo * a_mi, Suits suit, int rank)
310 {
311 	this->mi = a_mi;
312 	if ((this->card = new Card(suit, rank)) == NULL) {
313 		return;
314 	}
315 	faceUp = False;
316 	locationX = locationY = 0;
317 }
318 
draw(int pileCount)319 void CardView::draw(int pileCount)
320 {
321 	Display * display = MI_DISPLAY(mi);
322 	Window window = MI_WINDOW(mi);
323 	GC gc = MI_GC(mi);
324 	unsigned long red_pixel = (MI_NPIXELS(mi) > 2) ? MI_PIXEL(mi,0) : MI_BLACK_PIXEL(mi);
325 	unsigned long cyan_pixel = (MI_NPIXELS(mi) > 2) ? MI_PIXEL(mi,MI_NPIXELS(mi) / 2) : MI_WHITE_PIXEL(mi);
326 
327 	if (isFaceUp()) {
328 		XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
329 		XFillRectangle(display, window, gc, x(), y(), width(), height());
330 		if (card->whichColor() == col_red) {
331 			XSetForeground(display, gc, red_pixel);
332 		} else {
333 			XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
334 		}
335 		if (width() > 16 && height() > 39) {
336 #ifdef CENTERLABEL
337 			XDrawString(display, window, gc,
338 				x() + width() / 2 - strlen(RankNames[card->whichRank()]) * 8,
339 				y() + int_round(height() * 0.40),
340 				(char *) RankNames[card->whichRank()], strlen(RankNames[card->whichRank()]));
341 #else
342 			XDrawString(display, window, gc,
343 				x() + 10 - strlen(RankNames[card->whichRank()]) * 8 + width() / 8,
344 				y() + 25,
345 				(char *) RankNames[card->whichRank()], strlen(RankNames[card->whichRank()]));
346 #endif
347 		}
348 #ifdef SUITNAMES
349 		const char *SuitNames[] = {"Spade", "Diamond", "Club", "Heart"};
350 		XDrawString(display, window, gc, x() + 0.2, y() + int_round(height() * 0.8),
351 			(char *) SuitNames[card->whichSuit()], strlen(SuitNames[card->whichSuit()]));
352 #endif
353 #ifdef CENTERLABEL
354 		drawSuit(mi, card->whichSuit(),
355 			x() + int_round(width() * 0.35), y() + int_round(height() * 0.55),
356 			int_round(0.3 * width()), int_round(0.3 * height()));
357 #else
358 		if (width() > 16 && height() > 39) {
359 			drawSuit(mi, card->whichSuit(),
360 				x() + 5, y() + 30,
361 				int_round(0.3 * width()), int_round(0.3 * height()));
362 		} else {
363 			drawSuit(mi, card->whichSuit(),
364 				x() + 2, y() + 2,
365 				int_round(0.3 * width()), int_round(0.3 * height()));
366 		}
367 #endif
368 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
369 		XDrawLine(display, window, gc,
370 			x() + 1, y() + 1, x() + width() - 2, y() + 1);
371 		XDrawLine(display, window, gc,
372 			x() + width() - 2, y() + height() - 2,
373 			x() + 1, y() + height() - 2);
374 		XDrawLine(display, window, gc,
375 			x() + 1, y() + height() - 2, x() + 1, y() + 1);
376 		XDrawLine(display, window, gc,
377 			x() + width() - 2, y() + 1,
378 			x() + width() - 2, y() + height() - 2);
379 	} else {
380 		int n, s, e;
381 		int modCount = pileCount % 2; // shake it up a bit
382 
383 		XSetForeground(display, gc, cyan_pixel);
384 		XFillRectangle(display, window, gc,
385 			x(), y(), width(), height());
386 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
387 		n = x() + int_round(int_round(width() * 0.3)) + modCount;
388 		s = y() + int_round(height() * 0.1);
389 		e = y() + int_round(height() * 0.9) - 1;
390 		XDrawLine(display, window, gc,
391 			n, s, n, e);
392 		n = x() + int_round(width() * 0.7) - 1 + modCount;
393 		XDrawLine(display, window, gc,
394 			n, s, n, e);
395 		n = y() + int_round(height() * 0.3) + modCount;
396 		s = x() + int_round(height() * 0.1);
397 		e = x() + int_round(width() * 0.9) - 1;
398 		XDrawLine(display, window, gc,
399 			s, n, e, n);
400 		n = y() + int_round(height() * 0.7) - 1 + modCount;
401 		XDrawLine(display, window, gc,
402 			s, n, e, n);
403 	}
404 	// Shadow
405 #if 1
406 	XDrawLine(display, window, gc,
407 		x() - 1, y() - 1 , x() + width(), y() - 1);
408 	XDrawLine(display, window, gc,
409 		x() - 1, y() - 1, x() - 1, y() + height());
410 #else
411 	XDrawLine(display, window, gc,
412 		x() + 1, y() + height(), x() + width(), y() + height());
413 	XDrawLine(display, window, gc,
414 		x() + width(), y() + 1, x() + width(), y() + height());
415 #endif
416 }
417 
erase()418 void CardView::erase()
419 {
420 	Display * display = MI_DISPLAY(mi);
421 	Window window = MI_WINDOW(mi);
422 	GC gc = MI_GC(mi);
423 
424 	XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
425 	XFillRectangle(display, window, gc,
426 		x(), y(), width(), height());
427 }
428 
429 //pile.h
includes(int a,int b)430 Bool CardView::includes(int a, int b)
431 {
432 	return (a >= x() && (a <= x() + width()) &&
433 		b >= y() && (b <= y() + height()));
434 }
435 
436 class CardLink : public CardView
437 {
438 public:
439 	CardLink(ModeInfo * mi, Suits suit, int rank);
440 	CardLink(ModeInfo * mi, Card * card);
441 	CardLink * nextCard();
442 	void setLink(CardLink * card);
443 
444 private:
445 	CardLink * link;
446 };
447 
CardLink(ModeInfo * a_mi,Suits suit,int rank)448 inline CardLink::CardLink(ModeInfo *a_mi, Suits suit, int rank) : CardView(a_mi, suit, rank) { ; }
CardLink(ModeInfo * a_mi,Card * a_card)449 inline CardLink::CardLink(ModeInfo *a_mi, Card * a_card) : CardView(a_mi, a_card) { ; }
nextCard()450 inline CardLink * CardLink::nextCard() { return link; }
setLink(CardLink * a_card)451 inline void CardLink::setLink(CardLink * a_card) { link = a_card; }
452 
453 class CardPile
454 {
455 public:
456 	CardPile(ModeInfo *);
~CardPile()457 	virtual ~CardPile() { ; }
458 
459 	virtual void updateLocation(ModeInfo *);
460 	virtual void addCard(CardLink *);
461 	virtual Bool canTake(Card *);
462 	Bool contains(int, int); // mouse interaction
463 	virtual void displayPile();
464 	virtual Bool initialize();
465 	virtual void cleanup();
466 	CardLink * removeCard();
467 	virtual Bool select(int, int); // mouse interaction
468 	virtual Bool select(Bool); // generator
469 	int pileCount();
470 
471 protected:
472 	int x, y;
473 	ModeInfo * mi;
474 	CardLink *top;
475 };
476 
477 #define nilLink (CardLink *) 0
478 
CardPile(ModeInfo * a_mi)479 inline CardPile::CardPile(ModeInfo * a_mi) { this->mi = a_mi; top = nilLink; }
480 
481 class DealPile : public CardPile
482 {
483 public:
484 	DealPile(ModeInfo *);
485 	virtual void cleanup();
486 	Bool shuffle(CardPile *);
487 };
DealPile(ModeInfo * a_mi)488 inline DealPile::DealPile(ModeInfo * a_mi) : CardPile(a_mi) { ; }
489 
490 class SuitPile : public CardPile
491 {
492 public:
SuitPile(ModeInfo * a_mi,int s)493 	SuitPile(ModeInfo * a_mi, int s) : CardPile(a_mi) { suit = s; }
494 	virtual void updateLocation(ModeInfo *);
495 	virtual Bool canTake(Card *);
496 private:
497 	int suit;
498 };
499 
500 class AlternatePile : public CardPile
501 {
502 public:
AlternatePile(ModeInfo * a_mi,int p)503 	AlternatePile(ModeInfo * a_mi, int p) : CardPile(a_mi) { pile = p; }
504 	virtual void updateLocation(ModeInfo *);
505 	virtual void addCard(CardLink *);
506 	virtual Bool canTake(Card *);
507 	void copyBuild(CardLink *, CardPile *);
508 	virtual void displayPile();
509 	virtual Bool select(int, int);
510 	virtual Bool select(Bool);
511 	virtual Bool initialize();
512 private:
513 	int pile;
514 };
515 
516 class DeckPile : public CardPile
517 {
518 public:
DeckPile(ModeInfo * a_mi)519 	DeckPile(ModeInfo * a_mi) : CardPile(a_mi) { ; }
520 	virtual void updateLocation(ModeInfo *);
521 	virtual void addCard(CardLink *);
522 	virtual Bool initialize();
523 	virtual Bool select(int, int);
524 	virtual Bool select(Bool);
525 };
526 
527 class DiscardPile : public CardPile
528 {
529 public:
DiscardPile(ModeInfo * a_mi)530 	DiscardPile(ModeInfo * a_mi) : CardPile(a_mi) { ; }
531 	virtual void updateLocation(ModeInfo *);
532 	virtual void addCard(CardLink *);
533 	virtual Bool select(int, int);
534 	virtual Bool select(Bool);
535 };
536 
537 //game.h
538 class GameTable
539 {
540 public:
541 	GameTable(ModeInfo *);
542 	~GameTable();
543 	Bool newGame(ModeInfo *);
544 	Bool suitCanAdd(Card *);
545 	CardPile * suitAddPile(Card *);
546 	Bool alternateCanAdd(Card *);
547 	CardPile * alternateAddPile(Card *);
548 	void Resize(ModeInfo *);
549 	void Draw(ModeInfo *);
550 	void Redraw(ModeInfo *);
551 	Bool HandleMouse(ModeInfo *);
552 	Bool HandleGenerate();
553 
554 	DealPile *dealPile;
555 
556 	CardPile *deckPile;
557 	CardPile *discardPile;
558 protected:
559 	CardPile * allPiles[MAXALTERNATEPILES + MAXSUITS + 2];
560 	CardPile * suitPiles[MAXSUITS];
561 	CardPile * alternatePiles[MAXALTERNATEPILES];
562 };
563 
564 // pile.cc
565 
566 typedef struct {
567 	Bool        painted;
568 	GameTable * game;
569 	int         width, height, cardwidth, cardheight;
570 	int         showend;
571 #ifdef DOFONT
572 	int         fontascent, fontheight;
573 #endif
574 } solitairestruct;
575 
576 static solitairestruct *solitaire = (solitairestruct *) NULL;
577 
width()578 int CardView::width()
579 {
580 	solitairestruct *bp = &solitaire[MI_SCREEN(mi)];
581 
582 	return bp->cardwidth;
583 }
584 
height()585 int CardView::height()
586 {
587 	solitairestruct *bp = &solitaire[MI_SCREEN(mi)];
588 
589 	return bp->cardheight;
590 }
591 
addCard(CardLink * card)592 void CardPile::addCard(CardLink * card)
593 {
594 	if (card != nilLink) {
595 		card->setLink(top);
596 		top = card;
597 		top->moveTo(x, y);
598 	}
599 }
600 
updateLocation(ModeInfo * a_mi)601 void CardPile::updateLocation(ModeInfo * a_mi)
602 {
603 	;
604 }
605 
canTake(Card * card)606 Bool CardPile::canTake(Card * card)
607 {
608 	return False; // from virtual, card unused
609 }
610 
contains(int a,int b)611 Bool CardPile::contains(int a, int b)
612 {
613 	for (CardLink *p = top; p!= nilLink; p = p->nextCard())
614 		if (p->includes(a, b))
615 			return True;
616 	return False;
617 }
618 
pileCount()619 int CardPile::pileCount()
620 {
621 	int number = 0;
622 	for (CardLink *p = top; p!= nilLink; p = p->nextCard())
623 		number++;
624 	return number;
625 }
626 
displayPile()627 void CardPile::displayPile()
628 {
629 	solitairestruct *bp = &solitaire[MI_SCREEN(mi)];
630 	Display * display = MI_DISPLAY(mi);
631 	Window window = MI_WINDOW(mi);
632 	GC gc = MI_GC(mi);
633 
634 	if (top == nilLink) {
635 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
636 		XFillRectangle(display, window, gc,
637 			x, y, bp->cardwidth, bp->cardheight);
638 	} else {
639 		top->draw(pileCount());
640 	}
641 }
642 
initialize()643 Bool CardPile::initialize()
644 {
645 	top = nilLink;
646 	return True;
647 }
648 
cleanup()649 void CardPile::cleanup()
650 {
651 	CardLink *p;
652 
653 	while (top != nilLink) {
654   		p = top;
655 		top = top->nextCard();
656 		delete p;
657 	}
658 }
659 
removeCard()660 CardLink * CardPile::removeCard()
661 {
662 	CardLink *p;
663 
664 	if (top == nilLink)
665 		return nilLink;
666 	p = top;
667 	top = top->nextCard();
668 	return p;
669 }
670 
select(int a_x,int a_y)671 Bool CardPile::select(int a_x, int a_y)
672 {
673 	// from virtual, a_x & a_y unused
674 	return select(True);
675 }
676 
select(Bool first)677 Bool CardPile::select(Bool first)
678 {
679 	// from virtual, first unused
680 	return False;
681 }
682 
cleanup()683 void DealPile::cleanup()
684 {
685 	CardLink *p;
686 
687   while (top != nilLink) {
688     p = top;
689 		top = top->nextCard();
690 		delete p->thisCard();
691     delete p;
692    }
693 }
694 
shuffle(CardPile * pile)695 Bool DealPile::shuffle(CardPile * pile)
696 {
697 	CardLink *p, *q;
698 	int max, limit, i;
699 
700 	// first see how many cards we have
701 	for (max = 0, p = top; p != nilLink; p = p->nextCard()) {
702 		max++;
703 		if (p->isFaceUp())
704 			p->flip();
705 	}
706 	// then pull them out, randomly, one at a time
707 	for (; max > 0; max--) {
708 		limit = (int) ((LRAND() >> 3) % max) + 1;
709 		for ( i= 1, p = top; i <= limit; ) {
710 			while (p->isFaceUp())
711 				p = p->nextCard();
712 			i++;
713 			if (i <= limit)
714 				p = p->nextCard();
715 		}
716 		if ((q = new CardLink(mi, p->thisCard())) == NULL) {
717 			return False;
718 		}
719 		pile->addCard(q);
720 		p->flip();
721 	}
722 	return True;
723 }
724 
updateLocation(ModeInfo * a_mi)725 void DeckPile::updateLocation(ModeInfo * a_mi)
726 {
727 	solitairestruct *bp = &solitaire[MI_SCREEN(a_mi)];
728 
729 	//set pile
730 	x = int_round(((bp->cardwidth + 2.0 * MAXSUITS * bp->cardwidth *
731 		(MAXALTERNATEPILES + 1.0)) / (2.0 * MAXALTERNATEPILES)) +
732 		3 * bp->cardwidth / 4.0);
733 	y = int_round(0.25 * bp->cardheight);
734 
735 	// set card
736 	for (CardLink *p = top; p!= nilLink; p = p->nextCard()) {
737 		p->moveTo(x, y);
738 	}
739 }
740 
initialize()741 Bool DeckPile::initialize()
742 {
743 	solitairestruct *bp = &solitaire[MI_SCREEN(mi)];
744 
745 	CardPile::initialize();
746 	if (!bp->game->dealPile->shuffle(this))
747     	return False;
748 	return True;
749 }
750 
addCard(CardLink * c)751 void DeckPile::addCard(CardLink *c)
752 {
753 	if (c->isFaceUp())
754 		c->flip();
755 	CardPile::addCard(c);
756 }
757 
select(int a_x,int a_y)758 Bool DeckPile::select(int a_x, int a_y)
759 {
760 	// from virtual, a_x & a_y unused
761 	return select(True);
762 }
763 
764 // turn over a new card
select(Bool first)765 Bool DeckPile::select(Bool first)
766 {
767 	// from virtual, first unused
768 	solitairestruct *bp = &solitaire[MI_SCREEN(mi)];
769 	CardLink *c;
770 	Bool didSomething = False;
771 
772 	if (top != nilLink) {
773 		c = removeCard(); // should not this be 3 at a time
774 		if (c != nilLink)
775 			(bp->game->discardPile)->addCard(c);
776 		didSomething = True;
777 	}
778 	displayPile();
779 	(bp->game->discardPile)->displayPile();
780 	return didSomething;
781 }
782 
updateLocation(ModeInfo * a_mi)783 void DiscardPile::updateLocation(ModeInfo * a_mi)
784 {
785 	solitairestruct *bp = &solitaire[MI_SCREEN(a_mi)];
786 
787 	//set pile
788 	x = int_round(((bp->cardwidth + 2.0 * MAXSUITS * bp->cardwidth *
789 		(MAXALTERNATEPILES + 1.0)) / (2.0 * MAXALTERNATEPILES)) +
790 		2 * bp->cardwidth);
791 	y = int_round(0.25 * bp->cardheight);
792 
793 	// set card
794 	for (CardLink *p = top; p!= nilLink; p = p->nextCard()) {
795 		p->moveTo(x, y);
796 	}
797 }
798 
addCard(CardLink * c)799 void DiscardPile::addCard(CardLink *c)
800 {
801 	if (!(c->isFaceUp()))
802 		c->flip();
803 	CardPile::addCard(c);
804 }
805 
806 // play the current face card
select(int a_x,int a_y)807 Bool DiscardPile::select(int a_x, int a_y)
808 {
809 	// from virtual, a_x & a_y unused
810 	return select(True);
811 }
812 
813 // play the current face card
select(Bool first)814 Bool DiscardPile::select(Bool first)
815 {
816 	// from virtual, first unused
817 	solitairestruct *bp = &solitaire[MI_SCREEN(mi)];
818 	CardPile * pile;
819 
820 	if (top == nilLink)
821 		return False;
822 	// see if we can move it to a suit pile
823 	if (bp->game->suitCanAdd(top->thisCard())) {
824 		pile = bp->game->suitAddPile(top->thisCard());
825 		pile->addCard(removeCard());
826 		displayPile();
827 		pile->displayPile();
828 		return True;
829 	}
830 	if (bp->game->alternateCanAdd(top->thisCard())) {
831 		pile = bp->game->alternateAddPile(top->thisCard());
832 		pile->addCard(removeCard());
833 		displayPile();
834 		pile->displayPile();
835 		return True;
836 	}
837 	return False;
838 }
839 
updateLocation(ModeInfo * a_mi)840 void SuitPile::updateLocation(ModeInfo * a_mi)
841 {
842 	solitairestruct *bp = &solitaire[MI_SCREEN(a_mi)];
843 
844 	//set pile
845 	x = int_round(((bp->cardwidth + 2.0 * suit * bp->cardwidth *
846 		(MAXALTERNATEPILES + 1.0)) / (2.0 * MAXALTERNATEPILES)) +
847 		bp->cardwidth / 2.0);
848 	y = int_round(0.25 * bp->cardheight);
849 
850 	// set card
851 	for (CardLink *p = top; p!= nilLink; p = p->nextCard()) {
852 		p->moveTo(x, y);
853 	}
854 }
855 
canTake(Card * card)856 Bool SuitPile::canTake(Card * card)
857 {
858 	if (top == nilLink) { // empty so can take ace
859 		if (card->whichRank() == 1)
860 			return True;
861 		return False;
862 	}
863 	if ((top->thisCard())->whichSuit() != card->whichSuit())
864 		return False;
865 	if (((top->thisCard())->whichRank() + 1) == card->whichRank())
866 		return True;
867 	return False;
868 }
869 
updateLocation(ModeInfo * a_mi)870 void AlternatePile::updateLocation(ModeInfo * a_mi)
871 {
872 	solitairestruct *bp = &solitaire[MI_SCREEN(a_mi)];
873 	int ty;
874 
875 	//set pile
876 	x = (int_round((bp->cardwidth + 2.0 * pile * bp->cardwidth *
877 	 (MAXALTERNATEPILES + 1.0)) / (2.0 * MAXALTERNATEPILES)));
878 	y = int_round(1.5 * bp->cardheight);
879 
880 	CardLink *bf = nilLink;
881 	// set card
882 	for (CardLink *p = top; p!= nilLink; p = p->nextCard()) {
883 		p->moveTo(x, y);
884 		// find bottom faceup card
885 		// not setup to work with STRANGE_LAYOUT
886 		if (p->isFaceUp())
887 			bf = p;
888 	}
889 	if (bf != nilLink) {
890 		// ok but this is wrong.  Since we only have a singly link
891 		// we will do this slightly inefficiently...
892 		ty = y;
893 		while (bf != top) {
894 			CardLink *sp;
895 			for (sp = top; sp->nextCard() != bf; sp = sp->nextCard());
896 			bf = sp;
897 			ty += int_round(bp->cardheight * OVERLAP);
898 			sp->moveTo(x, ty);
899 		}
900 	}
901 }
902 
initialize()903 Bool AlternatePile::initialize()
904 {
905 	solitairestruct *bp = &solitaire[MI_SCREEN(mi)];
906 	int a_pile;
907 
908 	//put the right number of cards on the alternate
909 	(void) CardPile::initialize();
910 	for (a_pile = 0; a_pile <= this->pile; a_pile++)
911 		addCard((bp->game->deckPile)->removeCard());
912 	// flip the last one
913 	if (top != nilLink) {
914 		top->flip();
915 	}
916 	return True;
917 }
918 
addCard(CardLink * card)919 void AlternatePile::addCard(CardLink * card)
920 {
921 	solitairestruct *bp = &solitaire[MI_SCREEN(mi)];
922 	int tx, ty;
923 
924 	if (top == nilLink)
925 		CardPile::addCard(card);
926 	else {
927 		tx = top->x();
928 		ty = top->y();
929 
930 		// figure out where to place the card
931 #ifdef STRANGE_LAYOUT
932 		// This was the original logic... does not look right to me.
933 		if (!(top->isFaceUp() && top->nextCard() != nilLink &&
934 			(top->nextCard())->isFaceUp()))
935 #else
936 		if (top->isFaceUp())
937 #endif
938 			ty += int_round(bp->cardheight * OVERLAP);
939 		CardPile::addCard(card);
940 		top->moveTo(tx, ty);
941 	}
942 }
943 
canTake(Card * card)944 Bool AlternatePile::canTake(Card *card)
945 {
946 	if (top == nilLink) { // can take only kings on an empty pile
947 		if (card->whichRank() == MAXRANK) {
948 			return True;
949 		}
950 		return False;
951 	}
952 	// see if it is face up, colors are different, and the number is legal
953 	if ((top->thisCard())->whichColor() != card->whichColor() &&
954 	    ((top->thisCard())->whichRank() - 1) == card->whichRank()) {
955 		return True;
956 	}
957 	return False;
958 }
959 
copyBuild(CardLink * card,CardPile * a_pile)960 void AlternatePile::copyBuild(CardLink *card, CardPile * a_pile)
961 {
962 	CardLink *tempCard;
963 
964 	top->erase();
965 	tempCard = removeCard();
966 	displayPile();
967 	if (card != tempCard)
968 		copyBuild(card, a_pile);
969 	a_pile->addCard(tempCard);
970 	a_pile->displayPile();
971 }
972 
stackDisplay(CardLink * p)973 static void stackDisplay(CardLink *p)
974 {
975 	if (p->nextCard())
976 		stackDisplay(p->nextCard());
977 	p->draw(0);
978 }
979 
displayPile()980 void AlternatePile::displayPile()
981 {
982 	// Zero or one cards, can not do any better
983 	if (top == nilLink)
984 		CardPile::displayPile();
985 	else { // otherwise half display all the covered cards
986 		stackDisplay(top);
987 	}
988 }
989 
select(int a_x,int a_y)990 Bool AlternatePile::select(int a_x, int a_y)
991 {
992 	solitairestruct *bp = &solitaire[MI_SCREEN(mi)];
993 	CardLink *c;
994 
995 	// no cards, do nothing
996 	if (top == nilLink)
997 		return False;
998 
999 	// if top card is not flipped, flip it now
1000 	if (!top->isFaceUp()) {
1001 		top->flip();
1002 		top->draw(pileCount());
1003 		return True;
1004 	}
1005 
1006 	// if it was to top card, see if we can move it
1007 	if (top->includes(a_x, a_y)) {
1008 		// see if we ca move it to a suit pile
1009 		if (bp->game->suitCanAdd(top->thisCard())) {
1010 			copyBuild(top, bp->game->suitAddPile(top->thisCard()));
1011 			return True;
1012 		}
1013 		// else see if we can move a alternate pile but only if it is not part of pile
1014 		if (((top->nextCard() == nilLink) || !((top->nextCard())->isFaceUp())) &&
1015 			bp->game->alternateCanAdd(top->thisCard())) {
1016 			copyBuild(top, bp->game->alternateAddPile(top->thisCard()));
1017 			return True;
1018 		}
1019 	}
1020 
1021 	// else see if we can move a pile
1022 	for (c = top->nextCard(); c!= nilLink; c = c->nextCard())
1023 		if (c->isFaceUp() && c->includes(a_x, a_y)) {
1024 			if (bp->game->alternateCanAdd(c->thisCard())) {
1025 				copyBuild(c, bp->game->alternateAddPile(c->thisCard()));
1026 			}
1027 			return True;
1028 		}
1029 	return False;
1030 }
1031 
select(Bool first)1032 Bool AlternatePile::select(Bool first)
1033 {
1034 	solitairestruct *bp = &solitaire[MI_SCREEN(mi)];
1035 	CardLink *c;
1036 
1037 	// no cards, do nothing
1038 	if (top == nilLink)
1039 		return False;
1040 
1041 	// if top card is not flipped, flip it now
1042 	if (!top->isFaceUp()) {
1043 		top->flip();
1044 		top->draw(pileCount());
1045 		return True;
1046 	}
1047 	// see if we ca move it to a suit pile
1048 	if (bp->game->suitCanAdd(top->thisCard())) {
1049 		copyBuild(top, bp->game->suitAddPile(top->thisCard()));
1050 		return True;
1051 	}
1052 
1053 	// If all cards are not flipped it could lead to problems...
1054 	if (first)
1055 		return False;
1056 
1057 	// Other moves may be better but this is stupid
1058 	// Bouncing may result if one allows to take away moves
1059 	// find highest faceup card
1060 	c = top;
1061 	while (c->nextCard() != nilLink && (c->nextCard())->isFaceUp())
1062 		c = c->nextCard();
1063 
1064 	// if not king see if it can be moved to leave a space (else it will bounce)
1065 	if (((c->nextCard() != nilLink) ||
1066 	    ((c->thisCard())->whichRank() != MAXRANK)) &&
1067 	    bp->game->alternateCanAdd(c->thisCard())) {
1068 				copyBuild(c, bp->game->alternateAddPile(c->thisCard()));
1069 				return True;
1070 	}
1071 	return False;
1072 }
1073 
GameTable(ModeInfo * mi)1074 GameTable::GameTable(ModeInfo *mi)
1075 {
1076 	solitairestruct *bp = &solitaire[MI_SCREEN(mi)];
1077 	int suit, rank, pile;
1078 
1079 	bp->width = MI_WIDTH(mi);
1080 	bp->height = MI_HEIGHT(mi);
1081 	bp->showend = 0;
1082 	bp->cardwidth = bp->width / (MAXALTERNATEPILES + 1);
1083 	bp->cardheight = bp->height / 5;
1084 	// Create the original (unshuffled) deck (with no cards) ... dah daaah!
1085 	if ((dealPile = new DealPile(mi)) == NULL) {
1086 		return;
1087 	}
1088 	for (suit = 0; suit < MAXSUITS; suit++)
1089 		for (rank = 1; rank <= MAXRANK; rank++) {
1090             CardLink * deal;
1091 			// 52 pickup, creating new cards and creating links
1092 			if ((deal = new CardLink(mi, Suits(suit), rank)) == NULL) {
1093 				dealPile->cleanup();
1094 				delete dealPile;
1095 				return;
1096 			}
1097 			dealPile->addCard(deal);
1098 		}
1099 	// create 2 piles the deck and discard piles
1100 	if ((allPiles[0] = deckPile = new DeckPile(mi)) == NULL) {
1101 		dealPile->cleanup();
1102 		delete dealPile;
1103 		return;
1104 	}
1105 	deckPile->updateLocation(mi);
1106 	if ((allPiles[1] = discardPile = new DiscardPile(mi)) == NULL) {
1107 		dealPile->cleanup();
1108 		delete dealPile;
1109 		delete deckPile;
1110 		return;
1111 	}
1112 	discardPile->updateLocation(mi);
1113 
1114 	// create suit piles
1115 	for (suit = 0; suit < MAXSUITS; suit++) {
1116 		if ((allPiles[suit + 2] = suitPiles[Suits(suit)] = new SuitPile(mi, suit)) == NULL) {
1117 			delete this;
1118 			return;
1119 		}
1120 		(suitPiles[Suits(suit)])->updateLocation(mi);
1121 	}
1122 
1123 	// create alternate piles
1124 	for (pile = 0; pile < MAXALTERNATEPILES; pile++) {
1125 		if ((allPiles[pile + 2 + MAXSUITS] = alternatePiles[pile] =
1126 			new AlternatePile(mi, pile)) == NULL) {
1127 			delete this;
1128 			return;
1129 		}
1130 		(alternatePiles[pile])->updateLocation(mi);
1131 	}
1132 }
1133 
~GameTable()1134 GameTable::~GameTable()
1135 {
1136 	for (int pile = 0; pile < MAXPILES; pile++)
1137 		if (allPiles[pile]) {
1138 			allPiles[pile]->cleanup();
1139 			delete allPiles[pile];
1140 		}
1141 	if (dealPile) {
1142 		dealPile->cleanup();
1143 		delete dealPile;
1144 	}
1145 }
1146 
Resize(ModeInfo * mi)1147 void GameTable::Resize(ModeInfo *mi)
1148 {
1149 	solitairestruct *bp = &solitaire[MI_SCREEN(mi)];
1150 	int suit, pile;
1151 
1152 	bp->width = MI_WIDTH(mi);
1153 	bp->height = MI_HEIGHT(mi);
1154 	bp->cardwidth = bp->width / (MAXALTERNATEPILES + 1);
1155 	bp->cardheight = bp->height / 5;
1156 	deckPile->updateLocation(mi);
1157 	discardPile->updateLocation(mi);
1158 
1159 	// move suit piles
1160 	for (suit = 0; suit < MAXSUITS; suit++) {
1161 		(suitPiles[Suits(suit)])->updateLocation(mi);
1162 	}
1163 
1164 	// move alternate piles
1165 	for (pile = 0; pile < MAXALTERNATEPILES; pile++) {
1166 		(alternatePiles[pile])->updateLocation(mi);
1167 	}
1168 }
1169 
newGame(ModeInfo * mi)1170 Bool GameTable::newGame(ModeInfo * mi)
1171 {
1172 	int pile;
1173 
1174 	// initialize all the piles
1175 	for (pile = 0; pile < MAXPILES; pile++)
1176 		if (!allPiles[pile]->initialize())
1177 			return False;
1178 	// redraw the game window
1179 	Redraw(mi);
1180 	return True;
1181 }
1182 
Draw(ModeInfo * mi)1183 void GameTable::Draw(ModeInfo * mi)
1184 {
1185 	int pile;
1186 
1187 	// display the piles
1188 	for (pile = 0; pile < MAXPILES; pile++)
1189 		allPiles[pile]->displayPile();
1190 }
1191 
1192 // this is where the smarts is/is not
Redraw(ModeInfo * mi)1193 void GameTable::Redraw(ModeInfo * mi)
1194 {
1195 	//first clear the entire playing area
1196 	MI_CLEARWINDOW(mi);
1197 
1198 	Draw(mi);
1199 }
1200 
1201 // this is where the smarts is/is not
HandleMouse(ModeInfo * mi)1202 Bool GameTable::HandleMouse(ModeInfo * mi)
1203 {
1204 	Window r, c;
1205 	int cx, cy, rx, ry;
1206 	unsigned int m;
1207 	int pile;
1208 
1209 	(void) XQueryPointer(MI_DISPLAY(mi), MI_WINDOW(mi),
1210 	  &r, &c, &rx, &ry, &cx, &cy, &m);
1211 
1212 	if (cx <= 0  || cy <= 0 ||
1213 	    cx >= MI_WIDTH(mi) - 1 || cy >= MI_HEIGHT(mi) -1) {
1214 		return HandleGenerate();
1215 	}
1216 
1217 	for (pile = 0; pile < MAXPILES; pile++)
1218 		if (allPiles[pile]->contains(cx, cy)) {
1219 			(void) allPiles[pile]->select(cx, cy);
1220 			return True;
1221 		}
1222 	return True;
1223 }
1224 
1225 // if done right it looks smart...
HandleGenerate()1226 Bool GameTable::HandleGenerate()
1227 {
1228 	int pile;
1229 
1230 	// Start with the biggest stacks... usually the last ones
1231 	for (pile = MAXALTERNATEPILES - 1; pile >= 0; pile--)
1232 		if (alternatePiles[pile]->select(True))
1233 			return True;
1234 	for (pile = MAXALTERNATEPILES - 1; pile >= 0; pile--)
1235 		if (alternatePiles[pile]->select(False))
1236 			return True;
1237 	// Look for something to do in discard pile
1238 	if (discardPile->select(True))
1239 		return True;
1240 
1241 	// Get a new card or end it
1242 	return (deckPile->select(True));
1243 }
1244 
1245 // see if any of the suit piles can add a specific card
suitCanAdd(Card * card)1246 Bool GameTable::suitCanAdd(Card * card)
1247 {
1248 	int suit;
1249 
1250 	for (suit = 0; suit < MAXSUITS; suit++)
1251 		if (suitPiles[Suits(suit)]->canTake(card))
1252 			return True;
1253 	return False;
1254 }
1255 
1256 // see if any of the alternate piles can add a specific card
alternateCanAdd(Card * card)1257 Bool GameTable::alternateCanAdd(Card * card)
1258 {
1259 	int pile;
1260 
1261 	for (pile = 0; pile < MAXALTERNATEPILES; pile++)
1262 		if (alternatePiles[pile]->canTake(card))
1263 			return True;
1264 	return False;
1265 }
1266 
1267 // return which of the suit piles can add a card
suitAddPile(Card * card)1268 CardPile * GameTable::suitAddPile(Card * card)
1269 {
1270 	int suit;
1271 
1272 	for (suit = 0; suit < MAXSUITS; suit++)
1273 		if (suitPiles[Suits(suit)]->canTake(card))
1274 			return suitPiles[Suits(suit)];
1275 	(void) printf("suitAddPile\n");
1276 	return (CardPile *) NULL; // Hopefully we can not get here
1277 }
1278 
1279 // return which of the alternate piles can add a card
alternateAddPile(Card * card)1280 CardPile * GameTable::alternateAddPile(Card * card)
1281 {
1282 	int pile;
1283 
1284 	for (pile = 0; pile < MAXPILES; pile++)
1285 		if (alternatePiles[pile]->canTake(card))
1286 			return alternatePiles[pile];
1287 	(void) printf("alternateAddPile\n");
1288 	return (CardPile *) NULL; // Hopefully we can not get here
1289 }
1290 
1291 /* Yes, it's an ugly mix of 'C' and 'C++' functions */
1292 #ifdef STANDALONE
1293 static void draw_solitaire(ModeInfo * mi);
1294 #else
1295 extern "C" { void init_solitaire(ModeInfo * mi); }
1296 extern "C" { void draw_solitaire(ModeInfo * mi); }
1297 extern "C" { void change_solitaire(ModeInfo * mi); }
1298 extern "C" { void release_solitaire(ModeInfo * mi); }
1299 extern "C" { void refresh_solitaire(ModeInfo * mi); }
1300 #endif
1301 #ifndef DISABLE_INTERACTIVE
1302 static XrmOptionDescRec opts[] =
1303 {
1304 	{(char *) "-trackmouse", (char *) ".solitaire.trackmouse", XrmoptionNoArg, (caddr_t) "on"},
1305 	{(char *) "+trackmouse", (char *) ".solitaire.trackmouse", XrmoptionNoArg, (caddr_t) "off"}
1306 };
1307 
1308 static argtype vars[] =
1309 {
1310 	{(void *) & trackmouse, (char *) "trackmouse", (char *) "TrackMouse", (char *) DEF_TRACKMOUSE, t_Bool}
1311 };
1312 
1313 static OptionStruct desc[] =
1314 {
1315 	{(char *) "-/+trackmouse", (char *) "turn on/off the tracking of the mouse"}
1316 };
1317 
1318 ENTRYPOINT ModeSpecOpt solitaire_opts =
1319 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
1320 #else
1321 ModeSpecOpt solitaire_opts =
1322 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
1323 #endif
1324 
1325 #ifdef USE_MODULES
1326 ModStruct solitaire_description =
1327 {(char *) "solitaire", (char *) "init_solitaire",
1328  (char *) "draw_solitaire", (char *) "release_solitaire",
1329  (char *) "refresh_solitaire", (char *) "init_solitaire",
1330  (char *) NULL, &solitaire_opts,
1331  2000000, 1, 1, 1, 64, 1.0, (char *) "",
1332  (char *) "Shows Klondike's game of solitaire", 0, NULL};
1333 #endif
1334 
1335 /*
1336  *-----------------------------------------------------------------------------
1337  *-----------------------------------------------------------------------------
1338  *    Xlock hooks.
1339  *-----------------------------------------------------------------------------
1340  *-----------------------------------------------------------------------------
1341  */
1342 
1343 ENTRYPOINT void
refresh_solitaire(ModeInfo * mi)1344 refresh_solitaire(ModeInfo * mi)
1345 {
1346 	solitairestruct *bp;
1347 
1348 	if (solitaire == NULL)
1349 		return;
1350 	bp = &solitaire[MI_SCREEN(mi)];
1351 	if (!bp->game)
1352 		return;
1353 
1354 	if (bp->painted) {
1355 		bp->game->Redraw(mi);
1356 	  bp->painted = False;
1357 	}
1358 }
1359 
1360 /*
1361  *-----------------------------------------------------------------------------
1362  *    Initialize solitaire.  Called each time the window changes.
1363  *-----------------------------------------------------------------------------
1364  */
1365 
1366 ENTRYPOINT void
init_solitaire(ModeInfo * mi)1367 init_solitaire(ModeInfo * mi)
1368 {
1369 	solitairestruct *bp;
1370 
1371 	/*MI_INIT(mi, solitaire);*/
1372 	if (solitaire == NULL) {
1373 		if ((solitaire = (solitairestruct *) calloc(MI_NUM_SCREENS(mi),
1374 				sizeof(solitairestruct))) == NULL)
1375 			return;
1376 	}
1377 	bp = &solitaire[MI_SCREEN(mi)];
1378 
1379 	MI_CLEARWINDOW(mi);
1380 #ifdef DOFONT
1381 	Display    *display = MI_DISPLAY(mi);
1382 	XGCValues gcv;
1383 
1384 	if (mode_font == None)
1385 		mode_font = getFont(display);
1386 	if (mode_font != None) {
1387 		gcv.font = mode_font->fid;
1388 		/*XSetFont(display, MI_GC(mi), mode_font->fid);*/
1389 		gcv.graphics_exposures = False;
1390 		gcv.foreground = MI_WHITE_PIXEL(mi);
1391 		gcv.background = MI_BLACK_PIXEL(mi);
1392 		if ((mp->gc = XCreateGC(display, MI_WINDOW(mi),
1393 		  GCForeground | GCBackground | GCGraphicsExposures | GCFont,
1394 				&gcv)) == None) {
1395 			return;
1396 		}
1397 		mp->ascent = mode_font->ascent;
1398 		mp->height = font_height(mode_font);
1399 		for (i = 0; i < 256; i++)
1400 			char_width[i] = 8;
1401 	}
1402 #endif
1403 	if (bp->game) {
1404 		if (bp->width != MI_WIDTH(mi) || bp->height != MI_HEIGHT(mi)) {
1405 			/* Let us not be creative then... */
1406 			bp->game->Resize(mi);
1407 			bp->painted = True;
1408 			refresh_solitaire(mi);
1409 			return;
1410 		}
1411 		delete bp->game;
1412 	}
1413 	bp->painted = False;
1414 	if ((bp->game = new GameTable(mi)) == NULL) {
1415 		return;
1416 	}
1417 	if (!bp->game->newGame(mi)) {
1418 		delete bp->game;
1419 	}
1420 	draw_solitaire(mi);
1421 }
1422 
1423 /*
1424  *-----------------------------------------------------------------------------
1425  *    Called by the mainline code periodically to update the display.
1426  *-----------------------------------------------------------------------------
1427  */
1428 ENTRYPOINT void
draw_solitaire(ModeInfo * mi)1429 draw_solitaire(ModeInfo * mi)
1430 {
1431 	solitairestruct *bp;
1432 
1433 	if (solitaire == NULL)
1434 		return;
1435 	bp = &solitaire[MI_SCREEN(mi)];
1436 	if (!bp->game)
1437 		return;
1438 
1439 	MI_IS_DRAWN(mi) = True;
1440 	bp->painted = True;
1441 	if (bp->showend) {
1442 		bp->showend++;
1443 		if (bp->showend >= 10)
1444 			init_solitaire(mi);
1445 	} else if (trackmouse) {
1446 		if (!bp->game->HandleMouse(mi)) {
1447 			bp->showend++;
1448 		}
1449 	} else if (!bp->game->HandleGenerate()) {
1450 		bp->showend++;
1451 	}
1452 #ifdef STANDALONE
1453 	bp->game->Draw(mi);
1454 #endif
1455 }
1456 
1457 /*
1458  *-----------------------------------------------------------------------------
1459  *    The display is being taken away from us.  Free up malloc'ed
1460  *      memory and X resources that we've alloc'ed.  Only called
1461  *      once, we must zap everything for every screen.
1462  *-----------------------------------------------------------------------------
1463  */
1464 
1465 ENTRYPOINT void
release_solitaire(ModeInfo * mi)1466 release_solitaire(ModeInfo * mi)
1467 {
1468 	if (solitaire != NULL) {
1469 		for (int screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1470 			solitairestruct *bp = &solitaire[screen];
1471 
1472 			if (bp->game)
1473 				delete bp->game;
1474 		}
1475 		free(solitaire);
1476 		solitaire = (solitairestruct *) NULL;
1477 	}
1478 }
1479 
1480 XSCREENSAVER_MODULE ("Solitaire", solitaire)
1481 
1482 #endif	/* MODE_solitaire */
1483