Building the game engine with Solid/Sumo is now optional for scons using WITH_BF_SOLID.
[blender-staging.git] / source / gameengine / GamePlayer / ghost / GPG_Application.cpp
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  * GHOST Blender Player application implementation file.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #ifdef WIN32
36         #pragma warning (disable:4786) // suppress stl-MSVC debug info warning
37         #include <windows.h>
38 #endif
39
40 #include "GL/glew.h"
41 #include "GPU_extensions.h"
42
43 #include "GPG_Application.h"
44
45 #include <iostream>
46 #include <MT_assert.h>
47 #include <stdlib.h>
48
49 /**********************************
50  * Begin Blender include block
51  **********************************/
52 #ifdef __cplusplus
53 extern "C"
54 {
55 #endif  // __cplusplus
56 #include "BLI_blenlib.h"
57 #include "BLO_readfile.h"
58 #include "BKE_global.h"
59 #include "BKE_main.h"
60 #include "IMB_imbuf.h"
61 #include "DNA_scene_types.h"
62 #ifdef __cplusplus
63 }
64 #endif // __cplusplus
65 /**********************************
66  * End Blender include block
67  **********************************/
68
69
70 #include "SYS_System.h"
71 #include "KX_KetsjiEngine.h"
72
73 // include files needed by "KX_BlenderSceneConverter.h"
74 #include "GEN_Map.h"
75 #include "SCA_IActuator.h"
76 #include "RAS_MeshObject.h"
77 #include "RAS_OpenGLRasterizer.h"
78 #include "RAS_VAOpenGLRasterizer.h"
79 #include "RAS_ListRasterizer.h"
80 #include "RAS_GLExtensionManager.h"
81 #include "KX_PythonInit.h"
82 #include "KX_PyConstraintBinding.h"
83 #include "BL_Material.h" // MAXTEX
84
85 #include "KX_BlenderSceneConverter.h"
86 #include "NG_LoopBackNetworkDeviceInterface.h"
87 #include "SND_DeviceManager.h"
88
89 #include "GPC_MouseDevice.h"
90 #include "GPC_RenderTools.h"
91 #include "GPG_Canvas.h" 
92 #include "GPG_KeyboardDevice.h"
93 #include "GPG_System.h"
94
95 #include "STR_String.h"
96
97 #include "GHOST_ISystem.h"
98 #include "GHOST_IEvent.h"
99 #include "GHOST_IEventConsumer.h"
100 #include "GHOST_IWindow.h"
101 #include "GHOST_Rect.h"
102
103 static void frameTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time);
104
105 static GHOST_ISystem* fSystem = 0;
106 static const int kTimerFreq = 10;
107
108 GPG_Application::GPG_Application(GHOST_ISystem* system)
109         : m_startSceneName(""), 
110           m_startScene(0),
111           m_maggie(0),
112           m_exitRequested(0),
113           m_system(system), 
114           m_mainWindow(0), 
115           m_frameTimer(0), 
116           m_cursor(GHOST_kStandardCursorFirstCursor),
117           m_engineInitialized(0), 
118           m_engineRunning(0), 
119           m_isEmbedded(false),
120           m_ketsjiengine(0),
121           m_kxsystem(0), 
122           m_keyboard(0), 
123           m_mouse(0), 
124           m_canvas(0), 
125           m_rendertools(0), 
126           m_rasterizer(0), 
127           m_sceneconverter(0),
128           m_networkdevice(0), 
129           m_audiodevice(0),
130           m_blendermat(0),
131           m_blenderglslmat(0),
132           m_pyGlobalDictString(0),
133           m_pyGlobalDictString_Length(0)
134 {
135         fSystem = system;
136 }
137
138
139
140 GPG_Application::~GPG_Application(void)
141 {
142     if(m_pyGlobalDictString) {
143                 delete [] m_pyGlobalDictString;
144                 m_pyGlobalDictString = 0;
145                 m_pyGlobalDictString_Length = 0;
146         }
147
148         exitEngine();
149         fSystem->disposeWindow(m_mainWindow);
150 }
151
152
153
154 bool GPG_Application::SetGameEngineData(struct Main* maggie, Scene *scene)
155 {
156         bool result = false;
157
158         if (maggie != NULL && scene != NULL)
159         {
160                 G.scene = scene;
161                 m_maggie = maggie;
162                 m_startSceneName = scene->id.name+2;
163                 m_startScene = scene;
164                 result = true;
165         }
166
167         return result;
168 }
169
170
171 #ifdef WIN32
172 #define SCR_SAVE_MOUSE_MOVE_THRESHOLD 15
173
174 static HWND found_ghost_window_hwnd;
175 static GHOST_IWindow* ghost_window_to_find;
176 static WNDPROC ghost_wnd_proc;
177 static POINT scr_save_mouse_pos;
178
179 static LRESULT CALLBACK screenSaverWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
180 {
181         BOOL close = FALSE;
182         switch (uMsg)
183         {
184                 case WM_MOUSEMOVE:
185                 { 
186                         POINT pt; 
187                         GetCursorPos(&pt);
188                         LONG dx = scr_save_mouse_pos.x - pt.x;
189                         LONG dy = scr_save_mouse_pos.y - pt.y;
190                         if (abs(dx) > SCR_SAVE_MOUSE_MOVE_THRESHOLD
191                             || abs(dy) > SCR_SAVE_MOUSE_MOVE_THRESHOLD)
192                         {
193                                 close = TRUE;
194                         }
195                         scr_save_mouse_pos = pt;
196                         break;
197                 }
198                 case WM_LBUTTONDOWN: 
199                 case WM_MBUTTONDOWN: 
200                 case WM_RBUTTONDOWN: 
201                 case WM_KEYDOWN:
202                         close = TRUE;
203         }
204         if (close)
205                 PostMessage(hwnd,WM_CLOSE,0,0);
206         return CallWindowProc(ghost_wnd_proc, hwnd, uMsg, wParam, lParam);
207 }
208
209 BOOL CALLBACK findGhostWindowHWNDProc(HWND hwnd, LPARAM lParam)
210 {
211         GHOST_IWindow *p = (GHOST_IWindow*) GetWindowLong(hwnd, GWL_USERDATA);
212         BOOL ret = TRUE;
213         if (p == ghost_window_to_find)
214         {
215                 found_ghost_window_hwnd = hwnd;
216                 ret = FALSE;
217         }
218         return ret;
219 }
220
221 static HWND findGhostWindowHWND(GHOST_IWindow* window)
222 {
223         found_ghost_window_hwnd = NULL;
224         ghost_window_to_find = window;
225         EnumWindows(findGhostWindowHWNDProc, NULL);
226         return found_ghost_window_hwnd;
227 }
228
229 bool GPG_Application::startScreenSaverPreview(
230         HWND parentWindow,
231         const bool stereoVisual,
232         const int stereoMode)
233 {
234         bool success = false;
235
236         RECT rc;
237         if (GetWindowRect(parentWindow, &rc))
238         {
239                 int windowWidth = rc.right - rc.left;
240                 int windowHeight = rc.bottom - rc.top;
241                 STR_String title = "";
242                                                         
243                 m_mainWindow = fSystem->createWindow(title, 0, 0, windowWidth, windowHeight, GHOST_kWindowStateMinimized,
244                         GHOST_kDrawingContextTypeOpenGL, stereoVisual);
245                 if (!m_mainWindow) {
246                         printf("error: could not create main window\n");
247                         exit(-1);
248                 }
249
250                 HWND ghost_hwnd = findGhostWindowHWND(m_mainWindow);
251                 if (!ghost_hwnd) {
252                         printf("error: could find main window\n");
253                         exit(-1);
254                 }
255
256                 SetParent(ghost_hwnd, parentWindow);
257                 LONG style = GetWindowLong(ghost_hwnd, GWL_STYLE);
258                 LONG exstyle = GetWindowLong(ghost_hwnd, GWL_EXSTYLE);
259
260                 RECT adjrc = { 0, 0, windowWidth, windowHeight };
261                 AdjustWindowRectEx(&adjrc, style, FALSE, exstyle);
262
263                 style = (style & (~(WS_POPUP|WS_OVERLAPPEDWINDOW|WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_TILEDWINDOW ))) | WS_CHILD;
264                 SetWindowLong(ghost_hwnd, GWL_STYLE, style);
265                 SetWindowPos(ghost_hwnd, NULL, adjrc.left, adjrc.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE|SWP_NOACTIVATE);
266
267                 /* Check the size of the client rectangle of the window and resize the window
268                  * so that the client rectangle has the size requested.
269                  */
270                 m_mainWindow->setClientSize(windowWidth, windowHeight);
271
272                 success = initEngine(m_mainWindow, stereoMode);
273                 if (success) {
274                         success = startEngine();
275                 }
276
277         }
278         return success;
279 }
280
281 bool GPG_Application::startScreenSaverFullScreen(
282                 int width,
283                 int height,
284                 int bpp,int frequency,
285                 const bool stereoVisual,
286                 const int stereoMode)
287 {
288         bool ret = startFullScreen(width, height, bpp, frequency, stereoVisual, stereoMode);
289         if (ret)
290         {
291                 HWND ghost_hwnd = findGhostWindowHWND(m_mainWindow);
292                 if (ghost_hwnd != NULL)
293                 {
294                         GetCursorPos(&scr_save_mouse_pos);
295                         ghost_wnd_proc = (WNDPROC) GetWindowLong(ghost_hwnd, GWL_WNDPROC);
296                         SetWindowLong(ghost_hwnd,GWL_WNDPROC, (LONG) screenSaverWindowProc);
297                 }
298         }
299         return ret;
300 }
301
302 #endif
303
304 bool GPG_Application::startWindow(STR_String& title,
305         int windowLeft,
306         int windowTop,
307         int windowWidth,
308         int windowHeight,
309         const bool stereoVisual,
310         const int stereoMode)
311 {
312         bool success;
313         // Create the main window
314         //STR_String title ("Blender Player - GHOST");
315         m_mainWindow = fSystem->createWindow(title, windowLeft, windowTop, windowWidth, windowHeight, GHOST_kWindowStateNormal,
316                 GHOST_kDrawingContextTypeOpenGL, stereoVisual);
317         if (!m_mainWindow) {
318                 printf("error: could not create main window\n");
319                 exit(-1);
320         }
321
322         /* Check the size of the client rectangle of the window and resize the window
323          * so that the client rectangle has the size requested.
324          */
325         m_mainWindow->setClientSize(windowWidth, windowHeight);
326         m_mainWindow->setCursorVisibility(false);
327
328         success = initEngine(m_mainWindow, stereoMode);
329         if (success) {
330                 success = startEngine();
331         }
332         return success;
333 }
334
335 bool GPG_Application::startEmbeddedWindow(STR_String& title,
336         const GHOST_TEmbedderWindowID parentWindow, 
337         const bool stereoVisual, 
338         const int stereoMode) {
339
340         m_mainWindow = fSystem->createWindow(title, 0, 0, 0, 0, GHOST_kWindowStateNormal,
341                 GHOST_kDrawingContextTypeOpenGL, stereoVisual, parentWindow);
342
343         if (!m_mainWindow) {
344                 printf("error: could not create main window\n");
345                 exit(-1);
346         }
347         m_isEmbedded = true;
348
349         bool success = initEngine(m_mainWindow, stereoMode);
350         if (success) {
351                 success = startEngine();
352         }
353         return success;
354 }
355
356
357 bool GPG_Application::startFullScreen(
358                 int width,
359                 int height,
360                 int bpp,int frequency,
361                 const bool stereoVisual,
362                 const int stereoMode)
363 {
364         bool success;
365         // Create the main window
366         GHOST_DisplaySetting setting;
367         setting.xPixels = width;
368         setting.yPixels = height;
369         setting.bpp = bpp;
370         setting.frequency = frequency;
371
372         fSystem->beginFullScreen(setting, &m_mainWindow, stereoVisual);
373         m_mainWindow->setCursorVisibility(false);
374
375         success = initEngine(m_mainWindow, stereoMode);
376         if (success) {
377                 success = startEngine();
378         }
379         return success;
380 }
381
382
383
384
385 bool GPG_Application::StartGameEngine(int stereoMode)
386 {
387         bool success = initEngine(m_mainWindow, stereoMode);
388         
389         if (success)
390                 success = startEngine();
391
392         return success;
393 }
394
395
396
397 void GPG_Application::StopGameEngine()
398 {
399         exitEngine();
400 }
401
402
403
404 bool GPG_Application::processEvent(GHOST_IEvent* event)
405 {
406         bool handled = true;
407
408         switch (event->getType())
409         {
410                 case GHOST_kEventUnknown:
411                         break;
412
413                 case GHOST_kEventButtonDown:
414                         handled = handleButton(event, true);
415                         break;
416
417                 case GHOST_kEventButtonUp:
418                         handled = handleButton(event, false);
419                         break;
420                         
421                 case GHOST_kEventWheel:
422                         handled = handleWheel(event);
423                         break;
424
425                 case GHOST_kEventCursorMove:
426                         handled = handleCursorMove(event);
427                         break;
428
429                 case GHOST_kEventKeyDown:
430                         handleKey(event, true);
431                         break;
432
433                 case GHOST_kEventKeyUp:
434                         handleKey(event, false);
435                         break;
436
437
438                 case GHOST_kEventWindowClose:
439                         m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
440                         break;
441
442                 case GHOST_kEventWindowActivate:
443                         handled = false;
444                         break;
445                 case GHOST_kEventWindowDeactivate:
446                         handled = false;
447                         break;
448
449                 case GHOST_kEventWindowUpdate:
450                         {
451                                 GHOST_IWindow* window = event->getWindow();
452                                 if (!m_system->validWindow(window)) break;
453                                 // Update the state of the game engine
454                                 if (m_kxsystem && !m_exitRequested)
455                                 {
456                                         // Proceed to next frame
457                                         window->activateDrawingContext();
458
459                                         // first check if we want to exit
460                                         m_exitRequested = m_ketsjiengine->GetExitCode();
461                                         
462                                         // kick the engine
463                                         bool renderFrame = m_ketsjiengine->NextFrame();
464                                         if (renderFrame)
465                                         {
466                                                 // render the frame
467                                                 m_ketsjiengine->Render();
468                                         }
469                                 }
470                                 m_exitString = m_ketsjiengine->GetExitString();
471                         }
472                         break;
473                 
474                 case GHOST_kEventWindowSize:
475                         {
476                         GHOST_IWindow* window = event->getWindow();
477                         if (!m_system->validWindow(window)) break;
478                         if (m_canvas) {
479                                 GHOST_Rect bnds;
480                                 window->getClientBounds(bnds);
481                                 m_canvas->Resize(bnds.getWidth(), bnds.getHeight());
482                         }
483                         }
484                         break;
485                 
486                 default:
487                         handled = false;
488                         break;
489         }
490         return handled;
491 }
492
493
494
495 int GPG_Application::getExitRequested(void)
496 {
497         return m_exitRequested;
498 }
499
500
501
502 const STR_String& GPG_Application::getExitString(void)
503 {
504         return m_exitString;
505 }
506
507
508
509 bool GPG_Application::initEngine(GHOST_IWindow* window, const int stereoMode)
510 {
511         if (!m_engineInitialized)
512         {
513                 GPU_extensions_init();
514                 bgl::InitExtensions(true);
515
516                 // get and set the preferences
517                 SYS_SystemHandle syshandle = SYS_GetSystem();
518                 if (!syshandle)
519                         return false;
520                 
521                 // SYS_WriteCommandLineInt(syshandle, "fixedtime", 0);
522                 // SYS_WriteCommandLineInt(syshandle, "vertexarrays",1);                
523                 bool properties = (SYS_GetCommandLineInt(syshandle, "show_properties", 0) != 0);
524                 bool profile = (SYS_GetCommandLineInt(syshandle, "show_profile", 0) != 0);
525                 bool fixedFr = (G.fileflags & G_FILE_ENABLE_ALL_FRAMES);
526
527                 bool showPhysics = (G.fileflags & G_FILE_SHOW_PHYSICS);
528                 SYS_WriteCommandLineInt(syshandle, "show_physics", showPhysics);
529
530                 bool fixed_framerate= (SYS_GetCommandLineInt(syshandle, "fixed_framerate", fixedFr) != 0);
531                 bool frameRate = (SYS_GetCommandLineInt(syshandle, "show_framerate", 0) != 0);
532                 bool useLists = (SYS_GetCommandLineInt(syshandle, "displaylists", G.fileflags & G_FILE_DISPLAY_LISTS) != 0);
533                 bool nodepwarnings = (SYS_GetCommandLineInt(syshandle, "ignore_deprecation_warnings", 0) != 0);
534
535                 if(GLEW_ARB_multitexture && GLEW_VERSION_1_1)
536                         m_blendermat = (SYS_GetCommandLineInt(syshandle, "blender_material", 1) != 0);
537
538                 if(GPU_extensions_minimum_support())
539                         m_blenderglslmat = (SYS_GetCommandLineInt(syshandle, "blender_glsl_material", 1) != 0);
540                 else if(G.fileflags & G_FILE_GAME_MAT_GLSL)
541                         m_blendermat = false;
542
543                 // create the canvas, rasterizer and rendertools
544                 m_canvas = new GPG_Canvas(window);
545                 if (!m_canvas)
546                         return false;
547                                 
548                 m_canvas->Init();                               
549                 m_rendertools = new GPC_RenderTools();
550                 if (!m_rendertools)
551                         goto initFailed;
552                 
553                 if(useLists) {
554                         if(GLEW_VERSION_1_1)
555                                 m_rasterizer = new RAS_ListRasterizer(m_canvas, true);
556                         else
557                                 m_rasterizer = new RAS_ListRasterizer(m_canvas);
558                 }
559                 else if (GLEW_VERSION_1_1)
560                         m_rasterizer = new RAS_VAOpenGLRasterizer(m_canvas);
561                 else
562                         m_rasterizer = new RAS_OpenGLRasterizer(m_canvas);
563
564                 m_rasterizer->SetStereoMode((RAS_IRasterizer::StereoMode) stereoMode);
565                 if (!m_rasterizer)
566                         goto initFailed;
567                                                 
568                 // create the inputdevices
569                 m_keyboard = new GPG_KeyboardDevice();
570                 if (!m_keyboard)
571                         goto initFailed;
572                         
573                 m_mouse = new GPC_MouseDevice();
574                 if (!m_mouse)
575                         goto initFailed;
576                         
577                 // create a networkdevice
578                 m_networkdevice = new NG_LoopBackNetworkDeviceInterface();
579                 if (!m_networkdevice)
580                         goto initFailed;
581                         
582                 // get an audiodevice
583                 SND_DeviceManager::Subscribe();
584                 m_audiodevice = SND_DeviceManager::Instance();
585                 if (!m_audiodevice)
586                         goto initFailed;
587                 m_audiodevice->UseCD();
588                 
589                 // create a ketsjisystem (only needed for timing and stuff)
590                 m_kxsystem = new GPG_System (m_system);
591                 if (!m_kxsystem)
592                         goto initFailed;
593                 
594                 // create the ketsjiengine
595                 m_ketsjiengine = new KX_KetsjiEngine(m_kxsystem);
596                 
597                 // set the devices
598                 m_ketsjiengine->SetKeyboardDevice(m_keyboard);
599                 m_ketsjiengine->SetMouseDevice(m_mouse);
600                 m_ketsjiengine->SetNetworkDevice(m_networkdevice);
601                 m_ketsjiengine->SetCanvas(m_canvas);
602                 m_ketsjiengine->SetRenderTools(m_rendertools);
603                 m_ketsjiengine->SetRasterizer(m_rasterizer);
604                 m_ketsjiengine->SetNetworkDevice(m_networkdevice);
605                 m_ketsjiengine->SetAudioDevice(m_audiodevice);
606                 m_ketsjiengine->SetTimingDisplay(frameRate, false, false);
607
608                 CValue::SetDeprecationWarnings(nodepwarnings);
609
610                 m_ketsjiengine->SetUseFixedTime(fixed_framerate);
611                 m_ketsjiengine->SetTimingDisplay(frameRate, profile, properties);
612
613                 m_engineInitialized = true;
614         }
615
616         return m_engineInitialized;
617 initFailed:
618         delete m_kxsystem;
619         delete m_audiodevice;
620         delete m_networkdevice;
621         delete m_mouse;
622         delete m_keyboard;
623         delete m_rasterizer;
624         delete m_rendertools;
625         delete m_canvas;
626         m_canvas = NULL;
627         m_rendertools = NULL;
628         m_rasterizer = NULL;
629         m_keyboard = NULL;
630         m_mouse = NULL;
631         m_networkdevice = NULL;
632         m_audiodevice = NULL;
633         m_kxsystem = NULL;
634         return false;
635 }
636
637
638
639 bool GPG_Application::startEngine(void)
640 {
641         if (m_engineRunning) {
642                 return false;
643         }
644         
645         // Temporary hack to disable banner display for NaN approved content.
646         /*
647         m_canvas->SetBannerDisplayEnabled(true);        
648         Camera* cam;
649         cam = (Camera*)G.scene->camera->data;
650         if (cam) {
651         if (((cam->flag) & 48)==48) {
652         m_canvas->SetBannerDisplayEnabled(false);
653         }
654         }
655         else {
656         showError(CString("Camera data invalid."));
657         return false;
658         }
659         */
660         
661         // create a scene converter, create and convert the stratingscene
662         m_sceneconverter = new KX_BlenderSceneConverter(m_maggie,0, m_ketsjiengine);
663         if (m_sceneconverter)
664         {
665                 STR_String startscenename = m_startSceneName.Ptr();
666                 m_ketsjiengine->SetSceneConverter(m_sceneconverter);
667
668                 //      if (always_use_expand_framing)
669                 //              sceneconverter->SetAlwaysUseExpandFraming(true);
670                 if(m_blendermat && (G.fileflags & G_FILE_GAME_MAT))
671                         m_sceneconverter->SetMaterials(true);
672                 if(m_blenderglslmat && (G.fileflags & G_FILE_GAME_MAT_GLSL))
673                         m_sceneconverter->SetGLSLMaterials(true);
674
675                 KX_Scene* startscene = new KX_Scene(m_keyboard,
676                         m_mouse,
677                         m_networkdevice,
678                         m_audiodevice,
679                         startscenename,
680                         m_startScene);
681                 
682                 
683                 // some python things
684                 PyObject* dictionaryobject = initGamePlayerPythonScripting("Ketsji", psl_Lowest);
685                 m_ketsjiengine->SetPythonDictionary(dictionaryobject);
686                 initRasterizer(m_rasterizer, m_canvas);
687                 PyObject *gameLogic = initGameLogic(m_ketsjiengine, startscene);
688                 PyDict_SetItemString(dictionaryobject, "GameLogic", gameLogic); // Same as importing the module
689                 initGameKeys();
690                 initPythonConstraintBinding();
691                 initMathutils();
692 #ifdef WITH_FFMPEG
693         initVideoTexture();
694 #endif
695                 // Set the GameLogic.globalDict from marshal'd data, so we can
696                 // load new blend files and keep data in GameLogic.globalDict
697                 loadGamePythonConfig(m_pyGlobalDictString, m_pyGlobalDictString_Length);
698                 
699                 m_sceneconverter->ConvertScene(
700                         startscenename,
701                         startscene,
702                         dictionaryobject,
703                         m_keyboard,
704                         m_rendertools,
705                         m_canvas);
706                 m_ketsjiengine->AddScene(startscene);
707                 
708                 // Create a timer that is used to kick the engine
709                 if (!m_frameTimer) {
710                         m_frameTimer = m_system->installTimer(0, kTimerFreq, frameTimerProc, m_mainWindow);
711                 }
712                 m_rasterizer->Init();
713                 m_ketsjiengine->StartEngine(true);
714                 m_engineRunning = true;
715                 
716                 // Set the animation playback rate for ipo's and actions
717                 // the framerate below should patch with FPS macro defined in blendef.h
718                 // Could be in StartEngine set the framerate, we need the scene to do this
719                 m_ketsjiengine->SetAnimFrameRate( (((double) G.scene->r.frs_sec) / G.scene->r.frs_sec_base) );
720                 
721         }
722         
723         if (!m_engineRunning)
724         {
725                 stopEngine();
726         }
727         
728         return m_engineRunning;
729 }
730
731
732 void GPG_Application::stopEngine()
733 {
734         // GameLogic.globalDict gets converted into a buffer, and sorted in
735         // m_pyGlobalDictString so we can restore after python has stopped
736         // and started between .blend file loads.
737         if(m_pyGlobalDictString) {
738                 delete [] m_pyGlobalDictString;
739                 m_pyGlobalDictString = 0;
740         }
741
742         m_pyGlobalDictString_Length = saveGamePythonConfig(&m_pyGlobalDictString);
743         
744         // when exiting the mainloop
745         exitGamePythonScripting();
746         m_ketsjiengine->StopEngine();
747         m_networkdevice->Disconnect();
748
749         if (m_sceneconverter) {
750                 delete m_sceneconverter;
751                 m_sceneconverter = 0;
752         }
753         if (m_system && m_frameTimer) {
754                 m_system->removeTimer(m_frameTimer);
755                 m_frameTimer = 0;
756         }
757         m_engineRunning = false;
758 }
759
760
761 void GPG_Application::exitEngine()
762 {
763         if (m_ketsjiengine)
764         {
765                 stopEngine();
766                 delete m_ketsjiengine;
767                 m_ketsjiengine = 0;
768         }
769         if (m_kxsystem)
770         {
771                 delete m_kxsystem;
772                 m_kxsystem = 0;
773         }
774         if (m_audiodevice)
775         {
776                 SND_DeviceManager::Unsubscribe();
777                 m_audiodevice = 0;
778         }
779         if (m_networkdevice)
780         {
781                 delete m_networkdevice;
782                 m_networkdevice = 0;
783         }
784         if (m_mouse)
785         {
786                 delete m_mouse;
787                 m_mouse = 0;
788         }
789         if (m_keyboard)
790         {
791                 delete m_keyboard;
792                 m_keyboard = 0;
793         }
794         if (m_rasterizer)
795         {
796                 delete m_rasterizer;
797                 m_rasterizer = 0;
798         }
799         if (m_rendertools)
800         {
801                 delete m_rendertools;
802                 m_rendertools = 0;
803         }
804         if (m_canvas)
805         {
806                 delete m_canvas;
807                 m_canvas = 0;
808         }
809
810         libtiff_exit();
811 #ifdef WITH_QUICKTIME
812         quicktime_exit();
813 #endif
814         GPU_extensions_exit();
815
816         m_exitRequested = 0;
817         m_engineInitialized = false;
818 }
819
820 bool GPG_Application::handleWheel(GHOST_IEvent* event)
821 {
822         bool handled = false;
823         MT_assert(event);
824         if (m_mouse) 
825         {
826                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
827                 GHOST_TEventWheelData* wheelData = static_cast<GHOST_TEventWheelData*>(eventData);
828                 GPC_MouseDevice::TButtonId button;
829                 if (wheelData->z > 0)
830                         button = GPC_MouseDevice::buttonWheelUp;
831                 else
832                         button = GPC_MouseDevice::buttonWheelDown;
833                 m_mouse->ConvertButtonEvent(button, true);
834                 handled = true;
835         }
836         return handled;
837 }
838
839 bool GPG_Application::handleButton(GHOST_IEvent* event, bool isDown)
840 {
841         bool handled = false;
842         MT_assert(event);
843         if (m_mouse) 
844         {
845                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
846                 GHOST_TEventButtonData* buttonData = static_cast<GHOST_TEventButtonData*>(eventData);
847                 GPC_MouseDevice::TButtonId button;
848                 switch (buttonData->button)
849                 {
850                 case GHOST_kButtonMaskMiddle:
851                         button = GPC_MouseDevice::buttonMiddle;
852                         break;
853                 case GHOST_kButtonMaskRight:
854                         button = GPC_MouseDevice::buttonRight;
855                         break;
856                 case GHOST_kButtonMaskLeft:
857                 default:
858                         button = GPC_MouseDevice::buttonLeft;
859                         break;
860                 }
861                 m_mouse->ConvertButtonEvent(button, isDown);
862                 handled = true;
863         }
864         return handled;
865 }
866
867
868 bool GPG_Application::handleCursorMove(GHOST_IEvent* event)
869 {
870         bool handled = false;
871         MT_assert(event);
872         if (m_mouse && m_mainWindow)
873         {
874                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
875                 GHOST_TEventCursorData* cursorData = static_cast<GHOST_TEventCursorData*>(eventData);
876                 GHOST_TInt32 x, y;
877                 m_mainWindow->screenToClient(cursorData->x, cursorData->y, x, y);
878                 m_mouse->ConvertMoveEvent(x, y);
879                 handled = true;
880         }
881         return handled;
882 }
883
884
885 bool GPG_Application::handleKey(GHOST_IEvent* event, bool isDown)
886 {
887         bool handled = false;
888         MT_assert(event);
889         if (m_keyboard)
890         {
891                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
892                 GHOST_TEventKeyData* keyData = static_cast<GHOST_TEventKeyData*>(eventData);
893                 //no need for this test
894                 //if (fSystem->getFullScreen()) {
895                         if (keyData->key == GHOST_kKeyEsc && !m_keyboard->m_hookesc && !m_isEmbedded) {
896                                 m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
897                         }
898                 //}
899                 m_keyboard->ConvertEvent(keyData->key, isDown);
900                 handled = true;
901         }
902         return handled;
903 }
904
905
906
907 static void frameTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time)
908 {
909         GHOST_IWindow* window = (GHOST_IWindow*)task->getUserData();
910         if (fSystem->validWindow(window)) {
911                 window->invalidate();
912         }
913 }