]> git.ozlabs.org Git - ponghero.git/blob - prpong.c
Mouse support, and fix calibration to outer edges.
[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 <SDL/SDL.h>
13 #include <cwiid.h>
14 #include <assert.h>
15 #include <unistd.h>
16
17 #define MAX_X CWIID_IR_X_MAX
18 #define MAX_Y CWIID_IR_Y_MAX
19
20 struct coord
21 {
22         double x;
23         double y;
24 };
25
26 /* Obtained from http://tog.acm.org/GraphicsGems/gemsiii/insectc.c */
27 /* Faster Line Segment Intersection   */
28 /* Franklin Antonio                   */
29 static bool lines_intersect(const struct coord *start1,
30                             const struct coord *start2,
31                             const struct coord *start3,
32                             const struct coord *start4,
33                             struct coord *intersect)
34 {
35         double Ax,Bx,Cx,Ay,By,Cy,d,e,f,num;
36         struct coord lo, hi;
37
38         Ax = start2->x - start1->x;
39         Bx = start3->x - start4->x;
40
41         if(Ax<0) {                                      /* X bound box test*/
42                 lo.x = start2->x; hi.x = start1->x;
43         } else {
44                 hi.x = start2->x; lo.x = start1->x;
45         }
46         if(Bx>0) {
47                 if(hi.x < start4->x || start3->x < lo.x) return false;
48         } else {
49                 if(hi.x < start3->x || start4->x < lo.x) return false;
50         }
51
52         Ay = start2->y - start1->y;
53         By = start3->y - start4->y;
54
55         if(Ay<0) {                                      /* Y bound box test*/
56                 lo.y = start2->y; hi.y = start1->y;
57         } else {
58                 hi.y = start2->y; lo.y = start1->y;
59         }
60         if(By>0) {
61                 if(hi.y < start4->y || start3->y < lo.y) return false;
62         } else {
63                 if(hi.y < start3->y || start4->y < lo.y) return false;
64         }
65
66
67         Cx = start1->x - start3->x;
68         Cy = start1->y - start3->y;
69         d = By*Cx - Bx*Cy;                              /* alpha numerator*/
70         f = Ay*Bx - Ax*By;                              /* both denominator*/
71         if (f>0) {                                      /* alpha tests*/
72                 if (d < 0 || d > f) return false;
73         } else {
74                 if (d > 0 || d < f) return false;
75         }
76
77         e = Ax*Cy - Ay*Cx;                              /* beta numerator*/
78         if (f > 0) {                                    /* beta tests*/
79                 if (e < 0 || e > f) return false;
80         } else {
81                 if (e > 0 || e < f) return false;
82         }
83
84 /*compute intersection coordinates*/
85         /* Don't worry about tiny values of f (< 1/2 pixel across screen) */
86         if (fabs(f) < 1.0 / (2.0 * MAX_X * MAX_Y))
87                 return false;
88
89         num = d*Ax;                                     /* numerator */
90         intersect->x = start1->x + num / f;             /* intersection x */
91
92         num = d*Ay;
93         intersect->y = start1->y + num / f;             /* intersection y */
94
95         return true;
96 }
97
98 // A set of very useful macros that you will find in most
99 // code that I write whether I use them in a program or
100 // not.
101
102 #define abs(a) (((a)<0) ? -(a) : (a))
103 #define sign(a) (((a)<0) ? -1 : (a)>0 ? 1 : 0)
104
105 // The following code implements a Bresenham line drawing
106 // algorithm. There are 4 separate routines each optimized
107 // for one of the four pixel depths supported by SDL. SDL
108 // support many pixel formats, but it only support 8, 16,
109 // 24, and 32 bit pixels.
110
111 //----------------------------------------------------------
112
113 // Draw lines in 8 bit surfaces.
114
115 static void line8(SDL_Surface *s, 
116                   int x1, int y1, 
117                   int x2, int y2, 
118                   Uint32 color)
119 {
120   int d;
121   int x;
122   int y;
123   int ax;
124   int ay;
125   int sx;
126   int sy;
127   int dx;
128   int dy;
129
130   Uint8 *lineAddr;
131   Sint32 yOffset;
132
133   dx = x2 - x1;  
134   ax = abs(dx) << 1;  
135   sx = sign(dx);
136
137   dy = y2 - y1;  
138   ay = abs(dy) << 1;  
139   sy = sign(dy);
140   yOffset = sy * s->pitch;
141
142   x = x1;
143   y = y1;
144
145   lineAddr = ((Uint8 *)(s->pixels)) + (y * s->pitch);
146   if (ax>ay)
147   {                      /* x dominant */
148     d = ay - (ax >> 1);
149     for (;;)
150     {
151       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
152         *(lineAddr + x) = (Uint8)color;
153
154       if (x == x2)
155       {
156         return;
157       }
158       if (d>=0)
159       {
160         y += sy;
161         lineAddr += yOffset;
162         d -= ax;
163       }
164       x += sx;
165       d += ay;
166     }
167   }
168   else
169   {                      /* y dominant */
170     d = ax - (ay >> 1);
171     for (;;)
172     {
173       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
174         *(lineAddr + x) = (Uint8)color;
175
176       if (y == y2)
177       {
178         return;
179       }
180       if (d>=0) 
181       {
182         x += sx;
183         d -= ay;
184       }
185       y += sy;
186       lineAddr += yOffset;
187       d += ax;
188     }
189   }
190 }
191
192 //----------------------------------------------------------
193
194 // Draw lines in 16 bit surfaces. Note that this code will
195 // also work on 15 bit surfaces.
196
197 static void line16(SDL_Surface *s, 
198                    int x1, int y1, 
199                    int x2, int y2, 
200                    Uint32 color)
201 {
202   int d;
203   int x;
204   int y;
205   int ax;
206   int ay;
207   int sx;
208   int sy;
209   int dx;
210   int dy;
211
212   Uint8 *lineAddr;
213   Sint32 yOffset;
214
215   dx = x2 - x1;  
216   ax = abs(dx) << 1;  
217   sx = sign(dx);
218
219   dy = y2 - y1;  
220   ay = abs(dy) << 1;  
221   sy = sign(dy);
222   yOffset = sy * s->pitch;
223
224   x = x1;
225   y = y1;
226
227   lineAddr = ((Uint8 *)s->pixels) + (y * s->pitch);
228   if (ax>ay)
229   {                      /* x dominant */
230     d = ay - (ax >> 1);
231     for (;;)
232     {
233       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
234         *((Uint16 *)(lineAddr + (x << 1))) = (Uint16)color;
235
236       if (x == x2)
237       {
238         return;
239       }
240       if (d>=0)
241       {
242         y += sy;
243         lineAddr += yOffset;
244         d -= ax;
245       }
246       x += sx;
247       d += ay;
248     }
249   }
250   else
251   {                      /* y dominant */
252     d = ax - (ay >> 1);
253     for (;;)
254     {
255       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
256         *((Uint16 *)(lineAddr + (x << 1))) = (Uint16)color;
257
258       if (y == y2)
259       {
260         return;
261       }
262       if (d>=0) 
263       {
264         x += sx;
265         d -= ay;
266       }
267       y += sy;
268       lineAddr += yOffset;
269       d += ax;
270     }
271   }
272 }
273
274 //----------------------------------------------------------
275
276 // Draw lines in 24 bit surfaces. 24 bit surfaces require
277 // special handling because the pixels don't fall on even
278 // address boundaries. Instead of being able to store a
279 // single byte, word, or long you have to store 3
280 // individual bytes. As a result 24 bit graphics is slower
281 // than the other pixel sizes.
282
283 static void line24(SDL_Surface *s, 
284                    int x1, int y1, 
285                    int x2, int y2, 
286                    Uint32 color)
287 {
288   int d;
289   int x;
290   int y;
291   int ax;
292   int ay;
293   int sx;
294   int sy;
295   int dx;
296   int dy;
297
298   Uint8 *lineAddr;
299   Sint32 yOffset;
300
301 #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
302   color <<= 8;
303 #endif
304
305   dx = x2 - x1;  
306   ax = abs(dx) << 1;  
307   sx = sign(dx);
308
309   dy = y2 - y1;  
310   ay = abs(dy) << 1;  
311   sy = sign(dy);
312   yOffset = sy * s->pitch;
313
314   x = x1;
315   y = y1;
316
317   lineAddr = ((Uint8 *)(s->pixels)) + (y * s->pitch);
318   if (ax>ay)
319   {                      /* x dominant */
320     d = ay - (ax >> 1);
321     for (;;)
322     {
323       Uint8 *p = (lineAddr + (x * 3));
324       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
325         memcpy(p, &color, 3);
326
327       if (x == x2)
328       {
329         return;
330       }
331       if (d>=0)
332       {
333         y += sy;
334         lineAddr += yOffset;
335         d -= ax;
336       }
337       x += sx;
338       d += ay;
339     }
340   }
341   else
342   {                      /* y dominant */
343     d = ax - (ay >> 1);
344     for (;;)
345     {
346       Uint8 *p = (lineAddr + (x * 3));
347       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
348         memcpy(p, &color, 3);
349
350       if (y == y2)
351       {
352         return;
353       }
354       if (d>=0) 
355       {
356         x += sx;
357         d -= ay;
358       }
359       y += sy;
360       lineAddr += yOffset;
361       d += ax;
362     }
363   }
364 }
365
366 //----------------------------------------------------------
367
368 // Draw lines in 32 bit surfaces. Note that this routine
369 // ignores alpha values. It writes them into the surface
370 // if they are included in the pixel, but does nothing
371 // else with them.
372
373 static void line32(SDL_Surface *s, 
374                    int x1, int y1, 
375                    int x2, int y2, 
376                    Uint32 color)
377 {
378   int d;
379   int x;
380   int y;
381   int ax;
382   int ay;
383   int sx;
384   int sy;
385   int dx;
386   int dy;
387
388   Uint8 *lineAddr;
389   Sint32 yOffset;
390
391   dx = x2 - x1;  
392   ax = abs(dx) << 1;  
393   sx = sign(dx);
394
395   dy = y2 - y1;  
396   ay = abs(dy) << 1;  
397   sy = sign(dy);
398   yOffset = sy * s->pitch;
399
400   x = x1;
401   y = y1;
402
403   lineAddr = ((Uint8 *)(s->pixels)) + (y * s->pitch);
404   if (ax>ay)
405   {                      /* x dominant */
406     d = ay - (ax >> 1);
407     for (;;)
408     {
409       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
410         *((Uint32 *)(lineAddr + (x << 2))) = (Uint32)color;
411
412       if (x == x2)
413       {
414         return;
415       }
416       if (d>=0)
417       {
418         y += sy;
419         lineAddr += yOffset;
420         d -= ax;
421       }
422       x += sx;
423       d += ay;
424     }
425   }
426   else
427   {                      /* y dominant */
428     d = ax - (ay >> 1);
429     for (;;)
430     {
431       if (y >= 0 && y < MAX_Y && x >= 0 && x < MAX_X)
432         *((Uint32 *)(lineAddr + (x << 2))) = (Uint32)color;
433
434       if (y == y2)
435       {
436         return;
437       }
438       if (d>=0) 
439       {
440         x += sx;
441         d -= ay;
442       }
443       y += sy;
444       lineAddr += yOffset;
445       d += ax;
446     }
447   }
448 }
449
450 //----------------------------------------------------------
451
452 // Examine the depth of a surface and select a line
453 // drawing routine optimized for the bytes/pixel of the
454 // surface.
455
456 static void line(SDL_Surface *s, 
457                  int x1, int y1, 
458                  int x2, int y2, 
459                  Uint32 color)
460 {
461   switch (s->format->BytesPerPixel)
462   {
463   case 1:
464     line8(s, x1, y1, x2, y2, color);
465     break;
466   case 2:
467     line16(s, x1, y1, x2, y2, color);
468     break;
469   case 3:
470     line24(s, x1, y1, x2, y2, color);
471     break;
472   case 4:
473     line32(s, x1, y1, x2, y2, color);
474     break;
475   }
476 }
477
478 struct ball
479 {
480         struct coord pos;
481         struct coord move;
482 };
483
484 struct line_segment
485 {
486         struct coord start, end;
487         struct list_node list;
488         bool ignore;
489         struct timeval expires;
490 };
491
492 static void thick_line(SDL_Surface *s, const struct line_segment *l,
493                        unsigned color)
494 {
495         struct line_segment adj;
496         int x, y, minx = MAX_X, miny = MAX_Y, maxx = 0, maxy = 0;
497
498         SDL_LockSurface(s);
499
500         /* Cheap hack */
501         for (x = -1; x < 2; x++) {
502                 for (y = -1; y < 2; y++) {
503                         adj.start.x = l->start.x + x;
504                         adj.end.x = l->end.x + x;
505                         adj.start.y = l->start.y + y;
506                         adj.end.y = l->end.y + y;
507
508                         if (adj.start.x < minx)
509                                 minx = adj.start.x;
510                         if (adj.start.x > maxx)
511                                 maxx = adj.start.x;
512                         if (adj.start.y < miny)
513                                 miny = adj.start.y;
514                         if (adj.start.y > maxy)
515                                 maxy = adj.start.y;
516                         if (adj.end.x < minx)
517                                 minx = adj.end.x;
518                         if (adj.end.x > maxx)
519                                 maxx = adj.end.x;
520                         if (adj.end.y < miny)
521                                 miny = adj.end.y;
522                         if (adj.end.y > maxy)
523                                 maxy = adj.end.y;
524
525                         line(s, adj.start.x, adj.start.y, adj.end.x, adj.end.y,
526                              color);
527                 }
528         }
529
530         if (minx < 0)
531                 minx = 0;
532         if (miny < 0)
533                 miny = 0;
534         if (maxx >= MAX_X)
535                 maxx = MAX_X-1;
536         if (maxy >= MAX_Y)
537                 maxy = MAX_Y-1;
538         SDL_UpdateRect(s, minx, miny, maxx-minx, maxy-miny);
539         SDL_UnlockSurface(s);
540 }
541
542 static bool intersect(const struct coord *c1, const struct coord *c2,
543                       const struct line_segment *seg, struct coord *intersect)
544 {
545         return lines_intersect(c1, c2, &seg->start, &seg->end, intersect);
546 }
547
548 static double dist(const struct coord *c1, const struct coord *c2)
549 {
550         double x = (c1->x - c2->x), y = (c1->y - c2->y);
551         return sqrt(x * x + y * y);
552 }
553
554 static unsigned rad_to_deg(double rad)
555 {
556         return ((unsigned)(rad / M_PI * 180) + 360) % 360;
557 }
558
559 static void bounce(struct ball *ball, const struct line_segment *line,
560                    struct coord *move, double increase_speed)
561 {
562         double len, speed, lang, iang, oang;
563         struct coord isect, new;
564
565         new.x = ball->pos.x + move->x;
566         new.y = ball->pos.y + move->y;
567
568         /* How far were we supposed to move ball? */
569         len = sqrt(move->x * move->x + move->y * move->y);
570
571         /* How far is it to intersection? */
572         if (!intersect(&ball->pos, &new, line, &isect))
573                 errx(1, "No intersection any more?\n");;
574         len -= dist(&ball->pos, &isect);
575
576         /* Move ball to intersection. */
577         ball->pos = isect;
578         printf("ball is now at %f,%f\n", ball->pos.x, ball->pos.y);
579
580         /* Outgoing angle = 2 * line angle - incident angle. */
581         lang = atan2(line->end.x - line->start.x, line->end.y - line->start.y);
582         iang = atan2(ball->move.x, ball->move.y);
583         oang = 2 * lang - iang, 2*M_PI;
584         printf("lang = %u, iang = %u, oang=%u\n", 
585                rad_to_deg(lang),
586                rad_to_deg(iang),
587                rad_to_deg(oang));
588
589         /* Set new direction for ball, at slightly increased speed */
590         speed = sqrt(ball->move.x * ball->move.x + ball->move.y * ball->move.y);
591         speed += increase_speed;
592         ball->move.x = sin(oang) * speed;
593         ball->move.y = cos(oang) * speed;
594
595         /* Set move. */
596         move->x = sin(oang) * len;
597         move->y = cos(oang) * len;
598         printf("len = %f, move = %f,%f\n", len, move->x, move->y);
599 }
600
601 static struct line_segment border[] = {
602         { { 0, 0, }, { MAX_X-1, 0 } },
603         { { MAX_X-1, 0, }, { MAX_X-1, MAX_Y-1 } },
604         { { MAX_X-1, MAX_Y-1, }, { 0, MAX_Y-1 } },
605         { { 0, MAX_Y-1, }, { 0, 0 } },
606 };
607
608 static inline float deg_to_rad(float degrees)
609 {
610         return degrees * M_PI / 180;
611 }
612
613 static SDL_Surface *sub_surface(SDL_Surface *screen, int w, int h)
614 {
615         return SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
616                                     screen->format->BitsPerPixel,
617                                     screen->format->Rmask,
618                                     screen->format->Gmask,
619                                     screen->format->Bmask,
620                                     screen->format->Amask);
621 }
622
623 static SDL_Surface *ball_surface(SDL_Surface *screen)
624 {
625         SDL_Surface *ball;
626
627         /* Just like the screen surface. */
628         ball = sub_surface(screen, 5, 5);
629
630         /* Lock the surface */
631         SDL_LockSurface(ball);
632
633         /* Black, white corners. */
634         memset(ball->pixels, 0x00, ball->pitch * 5);
635         line(ball, 0, 0, 0, 0, 0xFFFFFFFF);
636         line(ball, 4, 0, 4, 0, 0xFFFFFFFF);
637         line(ball, 0, 4, 0, 4, 0xFFFFFFFF);
638         line(ball, 4, 4, 4, 4, 0xFFFFFFFF);
639         SDL_UnlockSurface(ball);
640
641         return ball;
642 }
643
644 static void clear_ignore(struct list_head *lines)
645 {
646         struct line_segment *i;
647         printf("Unignoring...\n");
648         list_for_each(lines, i, list)
649                 i->ignore = false;
650 }
651
652 static uint16_t *find_valid_point(struct cwiid_state *state)
653 {
654         unsigned int i;
655
656         for (i = 0; i < CWIID_IR_SRC_COUNT; i++) {
657                 if (state->ir_src[i].valid)
658                         return state->ir_src[i].pos;
659         }
660         return NULL;
661 }
662
663 static void calibrate(SDL_Surface *screen,
664                       cwiid_wiimote_t *wiimote,
665                       struct coord *calib, unsigned x, unsigned y)
666 {
667         uint16_t *pos;
668         struct cwiid_state state;
669         unsigned int last_x = MAX_X, last_y = MAX_Y, count = 0;
670
671         /* Must see it for a full half second. */
672         while (count < 20) {
673                 SDL_Event event;
674
675                 if (cwiid_get_state(wiimote, &state))
676                         errx(1, "No wii state");
677
678                 pos = find_valid_point(&state);
679                 if (!pos || pos[0] != last_x || pos[1] != last_y)
680                         count = 0;
681
682                 if (pos) {
683                         last_x = pos[0];
684                         last_y = pos[1];
685                         count++;
686                 }
687
688                 SDL_Delay(25);
689                 if (SDL_PollEvent(&event)
690                     && (event.type == SDL_QUIT
691                         || (event.type == SDL_KEYDOWN
692                             && event.key.keysym.sym == SDLK_ESCAPE))) {
693                         SDL_Quit();
694                         exit(0);
695                 }
696         }
697
698         calib->x = last_x;
699         calib->y = last_y;
700         printf("Calibration point: %u,%u\n", last_x, last_y);
701 }
702
703 static bool map_coord(unsigned int x, unsigned int y,
704                       const struct coord calib[],
705                       struct coord *res)
706 {
707         struct coord line_start, line_end;
708         struct coord pos = { x, y }, intersect;
709         double d1, d2;
710
711         /* Calibration layout:
712          *
713          *   0    1
714          *
715          *   3    2
716          *
717          * We figure out the distance to each side, and then map using:
718          *
719          * x = d1 / (d1 + d2) * (rawend - rawstart) + rawstart
720          */
721         line_start.x = 0;
722         line_end.x = MAX_X-1;
723         line_start.y = line_end.y = y;
724
725         if (!lines_intersect(&calib[0], &calib[3], &line_start, &line_end,
726                              &intersect))
727                 return false;
728         d1 = dist(&pos, &intersect);
729         if (!lines_intersect(&calib[1], &calib[2], &line_start, &line_end,
730                              &intersect))
731                 return false;
732         d2 = dist(&pos, &intersect);
733         res->x = d1 / (d1 + d2) * MAX_X;
734
735         line_start.y = 0;
736         line_end.y = MAX_Y-1;
737         line_start.x = line_end.x = x;
738
739         if (!lines_intersect(&calib[0], &calib[1], &line_start, &line_end,
740                              &intersect))
741                 return false;
742         d1 = dist(&pos, &intersect);
743         if (!lines_intersect(&calib[3], &calib[2], &line_start, &line_end,
744                              &intersect))
745                 return false;
746         d2 = dist(&pos, &intersect);
747         res->y = d1 / (d1 + d2) * MAX_Y;
748
749         return true;
750 }
751
752 static struct line_segment *new_line(unsigned int startx, unsigned int starty,
753                                      unsigned int endx, unsigned int endy,
754                                      const struct coord calib[])
755 {
756         struct line_segment *l = malloc(sizeof(*l));
757
758         l->ignore = false;
759         if (!map_coord(startx, starty, calib, &l->start)
760             || !map_coord(endx, endy, calib, &l->end)) {
761                 free(l);
762                 return NULL;
763         }
764         return l;
765 }
766
767
768 int main(int argc, char *argv[])
769 {
770         struct ball ball;
771         SDL_Surface *screen, *ballsur, *savesur = NULL;
772         Uint8  video_bpp;
773         Uint32 videoflags;
774         unsigned int i, time_since_last_ir = INT_MAX;
775         SDL_Rect rect;
776         struct timeval line_life = { 3, 0 };
777         cwiid_wiimote_t *wiimote;
778         struct cwiid_state state;
779         struct coord calib[4];
780         bdaddr_t addr = *BDADDR_ANY;
781         bool mouse = false, needs_calibration = true, drawing = false;
782         LIST_HEAD(lines);
783         struct cwiid_ir_src *ir, last_ir = { .valid = 0 };
784         struct line_segment *n;
785
786         videoflags = SDL_SWSURFACE;
787
788         if (argv[1] && streq(argv[1], "--fullscreen")) {
789                 videoflags |= SDL_FULLSCREEN;
790                 argc--;
791                 argv++;
792         }
793
794         if (argv[1] && strstarts(argv[1], "--calib=")) {
795                 needs_calibration = false;
796                 if (sscanf(argv[1], "--calib=%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
797                            &calib[0].x, &calib[0].y,
798                            &calib[1].x, &calib[1].y,
799                            &calib[2].x, &calib[2].y,
800                            &calib[3].x, &calib[3].y) != 8)
801                         errx(1, "Not enough calibration points");
802                 argc--;
803                 argv++;
804         }
805
806         if (argv[1] && streq(argv[1], "--mouse")) {
807                 mouse = true;
808                 needs_calibration = false;
809                 calib[0].x = calib[0].y = 0;
810                 calib[1].x = MAX_X - 1;
811                 calib[1].y = 0;
812                 calib[2].x = MAX_X - 1;
813                 calib[2].y = MAX_Y - 1;
814                 calib[3].x = 0;
815                 calib[3].y = MAX_Y - 1;
816                 argc--;
817                 argv++;
818         }
819
820         if (argc != 5)
821                 errx(1, "Usage: %s [--fullscreen] [--calib=x,y,x,y,x,y,x,y] [--mouse] ballx bally ballangle ballspeed\n"
822                      "   Where ballangle is 0 for north, 90 for east, etc\n",
823                         argv[0]);
824
825         ball.pos.x = atol(argv[1]);
826         ball.pos.y = atol(argv[2]);
827         ball.move.x = sin(deg_to_rad(atof(argv[3]))) * atol(argv[4]);
828         ball.move.y = cos(deg_to_rad(atof(argv[3]))) * atol(argv[4]);
829         printf("ball move = %f,%f\n", ball.move.x, ball.move.y);
830
831         /* Initialize SDL */
832         if (SDL_Init(SDL_INIT_VIDEO) < 0) {
833                 fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
834                 return(1);
835         }
836
837         video_bpp = 0;
838
839         if (!mouse) {
840                 printf("Put Wiimote in discoverable mode (press 1+2)\n");
841                 wiimote = cwiid_open(&addr, 0);
842                 if (!wiimote)
843                         errx(1, "Can't find the Wiimote");
844
845                 if (cwiid_set_rpt_mode(wiimote, CWIID_RPT_IR))
846                         errx(1, "Can't set IR repeat mode");
847         }
848
849         if ( (screen=SDL_SetVideoMode(MAX_X,MAX_Y,video_bpp,videoflags)) == NULL ) {
850                 errx(1, "Couldn't set %dx%dx%d video mode: %s",
851                      MAX_X, MAX_Y,video_bpp, SDL_GetError());
852         }
853
854         /* Set the surface pixels and refresh! */
855         if ( SDL_LockSurface(screen) < 0 ) {
856                 errx(1, "Couldn't lock the display surface: %s",
857                      SDL_GetError());
858         }
859         /* White. */
860         memset(screen->pixels, 0xFF, screen->pitch * screen->h);
861
862         SDL_UnlockSurface(screen);
863         SDL_UpdateRect(screen, 0, 0, 0, 0);
864
865         if (needs_calibration) {
866                 /* Calibration */
867                 calibrate(screen, wiimote, &calib[0], 0, 0);
868                 calibrate(screen, wiimote, &calib[1], MAX_X - 1, 0);
869                 calibrate(screen, wiimote, &calib[2], MAX_X - 1, MAX_Y - 1);
870                 calibrate(screen, wiimote, &calib[3], 0, MAX_Y - 1);
871         }
872
873         /* Draw borders, put them in list. */
874         for (i = 0; i < ARRAY_SIZE(border); i++) {
875                 border[i].ignore = false;
876                 border[i].expires.tv_sec = LONG_MAX;
877                 border[i].expires.tv_usec = 0;
878                 list_add(&lines, &border[i].list);
879                 thick_line(screen, &border[i], 0);
880         }
881
882         ballsur = ball_surface(screen);
883
884         for (;;) {
885                 struct coord new, isect, move = ball.move;
886                 struct line_segment *best_l, *l, *next;
887                 double best_d;
888                 SDL_Event event;
889                 struct timeval now;
890                 bool ignored = false;
891
892                 gettimeofday(&now, NULL);
893
894                 /* Expire lines */
895                 list_for_each_safe(&lines, l, next, list) {
896                         if (timercmp(&l->expires, &now, <)) {
897                                 printf("Deleting line %p\n", l);
898                                 list_del(&l->list);
899                                 /* FIXME: Undraw properly. */
900                                 thick_line(screen, l, 0xFFFFFFFF);
901                                 free(l);
902                         }
903                 }
904
905         again:
906                 best_d = MAX_X + MAX_Y;
907                 best_l = NULL;
908                 new.x = ball.pos.x + move.x;
909                 new.y = ball.pos.y + move.y;
910
911                 list_for_each(&lines, l, list) {
912                         if (!l->ignore && intersect(&ball.pos, &new, l, &isect)) {
913                                 double d;
914                                 d = dist(&ball.pos, &isect);
915                                 if (d < best_d) {
916                                         best_l = l;
917                                         best_d = d;
918                                 }
919                         }
920                 }
921
922                 if (best_l) {
923                         printf("Ball bouncing off (%f,%f %f,%f)!\n",
924                                best_l->start.x, best_l->start.y,
925                                best_l->end.x, best_l->end.y);
926                         /* Only increase speed on *first* bounce, to avoid
927                          * mega speed increases. */
928                         bounce(&ball, best_l, &move, ignored ? 0.0 : 0.1);
929
930                         /* If we moved, stop ignoring lines. */
931                         if (ignored && (move.x > 0.001 || move.y > 0.001))
932                                 clear_ignore(&lines);
933
934                         /* Don't hit the same line twice. */
935                         printf("Ignoring that line\n");
936                         best_l->ignore = ignored = true;
937                         goto again;
938                 }
939
940                 if (ignored && (move.x > 0.001 || move.y > 0.001)) {
941                         clear_ignore(&lines);
942                         printf("Moving by %f,%f to %f,%f\n",
943                                move.x, move.y, new.x, new.y);
944                 }
945
946                 /* Restore what was there before ball. */
947                 if (savesur) {
948                         SDL_BlitSurface(savesur, NULL, screen, &rect);
949                         SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h);
950                 } else {
951                         savesur = sub_surface(screen, ballsur->w, ballsur->h);
952                 }
953
954                 /* Save away image under new ball. */
955                 rect.w = ballsur->w;
956                 rect.h = ballsur->h;
957                 rect.x = new.x - ballsur->w/2;
958                 rect.y = new.y - ballsur->h/2;
959                 SDL_BlitSurface(screen, &rect, savesur, NULL);
960
961                 /* Draw new ball */
962                 SDL_BlitSurface(ballsur, NULL, screen, &rect);
963                 SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h);
964                 /* That SDL_BlitSurface can crop rect. */
965                 rect.x = new.x - ballsur->w/2;
966                 rect.y = new.y - ballsur->h/2;
967                 ball.pos = new;
968                 SDL_Delay(25);
969
970                 while (SDL_PollEvent(&event)) {
971                         if (event.type == SDL_QUIT
972                             || (event.type == SDL_KEYDOWN
973                                 && event.key.keysym.sym == SDLK_ESCAPE)) {
974                                 SDL_Quit();
975                                 return 0;
976                         }
977                         if (mouse) {
978                                 switch (event.type) {
979                                 case SDL_MOUSEBUTTONDOWN:
980                                         drawing = true;
981                                         break;
982                                 case SDL_MOUSEBUTTONUP:
983                                         drawing = false;
984                                         break;
985                                 case SDL_MOUSEMOTION:
986                                         if (!drawing)
987                                                 break;
988
989                                         n = new_line(event.motion.x,
990                                                      event.motion.y,
991                                                      event.motion.x
992                                                      + event.motion.xrel,
993                                                      event.motion.y
994                                                      + event.motion.yrel,
995                                                      calib);
996
997                                         if (n) {
998                                                 timeradd(&now, &line_life,
999                                                          &n->expires);
1000                                                 list_add_tail(&lines, &n->list);
1001                                                 thick_line(screen, n, 0);
1002                                         }
1003                                 }
1004                         }
1005                 }
1006
1007                 if (mouse)
1008                         continue;
1009
1010                 if (cwiid_get_state(wiimote, &state))
1011                         errx(1, "No wii state");
1012
1013                 /* Find biggest state. */
1014                 ir = &state.ir_src[0];
1015                 for (i = 0; i < CWIID_IR_SRC_COUNT; i++) {
1016                         if (!state.ir_src[i].valid)
1017                                 continue;
1018                         if (!ir->valid || state.ir_src[i].size > ir->size)
1019                                 ir = &state.ir_src[0];
1020                 }
1021
1022                 if (ir->valid) {
1023                         /* Give it some slack for missing one or two... */
1024                         if (time_since_last_ir <= 5) {
1025                                 n = new_line(last_ir.pos[0],
1026                                              last_ir.pos[1],
1027                                              ir->pos[0],
1028                                              ir->pos[1],
1029                                              calib);
1030                                 if (n) {
1031                                         timeradd(&now, &line_life, &n->expires);
1032                                         list_add_tail(&lines, &n->list);
1033                                         thick_line(screen, n, 0);
1034                                 }
1035                         }
1036                         time_since_last_ir = 0;
1037                         last_ir = *ir;
1038                 }
1039                 time_since_last_ir++;
1040         }
1041 }