view src/App.cpp @ 7:104dff305563

Added App::on_InputFocus and app::on_InputBlur to make sure the program does not receive mouse clicks when not in foreground. Added option in CmakeLists.txt to support compiling for gprof.
author Eris Caffee <discordia@eldalin.com>
date Mon, 15 Nov 2010 21:55:46 -0600
parents d691ce98f406
children 00de693e73be
line source
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Fracter - A simple Mandelbrot Set viewer.
4 //
5 // Copyright (C) 2010 Sarah Eris Horsley Caffee
6 //
7 // This file is part of Fracter.
8 //
9 // Fracter is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
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 // App.cpp
24 // Application class implementation
25 //
26 ////////////////////////////////////////////////////////////////////////////////
28 #include <iomanip>
29 #include <iostream>
30 #include <sstream>
31 #include <typeinfo>
32 #include <vector>
34 #if defined(WIN32)
35 #include <cstdlib>
36 #include <windows.h>
37 #include <Shlwapi.h>
39 #else
40 #include <cctype>
41 #include <cstring>
42 #include <cstdio>
44 #include <dirent.h>
45 #endif
48 #include "SDL.h"
50 #include "App.h"
52 #include "Options.h"
53 #include "Mandelbrot.h"
54 #include "Julia.h"
57 std::vector<View *> App::old_views;
59 ////////////////////////////////////////////////////////////////////////////////
61 App::App() :
62 running (true),
63 redraw (false),
64 surf_display (NULL),
65 surf_selection (NULL),
66 surf_fractal (NULL),
67 selection_x (-1),
68 selection_y (-1),
69 selection_width (-1),
70 setting_zoom (false),
71 can_set_julia_k (false),
72 setting_julia_k (false),
73 fractal (NULL)
74 {
75 }
78 ////////////////////////////////////////////////////////////////////////////////
79 App::~App()
80 {
81 old_views.clear();
82 }
85 ////////////////////////////////////////////////////////////////////////////////
87 int App::run()
88 {
89 if (init() == false)
90 return 1;
92 SDL_Event event;
94 while (running)
95 {
96 SDL_WaitEvent(&event);
97 do
98 {
99 on_Event(&event);
100 }
101 while (SDL_PollEvent(&event));
103 update();
105 if (redraw)
106 render();
107 }
109 cleanup();
111 return 0;
112 }
115 ////////////////////////////////////////////////////////////////////////////////
117 bool App::init()
118 {
119 if ((SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) == -1)
120 {
121 std::cerr << "SDL_Init() failed: " << SDL_GetError() << std::endl;
122 return false;
123 }
125 if ((surf_display = SDL_SetVideoMode(Options::width,
126 Options::height,
127 Options::bpp,
128 SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL)
129 {
130 std::cerr << "SDL_SetVideoMode() failed: "
131 << SDL_GetError() << std::endl;
132 return false;
133 }
135 SDL_EnableKeyRepeat(500, 100);
138 if (!create_new_surface(surf_selection)) { return false; }
139 if (!create_new_surface(surf_fractal)) { return false; }
142 std::cerr << "Available fractal types are" << std::endl;
143 std::map <std::string, FractalFactory *> mymap = get_Fractal_map();
144 std::map <std::string, FractalFactory *>::iterator it;
145 for ( it=mymap.begin() ; it != mymap.end(); it++ )
146 std::cerr << " " << (*it).first << std::endl;
148 FractalFactory * fractal_factory =
149 get_Fractal_map()[typeid(Mandelbrot).name()];
150 if (!fractal_factory)
151 {
152 std::cerr << "Mandelbrot set not available!" << std::endl;
153 return false;
154 }
155 fractal = fractal_factory->create();
156 fractal->init(surf_fractal);
157 redraw = true;
159 can_set_julia_k = true;
162 set_caption();
164 SDL_AddTimer(10, App::timer_callback, NULL);
166 return true;
167 }
170 ////////////////////////////////////////////////////////////////////////////////
172 void App::update()
173 {
174 }
177 ////////////////////////////////////////////////////////////////////////////////
179 void App::render()
180 {
181 if (redraw)
182 {
183 // Start with a clean slate.
184 SDL_FillRect(surf_display, &surf_display->clip_rect,
185 SDL_MapRGB(surf_display->format, 0, 0, 255));
187 fractal->calc_set();
188 SDL_BlitSurface(surf_fractal, NULL, surf_display, NULL);
190 // draw a selection box
191 if (setting_zoom)
192 {
193 SDL_BlitSurface(surf_selection, NULL, surf_display, NULL);
194 }
196 // Show it to the user.
197 SDL_Flip(surf_display);
199 redraw = false;
200 }
201 }
204 ////////////////////////////////////////////////////////////////////////////////
206 void App::cleanup()
207 {
209 if (surf_selection)
210 {
211 SDL_FreeSurface(surf_selection);
212 surf_selection = NULL;
213 }
214 if (surf_display)
215 {
216 SDL_FreeSurface(surf_display);
217 surf_display = NULL;
218 }
220 SDL_Quit();
221 }
224 ////////////////////////////////////////////////////////////////////////////////
226 void App::on_InputFocus()
227 {
228 SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
229 SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_ENABLE);
230 SDL_EventState(SDL_MOUSEBUTTONUP, SDL_ENABLE);
231 }
234 ////////////////////////////////////////////////////////////////////////////////
236 void App::on_InputBlur()
237 {
238 SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
239 SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE);
240 SDL_EventState(SDL_MOUSEBUTTONUP, SDL_IGNORE);
241 }
244 ////////////////////////////////////////////////////////////////////////////////
246 void App::on_KeyDown(SDL_KeyboardEvent key)
247 {
248 switch (key.keysym.sym)
249 {
250 case SDLK_ESCAPE:
251 case SDLK_q:
252 SDL_Event event;
253 event.type = SDL_QUIT;
254 SDL_PushEvent(&event);
255 break;
257 case SDLK_b:
258 restore_view();
259 redraw = true;
260 set_caption();
261 break;
263 case SDLK_s:
264 save_image();
265 break;
267 case SDLK_UP:
268 fractal->inc_max_iter();
269 set_caption();
270 break;
271 case SDLK_DOWN:
272 fractal->dec_max_iter();
273 set_caption();
274 break;
277 case SDLK_m:
278 if (fractal)
279 {
280 FractalFactory * fractal_factory =
281 get_Fractal_map()[typeid(Mandelbrot).name()];
282 if (!fractal_factory)
283 {
284 std::cerr << "Mandelbrot set not available!" << std::endl;
285 SDL_Event event;
286 event.type = SDL_QUIT;
287 SDL_PushEvent(&event);
288 }
289 else
290 {
291 save_view();
292 delete fractal;
293 fractal = fractal_factory->create();
294 fractal->init(surf_fractal);
295 redraw = true;
296 set_caption();
297 can_set_julia_k = true;
298 }
299 }
300 break;
302 case SDLK_j:
303 if (fractal)
304 {
305 FractalFactory * fractal_factory =
306 get_Fractal_map()[typeid(Julia).name()];
307 if (!fractal_factory)
308 {
309 std::cerr << "Julia set not available!" << std::endl;
310 }
311 else
312 {
313 save_view();
314 delete fractal;
315 fractal = fractal_factory->create();
316 fractal->init(surf_fractal);
317 redraw = true;
318 set_caption();
319 can_set_julia_k = false;
320 }
321 }
322 break;
324 case SDLK_k:
325 if (can_set_julia_k)
326 {
327 if (setting_julia_k) setting_julia_k = false;
328 else setting_julia_k = true;
329 }
330 break;
332 default:
333 break;
334 }
335 }
338 ////////////////////////////////////////////////////////////////////////////////
339 void App::on_KeyUp(SDL_KeyboardEvent key)
340 {
341 switch (key.keysym.sym)
342 {
343 case SDLK_UP:
344 fractal->set_calc_needed();
345 redraw = true;
346 break;
347 case SDLK_DOWN:
348 fractal->set_calc_needed();
349 redraw = true;
350 break;
351 default:
352 break;
353 }
354 }
357 ////////////////////////////////////////////////////////////////////////////////
358 void App::on_MouseMove(SDL_MouseMotionEvent motion)
359 {
360 if (setting_zoom)
361 {
362 Uint32 clear = SDL_MapRGBA(surf_selection->format,
363 0, 0, 0, SDL_ALPHA_TRANSPARENT);
364 Uint32 white = SDL_MapRGBA(surf_selection->format,
365 255, 255, 255, SDL_ALPHA_OPAQUE);
367 SDL_FillRect(surf_selection, &surf_selection->clip_rect, clear);
369 SDL_Rect rect;
370 rect.x = selection_x;
371 rect.y = selection_y;
372 int w = mouse_move_width(motion.x, motion.y);
373 rect.w = rect.h = w;
375 SDL_FillRect(surf_selection, &rect, white);
377 rect.x = selection_x + 1;
378 rect.y = selection_y + 1;
379 rect.w = rect.h = w - 2;
381 SDL_FillRect(surf_selection, &rect, clear);
383 redraw = true;
384 }
385 }
387 ////////////////////////////////////////////////////////////////////////////////
389 void App::on_LButtonDown(SDL_MouseButtonEvent button)
390 {
391 if (setting_julia_k)
392 {
393 set_julia_k(button.x, button.y);
394 }
395 else
396 {
397 selection_x = button.x;
398 selection_y = button.y;
399 setting_zoom = true;
400 }
401 }
404 ////////////////////////////////////////////////////////////////////////////////
406 void App::on_LButtonUp(SDL_MouseButtonEvent button)
407 {
408 if (setting_zoom)
409 {
410 selection_width = mouse_move_width(button.x, button.y);
411 if ( (button.x <= 0) ||
412 (button.x >= surf_display->w - 1) ||
413 (button.y <= 0) ||
414 (button.y >= surf_display->h - 1) ||
415 (selection_width == 0))
416 {
417 // Selection cancelled
418 setting_zoom = false;
419 return;
420 }
422 // calculate new min/max re/im
423 double Re_scale = (fractal->get_size())/(surf_fractal->w - 1);
424 double Im_scale = (fractal->get_size())/(surf_fractal->h - 1);
426 double new_re_min = fractal->get_re_min() + (selection_x) * Re_scale;
427 double new_im_max = fractal->get_im_max() - (selection_y) * Im_scale;
428 double new_size = selection_width * Re_scale;
430 save_view();
432 fractal->set_re_min(new_re_min);
433 fractal->set_im_max(new_im_max);
434 fractal->set_size(new_size);
435 fractal->set_calc_needed();
436 redraw = true;
438 set_caption();
440 setting_zoom = false;
441 }
442 }
445 ////////////////////////////////////////////////////////////////////////////////
446 void App::on_Resize(SDL_ResizeEvent resize)
447 {
448 redraw = true;
449 }
452 ////////////////////////////////////////////////////////////////////////////////
453 void App::on_Expose()
454 {
455 redraw = true;
456 }
459 ////////////////////////////////////////////////////////////////////////////////
461 void App::on_Quit()
462 {
463 running = false;
464 }
467 ////////////////////////////////////////////////////////////////////////////////
468 int App::mouse_move_width(int mx, int my)
469 {
470 int move_width = (mx - selection_x);
471 if (mx > selection_x)
472 {
473 move_width *= -1;
474 }
476 int tmp_i = (my - selection_y);
477 if (my < selection_y)
478 {
479 tmp_i *= -1;
480 }
482 if (tmp_i > move_width)
483 move_width = tmp_i;
485 return move_width;
486 }
489 ////////////////////////////////////////////////////////////////////////////////
490 bool App::create_new_surface(SDL_Surface * &surface)
491 {
492 if (surface)
493 {
494 SDL_FreeSurface(surface);
495 surface = NULL;
496 }
498 SDL_Surface * tmp;
499 if ((tmp = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA,
500 Options::width, Options::height,
501 Options::bpp, 0, 0, 0, 0))
502 == NULL)
503 {
504 std::cerr << "SDL_CreateRGBSurface() failed in "
505 << "App::create_new_surface(): "
506 << SDL_GetError() << std::endl;
507 return false;
508 }
510 surface = SDL_DisplayFormatAlpha(tmp);
511 SDL_FreeSurface(tmp);
513 return true;
514 }
517 ////////////////////////////////////////////////////////////////////////////////
518 void App::set_julia_k(int x, int y)
519 {
520 FractalFactory * fractal_factory = get_Fractal_map()[typeid(Julia).name()];
521 if (!fractal_factory)
522 {
523 std::cerr << "Julia set not available!" << std::endl;
524 return;
525 }
526 else
527 {
528 save_view();
530 delete fractal;
531 fractal = fractal_factory->create();
532 fractal->init(surf_fractal);
534 double k[2];
535 k[0]= fractal->get_re_min() + x *
536 (fractal->get_size())/(surf_fractal->w - 1);
537 k[1] = fractal->get_im_max() - y *
538 (fractal->get_size())/(surf_fractal->h - 1);
540 fractal->set_option("k", k);
541 fractal->set_calc_needed();
542 redraw = true;
544 set_caption();
545 setting_julia_k = false;
546 }
547 }
550 ////////////////////////////////////////////////////////////////////////////////
552 void App::restore_view()
553 {
554 if (old_views.size() > 0)
555 {
556 View * v = old_views.back();
557 old_views.pop_back();
559 if (v->get_type().compare(fractal->get_fractal_name()) != 0)
560 {
561 delete fractal;
562 FractalFactory * fractal_factory = get_Fractal_map()[v->get_type()];
563 if (!fractal_factory)
564 {
565 std::cerr << "Cannot restore old view: unknown Fractal type "
566 << v->get_type() << std::endl;
567 SDL_Event event;
568 event.type = SDL_QUIT;
569 SDL_PushEvent(&event);
570 return;
571 }
572 fractal = fractal_factory->create();
573 fractal->init(surf_fractal);
574 }
576 fractal->restore_view(v);
577 delete v;
578 }
579 }
582 ////////////////////////////////////////////////////////////////////////////////
583 void App::save_view()
584 {
585 View * v = fractal->save_view();
586 old_views.push_back(v);
587 }
590 ////////////////////////////////////////////////////////////////////////////////
591 void App::set_caption()
592 {
593 std::stringstream ss;
594 ss << fractal->get_display_name() << " / "
595 << "Real: " << fractal->get_re_min()
596 << " to "<< fractal->get_re_min() + fractal->get_size()
597 << " / Imaginary: " << fractal->get_im_max() - fractal->get_size()
598 << " to " << fractal->get_im_max()
599 << " / Max iterations: " << fractal->get_max_iter();
600 SDL_WM_SetCaption(ss.str().c_str(), NULL);
601 }
604 ////////////////////////////////////////////////////////////////////////////////
605 bool App::save_image()
606 {
607 int i = App::get_next_file_num();
608 std::stringstream name;
609 name << "fracter-" << std::setw(3) << std::setfill('0') << i << ".bmp";
611 if (SDL_SaveBMP(surf_display, name.str().c_str()) != 0)
612 {
613 std::cerr << "Unable to save image file " << name.str() << "" << std::endl;
614 return false;
615 }
617 std::cerr << "Saved image as " << name.str() << std::endl;
619 return true;
620 }
623 ////////////////////////////////////////////////////////////////////////////////
624 Uint32 App::timer_callback(Uint32 interval, void *param)
625 {
626 SDL_Event e;
628 e.type = SDL_USEREVENT;
629 e.user.code = 0;
630 e.user.data1 = NULL;
631 e.user.data2 = NULL;
632 SDL_PushEvent(& e);
634 return interval;
635 }
638 #if defined(WIN32)
639 ////////////////////////////////////////////////////////////////////////////////
640 //Code for Windows
641 int App::get_next_file_num()
642 {
643 static int i = 0;
645 if (i == 0)
646 {
647 WIN32_FIND_DATA ffd;
648 HANDLE hFind = INVALID_HANDLE_VALUE;
650 char * file_spec = "fracter-???.bmp";
651 hFind = FindFirstFile(file_spec, &ffd);
652 if (hFind != INVALID_HANDLE_VALUE)
653 {
654 std::string s;
655 std::stringstream ss;
656 do
657 {
658 std::cerr << ffd.cFileName << std::endl;
659 ss << ffd.cFileName;
660 if (ss.str().size() == 18)
661 {
662 s = ss.str().substr(0, 11);
663 if ( StrCmpI(s.c_str(), "fracter-") == 0 )
664 {
665 s = ss.str().substr(14, 4);
666 if (StrCmpI(s.c_str(), ".bmp") == 0)
667 {
668 s = ss.str().substr(11, 3);
669 int j = atoi(s.c_str());
670 if (j > i) i = j;
671 }
672 }
673 }
674 }
675 while (FindNextFile(hFind, &ffd) != 0);
676 }
677 }
679 i++;
680 return i;
681 }
685 #else
686 ////////////////////////////////////////////////////////////////////////////////
687 // Code for Linux
689 int App::scandir_filter(const struct dirent * d)
690 {
691 if (memcmp(d->d_name, "fracter-", 8) != 0) return 0;
692 if (memcmp(d->d_name+11, ".bmp", 4) != 0) return 0;
693 if (isdigit(d->d_name[8])
694 && isdigit(d->d_name[9])
695 && isdigit(d->d_name[10]))
696 return 1;
697 return 0;
698 }
701 int App::get_next_file_num()
702 {
703 static int i = 0;
705 if (i == 0)
706 {
707 struct dirent ** file_list;
708 int num_files = scandir(".", &file_list, scandir_filter, alphasort);
709 if (num_files != 0)
710 sscanf(file_list[num_files-1]->d_name, "fracter-%03d.bmp", &i);
711 }
712 i++;
714 return i;
715 }
717 #endif