1 /*
2 Copyright (c) 2009 Andrew Caudwell (acaudwell@gmail.com)
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "sdlapp.h"
29 #include "display.h"
30 #include "logger.h"
31 #include "SDL_syswm.h"
32
33 std::string gSDLAppResourceDir;
34 std::string gSDLAppConfDir;
35
36 #ifdef _WIN32
37 std::string gSDLAppPathSeparator = "\\";
38 #else
39 std::string gSDLAppPathSeparator = "/";
40 #endif
41
42 std::string gSDLAppTitle = "SDL App";
43 std::string gSDLAppExec = "sdlapp";
44
45 #ifdef _WIN32
46
47 HWND SDLApp::console_window = 0;
48 bool SDLApp::existing_console = false;
49
showConsole(bool show)50 void SDLApp::showConsole(bool show) {
51 if(!SDLApp::console_window) return;
52
53 ShowWindow( SDLApp::console_window, show);
54 }
55
initConsole()56 void SDLApp::initConsole() {
57 if(SDLApp::console_window != 0) return;
58
59 SDLApp::console_window = GetConsoleWindow();
60
61 // check if this is a new console or not
62 CONSOLE_SCREEN_BUFFER_INFO buffer_info;
63 if(GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &buffer_info )) {
64 existing_console = !(buffer_info.dwCursorPosition.X==0 && buffer_info.dwCursorPosition.Y==0);
65 // assume TERM env implies this is an existing terminal
66 } else if(getenv("TERM") != 0) {
67 existing_console = true;
68 }
69
70 // don't customize the console unless it was created for this program
71 if(existing_console) return;
72
73 //disable the close button so the user cant crash the application
74 HMENU hm = GetSystemMenu(SDLApp::console_window, false);
75 DeleteMenu(hm, SC_CLOSE, MF_BYCOMMAND);
76
77 //set title
78
79 char consoleTitle[512];
80 int console_suffix = 0;
81 sprintf(consoleTitle, "%s Console", gSDLAppTitle.c_str());
82
83 while(FindWindow(0, consoleTitle)) {
84 sprintf(consoleTitle, "%s Console %d", gSDLAppTitle.c_str(), ++console_suffix);
85 }
86
87 SetConsoleTitle(consoleTitle);
88 }
89
resizeConsole(int height)90 void SDLApp::resizeConsole(int height) {
91 if(SDLApp::console_window !=0 && !existing_console) {
92 RECT windowRect;
93 if(GetWindowRect(SDLApp::console_window, &windowRect)) {
94 float width = windowRect.right - windowRect.left;
95 MoveWindow(SDLApp::console_window,windowRect.left,windowRect.top,width,height,true);
96 }
97 }
98 }
99
100 #endif
101
SDLAppDirExists(std::string dir)102 bool SDLAppDirExists(std::string dir) {
103 struct stat st;
104 return !stat(dir.c_str(), &st) && S_ISDIR(st.st_mode);
105 }
106
SDLAppAddSlash(std::string path)107 std::string SDLAppAddSlash(std::string path) {
108
109 //append slash unless the path is empty
110 if(path.size() && path[path.size()-1] != gSDLAppPathSeparator[0]) {
111 path += gSDLAppPathSeparator;
112 }
113
114 return path;
115 }
116
117 //info message
SDLAppInfo(std::string msg)118 void SDLAppInfo(std::string msg) {
119 #ifdef _WIN32
120 SDLApp::showConsole(true);
121 #endif
122
123 printf("%s\n", msg.c_str());
124
125 #ifdef _WIN32
126 if(!SDLApp::existing_console) {
127 printf("\nPress Enter\n");
128 getchar();
129 }
130 #endif
131
132 exit(0);
133 }
134
135 //display error only
SDLAppQuit(std::string error)136 void SDLAppQuit(std::string error) {
137 SDL_Quit();
138
139 #ifdef _WIN32
140 SDLApp::showConsole(true);
141 #endif
142
143 fprintf(stderr, "%s: %s\n", gSDLAppExec.c_str(), error.c_str());
144 fprintf(stderr, "Try '%s --help' for more information.\n\n", gSDLAppExec.c_str());
145
146 #ifdef _WIN32
147 if(!SDLApp::existing_console) {
148 fprintf(stderr, "Press Enter\n");
149 getchar();
150 }
151 #endif
152
153 exit(1);
154 }
155
getClipboardText(std::string & text)156 bool SDLApp::getClipboardText(std::string& text) {
157
158 #if SDL_VERSION_ATLEAST(2,0,0)
159 char* clipboard_text = SDL_GetClipboardText();
160
161 if(clipboard_text!=0) {
162 text = std::string(clipboard_text);
163 } else {
164 text.resize(0);
165 }
166
167 return true;
168 #else
169 return false;
170 #endif
171 }
172
setClipboardText(const std::string & text)173 void SDLApp::setClipboardText(const std::string& text) {
174 #if SDL_VERSION_ATLEAST(2,0,0)
175 SDL_SetClipboardText(text.c_str());
176 #endif
177 }
178
SDLAppInit(std::string apptitle,std::string execname,std::string exepath)179 void SDLAppInit(std::string apptitle, std::string execname, std::string exepath) {
180 gSDLAppTitle = apptitle;
181 gSDLAppExec = execname;
182
183 std::string conf_dir = "";
184 std::string resource_dir = "data/";
185 std::string fonts_dir = "data/fonts/";
186 std::string shaders_dir = "data/shaders/";
187
188 #ifdef _WIN32
189
190 char szAppPath[MAX_PATH];
191 GetModuleFileName(0, szAppPath, MAX_PATH);
192
193 // Extract directory
194 std::string winexepath = std::string(szAppPath);
195
196 int pos = winexepath.rfind("\\");
197
198 std::string path = winexepath.substr(0, pos+1);
199 conf_dir = path + std::string("\\");
200 resource_dir = path + std::string("\\data\\");
201 fonts_dir = path + std::string("\\data\\fonts\\");
202 shaders_dir = path + std::string("\\data\\shaders\\");
203 #else
204 //get working directory
205 char cwd_buff[1024];
206
207 if(getcwd(cwd_buff, 1024) == cwd_buff) {
208 conf_dir = std::string(cwd_buff) + std::string("/");
209 resource_dir = std::string(cwd_buff) + std::string("/") + resource_dir;
210 fonts_dir = std::string(cwd_buff) + std::string("/") + fonts_dir;
211 shaders_dir = std::string(cwd_buff) + std::string("/") + shaders_dir;
212 }
213
214 #endif
215
216 #ifdef SDLAPP_CONF_DIR
217 if (SDLAppDirExists(SDLAPP_CONF_DIR)) {
218 conf_dir = SDLAPP_CONF_DIR;
219 }
220 #endif
221
222 #ifdef SDLAPP_RESOURCE_DIR
223 if (SDLAppDirExists(SDLAPP_RESOURCE_DIR)) {
224 resource_dir = SDLAPP_RESOURCE_DIR;
225 fonts_dir = SDLAPP_RESOURCE_DIR + std::string("/fonts/");
226 shaders_dir = SDLAPP_RESOURCE_DIR + std::string("/shaders/");
227 }
228 else if(!exepath.empty()) {
229 // make resource path relative to the directory of the executable
230 // if the resource directory doesn't exist
231 size_t pos = exepath.rfind("/");
232 if (pos != std::string::npos) {
233 std::string path = exepath.substr(0, pos+1);
234 resource_dir = path + std::string("data/");
235 fonts_dir = path + std::string("data/fonts/");
236 shaders_dir = path + std::string("data/shaders/");
237 }
238 }
239 #endif
240
241 #ifdef SDLAPP_FONT_DIR
242 if (SDLAppDirExists(SDLAPP_FONT_DIR)) {
243 fonts_dir = SDLAPP_FONT_DIR;
244 }
245 #endif
246
247 resource_dir = SDLAppAddSlash(resource_dir);
248 conf_dir = SDLAppAddSlash(conf_dir);
249 fonts_dir = SDLAppAddSlash(fonts_dir);
250 shaders_dir = SDLAppAddSlash(shaders_dir);
251
252 texturemanager.setDir(resource_dir);
253 fontmanager.setDir(fonts_dir);
254 shadermanager.setDir(shaders_dir);
255
256 gSDLAppResourceDir = resource_dir;
257 gSDLAppConfDir = conf_dir;
258 gSDLAppShaderDir = shaders_dir;
259
260 fontmanager.init();
261 }
262
SDLAppParseArgs(int argc,char * argv[],int * xres,int * yres,bool * fullscreen,std::vector<std::string> * otherargs)263 void SDLAppParseArgs(int argc, char *argv[], int* xres, int* yres, bool* fullscreen, std::vector<std::string>* otherargs) {
264
265 for (int i=1; i<argc; i++) {
266 debugLog("argv[%d] = %s", i, argv[i]);
267
268 std::string args(argv[i]);
269
270 if (args == "-f") {
271 *fullscreen = true;
272 continue;
273 }
274 else if (args == "-w") {
275 *fullscreen = false;
276 continue;
277 }
278
279 //get video mode
280 if(args.size()>1 && args[0] == '-' && args.rfind("x") != std::string::npos) {
281
282 std::string displayarg = args;
283
284 while(displayarg.size()>1 && displayarg[0] == '-') {
285 displayarg = displayarg.substr(1, displayarg.size()-1);
286 }
287
288 size_t x = displayarg.rfind("x");
289
290 if(x != std::string::npos) {
291 std::string widthstr = displayarg.substr(0, x);
292 std::string heightstr = displayarg.substr(x+1);
293
294 int width = atoi(widthstr.c_str());
295 int height = atoi(heightstr.c_str());
296
297 if(width>0 && height>0) {
298 debugLog("w=%d, h=%d",width,height);
299
300 *xres = width;
301 *yres = height;
302 continue;
303 }
304 }
305 }
306
307 // non display argument
308 if(otherargs != 0) {
309 otherargs->push_back(args);
310 }
311 }
312 }
313
SDLApp()314 SDLApp::SDLApp() {
315 fps=0;
316 return_code=0;
317 appFinished=false;
318 min_delta_msec = 8;
319 }
320
updateFramerate()321 void SDLApp::updateFramerate() {
322 if(fps_updater>0) {
323 fps = (float)frame_count / (float)fps_updater * 1000.0f;
324 } else {
325 fps = 0;
326 }
327 fps_updater = 0;
328 frame_count = 0;
329 }
330
isFinished()331 bool SDLApp::isFinished() {
332 return appFinished;
333 }
334
returnCode()335 int SDLApp::returnCode() {
336 return return_code;
337 }
338
stop(int return_code)339 void SDLApp::stop(int return_code) {
340 this->return_code = return_code;
341 appFinished=true;
342 }
343
handleEvent(SDL_Event & event)344 bool SDLApp::handleEvent(SDL_Event& event) {
345
346 switch(event.type) {
347 case SDL_QUIT:
348 quit();
349 break;
350
351 case SDL_MOUSEMOTION:
352 mouseMove(&event.motion);
353 break;
354
355 #if SDL_VERSION_ATLEAST(2,0,0)
356 case SDL_TEXTINPUT:
357 textInput(&event.text);
358 break;
359
360 case SDL_TEXTEDITING:
361 textEdit(&event.edit);
362 break;
363
364 case SDL_MOUSEWHEEL:
365 mouseWheel(&event.wheel);
366 break;
367
368 case SDL_WINDOWEVENT:
369 if(event.window.event == SDL_WINDOWEVENT_RESIZED) {
370 resize(event.window.data1, event.window.data2);
371 }
372 break;
373 #else
374 case SDL_VIDEORESIZE:
375 resize(event.resize.w, event.resize.h);
376 break;
377 #endif
378
379 case SDL_MOUSEBUTTONDOWN:
380 case SDL_MOUSEBUTTONUP:
381 mouseClick(&event.button);
382 break;
383
384 case SDL_KEYDOWN:
385 case SDL_KEYUP:
386 keyPress(&event.key);
387 break;
388
389 default:
390 return false;
391 }
392
393 return true;
394 }
395
run()396 int SDLApp::run() {
397
398 Uint32 msec=0, last_msec=0, buffer_msec=0, total_msec = 0;
399
400 frame_count = 0;
401 fps_updater = 0;
402
403 if(!appFinished) init();
404
405 msec = SDL_GetTicks();
406 last_msec = msec;
407
408 #if SDL_VERSION_ATLEAST(2,0,0)
409 //text input seems to be enabled by default, turn it off
410 SDL_StopTextInput();
411 #endif
412
413 while(!appFinished) {
414 last_msec = msec;
415 msec = SDL_GetTicks();
416
417 Uint32 delta_msec = msec - last_msec;
418
419 // cant have delta ticks be less than 8ms
420 buffer_msec += delta_msec;
421 if(buffer_msec < min_delta_msec) {
422 SDL_Delay(1);
423 continue;
424 }
425
426 delta_msec = buffer_msec;
427 buffer_msec =0;
428
429 //determine time elapsed since last time we were here
430 total_msec += delta_msec;
431
432 float t = total_msec / 1000.0f;
433 float dt = delta_msec / 1000.0f;
434
435 fps_updater += delta_msec;
436
437 //update framerate if a second has passed
438 if (fps_updater >= 1000) {
439 updateFramerate();
440 }
441
442 //process new events
443 SDL_Event event;
444 while ( SDL_PollEvent(&event) ) {
445 handleEvent(event);
446 }
447
448 update(t, dt);
449
450 //update display
451 display.update();
452 frame_count++;
453 }
454
455 return return_code;
456 }
457