1 //---------------------------------------------------------------------------
2 #include "stdafx.h"
3
4 #include <cassert>
5 #include <ctime>
6
7 #include "screen.h"
8 #include "sound.h"
9 #include "game.h"
10 #include "utils.h"
11 #include "gamestate.h"
12 #include "image.h"
13 #include "sector.h"
14 #include "panel.h"
15 #include "gui.h"
16 #include "player.h"
17
18 //---------------------------------------------------------------------------
19
Screen()20 Screen::Screen() : m_pos_x(0), m_pos_y(0), m_down_left(false), m_down_middle(false), m_down_right(false) {
21 #if SDL_MAJOR_VERSION == 1
22 surface = NULL;
23 #else
24 sdlWindow = NULL;
25 sdlRenderer = NULL;
26 width = 0;
27 height = 0;
28 #endif
29 }
30
~Screen()31 Screen::~Screen() {
32 }
33
canOpenFullscreen(int width,int height)34 bool Screen::canOpenFullscreen(int width, int height) {
35 #if SDL_MAJOR_VERSION == 1
36 if( SDL_VideoModeOK(width, height, 32, SDL_HWSURFACE|SDL_HWPALETTE|SDL_FULLSCREEN) != 0 )
37 return true;
38 return false;
39 #else
40 return true; // assume always true?
41 #endif
42 }
43
open(int screen_width,int screen_height,bool fullscreen)44 bool Screen::open(int screen_width, int screen_height, bool fullscreen) {
45 LOG("Screen::open(%d, %d, %s)\n", screen_width, screen_height, fullscreen?"fullscreen":"windowed");
46 #if SDL_MAJOR_VERSION == 1
47 if( fullscreen )
48 surface = SDL_SetVideoMode(screen_width, screen_height, 32, SDL_HWSURFACE|SDL_HWPALETTE|SDL_FULLSCREEN);
49 else
50 surface = SDL_SetVideoMode(screen_width, screen_height, 32, SDL_HWSURFACE|SDL_HWPALETTE);
51 if( surface == NULL ) {
52 LOG("failed to open screen at this resolution\n");
53 return false;
54 }
55 #else
56 if( SDL_CreateWindowAndRenderer(screen_width, screen_height, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_RESIZABLE, &sdlWindow, &sdlRenderer) != 0 ) {
57 LOG("failed to open screen at this resolution\n");
58 return false;
59 }
60 this->width = screen_width;
61 this->height = screen_height;
62 {
63 SDL_BlendMode blendMode = SDL_BLENDMODE_NONE;
64 SDL_GetRenderDrawBlendMode(sdlRenderer, &blendMode);
65 LOG("render draw blend mode: %d\n", blendMode);
66 }
67 SDL_SetRenderDrawBlendMode(sdlRenderer, SDL_BLENDMODE_BLEND); // needed for Screen::fillRectWithAlpha, as blending is off by default for drawing functions
68 #endif
69 LOG("screen opened ok\n");
70
71 if( game_g->isMobileUI() ) {
72 SDL_ShowCursor(false); // comment out to test with system cursor, for testing purposes when ran on non-touchscreen devices
73 }
74 else {
75 //SDL_ShowCursor(false);
76 // Set up a blank cursor
77 // We use this hacky way due to a bug in SDL_ShowCursor(false), where when alt-tabbing out of full screen mode in Windows, the mouse pointer is
78 // trapped in the top left corner. See
79 unsigned char data[1] = "";
80 unsigned char mask[1] = "";
81 SDL_Cursor *cursor = SDL_CreateCursor(data, mask, 8, 1, 0, 0);
82 SDL_Cursor *old_cursor = SDL_GetCursor();
83 SDL_SetCursor(cursor);
84 SDL_FreeCursor(old_cursor);
85 }
86
87 #if SDL_MAJOR_VERSION == 1
88 Image::setGraphicsOutput(surface);
89 #else
90 Image::setGraphicsOutput(sdlRenderer);
91 #endif
92
93 return true;
94 }
95
96 #if SDL_MAJOR_VERSION == 1
97 #else
setLogicalSize(int width,int height,bool smooth)98 void Screen::setLogicalSize(int width, int height, bool smooth) {
99 this->width = width;
100 this->height = height;
101 LOG("width, height: %d, %d\n", width, height);
102 if( smooth ) {
103 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // make the scaled rendering look smoother.
104 }
105 else {
106 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
107 }
108 SDL_RenderSetLogicalSize(sdlRenderer, width, height);
109 }
110 #endif
111
setTitle(const char * title)112 void Screen::setTitle(const char *title) {
113 #if SDL_MAJOR_VERSION == 1
114 SDL_WM_SetCaption(title, "");
115 #else
116 SDL_SetWindowTitle(sdlWindow, title);
117 #endif
118 }
119
clear()120 void Screen::clear() {
121 #if SDL_MAJOR_VERSION == 1
122 SDL_Rect rect;
123 rect.x = 0;
124 rect.y = 0;
125 rect.w = getWidth();
126 rect.h = getHeight();
127 SDL_FillRect(surface, &rect, 0);
128 #else
129 SDL_SetRenderDrawColor(sdlRenderer, 0, 0, 0, 255);
130 SDL_RenderClear(sdlRenderer);
131 #endif
132 }
133
refresh()134 void Screen::refresh() {
135 #if SDL_MAJOR_VERSION == 1
136 SDL_Flip(surface);
137 #else
138 SDL_RenderPresent(sdlRenderer);
139 #endif
140 }
141
getWidth() const142 int Screen::getWidth() const {
143 #if SDL_MAJOR_VERSION == 1
144 return surface->w;
145 #else
146 /*int w = 0, h = 0;
147 SDL_RenderGetLogicalSize(sdlRenderer, &w, &h);
148 return w;*/
149 //LOG("width = %d\n", width);
150 return this->width;
151 #endif
152 }
153
getHeight() const154 int Screen::getHeight() const {
155 #if SDL_MAJOR_VERSION == 1
156 return surface->h;
157 #else
158 /*int w = 0, h = 0;
159 SDL_RenderGetLogicalSize(sdlRenderer, &w, &h);
160 return h;*/
161 //LOG("height = %d\n", height);
162 return this->height;
163 #endif
164 }
165
fillRect(short x,short y,short w,short h,unsigned char r,unsigned char g,unsigned char b)166 void Screen::fillRect(short x, short y, short w, short h, unsigned char r, unsigned char g, unsigned char b) {
167 SDL_Rect rect;
168 rect.x = x;
169 rect.y = y;
170 rect.w = w;
171 rect.h = h;
172 #if SDL_MAJOR_VERSION == 1
173 Uint32 col = SDL_MapRGB(surface->format, r, g, b);
174 SDL_FillRect(surface, &rect, col);
175 #else
176 SDL_SetRenderDrawColor(sdlRenderer, r, g, b, 255);
177 SDL_RenderFillRect(sdlRenderer, &rect);
178 #endif
179 }
180
181 #if SDL_MAJOR_VERSION == 1
182 // not supported with SDL 1.2 (as SDL_FillRect can't do blending)!
183 #else
fillRectWithAlpha(short x,short y,short w,short h,unsigned char r,unsigned char g,unsigned char b,unsigned char alpha)184 void Screen::fillRectWithAlpha(short x, short y, short w, short h, unsigned char r, unsigned char g, unsigned char b, unsigned char alpha) {
185 SDL_Rect rect;
186 rect.x = x;
187 rect.y = y;
188 rect.w = w;
189 rect.h = h;
190 //LOG("fill rect %d %d %d %d\n", r, g, b, alpha);
191 SDL_SetRenderDrawColor(sdlRenderer, r, g, b, alpha);
192 SDL_RenderFillRect(sdlRenderer, &rect);
193 }
194 #endif
195
196 #if SDL_MAJOR_VERSION == 1
197 // not supported with SDL 1.2
198 #else
drawLine(short x1,short y1,short x2,short y2,unsigned char r,unsigned char g,unsigned char b)199 void Screen::drawLine(short x1, short y1, short x2, short y2, unsigned char r, unsigned char g, unsigned char b) {
200 SDL_SetRenderDrawColor(sdlRenderer, r, g, b, 255);
201 SDL_RenderDrawLine(sdlRenderer, x1, y1, x2, y2);
202 }
203 #endif
204
205 #if SDL_MAJOR_VERSION == 1
206 // not supported with SDL 1.2
207 #else
convertWindowToLogical(int * m_x,int * m_y) const208 void Screen::convertWindowToLogical(int *m_x, int *m_y) const {
209 SDL_Rect rect;
210 SDL_RenderGetViewport(sdlRenderer, &rect);
211 float scale_x = 0.0f, scale_y = 0.0f;
212 SDL_RenderGetScale(sdlRenderer, &scale_x, &scale_y);
213 //LOG("viewport: %d x %d : %d, %d\n", rect.x, rect.y, rect.w, rect.h);
214 //LOG("render scale: %f x %f\n", scale_x, scale_y);
215 *m_x = (int)(*m_x / scale_x);
216 *m_y = (int)(*m_y / scale_y);
217 *m_x -= rect.x;
218 *m_y -= rect.y;
219 }
220
getWindowSize(int * window_width,int * window_height) const221 void Screen::getWindowSize(int *window_width, int *window_height) const {
222 SDL_GetWindowSize(sdlWindow, window_width, window_height);
223 }
224 #endif
225
226
getMouseCoords(int * m_x,int * m_y) const227 void Screen::getMouseCoords(int *m_x, int *m_y) const {
228 // SDL_GetMouseState doesn't play well with touch events
229 /*SDL_GetMouseState(m_x, m_y);
230 // need to convert from window space to logical space
231 #if SDL_MAJOR_VERSION == 1
232 #else
233 this->convertWindowToLogical(m_x, m_y);
234 #endif*/
235 *m_x = this->m_pos_x;
236 *m_y = this->m_pos_y;
237 //LOG("Screen::getMouseCoords: %d, %d\n", *m_x, *m_y);
238 }
239
getMouseState(int * m_x,int * m_y,bool * m_left,bool * m_middle,bool * m_right) const240 bool Screen::getMouseState(int *m_x, int *m_y, bool *m_left, bool *m_middle, bool *m_right) const {
241 // SDL_GetMouseState doesn't play well with touch events
242 /*int m_b = SDL_GetMouseState(m_x, m_y);
243 *m_left = ( m_b & SDL_BUTTON(1) ) != 0;
244 *m_middle = ( m_b & SDL_BUTTON(2) ) != 0;
245 *m_right = ( m_b & SDL_BUTTON(3) ) != 0;
246 // need to convert from window space to logical space
247 #if SDL_MAJOR_VERSION == 1
248 #else
249 this->convertWindowToLogical(m_x, m_y);
250 #endif*/
251 *m_x = this->m_pos_x;
252 *m_y = this->m_pos_y;
253 *m_left = this->m_down_left;
254 *m_middle = this->m_down_middle;
255 *m_right = this->m_down_right;
256 /*if( *m_left ) {
257 LOG("mouse down at: %d, %d\n", *m_x, *m_y);
258 }*/
259 //LOG("Screen::getMouseState: %d, %d\n", *m_x, *m_y);
260 return ( *m_left || *m_middle || *m_right );
261 }
262
Application()263 Application::Application() : quit(false), blank_mouse(false), compute_fps(false), fps(0.0f), last_time(0) {
264 // uncomment to display fps
265 //compute_fps = true;
266 }
267
~Application()268 Application::~Application() {
269 LOG("quit SDL\n");
270 SDL_Quit();
271 }
272
init()273 bool Application::init() {
274 #if defined(_WIN32)
275 _putenv("SDL_VIDEO_CENTERED=0,0");
276 #elif defined(__MORPHOS__)
277 #else
278 setenv("SDL_VIDEO_CENTERED", "0,0", 1);
279 #endif
280 if( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) == -1 ) {
281 LOG("SDL_Init failed\n");
282 return false;
283 }
284 return true;
285 }
286
getTicks() const287 unsigned int Application::getTicks() const {
288 return SDL_GetTicks();
289 }
290
delay(unsigned int time)291 void Application::delay(unsigned int time) {
292 SDL_Delay(time);
293 }
294
295 const int TICK_INTERVAL = 16; // 62.5 fps max
296
wait()297 void Application::wait() {
298 unsigned int now = game_g->getApplication()->getTicks();
299 // use unsigned int and compare by subtraction to avoid overflow problems - see http://blogs.msdn.com/b/oldnewthing/archive/2005/05/31/423407.aspx, http://www.gammon.com.au/millis
300 unsigned int elapsed = now - last_time;
301 unsigned int delay = 0;
302 if( elapsed >= TICK_INTERVAL ) {
303 // fine, we've already passed TICK_INTERVAL
304 }
305 else {
306 delay = TICK_INTERVAL - elapsed;
307 game_g->getApplication()->delay(delay);
308 }
309 last_time = now + delay;
310 }
311
runMainLoop()312 void Application::runMainLoop() {
313 unsigned int elapsed_time = game_g->getApplication()->getTicks();
314
315 SDL_Event event;
316 quit = false;
317 int last_fps_time = clock();
318 const int fps_frames_c = 50;
319 int frames = 0;
320 while(!quit) {
321 if( compute_fps && frames == fps_frames_c ) {
322 int new_fps_time = clock();
323 float t = ((float)(new_fps_time - last_fps_time)) / (float)CLOCKS_PER_SEC;
324 this->fps = fps_frames_c / t;
325 //LOG("FPS: %f\n", fps);
326 frames = 0;
327 last_fps_time = new_fps_time;
328 }
329 frames++;
330
331 updateSound();
332
333 // draw screen
334 game_g->drawGame();
335
336 /* wait() to avoid 100% CPU - it's debatable whether we should do this,
337 * due to risk of SDL_Delay waiting too long, but since Gigalomania
338 * doesn't really need high frame rate, might as well avoid using full
339 * CPU. Also good for battery-life on mobile platforms.
340 * This also has the side-effect of meaning we don't call updateGame()
341 * with too small timestep - we have a minimum step of at least
342 * TICK_INTERVAL.
343 */
344 wait();
345
346 unsigned int new_time = game_g->getApplication()->getTicks();
347 //LOG("%d, %d\n", new_time, new_time - elapsed_time);
348 if( !game_g->isPaused() ) {
349 game_g->updateTime(new_time - elapsed_time);
350 }
351 else
352 game_g->updateTime(0);
353 elapsed_time = new_time;
354
355 // user input
356 while( SDL_PollEvent(&event) == 1 ) {
357 switch (event.type) {
358 case SDL_QUIT:
359 // SDL_QUIT may mean a message from the OS (e.g., OS shutting down) - so we quit immediately, saving the current state
360 // It will also be sent if the user clicks the window close button, but reasonable to also interpret this as "quit immediately"
361 // Also important for Android to quit immediately, as SDL_QUIT is sent when the screen is locked - if we don't quit immediately,
362 // the app hangs when the screen is unlocked.
363 game_g->requestQuit(true);
364 break;
365 case SDL_KEYDOWN:
366 {
367 #if SDL_MAJOR_VERSION == 1
368 SDL_keysym key = event.key.keysym;
369 #else
370 SDL_Keysym key = event.key.keysym;
371 #endif
372 if( key.sym == SDLK_ESCAPE || key.sym == SDLK_q
373 #if SDL_MAJOR_VERSION == 1
374 #else
375 || key.sym == SDLK_AC_BACK // SDLK_AC_BACK required for Android
376 #endif
377 ) {
378 game_g->requestQuit(false);
379 }
380 else if( key.sym == SDLK_p ) {
381 game_g->togglePause();
382 }
383 else if( key.sym == SDLK_RETURN ) {
384 game_g->keypressReturn();
385 }
386 break;
387 }
388 case SDL_MOUSEBUTTONDOWN:
389 {
390 //LOG("received mouse down\n");
391 int m_x = event.button.x;
392 int m_y = event.button.y;
393 game_g->getScreen()->setMousePos(m_x, m_y);
394 bool m_left = false, m_middle = false, m_right = false;
395 Uint8 button = event.button.button;
396 if( button == SDL_BUTTON_LEFT ) {
397 m_left = true;
398 game_g->getScreen()->setMouseLeft(true);
399 }
400 else if( button == SDL_BUTTON_MIDDLE ) {
401 m_middle = true;
402 game_g->getScreen()->setMouseMiddle(true);
403 }
404 else if( button == SDL_BUTTON_RIGHT ) {
405 m_right = true;
406 game_g->getScreen()->setMouseRight(true);
407 }
408
409 if( game_g->isPaused() ) {
410 // click automatically unpaused (needed to work without keyboard!)
411 game_g->togglePause();
412 }
413 else if( m_left || m_middle || m_right ) {
414 /*int m_x = 0, m_y = 0;
415 game_g->getScreen()->getMouseCoords(&m_x, &m_y);*/
416 //LOG("received mouse click: %d, %d\n", m_x, m_y);
417 game_g->mouseClick(m_x, m_y, m_left, m_middle, m_right, true);
418 }
419
420 break;
421 }
422 case SDL_MOUSEBUTTONUP:
423 {
424 //LOG("received mouse up\n");
425 Uint8 button = event.button.button;
426 if( button == SDL_BUTTON_LEFT ) {
427 game_g->getScreen()->setMouseLeft(false);
428 }
429 else if( button == SDL_BUTTON_MIDDLE ) {
430 game_g->getScreen()->setMouseMiddle(false);
431 }
432 else if( button == SDL_BUTTON_RIGHT ) {
433 game_g->getScreen()->setMouseRight(false);
434 }
435 break;
436 }
437 case SDL_MOUSEMOTION:
438 {
439 int old_m_x = 0, old_m_y = 0;
440 game_g->getScreen()->getMouseCoords(&old_m_x, &old_m_y);
441 int m_x = event.motion.x;
442 int m_y = event.motion.y;
443 //LOG(" mouse motion %d, %d\n", m_x, m_y);
444 //LOG(" old %d, %d\n", old_m_x, old_m_y);
445 // can't use SDL_TOUCH_MOUSEID, as event.motion.which doesn't seem to be supported for Windows 8
446 // need to allow some tolerance, as sometimes we get a rounding issue when calculating the coordinates from tfinger, when the window size isn't 1:1
447 int diff_x = abs(m_x - old_m_x);
448 int diff_y = abs(m_y - old_m_y);
449 if( diff_x > 1 || diff_y > 1 ) {
450 //LOG(" unblank\n");
451 //LOG(" mouse motion %d, %d\n", m_x, m_y);
452 //LOG(" old %d, %d\n", old_m_x, old_m_y);
453 this->blank_mouse = false;
454 }
455 game_g->getScreen()->setMousePos(m_x, m_y);
456 break;
457 }
458 #if SDL_MAJOR_VERSION == 1
459 #else
460 // support for touchscreens
461 // when a touch even occurs, we receive SDL_FINGERDOWN
462 // when the touch is released, we receive SDL_FINGERUP, followed by SDL_MOUSEBUTTONDOWN then SDL_MOUSEBUTTONUP, then SDL_MOUSEMOTION
463 case SDL_FINGERDOWN:
464 {
465 //LOG("received fingerdown: %f , %f\n", event.tfinger.x, event.tfinger.y);
466 int window_width = 0, window_height = 0;
467 game_g->getScreen()->getWindowSize(&window_width, &window_height);
468 int m_x = (int)(event.tfinger.x*window_width);
469 int m_y = (int)(event.tfinger.y*window_height);
470 //LOG(" %d, %d\n", m_x, m_y);
471 game_g->getScreen()->convertWindowToLogical(&m_x, &m_y);
472 //LOG(" logical %d, %d\n", m_x, m_y);
473 game_g->getScreen()->setMousePos(m_x, m_y);
474 game_g->getScreen()->setMouseLeft(true);
475 this->blank_mouse = true;
476 break;
477 }
478 case SDL_FINGERUP:
479 {
480 //LOG("received fingerup\n");
481 game_g->getScreen()->setMouseLeft(false);
482 this->blank_mouse = true;
483 // n.b., "click" is handled via SDL_MOUSEBUTTONUP
484 break;
485 }
486 case SDL_FINGERMOTION:
487 {
488 //LOG("received fingermotion: %f , %f\n", event.tfinger.x, event.tfinger.y);
489 int window_width = 0, window_height = 0;
490 game_g->getScreen()->getWindowSize(&window_width, &window_height);
491 int m_x = (int)(event.tfinger.x*window_width);
492 int m_y = (int)(event.tfinger.y*window_height);
493 //LOG(" %d, %d\n", m_x, m_y);
494 game_g->getScreen()->convertWindowToLogical(&m_x, &m_y);
495 //LOG(" logical %d, %d\n", m_x, m_y);
496 game_g->getScreen()->setMousePos(m_x, m_y);
497 this->blank_mouse = true;
498 break;
499 }
500 #endif
501 #if SDL_MAJOR_VERSION == 1
502 case SDL_ACTIVEEVENT:
503 #ifndef AROS
504 // disabled for AROS, as we receive inactive events when the mouse goes outside the window!
505 if( (event.active.state & SDL_APPINPUTFOCUS) != 0 || (event.active.state & SDL_APPACTIVE) != 0 ) {
506 if( event.active.gain == 1 ) {
507 // activate
508 game_g->activate();
509 }
510 else if( event.active.gain == 0 ) {
511 // inactive
512 game_g->deactivate();
513 }
514 }
515 #endif
516 break;
517 #else
518 case SDL_WINDOWEVENT:
519 if( event.window.event == SDL_WINDOWEVENT_SHOWN || event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED ) {
520 // activate
521 game_g->activate();
522 }
523 else if( event.window.event == SDL_WINDOWEVENT_HIDDEN || event.window.event == SDL_WINDOWEVENT_FOCUS_LOST ) {
524 // inactive
525 game_g->deactivate();
526 }
527 break;
528 #endif
529 }
530 }
531 SDL_PumpEvents();
532
533 game_g->updateGame();
534 }
535 }
536
537 //#endif
538