1 /*
2  * lcdtest: LCD monitor test pattern generator
3  *
4  * Main program
5  * $Id: lcdtest.c 44 2010-01-26 06:28:14Z eric $
6  * Copyright 2005, 2007, 2010 Eric Smith <eric@brouhaha.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 3 as
10  * published by the Free Software Foundation.  Note that permission is
11  * not granted to redistribute this program under the terms of any
12  * other version of the General Public License.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 
24 #include <stdarg.h>
25 #include <stdbool.h>
26 #include <stddef.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include <SDL/SDL.h>
33 #include <SDL/SDL_image.h>
34 #include <SDL/SDL_ttf.h>
35 
36 #define UNUSED __attribute__ ((unused))
37 
38 #define QMAKESTR(x) #x
39 #define MAKESTR(x) QMAKESTR(x)
40 
41 char *progname;
42 
43 
44 const char help_teaser_message [] =
45 {
46   "press / or ? for help"
47 };
48 
49 const char help_message [] =
50 {
51   "commands:\n"
52   "  Change foreground color:\n"
53   "      r:  red              w:  white\n"
54   "      g:  green            k:  black\n"
55   "      b:  blue\n"
56   "  Change pattern:\n"
57   "      s:  solid fill       h:  horizontal lines\n"
58   "      c:  crosshatch       v:  vertical lines\n"
59   "      d:  dots             x:  diagonal crosshatch\n"
60   "      a:  white vertical lines with two pixel pitch\n"
61   "  arrow keys: move lines in arrow direction\n"
62   "  plus:   increase spacing between lines\n"
63   "  minus:  decrease spacing between lines\n"
64   "  0..9:   select predefined line spacings\n"
65   "  q or Escape: exit program\n"
66   "  ? or /:  toggle display of help message\n"
67   "\n"
68   "lcdtest " MAKESTR(RELEASE) "    Free Software (GPLv3)\n"
69   "Copyright 2005-2010 Eric Smith <eric@brouhaha.com>\n"
70   "http://www.brouhaha.com/~eric/software/lcdtest/"
71 };
72 
73 
help(FILE * f)74 void help (FILE *f)
75 {
76   fprintf (f, "%s", help_message);
77 }
78 
79 
usage(FILE * f)80 void usage (FILE *f)
81 {
82   fprintf (f, "lcdtest " MAKESTR(RELEASE) " Copyright 2005-2007 Eric Smith <eric@brouhaha.com>\n");
83   fprintf (f, "http://www.brouhaha.com/~eric/software/lcdtest/");
84   fprintf (f, "\n");
85   fprintf (f, "usage:  %s [options]\n", progname);
86   fprintf (f, "options:\n");
87   fprintf (f, "    --help         show usage and command keys\n");
88   fprintf (f, "    --window       display in a window rather than full screen\n");
89   fprintf (f, "    -x <count>     use a video mode with the specified horizontal resolution\n");
90   fprintf (f, "    -y <count>     use a video mode with the specified vertical resolution\n");
91   fprintf (f, "    --list-modes   list available video modes (including resolution)\n");
92 }
93 
94 
fatal(int ret,char * format,...)95 void fatal (int ret, char *format, ...)
96 {
97   va_list ap;
98 
99   fprintf (stderr, "fatal error: ");
100   va_start (ap, format);
101   vfprintf (stderr, format, ap);
102   va_end (ap);
103   if (ret == 1)
104     usage (stderr);
105   exit (ret);
106 }
107 
108 
get_text_size(TTF_Font * font,const char * text,int * width,int * height)109 void get_text_size (TTF_Font *font, const char *text, int *width, int *height)
110 {
111   *width = 0;
112   *height = 0;
113 
114   while (*text)
115     {
116       const char *p;
117       int n;
118       int line_width, line_height;
119       char buf [200];
120 
121       p = strchr (text, '\n');
122       if (p)
123 	{
124 	  n = p - text;
125 	  p++;
126 	}
127       else
128 	{
129 	  n = strlen (text);
130 	  p = text + n;
131 	}
132 
133       if (n > (sizeof (buf) - 1))
134 	n = sizeof (buf) - 1;
135       if (n == 0)
136 	buf [n++] = ' ';
137       else
138 	strncpy (buf, text, n);
139       buf [n] = '\0';
140 
141       if (TTF_SizeText (font, buf, & line_width, & line_height) != 0)
142 	fatal (2, "TTF_SizeText failed (missing glyph?)\n");
143       *height += line_height;
144       if (line_width > *width)
145 	*width = line_width;
146 
147       text = p;
148     }
149 }
150 
create_text_surface(const char * message,const char * font_path,int font_size,SDL_Color fg,SDL_Color bg UNUSED)151 SDL_Surface *create_text_surface (const char *message,
152 				  const char *font_path,
153 				  int font_size,
154 				  SDL_Color fg,
155 				  SDL_Color bg UNUSED)
156 {
157   TTF_Font *font;
158   int width, height;
159   SDL_Surface *text_surface;
160   SDL_Rect dest_rect;
161   int line_height;
162 
163   if ((! TTF_WasInit ()) && (TTF_Init () != 0))
164     fatal (2, "can't initialize SDL_ttf\n");
165 
166   font = TTF_OpenFont (font_path, font_size);
167   if (! font)
168     fatal (2, "can't open font\n");
169 
170   line_height = TTF_FontLineSkip (font);
171 
172   get_text_size (font, message, & width, & height);
173   printf ("width: %d, height: %d\n", width, height);
174 
175   text_surface = SDL_CreateRGBSurface (SDL_HWSURFACE,
176 				       width,
177 				       height,
178 				       32,
179 				       0x000000ff,
180 				       0x0000ff00,
181 				       0x00ff0000,
182 				       0);
183 
184   dest_rect.w = width;
185   dest_rect.h = height;
186   dest_rect.x = 0;
187   dest_rect.y = 0;
188 
189   while (*message)
190     {
191       const char *p;
192       int n;
193       SDL_Surface *line_surface;
194       char buf [200];
195 
196       p = strchr (message, '\n');
197       if (p)
198 	{
199 	  n = p - message;
200 	  p++;
201 	}
202       else
203 	{
204 	  n = strlen (message);
205 	  p = message + n;
206 	}
207 
208       if (n > (sizeof (buf) - 1))
209 	n = sizeof (buf) - 1;
210       if (n == 0)
211 	buf [n++] = ' ';
212       else
213 	strncpy (buf, message, n);
214       buf [n] = '\0';
215 
216       if (n)
217 	{
218 	  line_surface = TTF_RenderText_Blended (font, buf, fg);
219 	  if (! line_surface)
220 	    fatal (2, "can't render text\n");
221 	}
222 
223       SDL_BlitSurface (line_surface,
224 		       & line_surface->clip_rect,
225 		       text_surface,
226 		       & dest_rect);
227       SDL_FreeSurface (line_surface);
228 
229       dest_rect.y += line_height;
230 
231       message = p;
232     }
233 
234   TTF_CloseFont (font);
235   return (text_surface);
236 }
237 
238 
239 typedef struct
240 {
241   uint8_t red;
242   uint8_t green;
243   uint8_t blue;
244 } my_color_t;
245 
246 my_color_t test_colors [] =
247 {
248   { 0xff, 0x00, 0x00 },
249   { 0x00, 0xff, 0x00 },
250   { 0x00, 0x00, 0xff },
251   { 0xff, 0xff, 0xff },
252   { 0x80, 0x80, 0x80 },
253   { 0x00, 0x00, 0x00 }
254 };
255 
256 
257 // For special keys without Unicode codepoints (such as escape and
258 // the cursor keys), we translate to a codepoint in the Unicode
259 // Private Use Area (PUA), from 0xe000 to 0xf8ff.
260 #define PUA_ESCAPE 0xe000
261 #define PUA_UP     0xe001
262 #define PUA_DOWN   0xe002
263 #define PUA_LEFT   0xe003
264 #define PUA_RIGHT  0xe004
265 
266 
267 // wait for a keypress and return the corresponding Unicode code point
268 
wait_key(void)269 wchar_t wait_key (void)
270 {
271   SDL_Event event;
272 
273   while (1)
274     {
275       SDL_WaitEvent (& event);
276       switch (event.type)
277 	{
278 	case SDL_KEYDOWN:
279 	  switch (event.key.keysym.sym)
280 	    {
281 	    case SDLK_ESCAPE:
282 	      return PUA_ESCAPE;
283 	    case SDLK_UP:
284 	      return PUA_UP;
285 	    case SDLK_DOWN:
286 	      return PUA_DOWN;
287 	    case SDLK_LEFT:
288 	      return PUA_LEFT;
289 	    case SDLK_RIGHT:
290 	      return PUA_RIGHT;
291 	    default:
292 	      if (event.key.keysym.unicode != 0)
293 		return event.key.keysym.unicode;
294 	    }
295 	  break;
296 	case SDL_QUIT:
297 	  exit (0);
298 	}
299     }
300 }
301 
302 
lock_screen(SDL_Surface * screen)303 void lock_screen (SDL_Surface *screen)
304 {
305   if (SDL_MUSTLOCK (screen))
306     if (SDL_LockSurface (screen) < 0)
307       fatal (2, "can't lock screen\n");
308 }
309 
310 
unlock_screen(SDL_Surface * screen)311 void unlock_screen (SDL_Surface *screen)
312 {
313   if (SDL_MUSTLOCK (screen))
314     SDL_UnlockSurface (screen);
315 }
316 
317 
fill_screen(SDL_Surface * screen,uint32_t color)318 void fill_screen (SDL_Surface *screen, uint32_t color)
319 {
320   SDL_Rect rect;
321   rect.x = 0;
322   rect.y = 0;
323   rect.w = screen->w;
324   rect.h = screen->h;
325   if (SDL_FillRect (screen, & rect, color) < 0)
326     fatal (2, "SDL_FillRect failed; %s\n", SDL_GetError ());
327 }
328 
329 
draw_horiz_lines(SDL_Surface * screen,uint32_t color,int pitch,int offset)330 void draw_horiz_lines (SDL_Surface *screen,
331 		       uint32_t color,
332 		       int pitch,
333 		       int offset)
334 {
335   SDL_Rect rect;
336   rect.x = 0;
337   rect.w = screen->w;
338   rect.h = 1;
339 
340   for (rect.y = offset; rect.y < screen->h; rect.y += pitch)
341     if (SDL_FillRect (screen, & rect, color) < 0)
342       fatal (2, "SDL_FillRect failed; %s\n", SDL_GetError ());
343 }
344 
345 
draw_vert_lines(SDL_Surface * screen,uint32_t color,int pitch,int offset)346 void draw_vert_lines (SDL_Surface *screen,
347 		      uint32_t color,
348 		      int pitch,
349 		      int offset)
350 {
351   SDL_Rect rect;
352   rect.y = 0;
353   rect.h = screen->h;
354   rect.w = 1;
355 
356   for (rect.x = offset; rect.x < screen->w; rect.x += pitch)
357     if (SDL_FillRect (screen, & rect, color) < 0)
358       fatal (2, "SDL_FillRect failed; %s\n", SDL_GetError ());
359 }
360 
361 
draw_diagonal_lines(SDL_Surface * screen,uint32_t color,int h_pitch,int h_offset,int v_pitch UNUSED,int v_offset,int slope)362 void draw_diagonal_lines (SDL_Surface *screen,
363 			  uint32_t color,
364 			  int h_pitch,
365 			  int h_offset,
366 			  int v_pitch UNUSED,
367 			  int v_offset,
368 			  int slope)  // -1 or +1 if 45 degree angle
369 {
370   SDL_Rect rect;
371   rect.h = 1;
372   rect.w = 1;
373 
374   h_offset -= (v_offset * slope);
375   if (h_offset < 0)
376     h_offset += h_pitch;
377   else if (h_offset >= h_pitch)
378     h_offset -= h_pitch;
379 
380   for (rect.y = 0; rect.y < screen->h; rect.y++)
381     {
382       for (rect.x = h_offset % h_pitch; rect.x < screen->w; rect.x += h_pitch)
383 	if (SDL_FillRect (screen, & rect, color) < 0)
384 	  fatal (2, "SDL_FillRect failed; %s\n", SDL_GetError ());
385       h_offset += slope;
386       if (h_offset < 0)
387 	h_offset += h_pitch;
388       else if (h_offset >= h_pitch)
389 	h_offset -= h_pitch;
390     }
391 }
392 
393 
draw_dots(SDL_Surface * screen,uint32_t color,int h_pitch,int h_offset,int v_pitch,int v_offset)394 void draw_dots (SDL_Surface *screen,
395 		uint32_t color,
396 		int h_pitch,
397 		int h_offset,
398 		int v_pitch,
399 		int v_offset)
400 {
401   SDL_Rect rect;
402   rect.h = 1;
403   rect.w = 1;
404 
405   for (rect.x = h_offset; rect.x < screen->w; rect.x += h_pitch)
406     for (rect.y = v_offset; rect.y < screen->h; rect.y += v_pitch)
407       if (SDL_FillRect (screen, & rect, color) < 0)
408 	fatal (2, "SDL_FillRect failed; %s\n", SDL_GetError ());
409 }
410 
411 
412 uint32_t black;
413 
414 
pattern_solid(SDL_Surface * screen,uint32_t color)415 void pattern_solid (SDL_Surface *screen, uint32_t color)
416 {
417   lock_screen (screen);
418   fill_screen (screen, color);
419   unlock_screen (screen);
420   SDL_UpdateRect (screen, 0, 0, screen->w, screen->h);
421 }
422 
pattern_horiz_lines(SDL_Surface * screen,uint32_t color,int v_pitch,int v_offset)423 void pattern_horiz_lines (SDL_Surface *screen,
424 			  uint32_t color,
425 			  int v_pitch,
426 			  int v_offset)
427 {
428   lock_screen (screen);
429   fill_screen (screen, black);
430   draw_horiz_lines (screen, color, v_pitch, v_offset);
431   unlock_screen (screen);
432   SDL_UpdateRect (screen, 0, 0, screen->w, screen->h);
433 }
434 
pattern_vert_lines(SDL_Surface * screen,uint32_t color,int h_pitch,int h_offset)435 void pattern_vert_lines (SDL_Surface *screen,
436 			 uint32_t color,
437 			 int h_pitch,
438 			 int h_offset)
439 {
440   lock_screen (screen);
441   fill_screen (screen, black);
442   draw_vert_lines (screen, color, h_pitch, h_offset);
443   unlock_screen (screen);
444   SDL_UpdateRect (screen, 0, 0, screen->w, screen->h);
445 }
446 
pattern_crosshatch(SDL_Surface * screen,uint32_t color,int h_pitch,int h_offset,int v_pitch,int v_offset)447 void pattern_crosshatch (SDL_Surface *screen,
448 			 uint32_t color,
449 			 int h_pitch,
450 			 int h_offset,
451 			 int v_pitch,
452 			 int v_offset)
453 {
454   lock_screen (screen);
455   fill_screen (screen, black);
456   draw_horiz_lines (screen, color, v_pitch, v_offset);
457   draw_vert_lines (screen, color, h_pitch, h_offset);
458   unlock_screen (screen);
459   SDL_UpdateRect (screen, 0, 0, screen->w, screen->h);
460 }
461 
pattern_diagonal(SDL_Surface * screen,uint32_t color,int h_pitch,int h_offset,int v_pitch,int v_offset)462 void pattern_diagonal (SDL_Surface *screen,
463 		       uint32_t color,
464 		       int h_pitch,
465 		       int h_offset,
466 		       int v_pitch,
467 		       int v_offset)
468 {
469   lock_screen (screen);
470   fill_screen (screen, black);
471   draw_diagonal_lines (screen, color, h_pitch, h_offset, v_pitch, v_offset, -1);
472   draw_diagonal_lines (screen, color, h_pitch, h_offset, v_pitch, v_offset, +1);
473   unlock_screen (screen);
474   SDL_UpdateRect (screen, 0, 0, screen->w, screen->h);
475 }
476 
pattern_dots(SDL_Surface * screen,uint32_t color,int h_pitch,int h_offset,int v_pitch,int v_offset)477 void pattern_dots (SDL_Surface *screen,
478 		   uint32_t color,
479 		   int h_pitch,
480 		   int h_offset,
481 		   int v_pitch,
482 		   int v_offset)
483 {
484   lock_screen (screen);
485   fill_screen (screen, black);
486   draw_dots (screen, color, h_pitch, h_offset, v_pitch, v_offset);
487   unlock_screen (screen);
488   SDL_UpdateRect (screen, 0, 0, screen->w, screen->h);
489 }
490 
491 
show_help(SDL_Surface * screen,SDL_Surface * help_image)492 void show_help (SDL_Surface *screen, SDL_Surface *help_image)
493 {
494   SDL_Rect src_rect, dest_rect;
495 
496   src_rect.x = 0;
497   src_rect.h = help_image->h;
498   src_rect.y = 0;
499   src_rect.w = help_image->w;
500 
501   dest_rect.x = (screen->w - help_image->w) / 2;
502   dest_rect.h = help_image->h;
503   dest_rect.y = (screen->h - help_image->h) / 2;
504   dest_rect.w = help_image->w;
505 
506   lock_screen (screen);
507   SDL_BlitSurface (help_image, & src_rect, screen, & dest_rect);
508   unlock_screen (screen);
509   SDL_UpdateRect (screen, dest_rect.x, dest_rect.y, dest_rect.w, dest_rect.h);
510 }
511 
512 
513 typedef enum { solid, vert_lines, horiz_lines, crosshatch, dots, diagonal } pattern_t;
514 
515 
516 #define set_pitch(x) do \
517                        if ((pattern != solid) && ((x) < screen->w) && ((x) < screen->h)) \
518                          pitch = (x); \
519                      while (0)
520 
521 
522 SDL_Surface *help_teaser_image;
523 SDL_Surface *help_image;
524 
525 
main_loop(SDL_Surface * screen)526 void main_loop (SDL_Surface *screen)
527 {
528   pattern_t pattern = crosshatch;
529   uint32_t fg_color;
530   int pitch = 32;
531   int h_offset = 0;
532   int v_offset = 0;
533   uint16_t key_unicode;
534   int help_flag = 1;  // 0 for hidden, 1 for teaser, 2 for full help
535 
536   black = SDL_MapRGB (screen->format, 0x00, 0x00, 0x00);
537 
538   fg_color = SDL_MapRGB (screen->format, 0xff, 0x00, 0x00);
539 
540   SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
541 
542   while (1)
543     {
544       if (h_offset >= pitch)
545 	h_offset %= pitch;
546       if (v_offset >= pitch)
547 	v_offset %= pitch;
548 
549       switch (pattern)
550 	{
551 	case solid:
552 	  pattern_solid (screen, fg_color);
553 	  break;
554 	case vert_lines:
555 	  pattern_vert_lines (screen, fg_color, pitch, h_offset);
556 	  break;
557 	case horiz_lines:
558 	  pattern_horiz_lines (screen, fg_color, pitch, v_offset);
559 	  break;
560 	case crosshatch:
561 	  pattern_crosshatch (screen, fg_color, pitch, h_offset, pitch, v_offset);
562 	  break;
563 	case diagonal:
564 	  pattern_diagonal (screen, fg_color, pitch, h_offset, pitch, v_offset);
565 	  break;
566 	case dots:
567 	  pattern_dots (screen, fg_color, pitch, h_offset, pitch, v_offset);
568 	  break;
569 	}
570 
571       if (help_flag == 1)
572 	show_help (screen, help_teaser_image);
573       else if (help_flag == 2)
574 	show_help (screen, help_image);
575 
576       key_unicode = wait_key ();
577       switch (key_unicode)
578 	{
579 	case PUA_ESCAPE:
580 	case L'q':
581 	case L'Q':
582 	  exit (0);
583 	case L'r':
584 	case L'R':
585 	  fg_color = SDL_MapRGB (screen->format, 0xff, 0x00, 0x00);
586 	  help_flag = 0;
587 	  break;
588 	case L'g':
589 	case L'G':
590 	  fg_color = SDL_MapRGB (screen->format, 0x00, 0xff, 0x00);
591 	  help_flag = 0;
592 	  break;
593 	case L'b':
594 	case L'B':
595 	  fg_color = SDL_MapRGB (screen->format, 0x00, 0x00, 0xff);
596 	  help_flag = 0;
597 	  break;
598 	case L'w':
599 	case L'W':
600 	  fg_color = SDL_MapRGB (screen->format, 0xff, 0xff, 0xff);
601 	  help_flag = 0;
602 	  break;
603 	case L'k':
604 	case L'K':
605 	  fg_color = SDL_MapRGB (screen->format, 0x00, 0x00, 0x00);
606 	  help_flag = 0;
607 	  break;
608 	case L'a':
609 	case L'A':
610 	  fg_color = SDL_MapRGB (screen->format, 0xff, 0xff, 0xff);
611 	  pattern = vert_lines;
612 	  pitch = 2;
613 	  h_offset = 0;
614 	  help_flag = 0;
615 	  break;
616 	case L's':
617 	case L'S':
618 	  pattern = solid;
619 	  help_flag = 0;
620 	  break;
621 	case L'v':
622 	case L'V':
623 	  pattern = vert_lines;
624 	  help_flag = 0;
625 	  break;
626 	case L'h':
627 	case L'H':
628 	  pattern = horiz_lines;
629 	  help_flag = 0;
630 	  break;
631 	case L'c':
632 	case L'C':
633 	  pattern = crosshatch;
634 	  help_flag = 0;
635 	  break;
636 	case L'x':
637 	case L'X':
638 	  pattern = diagonal;
639 	  help_flag = 0;
640 	  break;
641 	case L'd':
642 	case L'D':
643 	  pattern = dots;
644 	  help_flag = 0;
645 	  break;
646 	case PUA_UP:
647 	  v_offset = v_offset - 1;
648 	  if (v_offset < 0)
649 	    v_offset = pitch - 1;
650 	  help_flag = 0;
651 	  break;
652 	case PUA_DOWN:
653 	  v_offset = v_offset + 1;
654 	  if (v_offset >= pitch)
655 	    v_offset = 0;
656 	  help_flag = 0;
657 	  break;
658 	case PUA_LEFT:
659 	  h_offset = h_offset - 1;
660 	  if (h_offset < 0)
661 	    h_offset = pitch - 1;
662 	  help_flag = 0;
663 	  break;
664 	case PUA_RIGHT:
665 	  h_offset = h_offset + 1;
666 	  if (h_offset >= pitch)
667 	    h_offset = 0;
668 	  help_flag = 0;
669 	  break;
670 	case L'-':
671 	  if ((pattern != solid) && (pitch > 2))
672 	    pitch--;
673 	  help_flag = 0;
674 	  break;
675 	case L'+':
676 	case L'=':
677 	  set_pitch (pitch+1);
678 	  help_flag = 0;
679 	  break;
680 	case L'0': set_pitch (  4); help_flag = 0; break;
681 	case L'1': set_pitch (  8); help_flag = 0; break;
682 	case L'2': set_pitch ( 16); help_flag = 0; break;
683 	case L'3': set_pitch ( 32); help_flag = 0; break;
684 	case L'4': set_pitch ( 64); help_flag = 0; break;
685 	case L'5': set_pitch ( 96); help_flag = 0; break;
686 	case L'6': set_pitch (128); help_flag = 0; break;
687 	case L'7': set_pitch (192); help_flag = 0; break;
688 	case L'8': set_pitch (256); help_flag = 0; break;
689 	case L'9': set_pitch (320); help_flag = 0; break;
690 	case L'/':
691 	case L'?':
692 	  if (help_flag == 2)
693 	    help_flag = 0;
694 	  else
695 	    help_flag = 2;
696 	  break;
697 	default:
698 	  break;
699 	}
700     }
701 }
702 
703 
enumerate_sdl_modes(int * width,int * height,FILE * out)704 int enumerate_sdl_modes (int *width, int *height, FILE *out)
705 {
706   SDL_Rect **modes;
707   int w = -1;
708   int h = -1;
709   int m = -1;
710   int i;
711 
712   modes = SDL_ListModes (NULL, SDL_HWSURFACE | SDL_FULLSCREEN);
713 
714   if (! modes)
715     fatal (2, "no SDL display modes available\n");
716 
717   if (modes == (SDL_Rect **) -1)
718     fatal (2, "all SDL resolutions available (on hardware?)\n");
719 
720   for (i = 0; modes [i]; i++)
721     {
722       if (((*width) >= 0) & (modes [i]->w != *width))
723 	continue;
724       if (((*height) >= 0) & (modes [i]->h != *height))
725 	continue;
726       if (out)
727 	fprintf (out, "  mode %d: %d x %d\n", i, modes [i]->w, modes [i]->h);
728       if ((modes [i]->w >= w) && (modes [i]->h >= h))
729 	{
730 	  w = modes [i]->w;
731 	  h = modes [i]->h;
732 	  m = i;
733 	}
734     }
735 
736   if (m < 0)
737     fatal (2, "no matching SDL video modes found\n");
738 
739   *width = w;
740   *height = h;
741   return (m);
742 }
743 
744 
745 const char font_path [] = "/usr/local/share/fonts/Liberation/LiberationMono-Regular.ttf";
746 
main(int argc,char * argv[])747 int main (int argc, char *argv [])
748 {
749   SDL_Surface *screen;
750   bool fullscreen = true;
751   uint32_t sdl_flags;
752   int width = -1;
753   int height = -1;
754   SDL_Color black = { 0x00, 0x00, 0x00, 0x00 };
755   SDL_Color white = { 0xff, 0xff, 0xff, 0x00 };
756 
757   progname = argv [0];
758 
759   if (SDL_Init (SDL_INIT_VIDEO) < 0)
760     fatal (2, "SDL initialization error %s\n", SDL_GetError ());
761   atexit (SDL_Quit);
762   SDL_EnableUNICODE (1);
763 
764   help_teaser_image = create_text_surface (help_teaser_message,
765 					   font_path,
766 					   18,
767 					   white,
768 					   black);
769   help_image = create_text_surface (help_message,
770 				    font_path,
771 				    18,
772 				    white,
773 				    black);
774 
775   while (--argc)
776     {
777       argv++;
778       if ((strcmp (argv [0], "--help") == 0) ||
779 	  (strcmp (argv [0], "-?") == 0))
780 	{
781 	  usage (stderr);
782 	  help (stderr);
783 	  exit (0);
784 	}
785       else if (strcmp (argv [0], "--list-modes") == 0)
786 	{
787 	  fprintf (stderr, "SDL video modes:\n");
788 	  enumerate_sdl_modes (& width, & height, stderr);
789 	  exit (0);
790 	}
791       else if ((strcmp (argv [0], "--window") == 0) ||
792 	       (strcmp (argv [0], "-w") == 0))
793 	{
794 	  fullscreen = false;
795 	  if (width < 0)
796 	    width = 620;
797 	  if (height < 0)
798 	    height = 460;
799 	}
800       else if (strcmp (argv [0], "-x") == 0)
801 	{
802 	  if (--argc == 0)
803 	    fatal (1, "-x option must be followed by resolution\n");
804 	  width = atoi (argv [1]);
805 	  argv++;
806 	}
807       else if (strcmp (argv [0], "-y") == 0)
808 	{
809 	  if (--argc == 0)
810 	    fatal (1, "-y option must be followed by resolution\n");
811 	  height = atoi (argv [1]);
812 	  argv++;
813 	}
814       else
815 	fatal (1, "unrecognized option '%s'\n", argv [0]);
816     }
817 
818   if (fullscreen)
819     {
820       enumerate_sdl_modes (& width, & height, NULL);
821       //printf ("using video resolution %d x %d\n", width, height);
822       sdl_flags = SDL_HWSURFACE | SDL_FULLSCREEN;
823     }
824   else
825     sdl_flags = SDL_SWSURFACE;
826 
827   screen = SDL_SetVideoMode (width, height, 32, sdl_flags);
828   if (! screen)
829     fatal (2, "can't set video mode: %s\n", SDL_GetError ());
830 
831   if (fullscreen)
832     SDL_ShowCursor (SDL_DISABLE);
833 
834   main_loop (screen);
835 
836   exit (0);
837 }
838