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