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