Show mouse cursor without font
[hex-a-hop.git] / gfx.cpp
blobff4f1add90a7fd01c4c2e3e47cec774e5dc7b112
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
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 StateMakerBase* StateMakerBase::first = 0;
43 State* StateMakerBase::current = 0;
45 int SDL_focus = SDL_APPACTIVE | SDL_APPINPUTFOCUS; // Initial focus state
47 #ifdef WIN32
48 #include <windows.h>
49 #include <winuser.h>
50 #include <commdlg.h>
51 #include <direct.h>
53 bool tablet_system = false;
55 char* LoadSaveDialog(bool save, bool levels, const char * title)
57 OPENFILENAME f;
58 static char filename[1025] = "";
59 static char path[1025] = "C:\\WINDOWS\\Desktop\\New Folder\\Foo\\Levels";
60 char backupPath[1025];
61 _getcwd(backupPath, sizeof(backupPath)/sizeof(backupPath[0])-1);
63 memset(&f, 0, sizeof(f));
65 #define FILTER(desc, f) desc " (" f ")\0" f "\0"
66 f.lpstrFilter = FILTER("All known files","*.lev;*.sol")
67 FILTER("Level files","*.lev")
68 FILTER("Solution files","*.sol")
69 FILTER("All files","*.*");
70 #undef FILTER
72 f.lStructSize = sizeof(f);
73 f.lpstrFile = filename;
74 f.nMaxFile = sizeof(filename);
75 f.lpstrInitialDir = path;
76 f.lpstrTitle = title;
78 if (GetSaveFileName(&f)==TRUE)
80 // Remember user's choice of path!
81 _getcwd(path, sizeof(path)/sizeof(path[0])-1);
83 if (save)
85 int i = strlen(filename)-1;
86 while (i>0 && filename[i]!='.' && filename[i]!='\\' && filename[i]!='/') i--;
87 if (filename[i]!='.' && levels)
88 strcat(filename, ".lev");
89 if (filename[i]!='.' && !levels)
90 strcat(filename, ".sol");
92 _chdir(backupPath);
93 return filename;
96 _chdir(backupPath);
97 return 0;
99 #else
100 char* LoadSaveDialog(bool save, bool levels, const char * title)
102 return 0;
104 #endif
106 extern void test();
108 int mouse_buttons = 0;
109 int mousex= 10, mousey = 10;
110 int noMouse = 0;
111 int quitting = 0;
113 double stylusx= 0, stylusy= 0;
114 int stylusok= 0;
115 float styluspressure = 0;
116 SDL_Surface * screen = 0;
117 SDL_Surface * realScreen = 0;
118 SDLPango_Context *context = 0;
120 extern State* MakeWorld();
122 bool fullscreen = false;
124 void InitScreen()
126 #ifdef USE_OPENGL
127 SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
128 SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
129 SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
130 SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
131 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
133 // printf("SDL_SetVideoMode (OpenGL)\n");
134 realScreen = SDL_SetVideoMode(
135 SCREEN_W, SCREEN_H, // Width, Height
136 0, // Current BPP
137 SDL_OPENGL | (fullscreen ? SDL_FULLSCREEN : 0) );
138 #else
139 // printf("SDL_SetVideoMode (non-OpenGL)\n");
140 realScreen = SDL_SetVideoMode(
141 SCREEN_W, SCREEN_H, // Width, Height
142 0, // Current BPP
143 SDL_SWSURFACE | SDL_DOUBLEBUF | (fullscreen ? SDL_FULLSCREEN : 0) );
144 #endif
146 if (screen)
147 SDL_FreeSurface(screen);
149 SDL_Surface* tempscreen = SDL_CreateRGBSurface(
150 SDL_SWSURFACE,
151 SCREEN_W, SCREEN_H,
152 16, 0xf800, 0x07e0, 0x001f, 0);
154 screen = SDL_DisplayFormat(tempscreen);
155 SDL_FreeSurface(tempscreen);
158 void ToggleFullscreen()
160 fullscreen = !fullscreen;
161 InitScreen();
162 StateMakerBase::current->ScreenModeChanged();
164 String base_path;
166 /// determine length of longest line with current font (wrapping allowed if text_width != -1)
167 int SDLPangoTextHeight(const std::string &text_utf8, int text_width)
169 // SDLPango_SetMinimumSize limits indeed the maximal size! See
170 // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=438691
171 SDLPango_SetMinimumSize(context, text_width, 0);
172 SDLPango_SetText(context, text_utf8.c_str(), -1);
173 return SDLPango_GetLayoutHeight(context);
176 /** \brief Determine length of longest line with current font
178 * Whether line breaks are allowed or not needs to be set before using
179 * SDLPango_SetMinimumSize!
181 int SDLPangoTextWidth(const std::string &text_utf8)
183 SDLPango_SetText(context, text_utf8.c_str(), -1);
184 return SDLPango_GetLayoutWidth(context);
187 /// Display the specified UTF-8 text left aligned at (x,y)
188 void Print_Pango(int x, int y, const std::string &text_utf8)
190 // Workaround for possible crash, see
191 // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439071
192 if (text_utf8.size() == 0 || text_utf8.size() == 1 && text_utf8[0]==127)
193 return;
194 assert(text_utf8.find("\n") == std::string::npos);
195 SDLPango_SetMinimumSize(context, SCREEN_W, 0);
196 SDLPango_SetText(context, text_utf8.c_str(), -1);
197 SDL_Surface *surface = SDLPango_CreateSurfaceDraw(context);
198 SDL_Rect dst = {x, y, 1, 1};
199 SDL_BlitSurface(surface, NULL, screen, &dst);
200 SDL_FreeSurface(surface);
203 /** \brief Display the specified UTF-8 text according to the alignment
205 * If line breaks are already properly set (manually) the will be respected
206 * and no new line breaks will be added. This assumes that th text is not too
207 * wide.
209 * \param x the displayed text is horizontally centered around x
210 * \param y the displayed text starts at y
211 * \param width background window size into which the text needs to fit
212 * \param text_utf8 the text to be displayed, in UTF8 encoding
213 * \param align=1: horizontally centered around (x,y)
214 * */
215 void Print_Pango_Aligned(int x, int y, int width, const std::string &text_utf8, int align)
217 // Workaround for possible crash, see
218 // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439071
219 if (text_utf8.size() == 0 || text_utf8.size() == 1 && text_utf8[0]==127)
220 return;
221 if (width<=0)
222 return;
223 SDLPango_SetMinimumSize(context, width, 0);
224 int real_width = SDLPangoTextWidth(text_utf8);
225 // Workaround for a crash in SDL Pango, see
226 // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=439855
227 if (real_width>width)
228 SDLPango_SetMinimumSize(context, real_width, 0);
230 SDLPango_Alignment alignment;
231 if (align==0)
232 alignment = SDLPANGO_ALIGN_LEFT;
233 else if (align==2) {
234 alignment = SDLPANGO_ALIGN_RIGHT;
235 x -= width;
236 } else {
237 alignment = SDLPANGO_ALIGN_CENTER;
238 x -= width/2;
240 // SDLPango_SetText_GivenAlignment is not (yet?) part of the official Pango
241 // distribution, see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=437865
242 SDLPango_SetText_GivenAlignment(context, text_utf8.c_str(), -1, alignment);
243 SDL_Surface *surface = SDLPango_CreateSurfaceDraw(context);
244 SDL_Rect dst = {x, y, 1, 1};
245 SDL_BlitSurface(surface, NULL, screen, &dst);
246 SDL_FreeSurface(surface);
249 int TickTimer()
251 static int time = SDL_GetTicks();
252 int cap=40;
254 int x = SDL_GetTicks() - time;
255 time += x;
256 if (x<0) x = 0, time = SDL_GetTicks();
257 if (x>cap) x = cap;
259 return x;
262 int main(int argc, char * argv[])
264 base_path = argv[0];
265 for (int i=strlen(base_path)-1; i>=0; i--)
266 if (base_path[i]=='/' || base_path[i]=='\\')
268 base_path.truncate(i+1);
269 break;
271 // Check the path ends with a directory seperator
272 if (strlen(base_path)>0)
274 char last = base_path[strlen(base_path)-1];
275 if (last!='/' && last!='\\')
276 base_path = "";
278 #ifdef WIN32
279 if (strstr(base_path, "\\foo2___Win32_Debug\\"))
280 strstr(base_path, "\\foo2___Win32_Debug\\")[1] = '\0';
281 if (strstr(base_path, "\\Release\\"))
282 strstr(base_path, "\\Release\\")[1] = '\0';
283 #endif
284 // printf("SDL_Init\n");
287 // Experimental - create a splash screen window whilst loading
288 SDL_Init(SDL_INIT_VIDEO);
289 screen = SDL_SetVideoMode( 200,200,0,SDL_NOFRAME );
290 SDL_Rect r = {0,0,200,200};
291 SDL_FillRect(screen, &r, SDL_MapRGB(screen->format, 0, 0, 50));
292 SDL_Flip(screen);
295 SDL_Init(SDL_INIT_EVERYTHING | SDL_INIT_NOPARACHUTE);
296 SDLPango_Init();
297 context = SDLPango_CreateContext_GivenFontDesc("sans-serif bold 12");
298 SDLPango_SetDefaultColor(context, MATRIX_TRANSPARENT_BACK_WHITE_LETTER);
299 SDLPango_SetMinimumSize(context, SCREEN_W, 0);
301 SDL_Surface* icon = SDL_LoadBMP("graphics/icon.bmp");
302 if (icon)
304 static unsigned int mask[32] = {
305 0x00001fc0,
306 0x00003fe0,
307 0x00007ff0,
308 0x00007df8,
309 0x0000f0f8,
310 0x0000f07c,
311 0x0005f87c,
312 0x0fbfff3c,
314 0x1ffffffe,
315 0x3ffffffe,
316 0x3ffffffe,
317 0x7ffffffe,
318 0x7ffffffe,
319 0x7ffffffe,
320 0x7ffffffe,
321 0xefffffff,
323 0x1fffffff,
324 0x3fffffff,
325 0x3fffffff,
326 0x3fffffff,
327 0x3fffffff,
328 0x3fffffff,
329 0x3fffffff,
330 0x3ffffffe,
332 0x3ffffff8,
333 0x3ffffff0,
334 0x3ffffff0,
335 0x3ffffff0,
336 0x3fffffe0,
337 0x3fffffe0,
338 0x1ffffff0,
339 0x1ffffff1,
341 for (int i=0; i<32; i++)
342 mask[i] = mask[i]>>24 | (mask[i]>>8)&0xff00 | (mask[i]<<8)&0xff0000 | (mask[i]<<24)&0xff000000;
343 SDL_WM_SetIcon(icon, (unsigned char*) mask);
344 SDL_FreeSurface(icon);
347 InitScreen();
349 SDL_WarpMouse(SCREEN_W/2, SCREEN_H/2);
351 int videoExposed = 1;
353 #ifdef WIN32
354 HWND hwnd = 0;
355 #endif
356 #ifdef USE_BBTABLET
357 bbTabletDevice &td = bbTabletDevice::getInstance( );
358 SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
359 #endif
361 // printf("Main loop...\n");
363 StateMakerBase::GetNew();
364 int time = SDL_GetTicks();
366 while(!quitting)
368 SDL_Event e;
369 while(!SDL_PollEvent(&e) && !quitting)
371 int x = 0;
373 if ((SDL_focus & 6)==6)
375 videoExposed = 1;
377 x = TickTimer();
379 while (x<10)
381 SDL_Delay(10-x);
382 x += TickTimer();
385 StateMakerBase::current->Update(x / 1000.0);
387 else
389 // Not focussed. Try not to eat too much CPU!
390 SDL_Delay(150);
393 // experimental...
394 if (!noMouse)
395 StateMakerBase::current->Mouse(mousex, mousey, 0, 0, 0, 0, mouse_buttons);
397 if (videoExposed)
399 StateMakerBase::current->Render();
401 #ifdef USE_OPENGL
402 SDL_GL_SwapBuffers();
403 #else
404 if (screen && realScreen!=screen)
406 SDL_Rect r = {0,0,SCREEN_W,SCREEN_H};
407 SDL_BlitSurface(screen, &r, realScreen, &r);
409 SDL_Flip(realScreen);
410 #endif
411 videoExposed = 0;
414 #ifdef USE_BBTABLET
415 // Tablet ////////////////////////
416 bbTabletEvent evt;
417 while(hwnd!=NULL && td.getNextEvent(evt))
419 stylusok = 1;
420 RECT r;
421 if (tablet_system)
423 GetWindowRect(hwnd, &r);
424 stylusx = evt.x * GetSystemMetrics(SM_CXSCREEN);
425 stylusy = (1.0 - evt.y) * GetSystemMetrics(SM_CYSCREEN);
426 stylusx -= (r.left + GetSystemMetrics(SM_CXFIXEDFRAME));
427 stylusy -= (r.top + GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYCAPTION));;
429 else
431 GetClientRect(hwnd, &r);
432 stylusx = evt.x * r.right;
433 stylusy = (1.0 - evt.y) * r.bottom;
435 styluspressure = (evt.buttons & 1) ? evt.pressure : 0;
438 printf("id=%d csrtype=%d b=%x (%0.3f, %0.3f, %0.3f) p=%0.3f tp=%0.3f\n",
439 evt.id,
440 evt.type,
441 evt.buttons,
442 evt.x,
443 evt.y,
444 evt.z,
445 evt.pressure,
446 evt.tpressure
451 #endif
454 switch (e.type)
456 case SDL_VIDEOEXPOSE:
457 videoExposed = 1;
458 break;
460 #ifdef WIN32
461 case SDL_SYSWMEVENT:
463 SDL_SysWMmsg* m = e.syswm.msg;
464 hwnd = m->hwnd;
465 static bool init=false;
466 if (!init)
468 init = true;
469 DragAcceptFiles(hwnd, TRUE);
470 #ifdef USE_BBTABLET
471 td.initTablet(hwnd, tablet_system ? bbTabletDevice::SYSTEM_POINTER : bbTabletDevice::SEPARATE_POINTER );
472 if (!td.isValid())
473 printf("No tablet/driver found\n");
474 #endif
476 if (m->msg == WM_DROPFILES)
478 HDROP h = (HDROP)m->wParam;
480 char name[512];
481 if (DragQueryFile(h, 0xffffffff, 0, 0) == 1)
483 DragQueryFile(h, 0, name, sizeof(name)/sizeof(name[0]));
485 StateMakerBase::current->FileDrop(name);
488 DragFinish(h);
491 break;
493 #endif
495 case SDL_ACTIVEEVENT:
497 int gain = e.active.gain ? e.active.state : 0;
498 int loss = e.active.gain ? 0 : e.active.state;
499 SDL_focus = (SDL_focus | gain) & ~loss;
500 if (gain & SDL_APPACTIVE)
501 StateMakerBase::current->ScreenModeChanged();
502 if (loss & SDL_APPMOUSEFOCUS)
503 noMouse = 1;
504 else if (gain & SDL_APPMOUSEFOCUS)
505 noMouse = 0;
507 break;
510 case SDL_MOUSEMOTION:
511 noMouse = false;
512 StateMakerBase::current->Mouse(e.motion.x, e.motion.y, e.motion.x-mousex, e.motion.y-mousey, 0, 0, mouse_buttons);
513 mousex = e.motion.x; mousey = e.motion.y;
514 break;
515 case SDL_MOUSEBUTTONUP:
516 noMouse = false;
517 mouse_buttons &= ~(1<<(e.button.button-1));
518 StateMakerBase::current->Mouse(e.button.x, e.button.y, e.button.x-mousex, e.button.y-mousey,
519 0, 1<<(e.button.button-1), mouse_buttons);
520 mousex = e.button.x; mousey = e.button.y ;
521 break;
522 case SDL_MOUSEBUTTONDOWN:
523 noMouse = false;
524 mouse_buttons |= 1<<(e.button.button-1);
525 StateMakerBase::current->Mouse(e.button.x, e.button.y, e.button.x-mousex, e.button.y-mousey,
526 1<<(e.button.button-1), 0, mouse_buttons);
527 mousex = e.button.x; mousey = e.button.y ;
528 break;
530 case SDL_KEYUP:
531 StateMakerBase::current->KeyReleased(e.key.keysym.sym);
532 break;
534 case SDL_KEYDOWN:
536 SDL_KeyboardEvent & k = e.key;
538 if (k.keysym.sym==SDLK_F4 && (k.keysym.mod & KMOD_ALT))
540 quitting = 1;
542 else if (k.keysym.sym==SDLK_F12)
544 // Toggle system pointer controlled by tablet or not
545 #ifdef USE_BBTABLET
546 if (td.isValid())
548 tablet_system = !tablet_system;
549 td.setPointerMode(tablet_system ? bbTabletDevice::SYSTEM_POINTER : bbTabletDevice::SEPARATE_POINTER);
551 #endif
553 else if (k.keysym.sym==SDLK_RETURN && (k.keysym.mod & KMOD_ALT) && !(k.keysym.mod & KMOD_CTRL))
555 ToggleFullscreen();
557 else if (StateMakerBase::current->KeyPressed(k.keysym.sym, k.keysym.mod))
560 else if ((k.keysym.mod & (KMOD_ALT | KMOD_CTRL))==0)
562 StateMakerBase::GetNew(k.keysym.sym);
565 break;
567 case SDL_QUIT:
568 quitting = 1;
569 break;
573 SDLPango_FreeContext(context);
574 SDL_Quit();
575 return 0;