xref: /plan9/sys/src/games/xs.c (revision fa70af914bd0480af9c4b57059e37b4e3d9c5d24)
1 #include "xs.h"
2 
3 /*
4  * engine for 4s, 5s, etc
5  */
6 
7 Cursor whitearrow = {
8 	{0, 0},
9 	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
10 	 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC,
11 	 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
12 	 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, },
13 	{0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C,
14 	 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C,
15 	 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C,
16 	 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }
17 };
18 
19 enum
20 {
21 	CNone	= 0,
22 	CBounds	= 1,
23 	CPiece	= 2,
24 	NX	= 10,
25 	NY	= 20,
26 };
27 
28 enum{
29 	TIMER,
30 	MOUSE,
31 	RESHAPE,
32 	KBD,
33 	SUSPEND,
34 	NALT
35 };
36 
37 char		board[NY][NX];
38 Rectangle	rboard;
39 Point		pscore;
40 Point		scoresz;
41 int		pcsz = 32;
42 Point		pos;
43 Image	*bb, *bbmask, *bb2, *bb2mask;
44 Image	*whitemask;
45 Rectangle	br, br2;
46 long		points;
47 int		dt;
48 int		DY;
49 int		DMOUSE;
50 int		lastmx;
51 Mouse	mouse;
52 int		newscreen;
53 Channel	*timerc;
54 Channel	*suspc;
55 Channel	*mousec;
56 Channel	*kbdc;
57 Mousectl	*mousectl;
58 Keyboardctl	*kbdctl;
59 int		suspended;
60 
61 void		redraw(int);
62 
63 int	tsleep;
64 
65 Piece *piece;
66 
67 #define	NCOL	10
68 
69 uchar txbits[NCOL][32]={
70 	{0xDD,0xDD,0xFF,0xFF,0x77,0x77,0xFF,0xFF,
71 	 0xDD,0xDD,0xFF,0xFF,0x77,0x77,0xFF,0xFF,
72 	 0xDD,0xDD,0xFF,0xFF,0x77,0x77,0xFF,0xFF,
73 	 0xDD,0xDD,0xFF,0xFF,0x77,0x77,0xFF,0xFF},
74 	{0xDD,0xDD,0x77,0x77,0xDD,0xDD,0x77,0x77,
75 	 0xDD,0xDD,0x77,0x77,0xDD,0xDD,0x77,0x77,
76 	 0xDD,0xDD,0x77,0x77,0xDD,0xDD,0x77,0x77,
77 	 0xDD,0xDD,0x77,0x77,0xDD,0xDD,0x77,0x77},
78 	{0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
79 	 0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
80 	 0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
81 	 0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55},
82 	{0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
83 	 0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
84 	 0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
85 	 0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55},
86 	{0x22,0x22,0x88,0x88,0x22,0x22,0x88,0x88,
87 	 0x22,0x22,0x88,0x88,0x22,0x22,0x88,0x88,
88 	 0x22,0x22,0x88,0x88,0x22,0x22,0x88,0x88,
89 	 0x22,0x22,0x88,0x88,0x22,0x22,0x88,0x88},
90 	{0x22,0x22,0x00,0x00,0x88,0x88,0x00,0x00,
91 	 0x22,0x22,0x00,0x00,0x88,0x88,0x00,0x00,
92 	 0x22,0x22,0x00,0x00,0x88,0x88,0x00,0x00,
93 	 0x22,0x22,0x00,0x00,0x88,0x88,0x00,0x00},
94 	{0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
95 	 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
96 	 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
97 	 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00},
98 	{0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
99 	 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
100 	 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
101 	 0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00},
102 	{0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,
103 	 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,
104 	 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,
105 	 0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC},
106 	{0xCC,0xCC,0xCC,0xCC,0x33,0x33,0x33,0x33,
107 	 0xCC,0xCC,0xCC,0xCC,0x33,0x33,0x33,0x33,
108 	 0xCC,0xCC,0xCC,0xCC,0x33,0x33,0x33,0x33,
109 	 0xCC,0xCC,0xCC,0xCC,0x33,0x33,0x33,0x33},
110 };
111 
112 int txpix[NCOL] = {
113 	DYellow,	/* yellow */
114 	DCyan,	/* cyan */
115 	DGreen,	/* lime green */
116 	DGreyblue,	/* slate */
117 	DRed,	/* red */
118 	DGreygreen,	/* olive green */
119 	DBlue,	/* blue */
120 	0xFF55AAFF,	/* pink */
121 	0xFFAAFFFF,	/* lavender */
122 	0xBB005DFF,	/* maroon */
123 };
124 
125 Image *tx[NCOL];
126 
127 int
movemouse(void)128 movemouse(void)
129 {
130 	mouse.xy = Pt(rboard.min.x + Dx(rboard)/2, rboard.min.y +Dy(rboard)/2);
131 	moveto(mousectl, mouse.xy);
132 	return mouse.xy.x;
133 }
134 
135 int
warp(Point p,int x)136 warp(Point p, int x)
137 {
138 	if (!suspended && piece != nil) {
139 		x = pos.x + piece->sz.x*pcsz/2;
140 		if (p.y < rboard.min.y)
141 			p.y = rboard.min.y;
142 		if (p.y >= rboard.max.y)
143 			p.y = rboard.max.y - 1;
144 		moveto(mousectl, Pt(x, p.y));
145 	}
146 	return x;
147 }
148 
149 Piece *
rotr(Piece * p)150 rotr(Piece *p)
151 {
152 	if(p->rot == 3)
153 		return p-3;
154 	return p+1;
155 }
156 
157 Piece *
rotl(Piece * p)158 rotl(Piece *p)
159 {
160 	if(p->rot == 0)
161 		return p+3;
162 	return p-1;
163 }
164 
165 int
collide(Point pt,Piece * p)166 collide(Point pt, Piece *p)
167 {
168 	int i;
169 	int c = CNone;
170 
171 	pt.x = (pt.x - rboard.min.x) / pcsz;
172 	pt.y = (pt.y - rboard.min.y) / pcsz;
173 	for(i=0; i<N; i++){
174 		pt.x += p->d[i].x;
175 		pt.y += p->d[i].y;
176 		if(pt.x<0 || pt.x>=NX || pt.y<0 || pt.y>=NY)
177 			c |= CBounds;
178 		if(board[pt.y][pt.x])
179 			c |= CPiece;
180 	}
181 	return c;
182 }
183 
184 int
collider(Point pt,Point pmax)185 collider(Point pt, Point pmax)
186 {
187 	int i, j, pi, pj, n, m;
188 
189 	pi = (pt.x - rboard.min.x) / pcsz;
190 	pj = (pt.y - rboard.min.y) / pcsz;
191 	n = pmax.x / pcsz;
192 	m = pmax.y / pcsz + 1;
193 	for(i = pi; i < pi+n && i < NX; i++)
194 		for(j = pj; j < pj+m && j < NY; j++)
195 			if(board[j][i])
196 				return 1;
197 	return 0;
198 }
199 
200 void
setpiece(Piece * p)201 setpiece(Piece *p){
202 	int i;
203 	Rectangle r, r2;
204 	Point op, delta;
205 
206 	draw(bb, bb->r, display->white, nil, ZP);
207 	draw(bbmask, bbmask->r, display->transparent, nil, ZP);
208 	br = Rect(0, 0, 0, 0);
209 	br2 = br;
210 	piece = p;
211 	if(p == 0)
212 		return;
213 	r.min = bb->r.min;
214 	for(i=0; i<N; i++){
215 		r.min.x += p->d[i].x*pcsz;
216 		r.min.y += p->d[i].y*pcsz;
217 		r.max.x = r.min.x + pcsz;
218 		r.max.y = r.min.y + pcsz;
219 		if(i == 0){
220 			draw(bb, r, display->black, nil, ZP);
221 			draw(bb, insetrect(r, 1), tx[piece->tx], nil, ZP);
222 			draw(bbmask, r, display->opaque, nil, ZP);
223 			op = r.min;
224 		}else{
225 			draw(bb, r, bb, nil, op);
226 			draw(bbmask, r, bbmask, nil, op);
227 		}
228 		if(br.max.x < r.max.x)
229 			br.max.x = r.max.x;
230 		if(br.max.y < r.max.y)
231 			br.max.y = r.max.y;
232 	}
233 	br.max = subpt(br.max, bb->r.min);
234 	delta = Pt(0,DY);
235 	br2.max = addpt(br.max, delta);
236 	r = rectaddpt(br, bb2->r.min);
237 	r2 = rectaddpt(br2, bb2->r.min);
238 	draw(bb2, r2, display->white, nil, ZP);
239 	draw(bb2, rectaddpt(r,delta), bb, nil, bb->r.min);
240 	draw(bb2mask, r2, display->transparent, nil, ZP);
241 	draw(bb2mask, r, display->opaque, bbmask, bb->r.min);
242 	draw(bb2mask, rectaddpt(r,delta), display->opaque, bbmask, bb->r.min);
243 }
244 
245 void
drawpiece(void)246 drawpiece(void){
247 	draw(screen, rectaddpt(br, pos), bb, bbmask, bb->r.min);
248 	if (suspended)
249 		draw(screen, rectaddpt(br, pos), display->white, whitemask, ZP);
250 }
251 
252 void
undrawpiece(void)253 undrawpiece(void)
254 {
255 	Image *mask = nil;
256 	if(collider(pos, br.max))
257 		mask = bbmask;
258 	draw(screen, rectaddpt(br, pos), display->white, mask, bb->r.min);
259 }
260 
261 void
rest(void)262 rest(void)
263 {
264 	int i;
265 	Point pt;
266 
267 	pt = divpt(subpt(pos, rboard.min), pcsz);
268 	for(i=0; i<N; i++){
269 		pt.x += piece->d[i].x;
270 		pt.y += piece->d[i].y;
271 		board[pt.y][pt.x] = piece->tx+16;
272 	}
273 }
274 
275 int
canfit(Piece * p)276 canfit(Piece *p)
277 {
278 	static int dx[]={0, -1, 1, -2, 2, -3, 3, 4, -4};
279 	int i, j;
280 	Point z;
281 
282 	j = N + 1;
283 	if(j >= 4){
284 		j = p->sz.x;
285 		if(j<p->sz.y)
286 			j = p->sz.y;
287 		j = 2*j-1;
288 	}
289 	for(i=0; i<j; i++){
290 		z.x = pos.x + dx[i]*pcsz;
291 		z.y = pos.y;
292 		if(!collide(z, p)){
293 			z.y = pos.y + pcsz-1;
294 			if(!collide(z, p)){
295 				undrawpiece();
296 				pos.x = z.x;
297 				return 1;
298 			}
299 		}
300 	}
301 	return 0;
302 }
303 
304 void
score(int p)305 score(int p)
306 {
307 	char buf[128];
308 
309 	points += p;
310 	snprint(buf, sizeof(buf), "%.6ld", points);
311 	draw(screen, Rpt(pscore, addpt(pscore, scoresz)), display->white, nil, ZP);
312 	string(screen, pscore, display->black, ZP, font, buf);
313 }
314 
315 void
drawsq(Image * b,Point p,int ptx)316 drawsq(Image *b, Point p, int ptx){
317 	Rectangle r;
318 
319 	r.min = p;
320 	r.max.x = r.min.x+pcsz;
321 	r.max.y = r.min.y+pcsz;
322 	draw(b, r, display->black, nil, ZP);
323 	draw(b, insetrect(r, 1), tx[ptx], nil, ZP);
324 }
325 
326 void
drawboard(void)327 drawboard(void)
328 {
329 	int i, j;
330 
331 	border(screen, insetrect(rboard, -2), 2, display->black, ZP);
332 	draw(screen, Rect(rboard.min.x, rboard.min.y-2, rboard.max.x, rboard.min.y),
333 		display->white, nil, ZP);
334 	for(i=0; i<NY; i++)
335 		for(j=0; j<NX; j++)
336 			if(board[i][j])
337 				drawsq(screen, Pt(rboard.min.x+j*pcsz, rboard.min.y+i*pcsz), board[i][j]-16);
338 	score(0);
339 	if (suspended)
340 		draw(screen, screen->r, display->white, whitemask, ZP);
341 }
342 
343 void
choosepiece(void)344 choosepiece(void)
345 {
346 	int i;
347 
348 	do{
349 		i = nrand(NP);
350 		setpiece(&pieces[i]);
351 		pos = rboard.min;
352 		pos.x += nrand(NX)*pcsz;
353 	}while(collide(Pt(pos.x, pos.y+pcsz-DY), piece));
354 	drawpiece();
355 	flushimage(display, 1);
356 }
357 
358 int
movepiece(void)359 movepiece(void)
360 {
361 	Image *mask = nil;
362 
363 	if(collide(Pt(pos.x, pos.y+pcsz), piece))
364 		return 0;
365 	if(collider(pos, br2.max))
366 		mask = bb2mask;
367 	draw(screen, rectaddpt(br2, pos), bb2, mask, bb2->r.min);
368 	pos.y += DY;
369 	flushimage(display, 1);
370 	return 1;
371 }
372 
373 void
suspend(int s)374 suspend(int s)
375 {
376 	suspended = s;
377 	if (suspended)
378 		setcursor(mousectl, &whitearrow);
379 	else
380 		setcursor(mousectl, nil);
381 	if (!suspended)
382 		drawpiece();
383 	drawboard();
384 	flushimage(display, 1);
385 }
386 
387 void
pause(int t)388 pause(int t)
389 {
390 	int s;
391 	Alt alts[NALT+1];
392 
393 	alts[TIMER].c = timerc;
394 	alts[TIMER].v = nil;
395 	alts[TIMER].op = CHANRCV;
396 	alts[SUSPEND].c = suspc;
397 	alts[SUSPEND].v = &s;
398 	alts[SUSPEND].op = CHANRCV;
399 	alts[RESHAPE].c = mousectl->resizec;
400 	alts[RESHAPE].v = nil;
401 	alts[RESHAPE].op = CHANRCV;
402 	// avoid hanging up those writing ong mousec and kbdc
403 	// so just accept it all and keep mouse up-to-date
404 	alts[MOUSE].c = mousec;
405 	alts[MOUSE].v = &mouse;
406 	alts[MOUSE].op = CHANRCV;
407 	alts[KBD].c = kbdc;
408 	alts[KBD].v = nil;
409 	alts[KBD].op = CHANRCV;
410 	alts[NALT].op = CHANEND;
411 
412 	flushimage(display, 1);
413 	for(;;)
414 		switch(alt(alts)){
415 		case SUSPEND:
416 			if (!suspended && s) {
417 				suspend(1);
418 			} else if (suspended && !s) {
419 				suspend(0);
420 				lastmx = warp(mouse.xy, lastmx);
421 			}
422 			break;
423 		case TIMER:
424 			if(suspended)
425 				break;
426 			if((t -= tsleep) < 0)
427 				return;
428 			break;
429 		case RESHAPE:
430 			redraw(1);
431 			break;
432 		}
433 }
434 
435 int
horiz(void)436 horiz(void)
437 {
438 	int lev[MAXN];
439 	int i, j, h;
440 	Rectangle r;
441 
442 	h = 0;
443 	for(i=0; i<NY; i++){
444 		for(j=0; board[i][j]; j++)
445 			if(j == NX-1){
446 				lev[h++] = i;
447 				break;
448 			}
449 	}
450 	if(h == 0)
451 		return 0;
452 	r = rboard;
453 	newscreen = 0;
454 	for(j=0; j<h; j++){
455 		r.min.y = rboard.min.y + lev[j]*pcsz;
456 		r.max.y = r.min.y + pcsz;
457 		draw(screen, r, display->white, whitemask, ZP);
458 		flushimage(display, 1);
459 	}
460 	for(i=0; i<3; i++){
461 		pause(250);
462 		if(newscreen){
463 			drawboard();
464 			break;
465 		}
466 		for(j=0; j<h; j++){
467 			r.min.y = rboard.min.y + lev[j]*pcsz;
468 			r.max.y = r.min.y + pcsz;
469 			draw(screen, r, display->white, whitemask, ZP);
470 		}
471 		flushimage(display, 1);
472 	}
473 	r = rboard;
474 	for(j=0; j<h; j++){
475 		i = NY - lev[j] - 1;
476 		score(250+10*i*i);
477 		r.min.y = rboard.min.y;
478 		r.max.y = rboard.min.y+lev[j]*pcsz;
479 		draw(screen, rectaddpt(r, Pt(0,pcsz)), screen, nil, r.min);
480 		r.max.y = rboard.min.y+pcsz;
481 		draw(screen, r, display->white, nil, ZP);
482 		memcpy(&board[1][0], &board[0][0], NX*lev[j]);
483 		memset(&board[0][0], 0, NX);
484 	}
485 	flushimage(display, 1);
486 	return 1;
487 }
488 
489 void
mright(void)490 mright(void)
491 {
492 	if(!collide(Pt(pos.x+pcsz, pos.y), piece))
493 	if(!collide(Pt(pos.x+pcsz, pos.y+pcsz-DY), piece)){
494 		undrawpiece();
495 		pos.x += pcsz;
496 		drawpiece();
497 		flushimage(display, 1);
498 	}
499 }
500 
501 void
mleft(void)502 mleft(void)
503 {
504 	if(!collide(Pt(pos.x-pcsz, pos.y), piece))
505 	if(!collide(Pt(pos.x-pcsz, pos.y+pcsz-DY), piece)){
506 		undrawpiece();
507 		pos.x -= pcsz;
508 		drawpiece();
509 		flushimage(display, 1);
510 	}
511 }
512 
513 void
rright(void)514 rright(void)
515 {
516 	if(canfit(rotr(piece))){
517 		setpiece(rotr(piece));
518 		drawpiece();
519 		flushimage(display, 1);
520 	}
521 }
522 
523 void
rleft(void)524 rleft(void)
525 {
526 	if(canfit(rotl(piece))){
527 		setpiece(rotl(piece));
528 		drawpiece();
529 		flushimage(display, 1);
530 	}
531 }
532 
533 int fusst = 0;
534 int
drop(int f)535 drop(int f)
536 {
537 	if(f){
538 		score(5L*(rboard.max.y-pos.y)/pcsz);
539 		do; while(movepiece());
540 	}
541 	fusst = 0;
542 	rest();
543 	if(pos.y==rboard.min.y && !horiz())
544 		return 1;
545 	horiz();
546 	setpiece(0);
547 	pause(1500);
548 	choosepiece();
549 	lastmx = warp(mouse.xy, lastmx);
550 	return 0;
551 }
552 
553 int
play(void)554 play(void)
555 {
556 	int i;
557 	Mouse om;
558 	int s;
559 	Rune r;
560 	Alt alts[NALT+1];
561 
562 	alts[TIMER].c = timerc;
563 	alts[TIMER].v = nil;
564 	alts[TIMER].op = CHANRCV;
565 	alts[MOUSE].c = mousec;
566 	alts[MOUSE].v = &mouse;
567 	alts[MOUSE].op = CHANRCV;
568 	alts[SUSPEND].c = suspc;
569 	alts[SUSPEND].v = &s;
570 	alts[SUSPEND].op = CHANRCV;
571 	alts[RESHAPE].c = mousectl->resizec;
572 	alts[RESHAPE].v = nil;
573 	alts[RESHAPE].op = CHANRCV;
574 	alts[KBD].c = kbdc;
575 	alts[KBD].v = &r;
576 	alts[KBD].op = CHANRCV;
577 	alts[NALT].op = CHANEND;
578 
579 	dt = 64;
580 	lastmx = -1;
581 	lastmx = movemouse();
582 	choosepiece();
583 	lastmx = warp(mouse.xy, lastmx);
584 	for(;;)
585 	switch(alt(alts)){
586 	case MOUSE:
587 		if(suspended) {
588 			om = mouse;
589 			break;
590 		}
591 		if(lastmx < 0)
592 			lastmx = mouse.xy.x;
593 		if(mouse.xy.x > lastmx+DMOUSE){
594 			mright();
595 			lastmx = mouse.xy.x;
596 		}
597 		if(mouse.xy.x < lastmx-DMOUSE){
598 			mleft();
599 			lastmx = mouse.xy.x;
600 		}
601 		if(mouse.buttons&1 && !(om.buttons&1))
602 			rleft();
603 		if(mouse.buttons&2 && !(om.buttons&2))
604 			if(drop(1))
605 				return 1;
606 		if(mouse.buttons&4 && !(om.buttons&4))
607 			rright();
608 		om = mouse;
609 		break;
610 	case SUSPEND:
611 		if (!suspended && s)
612 			suspend(1);
613 		else
614 		if (suspended && !s) {
615 			suspend(0);
616 			lastmx = warp(mouse.xy, lastmx);
617 		}
618 		break;
619 	case RESHAPE:
620 		redraw(1);
621 		break;
622 	case KBD:
623 		if(suspended)
624 			break;
625 		switch(r){
626 		case 'f':
627 		case ';':
628 			mright();
629 			break;
630 		case 'a':
631 		case 'j':
632 			mleft();
633 			break;
634 		case 'd':
635 		case 'l':
636 			rright();
637 			break;
638 		case 's':
639 		case 'k':
640 			rleft();
641 			break;
642 		case ' ':
643 			if(drop(1))
644 				return 1;
645 			break;
646 		}
647 		break;
648 	case TIMER:
649 		if(suspended)
650 			break;
651 		dt -= tsleep;
652 		if(dt < 0){
653 			i = 1;
654 			dt = 16 * (points+nrand(10000)-5000) / 10000;
655 			if(dt >= 32){
656 				i += (dt-32)/16;
657 				dt = 32;
658 			}
659 			dt = 52-dt;
660 			while(i-- > 0)
661 				if(movepiece()==0 && ++fusst==40){
662 					if(drop(0))
663 						return 1;
664 					break;
665 				}
666 		}
667 		break;
668 	}
669 }
670 
671 void
setparms(void)672 setparms(void)
673 {
674 	char buf[32];
675 	int fd, n;
676 
677 	tsleep = 50;
678 	fd = open("/dev/hz", OREAD);
679 	if(fd < 0)
680 		return;
681 	n = read(fd, buf, sizeof buf - 1);
682 	close(fd);
683 	if(n < 0)
684 		return;
685 	buf[n] = '\0';
686 	tsleep = strtoul(buf, 0, 10);
687 	tsleep = (1000 + tsleep - 1) / tsleep;
688 }
689 
690 void
timerproc(void * v)691 timerproc(void *v)
692 {
693 	Channel *c;
694 	void **arg;
695 
696 	arg = v;
697 	c = (Channel*)arg;
698 
699 	for(;;){
700 		sleep(tsleep);
701 		send(c, nil);
702 	}
703 }
704 
705 void
suspproc(void *)706 suspproc(void *)
707 {
708 	Mouse mouse;
709 	Rune r;
710 	int s;
711 	Alt alts[NALT+1];
712 
713 	alts[TIMER].op = CHANNOP;
714 	alts[MOUSE].c = mousectl->c;
715 	alts[MOUSE].v = &mouse;
716 	alts[MOUSE].op = CHANRCV;
717 	alts[SUSPEND].op = CHANNOP;
718 	alts[RESHAPE].op = CHANNOP;
719 	alts[KBD].c = kbdctl->c;
720 	alts[KBD].v = &r;
721 	alts[KBD].op = CHANRCV;
722 	alts[NALT].op = CHANEND;
723 
724 	s = 0;
725 	for(;;)
726 		switch(alt(alts)){
727 		case MOUSE:
728 			send(mousec, &mouse);
729 			break;
730 		case KBD:
731 			switch(r){
732 			case 'q':
733 			case 'Q':
734 			case 0x04:
735 			case 0x7F:
736 				threadexitsall(nil);
737 			default:
738 				if(s) {
739 					s = 0;
740 					send(suspc, &s);
741 				} else
742 					switch(r){
743 					case 'z':
744 					case 'Z':
745 					case 'p':
746 					case 'P':
747 					case 0x1B:
748 						s = 1;
749 						send(suspc, &s);
750 						break;
751 					default:
752 						send(kbdc, &r);
753 					}
754 				break;
755 			}
756 		}
757 }
758 
759 void
redraw(int new)760 redraw(int new)
761 {
762 	Rectangle r;
763 	long dx, dy;
764 
765 	if(new && getwindow(display, Refmesg) < 0)
766 		sysfatal("can't reattach to window");
767 	r = screen->r;
768 	pos.x = (pos.x - rboard.min.x) / pcsz;
769 	pos.y = (pos.y - rboard.min.y) / pcsz;
770 	dx = r.max.x - r.min.x;
771 	dy = r.max.y - r.min.y - 2*32;
772 	DY = dx / NX;
773 	if(DY > dy / NY)
774 		DY = dy / NY;
775 	DY /= 8;
776 	if(DY > 4)
777 		DY = 4;
778 	pcsz = DY*8;
779 	DMOUSE = pcsz/3;
780 	if(pcsz < 8)
781 		sysfatal("screen too small: %d", pcsz);
782 	rboard = screen->r;
783 	rboard.min.x += (dx-pcsz*NX)/2;
784 	rboard.min.y += (dy-pcsz*NY)/2+32;
785 	rboard.max.x = rboard.min.x+NX*pcsz;
786 	rboard.max.y = rboard.min.y+NY*pcsz;
787 	pscore.x = rboard.min.x+8;
788 	pscore.y = rboard.min.y-32;
789 	scoresz = stringsize(font, "000000");
790 	pos.x = pos.x*pcsz + rboard.min.x;
791 	pos.y = pos.y*pcsz + rboard.min.y;
792 	if(bb){
793 		freeimage(bb);
794 		freeimage(bbmask);
795 		freeimage(bb2);
796 		freeimage(bb2mask);
797 	}
798 	bb = allocimage(display, Rect(0,0,N*pcsz,N*pcsz), screen->chan, 0, 0);
799 	bbmask = allocimage(display, Rect(0,0,N*pcsz,N*pcsz), GREY1, 0, 0);
800 	bb2 = allocimage(display, Rect(0,0,N*pcsz,N*pcsz+DY), screen->chan, 0, 0);
801 	bb2mask = allocimage(display, bb2->r, GREY1, 0, 0);
802 	if(bb==0 || bbmask==0 || bb2==0 || bb2mask==0)
803 		sysfatal("allocimage fail (bb)");
804 	draw(screen, screen->r, display->white, nil, ZP);
805 	drawboard();
806 	setpiece(piece);
807 	if(piece)
808 		drawpiece();
809 	lastmx = movemouse();
810 	newscreen = 1;
811 	flushimage(display, 1);
812 }
813 
814 void
usage(void)815 usage(void)
816 {
817 	fprint(2, "usage: %s\n", argv0);
818 	exits("usage");
819 }
820 
821 void
threadmain(int argc,char * argv[])822 threadmain(int argc, char *argv[])
823 {
824 	Image *tb;
825 	char buf[200];
826 	int i, scores;
827 	long starttime, endtime;
828 
829 	ARGBEGIN{
830 	default:
831 		usage();
832 	}ARGEND
833 	if(argc)
834 		usage();
835 
836 	suspended = 0;
837 	setparms();
838 	snprint(buf, sizeof(buf), "%ds", N);
839 	initdraw(0, 0, buf);
840 	mousectl = initmouse(nil, display->image);	/* BUG? */
841 	if(mousectl == nil)
842 		sysfatal("[45]s: mouse init failed: %r");
843 	kbdctl = initkeyboard(nil);	/* BUG? */
844 	if(kbdctl == nil)
845 		sysfatal("[45]s: keyboard init failed: %r");
846 	starttime = time(0);
847 	srand(starttime);
848 	snprint(buf, sizeof(buf), "/sys/games/lib/%dscores", N);
849 	scores = open(buf, OWRITE);
850 	if(scores < 0)
851 		sysfatal("can't open %s: %r", buf);
852 	tb = 0;
853 	if(screen->depth < 3){
854 		tb = allocimage(display, Rect(0,0,16,16), 0, 1, -1);
855 		if(tb == 0)
856 			sysfatal("allocimage fail (tb)");
857 	}
858 	for(i = 0; i<NCOL; i++){
859 		tx[i] = allocimage(display, Rect(0, 0, 16, 16), screen->chan, 1, txpix[i]);
860 		if(tx[i] == 0)
861 			sysfatal("allocimage fail (tx)");
862 		if(screen->depth < 3){
863 			loadimage(tb, tb->r, txbits[i], 32);
864 			draw(tx[i], tx[i]->r, tb, nil, ZP);
865 		}
866 	}
867 	if(tb != 0)
868 		freeimage(tb);
869 
870 	whitemask = allocimage(display, Rect(0,0,1,1), CMAP8, 1, setalpha(DWhite, 0x7F));
871 	if(whitemask==0)
872 		sysfatal("allocimage fail (whitemask)");
873 
874 	threadsetname("4s-5s");
875 	timerc= chancreate(sizeof(int), 0);
876 	proccreate(timerproc, timerc, 1024);
877 	suspc= chancreate(sizeof(int), 0);
878 	mousec= chancreate(sizeof(Mouse), 0);
879 	kbdc= chancreate(sizeof(Rune), 0);
880 	threadcreate(suspproc, nil, 1024);
881 	points = 0;
882 	memset(board, 0, sizeof(board));
883 	redraw(0);
884 	if(play()){
885 		endtime = time(0);
886 		fprint(scores, "%ld\t%s\t%lud\t%ld\n",
887 			points, getuser(), starttime, endtime-starttime);
888 	}
889 	threadexitsall(nil);
890 	exits(0);
891 }
892