Merge branch 'better-than-par'
[hex-a-hop.git] / gfx.cpp
blob0c8e5ae58d6109f85ae2afd39e402a8463587341
1 /*
2 Copyright (C) 2005-2007 Tom Beaumont
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
19 #include "i18n.h"
21 #include "state.h"
22 #include <cassert>
24 #ifdef WIN32
25 #include <SDL_syswm.h>
26 #include <shellapi.h> // Windows header for drag & drop
27 #ifdef USE_BBTABLET
28 #include "bbtablet/bbtablet.h"
29 #endif
30 #else
31 #undef USE_BBTABLET
32 #endif
34 // If included multiple times:
35 // BUG: multiple definition of `MATRIX_WHITE_BACK'
36 // see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=437517
37 #include "SDL_Pango.h"
39 #include <algorithm>
40 #include <string>
42 #ifndef DATA_DIR
43 #define DATA_DIR "."
44 #endif
46 StateMakerBase* StateMakerBase::first = 0;
47 State* StateMakerBase::current = 0;
49 int SDL_focus = SDL_APPACTIVE | SDL_APPINPUTFOCUS; // Initial focus state
51 #ifdef WIN32
52 #include <windows.h>
53 #include <winuser.h>
54 #include <commdlg.h>
55 #include <direct.h>
57 bool tablet_system = false;
59 char* LoadSaveDialog(bool save, bool levels, const char * title)
61 OPENFILENAME f;
62 static char filename[1025] = "";
63 static char path[1025] = "C:\\WINDOWS\\Desktop\\New Folder\\Foo\\Levels";
64 char backupPath[1025];
65 _getcwd(backupPath, sizeof(backupPath)/sizeof(backupPath[0])-1);
67 memset(&f, 0, sizeof(f));
69 #define FILTER(desc, f) desc " (" f ")\0" f "\0"
70 f.lpstrFilter = FILTER("All known files","*.lev;*.sol")
71 FILTER("Level files","*.lev")
72 FILTER("Solution files","*.sol")
73 FILTER("All files","*.*");
74 #undef FILTER
76 f.lStructSize = sizeof(f);
77 f.lpstrFile = filename;
78 f.nMaxFile = sizeof(filename);
79 f.lpstrInitialDir = path;
80 f.lpstrTitle = title;
82 if (GetSaveFileName(&f)==TRUE)
84 // Remember user's choice of path!
85 _getcwd(path, sizeof(path)/sizeof(path[0])-1);
87 if (save)
89 int i = strlen(filename)-1;
90 while (i>0 && filename[i]!='.' && filename[i]!='\\' && filename[i]!='/') i--;
91 if (filename[i]!='.' && levels)
92 strcat(filename, ".lev");
93 if (filename[i]!='.' && !levels)
94 strcat(filename, ".sol");
96 _chdir(backupPath);
97 return filename;
100 _chdir(backupPath);
101 return 0;
103 #else
104 char* LoadSaveDialog(bool /*save*/, bool /*levels*/, const char * /*title*/)
106 return 0;
108 #endif
110 extern void test();
112 int mouse_buttons = 0;
113 int mousex= 10, mousey = 10;
114 int noMouse = 0;
115 int quitting = 0;
117 double stylusx= 0, stylusy= 0;
118 int stylusok= 0;
119 float styluspressure = 0;
120 SDL_Surface * screen = 0;
121 SDL_Surface * realScreen = 0;
122 SDLPango_Context *context = 0;
124 extern State* MakeWorld();
126 bool fullscreen = false;
128 void InitScreen()
130 #ifdef USE_OPENGL
131 SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
132 SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
133 SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
134 SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
135 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
137 // printf("SDL_SetVideoMode (OpenGL)\n");
138 realScreen = SDL_SetVideoMode(
139 SCREEN_W, SCREEN_H, // Width, Height
140 0, // Current BPP
141 SDL_OPENGL | (fullscreen ? SDL_FULLSCREEN : 0) );
142 #else
143 // printf("SDL_SetVideoMode (non-OpenGL)\n");
144 realScreen = SDL_SetVideoMode(
145 SCREEN_W, SCREEN_H, // Width, Height
146 0, // Current BPP
147 SDL_SWSURFACE | SDL_DOUBLEBUF | (fullscreen ? SDL_FULLSCREEN : 0) );
148 #endif
150 if (screen)
151 SDL_FreeSurface(screen);
153 SDL_Surface* tempscreen = SDL_CreateRGBSurface(
154 SDL_SWSURFACE,
155 SCREEN_W, SCREEN_H,
156 16, 0xf800, 0x07e0, 0x001f, 0);
158 screen = SDL_DisplayFormat(tempscreen);
159 SDL_FreeSurface(tempscreen);
162 void ToggleFullscreen()
164 fullscreen = !fullscreen;
165 InitScreen();
166 StateMakerBase::current->ScreenModeChanged();
168 String base_path;
170 /// determine length of longest line with current font (wrapping allowed if text_width != -1)
171 int SDLPangoTextHeight(const std::string &text_utf8, int text_width)
173 // SDLPango_SetMinimumSize limits indeed the maximal size! See
174 // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=438691
175 SDLPango_SetMinimumSize(context, text_width, 0);
176 SDLPango_SetText(context, text_utf8.c_str(), -1);
177 return SDLPango_GetLayoutHeight(context);
180 /** \brief Determine length of longest line with current font
182 * Whether line breaks are allowed or not needs to be set before using
183 * SDLPango_SetMinimumSize!
185 int SDLPangoTextWidth(const std::string &text_utf8)
187 SDLPango_SetText(context, text_utf8.c_str(), -1);
188 return SDLPango_GetLayoutWidth(context);
191 /// Display the specified UTF-8 text left aligned at (x,y)
192 void Print_Pango(int x, int y, const std::string &text_utf8)
194 // Workaround for possible crash, see
195 // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439071
196 if (text_utf8.size() == 0 || text_utf8.size() == 1 && text_utf8[0]==127)
197 return;
198 assert(text_utf8.find("\n") == std::string::npos);
199 SDLPango_SetMinimumSize(context, SCREEN_W, 0);
200 SDLPango_SetText(context, text_utf8.c_str(), -1);
201 SDL_Surface *surface = SDLPango_CreateSurfaceDraw(context);
202 SDL_Rect dst = {x, y, 1, 1};
203 SDL_BlitSurface(surface, NULL, screen, &dst);
204 SDL_FreeSurface(surface);
207 /** \brief Display the specified UTF-8 text according to the alignment
209 * If line breaks are already properly set (manually) the will be respected
210 * and no new line breaks will be added. This assumes that th text is not too
211 * wide.
213 * \param x the displayed text is horizontally centered around x
214 * \param y the displayed text starts at y
215 * \param width background window size into which the text needs to fit
216 * \param text_utf8 the text to be displayed, in UTF8 encoding
217 * \param align=1: horizontally centered around (x,y)
218 * */
219 void Print_Pango_Aligned(int x, int y, int width, const std::string &text_utf8, int align)
221 // Workaround for possible crash, see
222 // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439071
223 if (text_utf8.size() == 0 || text_utf8.size() == 1 && text_utf8[0]==127)
224 return;
225 if (width<=0)
226 return;
227 SDLPango_SetMinimumSize(context, width, 0);
228 int real_width = SDLPangoTextWidth(text_utf8);
229 // Workaround for a crash in SDL Pango, see
230 // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439855
231 if (real_width>width)
232 SDLPango_SetMinimumSize(context, real_width, 0);
234 SDLPango_Alignment alignment;
235 if (align==0)
236 alignment = SDLPANGO_ALIGN_LEFT;
237 else if (align==2) {
238 alignment = SDLPANGO_ALIGN_RIGHT;
239 x -= width;
240 } else {
241 alignment = SDLPANGO_ALIGN_CENTER;
242 x -= width/2;
244 // SDLPango_SetText_GivenAlignment is not (yet?) part of the official Pango
245 // distribution, see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=437865
246 SDLPango_SetText_GivenAlignment(context, text_utf8.c_str(), -1, alignment);
247 SDL_Surface *surface = SDLPango_CreateSurfaceDraw(context);
248 SDL_Rect dst = {x, y, 1, 1};
249 SDL_BlitSurface(surface, NULL, screen, &dst);
250 SDL_FreeSurface(surface);
253 int TickTimer()
255 static int time = SDL_GetTicks();
256 int cap=40;
258 int x = SDL_GetTicks() - time;
259 time += x;
260 if (x<0) x = 0, time = SDL_GetTicks();
261 if (x>cap) x = cap;
263 return x;
266 int main(int /*argc*/, char * /*argv*/[])
268 base_path = DATA_DIR "/";
269 for (int i=strlen(base_path)-1; i>=0; i--)
270 if (base_path[i]=='/' || base_path[i]=='\\')
272 base_path.truncate(i+1);
273 break;
275 // Check the path ends with a directory seperator
276 if (strlen(base_path)>0)
278 char last = base_path[strlen(base_path)-1];
279 if (last!='/' && last!='\\')
280 base_path = "";
282 #ifdef WIN32
283 if (strstr(base_path, "\\foo2___Win32_Debug\\"))
284 strstr(base_path, "\\foo2___Win32_Debug\\")[1] = '\0';
285 if (strstr(base_path, "\\Release\\"))
286 strstr(base_path, "\\Release\\")[1] = '\0';
287 #endif
288 // printf("SDL_Init\n");
291 // Experimental - create a splash screen window whilst loading
292 SDL_Init(SDL_INIT_VIDEO);
293 screen = SDL_SetVideoMode( 200,200,0,SDL_NOFRAME );
294 SDL_Rect r = {0,0,200,200};
295 SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 0, 0, 50));
296 SDL_Flip(screen);
299 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
300 SDLPango_Init();
301 context = SDLPango_CreateContext_GivenFontDesc("sans-serif bold 12");
302 SDLPango_SetDefaultColor(context, MATRIX_TRANSPARENT_BACK_WHITE_LETTER);
303 SDLPango_SetMinimumSize(context, SCREEN_W, 0);
305 SDL_Surface* icon = SDL_LoadBMP("graphics/icon.bmp");
306 if (icon)
308 static unsigned int mask[32] = {
309 0x00001fc0,
310 0x00003fe0,
311 0x00007ff0,
312 0x00007df8,
313 0x0000f0f8,
314 0x0000f07c,
315 0x0005f87c,
316 0x0fbfff3c,
318 0x1ffffffe,
319 0x3ffffffe,
320 0x3ffffffe,
321 0x7ffffffe,
322 0x7ffffffe,
323 0x7ffffffe,
324 0x7ffffffe,
325 0xefffffff,
327 0x1fffffff,
328 0x3fffffff,
329 0x3fffffff,
330 0x3fffffff,
331 0x3fffffff,
332 0x3fffffff,
333 0x3fffffff,
334 0x3ffffffe,
336 0x3ffffff8,
337 0x3ffffff0,
338 0x3ffffff0,
339 0x3ffffff0,
340 0x3fffffe0,
341 0x3fffffe0,
342 0x1ffffff0,
343 0x1ffffff1,
345 for (int i=0; i<32; i++)
346 mask[i] = mask[i]>>24 | (mask[i]>>8)&0xff00 | (mask[i]<<8)&0xff0000 | (mask[i]<<24)&0xff000000;
347 SDL_WM_SetIcon(icon, (unsigned char*) mask);
348 SDL_FreeSurface(icon);
351 InitScreen();
353 SDL_WarpMouse(SCREEN_W/2, SCREEN_H/2);
355 int videoExposed = 1;
357 #ifdef WIN32
358 HWND hwnd = 0;
359 #endif
360 #ifdef USE_BBTABLET
361 bbTabletDevice &td = bbTabletDevice::getInstance( );
362 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
363 #endif
365 // printf("Main loop...\n");
367 StateMakerBase::GetNew();
369 while(!quitting)
371 SDL_Event e;
372 while(!SDL_PollEvent(&e) && !quitting)
374 int x = 0;
376 if ((SDL_focus & 6)==6)
378 videoExposed = 1;
380 x = TickTimer();
382 while (x<10)
384 SDL_Delay(10-x);
385 x += TickTimer();
388 StateMakerBase::current->Update(x / 1000.0);
390 else
392 // Not focussed. Try not to eat too much CPU!
393 SDL_Delay(150);
396 // experimental...
397 if (!noMouse)
398 StateMakerBase::current->Mouse(mousex, mousey, 0, 0, 0, 0, mouse_buttons);
400 if (videoExposed)
402 StateMakerBase::current->Render();
404 #ifdef USE_OPENGL
405 SDL_GL_SwapBuffers();
406 #else
407 if (screen && realScreen!=screen)
409 SDL_Rect r = {0,0,SCREEN_W,SCREEN_H};
410 SDL_BlitSurface(screen, &r, realScreen, &r);
412 SDL_Flip(realScreen);
413 #endif
414 videoExposed = 0;
417 SDL_Delay(10);
419 #ifdef USE_BBTABLET
420 // Tablet ////////////////////////
421 bbTabletEvent evt;
422 while(hwnd!=NULL && td.getNextEvent(evt))
424 stylusok = 1;
425 RECT r;
426 if (tablet_system)
428 GetWindowRect(hwnd, &r);
429 stylusx = evt.x * GetSystemMetrics(SM_CXSCREEN);
430 stylusy = (1.0 - evt.y) * GetSystemMetrics(SM_CYSCREEN);
431 stylusx -= (r.left + GetSystemMetrics(SM_CXFIXEDFRAME));
432 stylusy -= (r.top + GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYCAPTION));;
434 else
436 GetClientRect(hwnd, &r);
437 stylusx = evt.x * r.right;
438 stylusy = (1.0 - evt.y) * r.bottom;
440 styluspressure = (evt.buttons & 1) ? evt.pressure : 0;
443 printf("id=%d csrtype=%d b=%x (%0.3f, %0.3f, %0.3f) p=%0.3f tp=%0.3f\n",
444 evt.id,
445 evt.type,
446 evt.buttons,
447 evt.x,
448 evt.y,
449 evt.z,
450 evt.pressure,
451 evt.tpressure
456 #endif
459 switch (e.type)
461 case SDL_VIDEOEXPOSE:
462 videoExposed = 1;
463 break;
465 #ifdef WIN32
466 case SDL_SYSWMEVENT:
468 SDL_SysWMmsg* m = e.syswm.msg;
469 hwnd = m->hwnd;
470 static bool init=false;
471 if (!init)
473 init = true;
474 DragAcceptFiles(hwnd, TRUE);
475 #ifdef USE_BBTABLET
476 td.initTablet(hwnd, tablet_system ? bbTabletDevice::SYSTEM_POINTER : bbTabletDevice::SEPARATE_POINTER );
477 if (!td.isValid())
478 printf("No tablet/driver found\n");
479 #endif
481 if (m->msg == WM_DROPFILES)
483 HDROP h = (HDROP)m->wParam;
485 char name[512];
486 if (DragQueryFile(h, 0xffffffff, 0, 0) == 1)
488 DragQueryFile(h, 0, name, sizeof(name)/sizeof(name[0]));
490 StateMakerBase::current->FileDrop(name);
493 DragFinish(h);
496 break;
498 #endif
500 case SDL_ACTIVEEVENT:
502 int gain = e.active.gain ? e.active.state : 0;
503 int loss = e.active.gain ? 0 : e.active.state;
504 SDL_focus = (SDL_focus | gain) & ~loss;
505 if (gain & SDL_APPACTIVE)
506 StateMakerBase::current->ScreenModeChanged();
507 if (loss & SDL_APPMOUSEFOCUS)
508 noMouse = 1;
509 else if (gain & SDL_APPMOUSEFOCUS)
510 noMouse = 0;
512 break;
515 case SDL_MOUSEMOTION:
516 noMouse = false;
517 StateMakerBase::current->Mouse(e.motion.x, e.motion.y, e.motion.x-mousex, e.motion.y-mousey, 0, 0, mouse_buttons);
518 mousex = e.motion.x; mousey = e.motion.y;
519 break;
520 case SDL_MOUSEBUTTONUP:
521 noMouse = false;
522 mouse_buttons &= ~(1<<(e.button.button-1));
523 StateMakerBase::current->Mouse(e.button.x, e.button.y, e.button.x-mousex, e.button.y-mousey,
524 0, 1<<(e.button.button-1), mouse_buttons);
525 mousex = e.button.x; mousey = e.button.y ;
526 break;
527 case SDL_MOUSEBUTTONDOWN:
528 noMouse = false;
529 mouse_buttons |= 1<<(e.button.button-1);
530 StateMakerBase::current->Mouse(e.button.x, e.button.y, e.button.x-mousex, e.button.y-mousey,
531 1<<(e.button.button-1), 0, mouse_buttons);
532 mousex = e.button.x; mousey = e.button.y ;
533 break;
535 case SDL_KEYUP:
536 StateMakerBase::current->KeyReleased(e.key.keysym.sym);
537 break;
539 case SDL_KEYDOWN:
541 SDL_KeyboardEvent & k = e.key;
543 if (k.keysym.sym==SDLK_F4 && (k.keysym.mod & KMOD_ALT))
545 quitting = 1;
547 else if (k.keysym.sym==SDLK_F12)
549 // Toggle system pointer controlled by tablet or not
550 #ifdef USE_BBTABLET
551 if (td.isValid())
553 tablet_system = !tablet_system;
554 td.setPointerMode(tablet_system ? bbTabletDevice::SYSTEM_POINTER : bbTabletDevice::SEPARATE_POINTER);
556 #endif
558 else if (k.keysym.sym==SDLK_RETURN && (k.keysym.mod & KMOD_ALT) && !(k.keysym.mod & KMOD_CTRL))
560 ToggleFullscreen();
562 else if (StateMakerBase::current->KeyPressed(k.keysym.sym, k.keysym.mod))
565 else if ((k.keysym.mod & (KMOD_ALT | KMOD_CTRL))==0)
567 StateMakerBase::GetNew(k.keysym.sym);
570 break;
572 case SDL_QUIT:
573 quitting = 1;
574 break;
578 SDLPango_FreeContext(context);
579 SDL_Quit();
580 return 0;