]> git.ozlabs.org Git - ponghero.git/blob - prpong.c
ddf4afbde1e61f97e44083d4b86d74f75fa2ae63
[ponghero.git] / prpong.c
1 #define _GNU_SOURCE
2 #include <math.h>
3 #include <err.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <stdbool.h>
7 #include <sys/time.h>
8 #include <time.h>
9 #include <limits.h>
10 #include "stdrusty.h"
11 #include "list/list.h"
12 #include "talloc/talloc.h"
13 #include "container_of/container_of.h"
14 #include <SDL/SDL.h>
15 #include <SDL/SDL_image.h>
16 #include <cwiid.h>
17 #include <assert.h>
18 #include <unistd.h>
19
20 #define EXPIRY_SECS 3
21
22 #define MAX_X CWIID_IR_X_MAX
23 #define MAX_Y CWIID_IR_Y_MAX
24
25 #define LEFT_PLAYER 1
26 #define RIGHT_PLAYER 2
27
28 static LIST_HEAD(objects);
29
30 struct coord
31 {
32         double x;
33         double y;
34 };
35
36 /* Obtained from http://tog.acm.org/GraphicsGems/gemsiii/insectc.c */
37 /* Faster Line Segment Intersection   */
38 /* Franklin Antonio                   */
39 static bool lines_intersect(const struct coord *start1,
40                             const struct coord *start2,
41                             const struct coord *start3,
42                             const struct coord *start4,
43                             struct coord *intersect)
44 {
45         double Ax,Bx,Cx,Ay,By,Cy,d,e,f,num;
46         struct coord lo, hi;
47
48         Ax = start2->x - start1->x;
49         Bx = start3->x - start4->x;
50
51         if(Ax<0) {                                      /* X bound box test*/
52                 lo.x = start2->x; hi.x = start1->x;
53         } else {
54                 hi.x = start2->x; lo.x = start1->x;
55         }
56         if(Bx>0) {
57                 if(hi.x < start4->x || start3->x < lo.x) return false;
58         } else {
59                 if(hi.x < start3->x || start4->x < lo.x) return false;
60         }
61
62         Ay = start2->y - start1->y;
63         By = start3->y - start4->y;
64
65         if(Ay<0) {                                      /* Y bound box test*/
66                 lo.y = start2->y; hi.y = start1->y;
67         } else {
68                 hi.y = start2->y; lo.y = start1->y;
69         }
70         if(By>0) {
71                 if(hi.y < start4->y || start3->y < lo.y) return false;
72         } else {
73                 if(hi.y < start3->y || start4->y < lo.y) return false;
74         }
75
76
77         Cx = start1->x - start3->x;
78         Cy = start1->y - start3->y;
79         d = By*Cx - Bx*Cy;                              /* alpha numerator*/
80         f = Ay*Bx - Ax*By;                              /* both denominator*/
81         if (f>0) {                                      /* alpha tests*/
82                 if (d < 0 || d > f) return false;
83         } else {
84                 if (d > 0 || d < f) return false;
85         }
86
87         e = Ax*Cy - Ay*Cx;                              /* beta numerator*/
88         if (f > 0) {                                    /* beta tests*/
89                 if (e < 0 || e > f) return false;
90         } else {
91                 if (e > 0 || e < f) return false;
92         }
93
94 /*compute intersection coordinates*/
95         /* Don't worry about tiny values of f (< 1/2 pixel across screen) */
96         if (fabs(f) < 1.0 / (2.0 * MAX_X * MAX_Y))
97                 return false;
98
99         num = d*Ax;                                     /* numerator */
100         intersect->x = start1->x + num / f;             /* intersection x */
101
102         num = d*Ay;
103         intersect->y = start1->y + num / f;             /* intersection y */
104
105         return true;
106 }
107
108 // A set of very useful macros that you will find in most
109 // code that I write whether I use them in a program or
110 // not.
111
112 #define abs(a) (((a)<0) ? -(a) : (a))
113 #define sign(a) (((a)<0) ? -1 : (a)>0 ? 1 : 0)
114
115 // The following code implements a Bresenham line drawing
116 // algorithm. There are 4 separate routines each optimized
117 // for one of the four pixel depths supported by SDL. SDL
118 // support many pixel formats, but it only support 8, 16,
119 // 24, and 32 bit pixels.
120
121 //----------------------------------------------------------
122
123 // Draw lines in 8 bit surfaces.
124
125 static void line8(SDL_Surface *s, 
126                   int x1, int y1, 
127                   int x2, int y2, 
128                   Uint32 color)
129 {
130   int d;
131   int x;
132   int y;
133   int ax;
134   int ay;
135   int sx;
136   int sy;
137   int dx;
138   int dy;
139
140   Uint8 *lineAddr;
141   Sint32 yOffset;
142
143   dx = x2 - x1;  
144   ax = abs(dx) << 1;  
145   sx = sign(dx);
146
147   dy = y2 - y1;  
148   ay = abs(dy) << 1;  
149   sy = sign(dy);
150   yOffset = sy * s->pitch;
151
152   x = x1;
153   y = y1;
154
155   lineAddr = ((Uint8 *)(s->pixels)) + (y * s->pitch);
156   if (ax>ay)
157   {                      /* x dominant */
158     d = ay - (ax >> 1);
159     for (;;)
160     {
161       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
162         *(lineAddr + x) = (Uint8)color;
163
164       if (x == x2)
165       {
166         return;
167       }
168       if (d>=0)
169       {
170         y += sy;
171         lineAddr += yOffset;
172         d -= ax;
173       }
174       x += sx;
175       d += ay;
176     }
177   }
178   else
179   {                      /* y dominant */
180     d = ax - (ay >> 1);
181     for (;;)
182     {
183       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
184         *(lineAddr + x) = (Uint8)color;
185
186       if (y == y2)
187       {
188         return;
189       }
190       if (d>=0) 
191       {
192         x += sx;
193         d -= ay;
194       }
195       y += sy;
196       lineAddr += yOffset;
197       d += ax;
198     }
199   }
200 }
201
202 //----------------------------------------------------------
203
204 // Draw lines in 16 bit surfaces. Note that this code will
205 // also work on 15 bit surfaces.
206
207 static void line16(SDL_Surface *s, 
208                    int x1, int y1, 
209                    int x2, int y2, 
210                    Uint32 color)
211 {
212   int d;
213   int x;
214   int y;
215   int ax;
216   int ay;
217   int sx;
218   int sy;
219   int dx;
220   int dy;
221
222   Uint8 *lineAddr;
223   Sint32 yOffset;
224
225   dx = x2 - x1;  
226   ax = abs(dx) << 1;  
227   sx = sign(dx);
228
229   dy = y2 - y1;  
230   ay = abs(dy) << 1;  
231   sy = sign(dy);
232   yOffset = sy * s->pitch;
233
234   x = x1;
235   y = y1;
236
237   lineAddr = ((Uint8 *)s->pixels) + (y * s->pitch);
238   if (ax>ay)
239   {                      /* x dominant */
240     d = ay - (ax >> 1);
241     for (;;)
242     {
243       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
244         *((Uint16 *)(lineAddr + (x << 1))) = (Uint16)color;
245
246       if (x == x2)
247       {
248         return;
249       }
250       if (d>=0)
251       {
252         y += sy;
253         lineAddr += yOffset;
254         d -= ax;
255       }
256       x += sx;
257       d += ay;
258     }
259   }
260   else
261   {                      /* y dominant */
262     d = ax - (ay >> 1);
263     for (;;)
264     {
265       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
266         *((Uint16 *)(lineAddr + (x << 1))) = (Uint16)color;
267
268       if (y == y2)
269       {
270         return;
271       }
272       if (d>=0) 
273       {
274         x += sx;
275         d -= ay;
276       }
277       y += sy;
278       lineAddr += yOffset;
279       d += ax;
280     }
281   }
282 }
283
284 //----------------------------------------------------------
285
286 // Draw lines in 24 bit surfaces. 24 bit surfaces require
287 // special handling because the pixels don't fall on even
288 // address boundaries. Instead of being able to store a
289 // single byte, word, or long you have to store 3
290 // individual bytes. As a result 24 bit graphics is slower
291 // than the other pixel sizes.
292
293 static void line24(SDL_Surface *s, 
294                    int x1, int y1, 
295                    int x2, int y2, 
296                    Uint32 color)
297 {
298   int d;
299   int x;
300   int y;
301   int ax;
302   int ay;
303   int sx;
304   int sy;
305   int dx;
306   int dy;
307
308   Uint8 *lineAddr;
309   Sint32 yOffset;
310
311 #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
312   color <<= 8;
313 #endif
314
315   dx = x2 - x1;  
316   ax = abs(dx) << 1;  
317   sx = sign(dx);
318
319   dy = y2 - y1;  
320   ay = abs(dy) << 1;  
321   sy = sign(dy);
322   yOffset = sy * s->pitch;
323
324   x = x1;
325   y = y1;
326
327   lineAddr = ((Uint8 *)(s->pixels)) + (y * s->pitch);
328   if (ax>ay)
329   {                      /* x dominant */
330     d = ay - (ax >> 1);
331     for (;;)
332     {
333       Uint8 *p = (lineAddr + (x * 3));
334       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
335         memcpy(p, &color, 3);
336
337       if (x == x2)
338       {
339         return;
340       }
341       if (d>=0)
342       {
343         y += sy;
344         lineAddr += yOffset;
345         d -= ax;
346       }
347       x += sx;
348       d += ay;
349     }
350   }
351   else
352   {                      /* y dominant */
353     d = ax - (ay >> 1);
354     for (;;)
355     {
356       Uint8 *p = (lineAddr + (x * 3));
357       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
358         memcpy(p, &color, 3);
359
360       if (y == y2)
361       {
362         return;
363       }
364       if (d>=0) 
365       {
366         x += sx;
367         d -= ay;
368       }
369       y += sy;
370       lineAddr += yOffset;
371       d += ax;
372     }
373   }
374 }
375
376 //----------------------------------------------------------
377
378 // Draw lines in 32 bit surfaces. Note that this routine
379 // ignores alpha values. It writes them into the surface
380 // if they are included in the pixel, but does nothing
381 // else with them.
382
383 static void line32(SDL_Surface *s, 
384                    int x1, int y1, 
385                    int x2, int y2, 
386                    Uint32 color)
387 {
388   int d;
389   int x;
390   int y;
391   int ax;
392   int ay;
393   int sx;
394   int sy;
395   int dx;
396   int dy;
397
398   Uint8 *lineAddr;
399   Sint32 yOffset;
400
401   dx = x2 - x1;  
402   ax = abs(dx) << 1;  
403   sx = sign(dx);
404
405   dy = y2 - y1;  
406   ay = abs(dy) << 1;  
407   sy = sign(dy);
408   yOffset = sy * s->pitch;
409
410   x = x1;
411   y = y1;
412
413   lineAddr = ((Uint8 *)(s->pixels)) + (y * s->pitch);
414   if (ax>ay)
415   {                      /* x dominant */
416     d = ay - (ax >> 1);
417     for (;;)
418     {
419       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
420         *((Uint32 *)(lineAddr + (x << 2))) = (Uint32)color;
421
422       if (x == x2)
423       {
424         return;
425       }
426       if (d>=0)
427       {
428         y += sy;
429         lineAddr += yOffset;
430         d -= ax;
431       }
432       x += sx;
433       d += ay;
434     }
435   }
436   else
437   {                      /* y dominant */
438     d = ax - (ay >> 1);
439     for (;;)
440     {
441       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
442         *((Uint32 *)(lineAddr + (x << 2))) = (Uint32)color;
443
444       if (y == y2)
445       {
446         return;
447       }
448       if (d>=0) 
449       {
450         x += sx;
451         d -= ay;
452       }
453       y += sy;
454       lineAddr += yOffset;
455       d += ax;
456     }
457   }
458 }
459
460 //----------------------------------------------------------
461
462 // Examine the depth of a surface and select a line
463 // drawing routine optimized for the bytes/pixel of the
464 // surface.
465
466 static void line(SDL_Surface *s, 
467                  int x1, int y1, 
468                  int x2, int y2, 
469                  Uint32 color)
470 {
471   switch (s->format->BytesPerPixel)
472   {
473   case 1:
474     line8(s, x1, y1, x2, y2, color);
475     break;
476   case 2:
477     line16(s, x1, y1, x2, y2, color);
478     break;
479   case 3:
480     line24(s, x1, y1, x2, y2, color);
481     break;
482   case 4:
483     line32(s, x1, y1, x2, y2, color);
484     break;
485   }
486 }
487
488 /* Bounding box to be updated. */
489 static bool needs_update;
490 static struct coord start_update, end_update;
491
492 struct object
493 {
494         /* List of all objects. */
495         struct list_node list;
496         /* Bounding box. */
497         struct coord start, end;
498
499         void (*redraw)(SDL_Surface *s, struct object *me);
500 };
501
502 struct ball
503 {
504         struct object object;
505
506         struct coord pos;
507         struct coord move;
508         SDL_Surface *image;
509 };
510
511 struct line_segment
512 {
513         struct object object;
514
515         struct coord start, end;
516         struct list_node list;
517         bool ignore;
518         struct timeval expires;
519         /* Does someone score if they hit this line ? */
520         struct score *score;
521 };
522
523 struct score
524 {
525         struct object object;
526
527         SDL_Surface *image;
528         unsigned int value;
529         SDL_Surface *won;
530 };
531
532 static void thick_line(SDL_Surface *s, const struct line_segment *l,
533                        unsigned color)
534 {
535         struct line_segment adj;
536         int x, y, minx = MAX_X, miny = MAX_Y, maxx = 0, maxy = 0;
537
538         SDL_LockSurface(s);
539
540         /* Cheap hack */
541         for (x = -1; x < 2; x++) {
542                 for (y = -1; y < 2; y++) {
543                         adj.start.x = l->start.x + x;
544                         adj.end.x = l->end.x + x;
545                         adj.start.y = l->start.y + y;
546                         adj.end.y = l->end.y + y;
547
548                         if (adj.start.x < minx)
549                                 minx = adj.start.x;
550                         if (adj.start.x > maxx)
551                                 maxx = adj.start.x;
552                         if (adj.start.y < miny)
553                                 miny = adj.start.y;
554                         if (adj.start.y > maxy)
555                                 maxy = adj.start.y;
556                         if (adj.end.x < minx)
557                                 minx = adj.end.x;
558                         if (adj.end.x > maxx)
559                                 maxx = adj.end.x;
560                         if (adj.end.y < miny)
561                                 miny = adj.end.y;
562                         if (adj.end.y > maxy)
563                                 maxy = adj.end.y;
564
565                         line(s, adj.start.x, adj.start.y, adj.end.x, adj.end.y,
566                              color);
567                 }
568         }
569
570         if (minx < 0)
571                 minx = 0;
572         if (miny < 0)
573                 miny = 0;
574         if (maxx >= MAX_X)
575                 maxx = MAX_X-1;
576         if (maxy >= MAX_Y)
577                 maxy = MAX_Y-1;
578         SDL_UpdateRect(s, minx, miny, maxx-minx, maxy-miny);
579         SDL_UnlockSurface(s);
580 }
581
582 static bool intersect(const struct coord *c1, const struct coord *c2,
583                       const struct line_segment *seg, struct coord *intersect)
584 {
585         return lines_intersect(c1, c2, &seg->start, &seg->end, intersect);
586 }
587
588 static double dist(const struct coord *c1, const struct coord *c2)
589 {
590         double x = (c1->x - c2->x), y = (c1->y - c2->y);
591         return sqrt(x * x + y * y);
592 }
593
594 static unsigned rad_to_deg(double rad)
595 {
596         return ((unsigned)(rad / M_PI * 180) + 360) % 360;
597 }
598
599 static void bounce(struct ball *ball, const struct line_segment *line,
600                    struct coord *move, double increase_speed)
601 {
602         double len, speed, lang, iang, oang;
603         struct coord isect, new;
604
605         new.x = ball->pos.x + move->x;
606         new.y = ball->pos.y + move->y;
607
608         /* How far were we supposed to move ball? */
609         len = sqrt(move->x * move->x + move->y * move->y);
610
611         /* How far is it to intersection? */
612         if (!intersect(&ball->pos, &new, line, &isect))
613                 errx(1, "No intersection any more?\n");;
614         len -= dist(&ball->pos, &isect);
615
616         /* Move ball to intersection. */
617         ball->pos = isect;
618         printf("ball is now at %f,%f\n", ball->pos.x, ball->pos.y);
619
620         /* Outgoing angle = 2 * line angle - incident angle. */
621         lang = atan2(line->end.x - line->start.x, line->end.y - line->start.y);
622         iang = atan2(ball->move.x, ball->move.y);
623         oang = 2 * lang - iang, 2*M_PI;
624         printf("lang = %u, iang = %u, oang=%u\n", 
625                rad_to_deg(lang),
626                rad_to_deg(iang),
627                rad_to_deg(oang));
628
629         /* Set new direction for ball, at slightly increased speed */
630         speed = sqrt(ball->move.x * ball->move.x + ball->move.y * ball->move.y);
631         speed += increase_speed;
632         ball->move.x = sin(oang) * speed;
633         ball->move.y = cos(oang) * speed;
634
635         /* Set move. */
636         move->x = sin(oang) * len;
637         move->y = cos(oang) * len;
638         printf("len = %f, move = %f,%f\n", len, move->x, move->y);
639 }
640
641 static struct line_segment border[] = {
642         { .start = { 0, 0, }, .end = { MAX_X-1, 0 } },
643         { .start = { MAX_X-1, 0, }, .end = { MAX_X-1, MAX_Y-1 } },
644         { .start = { MAX_X-1, MAX_Y-1, }, .end = { 0, MAX_Y-1 } },
645         { .start = { 0, MAX_Y-1, }, .end = { 0, 0 } },
646 };
647
648 static inline float deg_to_rad(float degrees)
649 {
650         return degrees * M_PI / 180;
651 }
652
653 static SDL_Surface *sub_surface(SDL_Surface *screen, int w, int h)
654 {
655         return SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
656                                     screen->format->BitsPerPixel,
657                                     screen->format->Rmask,
658                                     screen->format->Gmask,
659                                     screen->format->Bmask,
660                                     screen->format->Amask);
661 }
662
663 static SDL_Surface *ball_surface(SDL_Surface *screen)
664 {
665         SDL_Surface *ball;
666
667         /* Just like the screen surface. */
668         ball = sub_surface(screen, 5, 5);
669
670         /* Lock the surface */
671         SDL_LockSurface(ball);
672
673         /* Black, white corners. */
674         memset(ball->pixels, 0x00, ball->pitch * 5);
675         line(ball, 0, 0, 0, 0, 0xFFFFFFFF);
676         line(ball, 4, 0, 4, 0, 0xFFFFFFFF);
677         line(ball, 0, 4, 0, 4, 0xFFFFFFFF);
678         line(ball, 4, 4, 4, 4, 0xFFFFFFFF);
679         SDL_UnlockSurface(ball);
680
681         return ball;
682 }
683
684 static void clear_ignore(struct list_head *lines)
685 {
686         struct line_segment *i;
687         printf("Unignoring...\n");
688         list_for_each(lines, i, list)
689                 i->ignore = false;
690 }
691
692 static uint16_t *find_valid_point(struct cwiid_state *state)
693 {
694         unsigned int i;
695
696         for (i = 0; i < CWIID_IR_SRC_COUNT; i++) {
697                 if (state->ir_src[i].valid)
698                         return state->ir_src[i].pos;
699         }
700         return NULL;
701 }
702
703 static void calibrate(SDL_Surface *screen,
704                       cwiid_wiimote_t *wiimote,
705                       struct coord *calib, unsigned x, unsigned y)
706 {
707         uint16_t *pos;
708         struct cwiid_state state;
709         unsigned int last_x = MAX_X, last_y = MAX_Y, count = 0;
710
711         /* Must see it for a full half second. */
712         while (count < 20) {
713                 SDL_Event event;
714
715                 if (cwiid_get_state(wiimote, &state))
716                         errx(1, "No wii state");
717
718                 pos = find_valid_point(&state);
719                 if (!pos || pos[0] != last_x || pos[1] != last_y)
720                         count = 0;
721
722                 if (pos) {
723                         last_x = pos[0];
724                         last_y = pos[1];
725                         count++;
726                 }
727
728                 SDL_Delay(25);
729                 if (SDL_PollEvent(&event)
730                     && (event.type == SDL_QUIT
731                         || (event.type == SDL_KEYDOWN
732                             && event.key.keysym.sym == SDLK_ESCAPE))) {
733                         SDL_Quit();
734                         exit(0);
735                 }
736         }
737
738         calib->x = last_x;
739         calib->y = last_y;
740         printf("Calibration point: %u,%u\n", last_x, last_y);
741 }
742
743 static bool map_coord(unsigned int x, unsigned int y,
744                       const struct coord calib[],
745                       struct coord *res)
746 {
747         struct coord line_start, line_end;
748         struct coord pos = { x, y }, intersect;
749         double d1, d2;
750
751         /* Calibration layout:
752          *
753          *   0    1
754          *
755          *   3    2
756          *
757          * We figure out the distance to each side, and then map using:
758          *
759          * x = d1 / (d1 + d2) * (rawend - rawstart) + rawstart
760          */
761         line_start.x = 0;
762         line_end.x = MAX_X-1;
763         line_start.y = line_end.y = y;
764
765         if (!lines_intersect(&calib[0], &calib[3], &line_start, &line_end,
766                              &intersect))
767                 return false;
768         d1 = dist(&pos, &intersect);
769         if (!lines_intersect(&calib[1], &calib[2], &line_start, &line_end,
770                              &intersect))
771                 return false;
772         d2 = dist(&pos, &intersect);
773         res->x = d1 / (d1 + d2) * MAX_X;
774
775         line_start.y = 0;
776         line_end.y = MAX_Y-1;
777         line_start.x = line_end.x = x;
778
779         if (!lines_intersect(&calib[0], &calib[1], &line_start, &line_end,
780                              &intersect))
781                 return false;
782         d1 = dist(&pos, &intersect);
783         if (!lines_intersect(&calib[3], &calib[2], &line_start, &line_end,
784                              &intersect))
785                 return false;
786         d2 = dist(&pos, &intersect);
787         res->y = d1 / (d1 + d2) * MAX_Y;
788
789         return true;
790 }
791
792 static void add_update(const struct object *obj)
793 {
794         if (!needs_update) {
795                 start_update = obj->start;
796                 end_update = obj->end;
797                 needs_update = true;
798                 return;
799         }
800         if (obj->start.x < start_update.x)
801                 start_update.x = obj->start.x;
802         if (obj->start.y < start_update.y)
803                 start_update.y = obj->start.y;
804         if (obj->end.x > end_update.x)
805                 end_update.x = obj->end.x;
806         if (obj->end.y > end_update.y)
807                 end_update.y = obj->end.y;
808 }
809
810 static void destroy_object(struct object *object)
811 {
812         list_del(&object->list);
813         add_update(object);
814 }
815
816 static void add_object(struct list_head *list,
817                        struct object *obj,
818                        unsigned int startx, unsigned int starty,
819                        unsigned int width, unsigned int height,
820                        void (*redraw)(SDL_Surface *s, struct object *me))
821 {
822         list_add_tail(list, &obj->list);
823         obj->start.x = startx;
824         obj->start.y = starty;
825         obj->end.x = startx + width;
826         obj->end.y = starty + height;
827         obj->redraw = redraw;
828         add_update(obj);
829 }
830
831 static void redraw_line(SDL_Surface *s, struct object *me)
832 {
833         struct line_segment *l = container_of(me, struct line_segment, object);
834
835         thick_line(s, l, 0);
836 }
837
838 static int destroy_line(struct line_segment *l)
839 {
840         list_del(&l->list);
841         destroy_object(&l->object);
842         return 0;
843 }
844
845 static void line_object_setup(struct list_head *lines,
846                               struct list_head *objects,
847                               struct line_segment *l)
848 {
849         unsigned int tmp, startx, endx, starty, endy;
850
851         list_add_tail(lines, &l->list);
852
853         startx = l->start.x;
854         starty = l->start.y;
855         endx = l->end.x;
856         endy = l->end.y;
857
858         /* Find bounding box */
859         if (startx > endx) {
860                 tmp = endx;
861                 endx = startx;
862                 startx = tmp;
863         }
864         if (starty > endy) {
865                 tmp = endy;
866                 endy = starty;
867                 starty = tmp;
868         }
869         /* Padding for thick lines (beware underflow) */
870         if (startx > 0)
871                 startx--;
872         if (starty > 0)
873                 starty--;
874         endx++;
875         endy++;
876
877         add_object(objects, &l->object, startx, starty,
878                    endx - startx, endy - starty, redraw_line);
879 }
880
881 static void add_line(struct list_head *lines,
882                      unsigned int startx, unsigned int starty,
883                      unsigned int endx, unsigned int endy,
884                      const struct coord calib[])
885 {
886         struct line_segment *l = talloc(NULL, struct line_segment);
887         struct timeval now;
888
889         if (!map_coord(startx, starty, calib, &l->start)
890             || !map_coord(endx, endy, calib, &l->end)) {
891                 talloc_free(l);
892                 return;
893         }
894
895         l->ignore = false;
896         l->score = NULL;
897         gettimeofday(&now, NULL);
898         l->expires = now;
899         l->expires.tv_sec += EXPIRY_SECS;
900
901         line_object_setup(lines, &objects, l);
902         talloc_set_destructor(l, destroy_line);
903 }
904
905 static void redraw_ball(SDL_Surface *s, struct object *me)
906 {
907         struct ball *b = container_of(me, struct ball, object);
908         SDL_Rect rect = { .x = b->pos.x - b->image->w/2,
909                           .y = b->pos.y - b->image->h/2 };
910
911         SDL_BlitSurface(b->image, NULL, s, &rect);
912 }
913
914 static void redraw_score(SDL_Surface *s, struct object *me)
915 {
916         struct score *score = container_of(me, struct score, object);
917         SDL_Rect rect = { .x = score->object.start.x,
918                           .y = score->object.start.y };
919
920         SDL_BlitSurface(score->image, NULL, s, &rect);
921 }
922
923 static SDL_Surface *score(struct score *s)
924 {
925         char filename[40];
926
927         s->value++;
928         SDL_FreeSurface(s->image);
929         sprintf(filename, "images/%u.png", s->value);
930         s->image = IMG_Load(filename);
931         /* No more images?  You won! */
932         if (!s->image)
933                 return s->won;
934
935         add_update(&s->object);
936         return NULL;
937 }
938
939 /* Due to rounding, we exaggerate bounding box by 1 here. */
940 static void do_updates(struct list_head *objects, SDL_Surface *screen)
941 {
942         struct object *i;
943         unsigned int y, startx, starty, endx, endy;
944
945         /* Clip */
946         if (start_update.x <= 0)
947                 startx = 0;
948         else
949                 startx = start_update.x - 1;
950         if (start_update.y <= 0)
951                 starty = 0;
952         else
953                 starty = start_update.y - 1;
954
955         endx = end_update.x+1;
956         endy = end_update.y+1;
957
958         if (endx > screen->w)
959                 endx = screen->w;
960         if (endy > screen->h)
961                 endy = screen->h;
962
963         SDL_LockSurface(screen);
964         /* First we clear the area to white. */
965         for (y = starty; y < endy; y++) {
966                 memset(screen->pixels + y * screen->pitch
967                        + screen->format->BytesPerPixel * startx,
968                        0xFF,
969                        (endx - startx) * screen->format->BytesPerPixel);
970         }
971         SDL_UnlockSurface(screen);
972
973         /* Now redraw everything which overlaps it */
974         list_for_each(objects, i, list) {
975                 if (i->end.x < startx)
976                         continue;
977                 if (i->end.y < starty)
978                         continue;
979                 if (i->start.x > endx)
980                         continue;
981                 if (i->start.y > endy)
982                         continue;
983                 i->redraw(screen, i);
984         }
985
986         SDL_UpdateRect(screen, startx, starty, endx - startx, endy - starty);
987
988         /* Reset bounding box */
989         needs_update = false;
990 }
991
992 static bool same_side(const struct ball *ball, unsigned int x)
993 {
994         return (ball->pos.x >= MAX_X/2) == (x >= MAX_X/2);
995 }
996
997 int main(int argc, char *argv[])
998 {
999         struct ball ball;
1000         SDL_Surface *screen;
1001         Uint8  video_bpp;
1002         Uint32 videoflags;
1003         unsigned int i, time_since_last_ir = INT_MAX;
1004         cwiid_wiimote_t *wiimote;
1005         struct cwiid_state state;
1006         struct coord calib[4];
1007         bdaddr_t addr = *BDADDR_ANY;
1008         bool mouse = false, needs_calibration = true, drawing = false;
1009         LIST_HEAD(lines);
1010         struct cwiid_ir_src *ir, last_ir = { .valid = 0 };
1011         struct score left, right;
1012
1013         videoflags = SDL_SWSURFACE;
1014
1015         if (argv[1] && streq(argv[1], "--fullscreen")) {
1016                 videoflags |= SDL_FULLSCREEN;
1017                 argc--;
1018                 argv++;
1019         }
1020
1021         if (argv[1] && strstarts(argv[1], "--calib=")) {
1022                 needs_calibration = false;
1023                 if (sscanf(argv[1], "--calib=%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
1024                            &calib[0].x, &calib[0].y,
1025                            &calib[1].x, &calib[1].y,
1026                            &calib[2].x, &calib[2].y,
1027                            &calib[3].x, &calib[3].y) != 8)
1028                         errx(1, "Not enough calibration points");
1029                 argc--;
1030                 argv++;
1031         }
1032
1033         if (argv[1] && streq(argv[1], "--mouse")) {
1034                 mouse = true;
1035                 needs_calibration = false;
1036                 calib[0].x = calib[0].y = 0;
1037                 calib[1].x = MAX_X - 1;
1038                 calib[1].y = 0;
1039                 calib[2].x = MAX_X - 1;
1040                 calib[2].y = MAX_Y - 1;
1041                 calib[3].x = 0;
1042                 calib[3].y = MAX_Y - 1;
1043                 argc--;
1044                 argv++;
1045         }
1046
1047         if (argc != 2)
1048                 errx(1, "Usage: %s [--fullscreen] [--calib=x,y,x,y,x,y,x,y] [--mouse] speed\n",
1049                         argv[0]);
1050
1051         /* Initialize SDL */
1052         if (SDL_Init(SDL_INIT_VIDEO) < 0) {
1053                 fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
1054                 return(1);
1055         }
1056
1057         video_bpp = 0;
1058
1059         if (!mouse) {
1060                 printf("Put Wiimote in discoverable mode (press 1+2)\n");
1061                 wiimote = cwiid_open(&addr, 0);
1062                 if (!wiimote)
1063                         errx(1, "Can't find the Wiimote");
1064
1065                 if (cwiid_set_rpt_mode(wiimote, CWIID_RPT_IR))
1066                         errx(1, "Can't set IR repeat mode");
1067         }
1068
1069         if ((screen=SDL_SetVideoMode(MAX_X,MAX_Y,video_bpp,videoflags)) == NULL) {
1070                 errx(1, "Couldn't set %dx%dx%d video mode: %s",
1071                      MAX_X, MAX_Y,video_bpp, SDL_GetError());
1072         }
1073
1074         /* Set the surface pixels and refresh! */
1075         if (SDL_LockSurface(screen) < 0) {
1076                 errx(1, "Couldn't lock the display surface: %s",
1077                      SDL_GetError());
1078         }
1079         /* White. */
1080         memset(screen->pixels, 0xFF, screen->pitch * screen->h);
1081
1082         SDL_UnlockSurface(screen);
1083         SDL_UpdateRect(screen, 0, 0, 0, 0);
1084
1085         if (needs_calibration) {
1086                 /* Calibration */
1087                 calibrate(screen, wiimote, &calib[0], 0, 0);
1088                 calibrate(screen, wiimote, &calib[1], MAX_X - 1, 0);
1089                 calibrate(screen, wiimote, &calib[2], MAX_X - 1, MAX_Y - 1);
1090                 calibrate(screen, wiimote, &calib[3], 0, MAX_Y - 1);
1091         }
1092
1093         /* Create borders, put them in list. */
1094         for (i = 0; i < ARRAY_SIZE(border); i++) {
1095                 border[i].ignore = false;
1096                 border[i].expires.tv_sec = LONG_MAX;
1097                 border[i].expires.tv_usec = 0;
1098                 line_object_setup(&lines, &objects, &border[i]);
1099         }
1100
1101         /* Set up scores. */
1102         left.value = right.value = 0;
1103         left.image = IMG_Load("images/0.png");
1104         left.won = IMG_Load("images/left-won.png");
1105         right.image = IMG_Load("images/0.png");
1106         right.won = IMG_Load("images/right-won.png");
1107         if (!left.image || !right.image || !left.won || !right.won)
1108                 err(1, "Opening score images");
1109
1110         add_object(&objects, &left.object,
1111                    MAX_X/6 - left.image->w/2, 10,
1112                    left.image->w, left.image->h, redraw_score);
1113         add_object(&objects, &right.object,
1114                    MAX_X - MAX_X/6 - left.image->w/2, 10,
1115                    left.image->w, left.image->h, redraw_score);
1116
1117         /* Hit right border = score to left, and vice versa. */
1118         border[1].score = &left;
1119         border[3].score = &right;
1120
1121         srandom(time(NULL));
1122         ball.pos.x = MAX_X/2;
1123         ball.pos.y = MAX_Y/2;
1124         ball.move.x = (random() % 2 ? -1 : 1) * atoi(argv[1]);
1125         ball.move.y = 0;
1126         ball.image = ball_surface(screen);
1127         add_object(&objects, &ball.object,
1128                    ball.pos.x - ball.image->w/2,
1129                    ball.pos.y - ball.image->h/2,
1130                    ball.image->w, ball.image->h, redraw_ball);
1131
1132         for (;;) {
1133                 struct coord new, isect, move = ball.move;
1134                 struct line_segment *best_l, *l, *next;
1135                 double best_d;
1136                 SDL_Event event;
1137                 struct timeval now;
1138                 bool ignored = false;
1139
1140                 gettimeofday(&now, NULL);
1141
1142                 /* Expire lines */
1143                 list_for_each_safe(&lines, l, next, list) {
1144                         if (timercmp(&l->expires, &now, <))
1145                                 talloc_free(l);
1146                 }
1147
1148         again:
1149                 best_d = MAX_X + MAX_Y;
1150                 best_l = NULL;
1151                 new.x = ball.pos.x + move.x;
1152                 new.y = ball.pos.y + move.y;
1153
1154                 list_for_each(&lines, l, list) {
1155                         if (!l->ignore && intersect(&ball.pos, &new, l, &isect)) {
1156                                 double d;
1157                                 d = dist(&ball.pos, &isect);
1158                                 if (d < best_d) {
1159                                         best_l = l;
1160                                         best_d = d;
1161                                 }
1162                         }
1163                 }
1164
1165                 if (best_l) {
1166                         printf("Ball bouncing off (%f,%f %f,%f)!\n",
1167                                best_l->start.x, best_l->start.y,
1168                                best_l->end.x, best_l->end.y);
1169                         /* Only increase speed on *first* bounce, to avoid
1170                          * mega speed increases. */
1171                         bounce(&ball, best_l, &move, ignored ? 0.0 : 0.1);
1172
1173                         /* If we moved, stop ignoring lines. */
1174                         if (ignored && (move.x > 0.001 || move.y > 0.001))
1175                                 clear_ignore(&lines);
1176
1177                         /* Don't hit the same line twice. */
1178                         printf("Ignoring that line\n");
1179                         best_l->ignore = ignored = true;
1180                         if (best_l->score) {
1181                                 SDL_Surface *won = score(best_l->score);
1182                                 if (won) {
1183                                         SDL_Rect rect;
1184
1185                                         rect.x = MAX_X/2 - won->w/2;
1186                                         rect.y = MAX_Y/2 - won->h/2;
1187                                         rect.w = won->w;
1188                                         rect.h = won->h;
1189                                         SDL_BlitSurface(won, NULL, screen,
1190                                                         &rect);
1191                                         SDL_UpdateRect(screen,
1192                                                        rect.x, rect.y,
1193                                                        rect.w, rect.h);
1194                                         SDL_Delay(3000);
1195                                         exit(0);
1196                                 }
1197                         }
1198                         goto again;
1199                 }
1200
1201                 if (ignored && (move.x > 0.001 || move.y > 0.001)) {
1202                         clear_ignore(&lines);
1203                         printf("Moving by %f,%f to %f,%f\n",
1204                                move.x, move.y, new.x, new.y);
1205                 }
1206
1207                 /* We also need to redraw under old ball. */
1208                 add_update(&ball.object);
1209
1210                 /* Move ball. */
1211                 ball.pos = new;
1212                 ball.object.start.x = ball.pos.x - ball.image->w/2;
1213                 ball.object.start.y = ball.pos.y - ball.image->h/2;
1214                 ball.object.end.x = ball.pos.x + ball.image->w/2;
1215                 ball.object.end.y = ball.pos.y + ball.image->h/2;
1216
1217                 /* Need to draw under new ball. */
1218                 add_update(&ball.object);
1219
1220                 /* Clears, redraws and resets the updates */
1221                 do_updates(&objects, screen);
1222
1223                 SDL_Delay(25);
1224
1225                 while (SDL_PollEvent(&event)) {
1226                         if (event.type == SDL_QUIT
1227                             || (event.type == SDL_KEYDOWN
1228                                 && event.key.keysym.sym == SDLK_ESCAPE)) {
1229                                 SDL_Quit();
1230                                 return 0;
1231                         }
1232                         if (mouse) {
1233                                 switch (event.type) {
1234                                 case SDL_MOUSEBUTTONDOWN:
1235                                         drawing = true;
1236                                         break;
1237                                 case SDL_MOUSEBUTTONUP:
1238                                         drawing = false;
1239                                         break;
1240                                 case SDL_MOUSEMOTION:
1241                                         if (!drawing ||
1242                                             !same_side(&ball, event.motion.x))
1243                                                 break;
1244
1245                                         add_line(&lines, event.motion.x,
1246                                                  event.motion.y,
1247                                                  event.motion.x
1248                                                  + event.motion.xrel,
1249                                                  event.motion.y
1250                                                  + event.motion.yrel,
1251                                                  calib);
1252                                 }
1253                         }
1254                 }
1255
1256                 if (mouse)
1257                         continue;
1258
1259                 if (cwiid_get_state(wiimote, &state))
1260                         errx(1, "No wii state");
1261
1262                 /* Find biggest state. */
1263                 ir = &state.ir_src[0];
1264                 for (i = 0; i < CWIID_IR_SRC_COUNT; i++) {
1265                         if (!state.ir_src[i].valid)
1266                                 continue;
1267                         /* Only look at dots on same side as ball */
1268                         if (!same_side(&ball, state.ir_src[i].pos[0]))
1269                                 continue;
1270                         if (!ir->valid || state.ir_src[i].size > ir->size)
1271                                 ir = &state.ir_src[0];
1272                 }
1273
1274                 if (ir->valid) {
1275                         /* Give it some slack for missing one or two... */
1276                         if (time_since_last_ir <= 5
1277                             && same_side(&ball, last_ir.pos[0])) {
1278                                 add_line(&lines, last_ir.pos[0],
1279                                          last_ir.pos[1],
1280                                          ir->pos[0],
1281                                          ir->pos[1],
1282                                          calib);
1283                         }
1284                         time_since_last_ir = 0;
1285                         last_ir = *ir;
1286                 }
1287                 time_since_last_ir++;
1288         }
1289 }