| 1 | /* |
|---|
| 2 | ================================================================================= |
|---|
| 3 | This file is part of Cafu, the open-source game engine and graphics engine |
|---|
| 4 | for multiplayer, cross-platform, real-time 3D action. |
|---|
| 5 | Copyright (C) 2002-2012 Carsten Fuchs Software. |
|---|
| 6 | |
|---|
| 7 | Cafu is free software: you can redistribute it and/or modify it under the terms |
|---|
| 8 | of the GNU General Public License as published by the Free Software Foundation, |
|---|
| 9 | either version 3 of the License, or (at your option) any later version. |
|---|
| 10 | |
|---|
| 11 | Cafu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
|---|
| 12 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
|---|
| 13 | PURPOSE. See the GNU General Public License for more details. |
|---|
| 14 | |
|---|
| 15 | You should have received a copy of the GNU General Public License |
|---|
| 16 | along with Cafu. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 17 | |
|---|
| 18 | For support and more information about Cafu, visit us at <http://www.cafu.de>. |
|---|
| 19 | ================================================================================= |
|---|
| 20 | */ |
|---|
| 21 | |
|---|
| 22 | #include "MainCanvas.hpp" |
|---|
| 23 | #include "AppCafu.hpp" |
|---|
| 24 | #include "MainFrame.hpp" |
|---|
| 25 | #include "Client/Client.hpp" |
|---|
| 26 | #include "Client/ClientWindow.hpp" |
|---|
| 27 | #include "Server/Server.hpp" |
|---|
| 28 | |
|---|
| 29 | #include "Bitmap/Bitmap.hpp" |
|---|
| 30 | #include "ClipSys/CollisionModelMan.hpp" |
|---|
| 31 | #include "ConsoleCommands/Console.hpp" |
|---|
| 32 | #include "ConsoleCommands/ConsoleInterpreter.hpp" |
|---|
| 33 | #include "ConsoleCommands/ConsoleComposite.hpp" |
|---|
| 34 | #include "ConsoleCommands/ConsoleStringBuffer.hpp" |
|---|
| 35 | #include "ConsoleCommands/ConVar.hpp" |
|---|
| 36 | #include "GuiSys/ConsoleByWindow.hpp" |
|---|
| 37 | #include "GuiSys/GuiImpl.hpp" |
|---|
| 38 | #include "GuiSys/GuiManImpl.hpp" |
|---|
| 39 | #include "GuiSys/Window.hpp" |
|---|
| 40 | #include "MaterialSystem/MaterialManager.hpp" |
|---|
| 41 | #include "MaterialSystem/Renderer.hpp" |
|---|
| 42 | #include "MaterialSystem/TextureMap.hpp" |
|---|
| 43 | #include "Models/ModelManager.hpp" |
|---|
| 44 | #include "OpenGL/OpenGLWindow.hpp" // For CaMouseEventT and CaKeyboardEventT. |
|---|
| 45 | #include "SoundSystem/SoundShaderManager.hpp" |
|---|
| 46 | #include "SoundSystem/SoundSys.hpp" |
|---|
| 47 | #include "../Games/Game.hpp" |
|---|
| 48 | #include "PlatformAux.hpp" |
|---|
| 49 | |
|---|
| 50 | #ifndef _WIN32 |
|---|
| 51 | #include <dlfcn.h> |
|---|
| 52 | #define __stdcall |
|---|
| 53 | #define GetProcAddress dlsym |
|---|
| 54 | #define FreeLibrary dlclose |
|---|
| 55 | #endif |
|---|
| 56 | |
|---|
| 57 | |
|---|
| 58 | class SvGuiCallbT : public ServerT::GuiCallbackI |
|---|
| 59 | { |
|---|
| 60 | public: |
|---|
| 61 | |
|---|
| 62 | SvGuiCallbT() |
|---|
| 63 | : MainMenuGui(NULL) |
|---|
| 64 | { |
|---|
| 65 | } |
|---|
| 66 | |
|---|
| 67 | void OnServerStateChanged(const char* NewState) const |
|---|
| 68 | { |
|---|
| 69 | if (!MainMenuGui) return; |
|---|
| 70 | |
|---|
| 71 | MainMenuGui->CallLuaFunc("OnServerStateChanged", "s", NewState); |
|---|
| 72 | } |
|---|
| 73 | |
|---|
| 74 | cf::GuiSys::GuiI* MainMenuGui; |
|---|
| 75 | }; |
|---|
| 76 | |
|---|
| 77 | |
|---|
| 78 | BEGIN_EVENT_TABLE(MainCanvasT, wxGLCanvas) |
|---|
| 79 | EVT_PAINT(MainCanvasT::OnPaint) |
|---|
| 80 | EVT_IDLE(MainCanvasT::OnIdle) |
|---|
| 81 | EVT_SIZE(MainCanvasT::OnSize) |
|---|
| 82 | |
|---|
| 83 | EVT_MOTION (MainCanvasT::OnMouseMove ) |
|---|
| 84 | EVT_MOUSEWHEEL(MainCanvasT::OnMouseWheel) |
|---|
| 85 | EVT_LEFT_DOWN (MainCanvasT::OnLMouseDown) |
|---|
| 86 | EVT_LEFT_UP (MainCanvasT::OnLMouseUp ) |
|---|
| 87 | EVT_RIGHT_DOWN(MainCanvasT::OnRMouseDown) |
|---|
| 88 | EVT_RIGHT_UP (MainCanvasT::OnRMouseUp ) |
|---|
| 89 | |
|---|
| 90 | EVT_KEY_DOWN(MainCanvasT::OnKeyDown) |
|---|
| 91 | EVT_KEY_UP (MainCanvasT::OnKeyUp ) |
|---|
| 92 | EVT_CHAR (MainCanvasT::OnKeyChar) |
|---|
| 93 | END_EVENT_TABLE() |
|---|
| 94 | |
|---|
| 95 | |
|---|
| 96 | static int OpenGLAttributeList[]= |
|---|
| 97 | { |
|---|
| 98 | WX_GL_RGBA, |
|---|
| 99 | WX_GL_DOUBLEBUFFER, |
|---|
| 100 | WX_GL_MIN_RED, 8, |
|---|
| 101 | WX_GL_MIN_GREEN, 8, |
|---|
| 102 | WX_GL_MIN_BLUE, 8, |
|---|
| 103 | WX_GL_MIN_ALPHA, 8, |
|---|
| 104 | WX_GL_DEPTH_SIZE, 16, |
|---|
| 105 | WX_GL_STENCIL_SIZE, 8, |
|---|
| 106 | 0 // Zero indicates the end of the array. |
|---|
| 107 | }; |
|---|
| 108 | |
|---|
| 109 | |
|---|
| 110 | MainCanvasT::MainCanvasT(MainFrameT* Parent) |
|---|
| 111 | : wxGLCanvas(Parent, wxID_ANY, OpenGLAttributeList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS, "CafuMainCanvas"), |
|---|
| 112 | m_Parent(Parent), |
|---|
| 113 | m_InitState(INIT_REQUIRED), |
|---|
| 114 | m_GLContext(NULL), |
|---|
| 115 | m_RendererDLL(NULL), |
|---|
| 116 | m_ModelManager(NULL), |
|---|
| 117 | m_GuiResources(NULL), |
|---|
| 118 | m_SoundSysDLL(NULL), |
|---|
| 119 | m_GameDLL(NULL), |
|---|
| 120 | m_Client(NULL), |
|---|
| 121 | m_Server(NULL), |
|---|
| 122 | m_SvGuiCallback(NULL), |
|---|
| 123 | m_ConByGuiWin(NULL), |
|---|
| 124 | m_Timer(), |
|---|
| 125 | m_TotalTime(0.0), |
|---|
| 126 | m_LastMousePos(IN_OTHER_2D_GUI) |
|---|
| 127 | { |
|---|
| 128 | m_GLContext=new wxGLContext(this); |
|---|
| 129 | |
|---|
| 130 | SetCursor(wxCURSOR_BLANK); |
|---|
| 131 | SetBackgroundStyle(wxBG_STYLE_PAINT); |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | |
|---|
| 135 | MainCanvasT::~MainCanvasT() |
|---|
| 136 | { |
|---|
| 137 | m_InitState=INIT_REQUIRED; |
|---|
| 138 | |
|---|
| 139 | wxGetApp().GetConComposite().Detach(m_ConByGuiWin); |
|---|
| 140 | delete m_ConByGuiWin; |
|---|
| 141 | m_ConByGuiWin=NULL; |
|---|
| 142 | |
|---|
| 143 | delete m_Client; m_Client=NULL; |
|---|
| 144 | delete m_Server; m_Server=NULL; |
|---|
| 145 | |
|---|
| 146 | if (m_SvGuiCallback) |
|---|
| 147 | { |
|---|
| 148 | m_SvGuiCallback->MainMenuGui=NULL; |
|---|
| 149 | delete m_SvGuiCallback; m_SvGuiCallback=NULL; |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | |
|---|
| 153 | // Release the Game. |
|---|
| 154 | if (cf::GameSys::Game) |
|---|
| 155 | { |
|---|
| 156 | cf::GameSys::Game->Release(); |
|---|
| 157 | cf::GameSys::Game=NULL; |
|---|
| 158 | } |
|---|
| 159 | |
|---|
| 160 | // This code has been moved down, see there for details. |
|---|
| 161 | // if (m_GameDLL) |
|---|
| 162 | // { |
|---|
| 163 | // FreeLibrary(m_GameDLL); |
|---|
| 164 | // m_GameDLL=NULL; |
|---|
| 165 | // } |
|---|
| 166 | |
|---|
| 167 | // When the game has been unloaded, no collision models must be left in the collision model manager. |
|---|
| 168 | wxASSERT(cf::ClipSys::CollModelMan->GetUniqueCMCount()==0); |
|---|
| 169 | |
|---|
| 170 | |
|---|
| 171 | // Release the GuiManager (*before* the renderer). |
|---|
| 172 | if (cf::GuiSys::GuiMan) |
|---|
| 173 | { |
|---|
| 174 | delete cf::GuiSys::GuiMan; |
|---|
| 175 | cf::GuiSys::GuiMan=NULL; |
|---|
| 176 | } |
|---|
| 177 | |
|---|
| 178 | |
|---|
| 179 | // Release the Cafu Sound System. |
|---|
| 180 | if (SoundSystem) |
|---|
| 181 | { |
|---|
| 182 | SoundSystem->Release(); |
|---|
| 183 | SoundSystem=NULL; |
|---|
| 184 | } |
|---|
| 185 | |
|---|
| 186 | if (m_SoundSysDLL) |
|---|
| 187 | { |
|---|
| 188 | FreeLibrary(m_SoundSysDLL); |
|---|
| 189 | m_SoundSysDLL=NULL; |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | |
|---|
| 193 | // Release the GUI resources. |
|---|
| 194 | if (m_GuiResources) |
|---|
| 195 | { |
|---|
| 196 | delete m_GuiResources; |
|---|
| 197 | m_GuiResources=NULL; |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | |
|---|
| 201 | // Release the model manager. |
|---|
| 202 | if (m_ModelManager) |
|---|
| 203 | { |
|---|
| 204 | delete m_ModelManager; |
|---|
| 205 | m_ModelManager=NULL; |
|---|
| 206 | } |
|---|
| 207 | |
|---|
| 208 | |
|---|
| 209 | // Release the Cafu Material System. |
|---|
| 210 | if (MatSys::TextureMapManager) |
|---|
| 211 | { |
|---|
| 212 | // MatSys::TextureMapManager->FreeTextureMap(m_WhiteTexture); |
|---|
| 213 | MatSys::TextureMapManager=NULL; |
|---|
| 214 | } |
|---|
| 215 | |
|---|
| 216 | if (MatSys::Renderer) |
|---|
| 217 | { |
|---|
| 218 | MatSys::Renderer->Release(); |
|---|
| 219 | MatSys::Renderer=NULL; |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | if (m_RendererDLL) |
|---|
| 223 | { |
|---|
| 224 | FreeLibrary(m_RendererDLL); |
|---|
| 225 | m_RendererDLL=NULL; |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | // This code used to be further up, but under Windows at r423, the following problem exists: |
|---|
| 229 | // Class CafuModelT is derived from class ModelT, and thus has a virtual destructor. |
|---|
| 230 | // When we call ModelManagerT::GetModel() in the game DLL, everything works all right, |
|---|
| 231 | // but the code linked to the game DLL is used to create the new model, which causes |
|---|
| 232 | // the vtable of the newly created model to point to the destructor code in the game DLL. |
|---|
| 233 | // When the call FreeLibrary(m_GameDLL); before the destructors of the models in the |
|---|
| 234 | // m_ModelManager are run, we essentially remove the code that the virtual destructors |
|---|
| 235 | // are pointing to, causing access violation. |
|---|
| 236 | // Moving the call to FreeLibrary() below the "delete m_ModelManager;" fixes the problem. |
|---|
| 237 | // |
|---|
| 238 | // Also see this report of someone else experiencing the same problem: |
|---|
| 239 | // http://social.msdn.microsoft.com/forums/en-US/vclanguage/thread/dacc7dbd-2775-4e86-a429-8dd32fae0e33 |
|---|
| 240 | if (m_GameDLL) |
|---|
| 241 | { |
|---|
| 242 | FreeLibrary(m_GameDLL); |
|---|
| 243 | m_GameDLL=NULL; |
|---|
| 244 | } |
|---|
| 245 | } |
|---|
| 246 | |
|---|
| 247 | |
|---|
| 248 | // A helper function modelled analogous to the PlatformAux::GetRenderer() function. |
|---|
| 249 | static cf::GameSys::GameI* LoadGameDLL(const std::string& GameDllName, HMODULE& GameDLL) |
|---|
| 250 | { |
|---|
| 251 | #ifdef SCONS_BUILD_DIR |
|---|
| 252 | #define QUOTE(str) QUOTE_HELPER(str) |
|---|
| 253 | #define QUOTE_HELPER(str) #str |
|---|
| 254 | |
|---|
| 255 | #ifdef _WIN32 |
|---|
| 256 | const std::string GameDllPathName=std::string("Games/")+GameDllName+"/Code/"+QUOTE(SCONS_BUILD_DIR)+"/"+GameDllName+".dll"; |
|---|
| 257 | #else |
|---|
| 258 | const std::string GameDllPathName=std::string("Games/")+GameDllName+"/Code/"+QUOTE(SCONS_BUILD_DIR)+"/lib"+GameDllName+".so"; |
|---|
| 259 | #endif |
|---|
| 260 | |
|---|
| 261 | #undef QUOTE |
|---|
| 262 | #undef QUOTE_HELPER |
|---|
| 263 | #else |
|---|
| 264 | const std::string GameDllPathName=std::string("Games/")+GameDllName+"/Code/"+GameDllName+PlatformAux::GetEnvFileSuffix()+".dll"; |
|---|
| 265 | #endif |
|---|
| 266 | |
|---|
| 267 | |
|---|
| 268 | #ifdef _WIN32 |
|---|
| 269 | GameDLL=LoadLibraryA(GameDllPathName.c_str()); |
|---|
| 270 | if (!GameDLL) { Console->Warning(cf::va("Could not load the game DLL at %s.\n", GameDllPathName.c_str())); return NULL; } |
|---|
| 271 | #else |
|---|
| 272 | // Note that RTLD_GLOBAL must *not* be passed-in here, or else we get in trouble with subsequently loaded libraries. |
|---|
| 273 | // (E.g. it causes dlsym(GameDLL, "GetGame") to return identical results for different GameDLLs.) |
|---|
| 274 | // Please refer to the man page of dlopen for more details. |
|---|
| 275 | GameDLL=dlopen(GameDllPathName.c_str(), RTLD_NOW); |
|---|
| 276 | if (!GameDLL) { Console->Warning(cf::va("Could not load the game DLL at %s (%s).\n", GameDllPathName.c_str(), dlerror())); return NULL; } |
|---|
| 277 | #endif |
|---|
| 278 | |
|---|
| 279 | |
|---|
| 280 | typedef cf::GameSys::GameI* (__stdcall *GetGameFuncT)(MatSys::RendererI* Renderer, |
|---|
| 281 | MatSys::TextureMapManagerI* TexMapMan, MaterialManagerI* MatMan, cf::GuiSys::GuiManI* GuiMan_, cf::ConsoleI* Console_, |
|---|
| 282 | ConsoleInterpreterI* ConInterpreter_, cf::ClipSys::CollModelManI* CollModelMan_, SoundSysI* SoundSystem_, |
|---|
| 283 | SoundShaderManagerI* SoundShaderManager_); |
|---|
| 284 | |
|---|
| 285 | #if defined(_WIN32) && !defined(_WIN64) |
|---|
| 286 | GetGameFuncT GetGameFunc=(GetGameFuncT)GetProcAddress(GameDLL, "_GetGame@36"); |
|---|
| 287 | #else |
|---|
| 288 | GetGameFuncT GetGameFunc=(GetGameFuncT)GetProcAddress(GameDLL, "GetGame"); |
|---|
| 289 | #endif |
|---|
| 290 | |
|---|
| 291 | if (!GetGameFunc) { Console->Warning("Could not get the address of the GetGame() function.\n"); FreeLibrary(GameDLL); GameDLL=NULL; return NULL; } |
|---|
| 292 | |
|---|
| 293 | |
|---|
| 294 | // When we get here, the other interfaces must already have been implementations assigned. |
|---|
| 295 | assert(MatSys::Renderer); |
|---|
| 296 | assert(MatSys::TextureMapManager); |
|---|
| 297 | assert(MaterialManager); |
|---|
| 298 | assert(cf::GuiSys::GuiMan); |
|---|
| 299 | assert(Console); |
|---|
| 300 | assert(ConsoleInterpreter); |
|---|
| 301 | assert(cf::ClipSys::CollModelMan); |
|---|
| 302 | |
|---|
| 303 | cf::GameSys::GameI* Game=GetGameFunc(MatSys::Renderer, MatSys::TextureMapManager, MaterialManager, cf::GuiSys::GuiMan, Console, ConsoleInterpreter, cf::ClipSys::CollModelMan, SoundSystem, SoundShaderManager); |
|---|
| 304 | |
|---|
| 305 | if (!Game) { Console->Warning("Could not get the game implementation.\n"); FreeLibrary(GameDLL); GameDLL=NULL; return NULL; } |
|---|
| 306 | |
|---|
| 307 | return Game; |
|---|
| 308 | } |
|---|
| 309 | |
|---|
| 310 | |
|---|
| 311 | void MainCanvasT::Initialize() |
|---|
| 312 | { |
|---|
| 313 | extern ConVarT Options_ClientDesiredRenderer; |
|---|
| 314 | extern ConVarT Options_ClientTextureDetail; |
|---|
| 315 | extern ConVarT Options_ClientDesiredSoundSystem; |
|---|
| 316 | extern ConVarT Options_ServerGameName; |
|---|
| 317 | |
|---|
| 318 | m_InitState=INIT_FAILED; |
|---|
| 319 | |
|---|
| 320 | // Initialize the Material System. |
|---|
| 321 | wxASSERT(this->IsShownOnScreen()); |
|---|
| 322 | |
|---|
| 323 | // If this call was in the ctor, it would trigger an assertion in debug build and yield an invalid (unusable) |
|---|
| 324 | // OpenGL context in release builds (the GL code in the MatSys::Renderer->IsSupported() methods would fail). |
|---|
| 325 | this->SetCurrent(*m_GLContext); |
|---|
| 326 | |
|---|
| 327 | |
|---|
| 328 | try |
|---|
| 329 | { |
|---|
| 330 | // Obtain the specified MatSys renderer (or if none is specified, automatically find the "best"). |
|---|
| 331 | const wxString RendererName=wxString(Options_ClientDesiredRenderer.GetValueString()).Trim(); |
|---|
| 332 | |
|---|
| 333 | if (RendererName!="" && !RendererName.StartsWith("#")) |
|---|
| 334 | MatSys::Renderer=PlatformAux::GetRenderer(std::string(RendererName), m_RendererDLL); |
|---|
| 335 | |
|---|
| 336 | if (MatSys::Renderer==NULL || m_RendererDLL==NULL) |
|---|
| 337 | MatSys::Renderer=PlatformAux::GetBestRenderer(m_RendererDLL); |
|---|
| 338 | |
|---|
| 339 | if (MatSys::Renderer==NULL || m_RendererDLL==NULL) |
|---|
| 340 | throw std::runtime_error("Could not find a renderer that is supported on your system."); |
|---|
| 341 | |
|---|
| 342 | MatSys::Renderer->Initialize(); |
|---|
| 343 | |
|---|
| 344 | |
|---|
| 345 | // Obtain the texture map manager from the previously loaded renderer DLL. |
|---|
| 346 | MatSys::TextureMapManager=PlatformAux::GetTextureMapManager(m_RendererDLL); |
|---|
| 347 | |
|---|
| 348 | if (MatSys::TextureMapManager==NULL) |
|---|
| 349 | throw std::runtime_error("Could not get the TextureMapManager from the renderer DLL."); |
|---|
| 350 | |
|---|
| 351 | switch (Options_ClientTextureDetail.GetValueInt()) |
|---|
| 352 | { |
|---|
| 353 | case 1: MatSys::TextureMapManager->SetMaxTextureSize(256); break; |
|---|
| 354 | case 2: MatSys::TextureMapManager->SetMaxTextureSize(128); break; |
|---|
| 355 | } |
|---|
| 356 | |
|---|
| 357 | |
|---|
| 358 | // Initialize the model manager and the GUI resources. |
|---|
| 359 | m_ModelManager=new ModelManagerT(); |
|---|
| 360 | m_GuiResources=new cf::GuiSys::GuiResourcesT(*m_ModelManager); |
|---|
| 361 | |
|---|
| 362 | |
|---|
| 363 | // Initialize the sound system. |
|---|
| 364 | const wxString SoundSysName=wxString(Options_ClientDesiredSoundSystem.GetValueString()).Trim(); |
|---|
| 365 | |
|---|
| 366 | if (SoundSysName!="" && !SoundSysName.StartsWith("#")) |
|---|
| 367 | SoundSystem=PlatformAux::GetSoundSys(std::string(SoundSysName), m_SoundSysDLL); |
|---|
| 368 | |
|---|
| 369 | if (SoundSystem==NULL || m_SoundSysDLL==NULL) |
|---|
| 370 | SoundSystem=PlatformAux::GetBestSoundSys(m_SoundSysDLL); |
|---|
| 371 | |
|---|
| 372 | if (SoundSystem==NULL || m_SoundSysDLL==NULL) |
|---|
| 373 | throw std::runtime_error("Could not find a sound system that is supported on your system."); |
|---|
| 374 | |
|---|
| 375 | if (!SoundSystem->Initialize()) |
|---|
| 376 | { |
|---|
| 377 | Console->Print("WARNING: Sound system failed to initialize!\n"); |
|---|
| 378 | Console->Print("Sorry, but this is probably due to sound driver problems with your computer.\n"); |
|---|
| 379 | Console->Print("We'll proceed anyway, with sound effects disabled.\n"); |
|---|
| 380 | |
|---|
| 381 | SoundSystem=NULL; |
|---|
| 382 | FreeLibrary(m_SoundSysDLL); |
|---|
| 383 | |
|---|
| 384 | #ifdef SCONS_BUILD_DIR |
|---|
| 385 | #define QUOTE(str) QUOTE_HELPER(str) |
|---|
| 386 | #define QUOTE_HELPER(str) #str |
|---|
| 387 | #ifdef _WIN32 |
|---|
| 388 | const std::string NullPaths[]={ std::string("Libs/")+QUOTE(SCONS_BUILD_DIR)+"/SoundSystem/SoundSysNull.dll", "./SoundSysNull.dll" }; |
|---|
| 389 | #else |
|---|
| 390 | const std::string NullPaths[]={ std::string("Libs/")+QUOTE(SCONS_BUILD_DIR)+"/SoundSystem/libSoundSysNull.so", "./libSoundSysNull.so" }; |
|---|
| 391 | #endif |
|---|
| 392 | #undef QUOTE |
|---|
| 393 | #undef QUOTE_HELPER |
|---|
| 394 | #endif |
|---|
| 395 | |
|---|
| 396 | SoundSystem=PlatformAux::GetSoundSys(NullPaths[0], m_SoundSysDLL); |
|---|
| 397 | |
|---|
| 398 | if (SoundSystem==NULL || m_SoundSysDLL==NULL) |
|---|
| 399 | SoundSystem=PlatformAux::GetSoundSys(NullPaths[1], m_SoundSysDLL); |
|---|
| 400 | |
|---|
| 401 | // Null sound system should always be there and loadable... |
|---|
| 402 | if (SoundSystem==NULL || m_SoundSysDLL==NULL) |
|---|
| 403 | throw std::runtime_error("Could not load the Null sound system."); |
|---|
| 404 | |
|---|
| 405 | SoundSystem->Initialize(); // Init of Null sound system always succeeds. |
|---|
| 406 | } |
|---|
| 407 | |
|---|
| 408 | static ConVarT InitialMasterVolume("snd_InitialMasterVolume", 1.0, ConVarT::FLAG_MAIN_EXE | ConVarT::FLAG_PERSISTENT, "The master volume with which the sound system is initialized.", 0.0, 1.0); |
|---|
| 409 | SoundSystem->SetMasterVolume(float(InitialMasterVolume.GetValueDouble())); |
|---|
| 410 | |
|---|
| 411 | |
|---|
| 412 | // Initialize the GUI systems GUI manager. |
|---|
| 413 | // - This has to be done *after* all materials are loaded (AppCafuT::OnInit()) and after the MatSys::Renderer |
|---|
| 414 | // has been initialized, so that the GuiMan finds its default material and can register it for rendering. |
|---|
| 415 | // (This is no longer exactly true: each GUI has now its own local material manager! See r359 from 2011-08-29 for details.) |
|---|
| 416 | // - It has to be done *before* the game is initialized, because even the server needs access to it |
|---|
| 417 | // when it loads static detail model entities that have world/entity-GUIs. |
|---|
| 418 | cf::GuiSys::GuiMan=new cf::GuiSys::GuiManImplT(*m_GuiResources); |
|---|
| 419 | |
|---|
| 420 | |
|---|
| 421 | // Provide a definition for Game, that is, set the global (Cafu.exe-wide) cf::GameSys::Game pointer |
|---|
| 422 | // to a GameI implementation that is provided by a dynamically loaded game DLL. |
|---|
| 423 | // This is analogous to the Material System, where Renderer DLLs provide renderer and texture manager implementations. |
|---|
| 424 | cf::GameSys::Game=LoadGameDLL(Options_ServerGameName.GetValueString(), m_GameDLL); |
|---|
| 425 | |
|---|
| 426 | if (cf::GameSys::Game==NULL || m_GameDLL==NULL) |
|---|
| 427 | throw std::runtime_error("Could not load game "+Options_ServerGameName.GetValueString()+"."); |
|---|
| 428 | |
|---|
| 429 | cf::GameSys::Game->Initialize(true /*(Options_RunMode.GetValueInt() & CLIENT_RUNMODE)>0*/, |
|---|
| 430 | true /*(Options_RunMode.GetValueInt() & SERVER_RUNMODE)>0*/, |
|---|
| 431 | *m_ModelManager); |
|---|
| 432 | |
|---|
| 433 | |
|---|
| 434 | // Create the client and server instances. |
|---|
| 435 | m_SvGuiCallback=new SvGuiCallbT(); |
|---|
| 436 | m_Server=new ServerT(Options_ServerGameName.GetValueString(), *m_SvGuiCallback, *m_ModelManager); |
|---|
| 437 | m_Client=new ClientT(*m_ModelManager); // The client initializes in IDLE state. |
|---|
| 438 | |
|---|
| 439 | |
|---|
| 440 | // Finish the initialization of the GuiSys. |
|---|
| 441 | // Note that in the line below, the call to gui:setMousePos() is important, because it sets "MouseOverWindow" in the GUI properly to "Cl". |
|---|
| 442 | // Without this, a left mouse button click that was not preceeded by a mouse movement would erroneously remove the input focus from "Cl". |
|---|
| 443 | cf::GuiSys::GuiImplT* ClientGui =new cf::GuiSys::GuiImplT(*m_GuiResources, "Cl=gui:new('ClientWindowT'); gui:SetRootWindow(Cl); gui:showMouse(false); gui:setMousePos(320, 240); gui:setFocus(Cl); Cl:SetName('Client');", true); |
|---|
| 444 | cf::GuiSys::WindowT* ClientWindow=ClientGui->GetRootWindow()->Find("Client"); |
|---|
| 445 | ClientWindowT* ClWin =dynamic_cast<ClientWindowT*>(ClientWindow); |
|---|
| 446 | |
|---|
| 447 | assert(ClWin!=NULL); |
|---|
| 448 | if (ClWin) ClWin->SetClient(m_Client); |
|---|
| 449 | cf::GuiSys::GuiMan->Register(ClientGui); |
|---|
| 450 | |
|---|
| 451 | |
|---|
| 452 | cf::GuiSys::GuiI* MainMenuGui=cf::GuiSys::GuiMan->Find("Games/"+Options_ServerGameName.GetValueString()+"/GUIs/MainMenu/MainMenu_main.cgui", true); |
|---|
| 453 | if (MainMenuGui==NULL) |
|---|
| 454 | { |
|---|
| 455 | MainMenuGui=new cf::GuiSys::GuiImplT(*m_GuiResources, "Err=gui:new('WindowT'); gui:SetRootWindow(Err); gui:activate(true); gui:setInteractive(true); gui:showMouse(false); Err:set('rect', 0, 0, 640, 480); Err:set('textScale', 0.8); Err:set('text', 'Error loading MainMenu_main.cgui,\\nsee console <F1> for details.');", true); |
|---|
| 456 | cf::GuiSys::GuiMan->Register(MainMenuGui); |
|---|
| 457 | } |
|---|
| 458 | m_Client->SetMainMenuGui(MainMenuGui); |
|---|
| 459 | m_SvGuiCallback->MainMenuGui=MainMenuGui; // This is the callback for the server, so that it can let the MainMenuGui know about its state changes. |
|---|
| 460 | m_SvGuiCallback->OnServerStateChanged("idle"); // Argh, this is a HACK for setting the initial state... can we move this / do it better? |
|---|
| 461 | |
|---|
| 462 | cf::GuiSys::GuiI* ConsoleGui =cf::GuiSys::GuiMan->Find("Games/"+Options_ServerGameName.GetValueString()+"/GUIs/Console.cgui", true); |
|---|
| 463 | cf::GuiSys::WindowT* ConsoleWindow=ConsoleGui ? ConsoleGui->GetRootWindow()->Find("ConsoleOutput") : NULL; |
|---|
| 464 | |
|---|
| 465 | // Copy the previously collected console output to the new graphical console. |
|---|
| 466 | m_ConByGuiWin=new cf::GuiSys::ConsoleByWindowT(ConsoleWindow); |
|---|
| 467 | m_ConByGuiWin->Print(wxGetApp().GetConBuffer().GetBuffer()); |
|---|
| 468 | wxGetApp().GetConComposite().Attach(m_ConByGuiWin); |
|---|
| 469 | |
|---|
| 470 | m_InitState=INIT_SUCCESS; |
|---|
| 471 | } |
|---|
| 472 | catch (const std::runtime_error& RE) |
|---|
| 473 | { |
|---|
| 474 | wxMessageDialog Msg(NULL, |
|---|
| 475 | wxString("While initializing Cafu, the following error occurred:\n\n") + typeid(RE).name() + "\n" + RE.what(), |
|---|
| 476 | "Cafu Initialization Error", |
|---|
| 477 | wxOK | wxCANCEL | wxCANCEL_DEFAULT | wxICON_ERROR); |
|---|
| 478 | |
|---|
| 479 | Msg.SetExtendedMessage("Sorry it didn't work out.\nTo get help, please post this error at the Cafu forums or mailing-list."); |
|---|
| 480 | Msg.SetOKCancelLabels("Open www.cafu.de", "Close"); |
|---|
| 481 | |
|---|
| 482 | while (Msg.ShowModal()==wxID_OK) |
|---|
| 483 | { |
|---|
| 484 | wxLaunchDefaultBrowser("http://www.cafu.de"); |
|---|
| 485 | } |
|---|
| 486 | |
|---|
| 487 | m_Parent->Destroy(); |
|---|
| 488 | } |
|---|
| 489 | } |
|---|
| 490 | |
|---|
| 491 | |
|---|
| 492 | static const char* ScreenShotSuffixTypes[] = { "jpg", "png", "bmp", NULL }; |
|---|
| 493 | static ConVarT ScreenShotSuffix("screenSuffix", "jpg", ConVarT::FLAG_MAIN_EXE, "The suffix to be used for screen-shot image files. Only \"jpg\", \"png\" and \"bmp\" are allowed.", ScreenShotSuffixTypes); |
|---|
| 494 | |
|---|
| 495 | |
|---|
| 496 | void MainCanvasT::TakeScreenshot() const |
|---|
| 497 | { |
|---|
| 498 | const unsigned int Width =GetClientSize().GetWidth(); |
|---|
| 499 | const unsigned int Height=GetClientSize().GetHeight(); |
|---|
| 500 | static ArrayT<uint32_t> FrameBuffer; |
|---|
| 501 | |
|---|
| 502 | FrameBuffer.Overwrite(); |
|---|
| 503 | FrameBuffer.PushBackEmpty(Width*Height); |
|---|
| 504 | |
|---|
| 505 | // Read the pixels from the OpenGL back buffer into FrameBuffer. |
|---|
| 506 | // Note that the first two parameters (0, 0) specify the left BOTTOM corner of the desired rectangle! |
|---|
| 507 | glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, &FrameBuffer[0]); |
|---|
| 508 | |
|---|
| 509 | // As mentioned above is the data in the FrameBuffer bottom-up. |
|---|
| 510 | // Thus now swap all lines (vertical mirroring). |
|---|
| 511 | for (unsigned int y=0; y<Height/2; y++) |
|---|
| 512 | { |
|---|
| 513 | uint32_t* UpperRow=&FrameBuffer[0]+ y *Width; |
|---|
| 514 | uint32_t* LowerRow=&FrameBuffer[0]+(Height-y-1)*Width; |
|---|
| 515 | |
|---|
| 516 | for (unsigned int x=0; x<Width; x++) |
|---|
| 517 | { |
|---|
| 518 | const uint32_t Swap=*UpperRow; |
|---|
| 519 | |
|---|
| 520 | *UpperRow=*LowerRow; |
|---|
| 521 | *LowerRow=Swap; |
|---|
| 522 | |
|---|
| 523 | UpperRow++; |
|---|
| 524 | LowerRow++; |
|---|
| 525 | } |
|---|
| 526 | } |
|---|
| 527 | |
|---|
| 528 | // Find the next free image name and save the file. |
|---|
| 529 | for (unsigned int Nr=0; Nr<100000; Nr++) |
|---|
| 530 | { |
|---|
| 531 | const wxString FileName=wxString::Format("pic%04u", Nr) + "." + ScreenShotSuffix.GetValueString(); |
|---|
| 532 | |
|---|
| 533 | if (!wxFileExists(FileName)) |
|---|
| 534 | { |
|---|
| 535 | if (BitmapT(Width, Height, &FrameBuffer[0]).SaveToDisk(FileName)) |
|---|
| 536 | { |
|---|
| 537 | Console->Print(std::string("Screenshot \"")+FileName.ToStdString()+"\" saved.\n"); |
|---|
| 538 | } |
|---|
| 539 | else |
|---|
| 540 | { |
|---|
| 541 | Console->Warning(std::string("Screenshot \"")+FileName.ToStdString()+"\" could not be saved.\n"); |
|---|
| 542 | } |
|---|
| 543 | return; |
|---|
| 544 | } |
|---|
| 545 | } |
|---|
| 546 | } |
|---|
| 547 | |
|---|
| 548 | |
|---|
| 549 | void MainCanvasT::OnPaint(wxPaintEvent& PE) |
|---|
| 550 | { |
|---|
| 551 | wxPaintDC dc(this); // It is VERY important not to omit this, or otherwise everything goes havoc. |
|---|
| 552 | |
|---|
| 553 | if (m_InitState==INIT_REQUIRED) |
|---|
| 554 | { |
|---|
| 555 | dc.SetBackground(*wxBLACK_BRUSH); |
|---|
| 556 | dc.Clear(); |
|---|
| 557 | dc.SetBackgroundMode(wxTRANSPARENT); |
|---|
| 558 | dc.SetTextForeground(wxColour(255, 200, 0)); |
|---|
| 559 | dc.DrawText("Initializing Cafu...", 10, 40); |
|---|
| 560 | |
|---|
| 561 | // This code is in this place due to a few peculiarities of OpenGL under GTK that do not exist under MSW: |
|---|
| 562 | // - An OpenGL context can only be made current with a canvas that is shown on the screen. |
|---|
| 563 | // - Relying on EVT_SHOW however is not a good approach, see the discussions at |
|---|
| 564 | // <http://thread.gmane.org/gmane.comp.lib.wxwidgets.general/68490> and |
|---|
| 565 | // <http://thread.gmane.org/gmane.comp.lib.wxwidgets.general/70607> for details. |
|---|
| 566 | // Consequently, the first and best opportunity for initialization is here. |
|---|
| 567 | Initialize(); |
|---|
| 568 | } |
|---|
| 569 | } |
|---|
| 570 | |
|---|
| 571 | |
|---|
| 572 | void MainCanvasT::OnSize(wxSizeEvent& SE) |
|---|
| 573 | { |
|---|
| 574 | if (m_InitState!=INIT_SUCCESS) return; |
|---|
| 575 | |
|---|
| 576 | const wxSize Size=SE.GetSize(); |
|---|
| 577 | |
|---|
| 578 | if (Size.x>0 && Size.y>0) |
|---|
| 579 | MatSys::Renderer->SetViewport(0, 0, Size.x, Size.y); |
|---|
| 580 | } |
|---|
| 581 | |
|---|
| 582 | |
|---|
| 583 | void MainCanvasT::OnIdle(wxIdleEvent& IE) |
|---|
| 584 | { |
|---|
| 585 | IE.RequestMore(); |
|---|
| 586 | |
|---|
| 587 | // Beende das Programm, wenn das letzte aktive GUI geschlossen wurde. |
|---|
| 588 | // if (cf::GuiSys::GuiMan->GetNumberOfActiveGUIs()==0) m_Parent->Close(); |
|---|
| 589 | |
|---|
| 590 | static ConVarT quit("quit", false, ConVarT::FLAG_MAIN_EXE, "The program quits if this variable is set to 1 (true)."); |
|---|
| 591 | if (quit.GetValueBool()) |
|---|
| 592 | { |
|---|
| 593 | quit.SetValue(false); // Immediately reset the value, so that we're able to restart the game from a loop that governs the master loop... |
|---|
| 594 | m_Parent->Close(); |
|---|
| 595 | } |
|---|
| 596 | |
|---|
| 597 | |
|---|
| 598 | const double FrameTimeD=m_Timer.GetSecondsSinceLastCall(); |
|---|
| 599 | const float FrameTimeF=float(FrameTimeD); |
|---|
| 600 | |
|---|
| 601 | m_TotalTime+=FrameTimeD; |
|---|
| 602 | |
|---|
| 603 | extern ConVarT GlobalTime; |
|---|
| 604 | GlobalTime.SetValue(m_TotalTime); |
|---|
| 605 | |
|---|
| 606 | |
|---|
| 607 | if (m_InitState!=INIT_SUCCESS) return; |
|---|
| 608 | |
|---|
| 609 | cf::GuiSys::GuiMan->DistributeClockTickEvents(FrameTimeF); |
|---|
| 610 | |
|---|
| 611 | // If the active GUI is the client GUI, see how far the mouse cursor is off center, |
|---|
| 612 | // derive a mouse event from it, then recenter the mouse cursor. |
|---|
| 613 | cf::GuiSys::GuiI* ActiveGui=cf::GuiSys::GuiMan->GetTopmostActiveAndInteractive(); |
|---|
| 614 | |
|---|
| 615 | if (ActiveGui && ActiveGui->GetRootWindow()->Name=="Client") |
|---|
| 616 | { |
|---|
| 617 | const wxPoint MousePos =ScreenToClient(wxGetMousePosition()); // Note: ScreenToClient() is a method of wxWindow. |
|---|
| 618 | const wxSize WinCenter =GetClientSize()/2; |
|---|
| 619 | const wxPoint MouseDelta=MousePos-WinCenter; |
|---|
| 620 | |
|---|
| 621 | if (m_LastMousePos==IN_CLIENT_3D_GUI) |
|---|
| 622 | { |
|---|
| 623 | CaMouseEventT ME; |
|---|
| 624 | |
|---|
| 625 | ME.Type =CaMouseEventT::CM_MOVE_X; |
|---|
| 626 | ME.Amount=MouseDelta.x; |
|---|
| 627 | if (ME.Amount!=0) ActiveGui->ProcessDeviceEvent(ME); |
|---|
| 628 | |
|---|
| 629 | ME.Type =CaMouseEventT::CM_MOVE_Y; |
|---|
| 630 | ME.Amount=MouseDelta.y; |
|---|
| 631 | if (ME.Amount!=0) ActiveGui->ProcessDeviceEvent(ME); |
|---|
| 632 | } |
|---|
| 633 | |
|---|
| 634 | if (MouseDelta.x || MouseDelta.y) WarpPointer(WinCenter.x, WinCenter.y); |
|---|
| 635 | m_LastMousePos=IN_CLIENT_3D_GUI; |
|---|
| 636 | } |
|---|
| 637 | |
|---|
| 638 | if (!m_Parent->IsIconized()) |
|---|
| 639 | { |
|---|
| 640 | // Render all the GUIs. |
|---|
| 641 | MatSys::Renderer->BeginFrame(m_TotalTime); |
|---|
| 642 | |
|---|
| 643 | cf::GuiSys::GuiMan->RenderAll(); |
|---|
| 644 | |
|---|
| 645 | MatSys::Renderer->EndFrame(); |
|---|
| 646 | this->SwapBuffers(); |
|---|
| 647 | } |
|---|
| 648 | else |
|---|
| 649 | { |
|---|
| 650 | // The main window is minimized - give other applications a chance to run. |
|---|
| 651 | wxMilliSleep(5); |
|---|
| 652 | } |
|---|
| 653 | |
|---|
| 654 | // Update the sound system (Reposition sound sources, update streams). |
|---|
| 655 | SoundSystem->Update(); |
|---|
| 656 | |
|---|
| 657 | // Run a client and a server frame. |
|---|
| 658 | m_Client->MainLoop(FrameTimeF); |
|---|
| 659 | if (m_Server) m_Server->MainLoop(); |
|---|
| 660 | } |
|---|
| 661 | |
|---|
| 662 | |
|---|
| 663 | void MainCanvasT::OnMouseMove(wxMouseEvent& ME) |
|---|
| 664 | { |
|---|
| 665 | if (m_InitState!=INIT_SUCCESS) return; |
|---|
| 666 | |
|---|
| 667 | cf::GuiSys::GuiI* Gui=cf::GuiSys::GuiMan->GetTopmostActiveAndInteractive(); |
|---|
| 668 | |
|---|
| 669 | // This is equivalent to calling cf::GuiSys::GuiMan->ProcessDeviceEvent(MouseEvent), |
|---|
| 670 | // but computing the amount of mouse movement is much easier (and more precise) like this. |
|---|
| 671 | if (Gui && Gui->GetRootWindow()->Name!="Client") |
|---|
| 672 | { |
|---|
| 673 | float OldMousePosX; |
|---|
| 674 | float OldMousePosY; |
|---|
| 675 | |
|---|
| 676 | Gui->GetMousePos(OldMousePosX, OldMousePosY); |
|---|
| 677 | |
|---|
| 678 | const float NewMousePosX=ME.GetX()*(cf::GuiSys::VIRTUAL_SCREEN_SIZE_X/GetSize().x); |
|---|
| 679 | const float NewMousePosY=ME.GetY()*(cf::GuiSys::VIRTUAL_SCREEN_SIZE_Y/GetSize().y); |
|---|
| 680 | |
|---|
| 681 | // This works, but doesn't forward the mouse event to the windows. |
|---|
| 682 | // Gui->SetMousePos(NewMousePosX, NewMousePosY); |
|---|
| 683 | |
|---|
| 684 | CaMouseEventT ME; |
|---|
| 685 | |
|---|
| 686 | ME.Type =CaMouseEventT::CM_MOVE_X; |
|---|
| 687 | ME.Amount=NewMousePosX-OldMousePosX; |
|---|
| 688 | if (ME.Amount!=0) Gui->ProcessDeviceEvent(ME); |
|---|
| 689 | |
|---|
| 690 | ME.Type =CaMouseEventT::CM_MOVE_Y; |
|---|
| 691 | ME.Amount=NewMousePosY-OldMousePosY; |
|---|
| 692 | if (ME.Amount!=0) Gui->ProcessDeviceEvent(ME); |
|---|
| 693 | |
|---|
| 694 | m_LastMousePos=IN_OTHER_2D_GUI; |
|---|
| 695 | } |
|---|
| 696 | } |
|---|
| 697 | |
|---|
| 698 | |
|---|
| 699 | void MainCanvasT::OnMouseWheel(wxMouseEvent& ME) |
|---|
| 700 | { |
|---|
| 701 | if (m_InitState!=INIT_SUCCESS) return; |
|---|
| 702 | |
|---|
| 703 | CaMouseEventT MouseEvent; |
|---|
| 704 | |
|---|
| 705 | MouseEvent.Type =CaMouseEventT::CM_MOVE_Z; |
|---|
| 706 | MouseEvent.Amount=ME.GetWheelDelta(); |
|---|
| 707 | |
|---|
| 708 | cf::GuiSys::GuiMan->ProcessDeviceEvent(MouseEvent); |
|---|
| 709 | } |
|---|
| 710 | |
|---|
| 711 | |
|---|
| 712 | void MainCanvasT::OnLMouseDown(wxMouseEvent& ME) |
|---|
| 713 | { |
|---|
| 714 | if (m_InitState!=INIT_SUCCESS) return; |
|---|
| 715 | |
|---|
| 716 | CaMouseEventT MouseEvent; |
|---|
| 717 | |
|---|
| 718 | MouseEvent.Type =CaMouseEventT::CM_BUTTON0; |
|---|
| 719 | MouseEvent.Amount=1; |
|---|
| 720 | |
|---|
| 721 | cf::GuiSys::GuiMan->ProcessDeviceEvent(MouseEvent); |
|---|
| 722 | } |
|---|
| 723 | |
|---|
| 724 | |
|---|
| 725 | void MainCanvasT::OnLMouseUp(wxMouseEvent& ME) |
|---|
| 726 | { |
|---|
| 727 | if (m_InitState!=INIT_SUCCESS) return; |
|---|
| 728 | |
|---|
| 729 | CaMouseEventT MouseEvent; |
|---|
| 730 | |
|---|
| 731 | MouseEvent.Type=CaMouseEventT::CM_BUTTON0; |
|---|
| 732 | MouseEvent.Amount=0; |
|---|
| 733 | |
|---|
| 734 | cf::GuiSys::GuiMan->ProcessDeviceEvent(MouseEvent); |
|---|
| 735 | } |
|---|
| 736 | |
|---|
| 737 | |
|---|
| 738 | void MainCanvasT::OnRMouseDown(wxMouseEvent& ME) |
|---|
| 739 | { |
|---|
| 740 | if (m_InitState!=INIT_SUCCESS) return; |
|---|
| 741 | |
|---|
| 742 | CaMouseEventT MouseEvent; |
|---|
| 743 | |
|---|
| 744 | MouseEvent.Type =CaMouseEventT::CM_BUTTON1; |
|---|
| 745 | MouseEvent.Amount=1; |
|---|
| 746 | |
|---|
| 747 | cf::GuiSys::GuiMan->ProcessDeviceEvent(MouseEvent); |
|---|
| 748 | } |
|---|
| 749 | |
|---|
| 750 | |
|---|
| 751 | void MainCanvasT::OnRMouseUp(wxMouseEvent& ME) |
|---|
| 752 | { |
|---|
| 753 | if (m_InitState!=INIT_SUCCESS) return; |
|---|
| 754 | |
|---|
| 755 | CaMouseEventT MouseEvent; |
|---|
| 756 | |
|---|
| 757 | MouseEvent.Type =CaMouseEventT::CM_BUTTON1; |
|---|
| 758 | MouseEvent.Amount=0; |
|---|
| 759 | |
|---|
| 760 | cf::GuiSys::GuiMan->ProcessDeviceEvent(MouseEvent); |
|---|
| 761 | } |
|---|
| 762 | |
|---|
| 763 | |
|---|
| 764 | struct KeyCodePairT |
|---|
| 765 | { |
|---|
| 766 | wxKeyCode wxKC; |
|---|
| 767 | CaKeyboardEventT::KeyT CaKC; |
|---|
| 768 | }; |
|---|
| 769 | |
|---|
| 770 | |
|---|
| 771 | static const KeyCodePairT KeyCodes[]= |
|---|
| 772 | { |
|---|
| 773 | { WXK_BACK, CaKeyboardEventT::CK_BACKSPACE }, |
|---|
| 774 | { WXK_TAB, CaKeyboardEventT::CK_TAB }, |
|---|
| 775 | { WXK_RETURN, CaKeyboardEventT::CK_RETURN }, |
|---|
| 776 | { WXK_ESCAPE, CaKeyboardEventT::CK_ESCAPE }, |
|---|
| 777 | { WXK_SPACE, CaKeyboardEventT::CK_SPACE }, |
|---|
| 778 | { (wxKeyCode)48, CaKeyboardEventT::CK_0 }, |
|---|
| 779 | { (wxKeyCode)49, CaKeyboardEventT::CK_1 }, |
|---|
| 780 | { (wxKeyCode)50, CaKeyboardEventT::CK_2 }, |
|---|
| 781 | { (wxKeyCode)51, CaKeyboardEventT::CK_3 }, |
|---|
| 782 | { (wxKeyCode)52, CaKeyboardEventT::CK_4 }, |
|---|
| 783 | { (wxKeyCode)53, CaKeyboardEventT::CK_5 }, |
|---|
| 784 | { (wxKeyCode)54, CaKeyboardEventT::CK_6 }, |
|---|
| 785 | { (wxKeyCode)55, CaKeyboardEventT::CK_7 }, |
|---|
| 786 | { (wxKeyCode)56, CaKeyboardEventT::CK_8 }, |
|---|
| 787 | { (wxKeyCode)57, CaKeyboardEventT::CK_9 }, |
|---|
| 788 | { (wxKeyCode)65, CaKeyboardEventT::CK_A }, |
|---|
| 789 | { (wxKeyCode)66, CaKeyboardEventT::CK_B }, |
|---|
| 790 | { (wxKeyCode)67, CaKeyboardEventT::CK_C }, |
|---|
| 791 | { (wxKeyCode)68, CaKeyboardEventT::CK_D }, |
|---|
| 792 | { (wxKeyCode)69, CaKeyboardEventT::CK_E }, |
|---|
| 793 | { (wxKeyCode)70, CaKeyboardEventT::CK_F }, |
|---|
| 794 | { (wxKeyCode)71, CaKeyboardEventT::CK_G }, |
|---|
| 795 | { (wxKeyCode)72, CaKeyboardEventT::CK_H }, |
|---|
| 796 | { (wxKeyCode)73, CaKeyboardEventT::CK_I }, |
|---|
| 797 | { (wxKeyCode)74, CaKeyboardEventT::CK_J }, |
|---|
| 798 | { (wxKeyCode)75, CaKeyboardEventT::CK_K }, |
|---|
| 799 | { (wxKeyCode)76, CaKeyboardEventT::CK_L }, |
|---|
| 800 | { (wxKeyCode)77, CaKeyboardEventT::CK_M }, |
|---|
| 801 | { (wxKeyCode)78, CaKeyboardEventT::CK_N }, |
|---|
| 802 | { (wxKeyCode)79, CaKeyboardEventT::CK_O }, |
|---|
| 803 | { (wxKeyCode)80, CaKeyboardEventT::CK_P }, |
|---|
| 804 | { (wxKeyCode)81, CaKeyboardEventT::CK_Q }, |
|---|
| 805 | { (wxKeyCode)82, CaKeyboardEventT::CK_R }, |
|---|
| 806 | { (wxKeyCode)83, CaKeyboardEventT::CK_S }, |
|---|
| 807 | { (wxKeyCode)84, CaKeyboardEventT::CK_T }, |
|---|
| 808 | { (wxKeyCode)85, CaKeyboardEventT::CK_U }, |
|---|
| 809 | { (wxKeyCode)86, CaKeyboardEventT::CK_V }, |
|---|
| 810 | { (wxKeyCode)87, CaKeyboardEventT::CK_W }, |
|---|
| 811 | { (wxKeyCode)88, CaKeyboardEventT::CK_X }, |
|---|
| 812 | { (wxKeyCode)89, CaKeyboardEventT::CK_Y }, |
|---|
| 813 | { (wxKeyCode)90, CaKeyboardEventT::CK_Z }, |
|---|
| 814 | { WXK_DELETE, CaKeyboardEventT::CK_DELETE }, |
|---|
| 815 | //{ WXK_START, CaKeyboardEventT:: }, |
|---|
| 816 | //{ WXK_LBUTTON, CaKeyboardEventT:: }, |
|---|
| 817 | //{ WXK_RBUTTON, CaKeyboardEventT:: }, |
|---|
| 818 | //{ WXK_CANCEL, CaKeyboardEventT:: }, |
|---|
| 819 | //{ WXK_MBUTTON, CaKeyboardEventT:: }, |
|---|
| 820 | //{ WXK_CLEAR, CaKeyboardEventT:: }, |
|---|
| 821 | { WXK_SHIFT, CaKeyboardEventT::CK_LSHIFT }, |
|---|
| 822 | { WXK_ALT, CaKeyboardEventT::CK_LMENU }, |
|---|
| 823 | { WXK_CONTROL, CaKeyboardEventT::CK_LCONTROL }, |
|---|
| 824 | //{ WXK_MENU, CaKeyboardEventT:: }, |
|---|
| 825 | { WXK_PAUSE, CaKeyboardEventT::CK_PAUSE }, |
|---|
| 826 | { WXK_CAPITAL, CaKeyboardEventT::CK_CAPITAL }, |
|---|
| 827 | { WXK_END, CaKeyboardEventT::CK_END }, |
|---|
| 828 | { WXK_HOME, CaKeyboardEventT::CK_HOME }, |
|---|
| 829 | { WXK_LEFT, CaKeyboardEventT::CK_LEFT }, |
|---|
| 830 | { WXK_UP, CaKeyboardEventT::CK_UP }, |
|---|
| 831 | { WXK_RIGHT, CaKeyboardEventT::CK_RIGHT }, |
|---|
| 832 | { WXK_DOWN, CaKeyboardEventT::CK_DOWN }, |
|---|
| 833 | //{ WXK_SELECT, CaKeyboardEventT:: }, |
|---|
| 834 | //{ WXK_PRINT, CaKeyboardEventT:: }, |
|---|
| 835 | //{ WXK_EXECUTE, CaKeyboardEventT:: }, |
|---|
| 836 | //{ WXK_SNAPSHOT, CaKeyboardEventT:: }, |
|---|
| 837 | { WXK_INSERT, CaKeyboardEventT::CK_INSERT }, |
|---|
| 838 | //{ WXK_HELP, CaKeyboardEventT:: }, |
|---|
| 839 | { WXK_NUMPAD0, CaKeyboardEventT::CK_NUMPAD0 }, |
|---|
| 840 | { WXK_NUMPAD1, CaKeyboardEventT::CK_NUMPAD1 }, |
|---|
| 841 | { WXK_NUMPAD2, CaKeyboardEventT::CK_NUMPAD2 }, |
|---|
| 842 | { WXK_NUMPAD3, CaKeyboardEventT::CK_NUMPAD3 }, |
|---|
| 843 | { WXK_NUMPAD4, CaKeyboardEventT::CK_NUMPAD4 }, |
|---|
| 844 | { WXK_NUMPAD5, CaKeyboardEventT::CK_NUMPAD5 }, |
|---|
| 845 | { WXK_NUMPAD6, CaKeyboardEventT::CK_NUMPAD6 }, |
|---|
| 846 | { WXK_NUMPAD7, CaKeyboardEventT::CK_NUMPAD7 }, |
|---|
| 847 | { WXK_NUMPAD8, CaKeyboardEventT::CK_NUMPAD8 }, |
|---|
| 848 | { WXK_NUMPAD9, CaKeyboardEventT::CK_NUMPAD9 }, |
|---|
| 849 | //{ WXK_MULTIPLY, CaKeyboardEventT:: }, |
|---|
| 850 | //{ WXK_ADD, CaKeyboardEventT:: }, |
|---|
| 851 | { WXK_SEPARATOR, CaKeyboardEventT::CK_COMMA }, |
|---|
| 852 | { WXK_SUBTRACT, CaKeyboardEventT::CK_MINUS }, |
|---|
| 853 | { WXK_DECIMAL, CaKeyboardEventT::CK_PERIOD }, |
|---|
| 854 | { WXK_DIVIDE, CaKeyboardEventT::CK_SLASH }, |
|---|
| 855 | { WXK_F1, CaKeyboardEventT::CK_F1 }, |
|---|
| 856 | { WXK_F2, CaKeyboardEventT::CK_F2 }, |
|---|
| 857 | { WXK_F3, CaKeyboardEventT::CK_F3 }, |
|---|
| 858 | { WXK_F4, CaKeyboardEventT::CK_F4 }, |
|---|
| 859 | { WXK_F5, CaKeyboardEventT::CK_F5 }, |
|---|
| 860 | { WXK_F6, CaKeyboardEventT::CK_F6 }, |
|---|
| 861 | { WXK_F7, CaKeyboardEventT::CK_F7 }, |
|---|
| 862 | { WXK_F8, CaKeyboardEventT::CK_F8 }, |
|---|
| 863 | { WXK_F9, CaKeyboardEventT::CK_F9 }, |
|---|
| 864 | { WXK_F10, CaKeyboardEventT::CK_F10 }, |
|---|
| 865 | { WXK_F11, CaKeyboardEventT::CK_F11 }, |
|---|
| 866 | { WXK_F12, CaKeyboardEventT::CK_F12 }, |
|---|
| 867 | { WXK_F13, CaKeyboardEventT::CK_F13 }, |
|---|
| 868 | { WXK_F14, CaKeyboardEventT::CK_F14 }, |
|---|
| 869 | { WXK_F15, CaKeyboardEventT::CK_F15 }, |
|---|
| 870 | { WXK_NUMLOCK, CaKeyboardEventT::CK_NUMLOCK }, |
|---|
| 871 | { WXK_SCROLL, CaKeyboardEventT::CK_SCROLL }, |
|---|
| 872 | { WXK_PAGEUP, CaKeyboardEventT::CK_PGUP }, |
|---|
| 873 | { WXK_PAGEDOWN, CaKeyboardEventT::CK_PGDN }, |
|---|
| 874 | //{ WXK_NUMPAD_SPACE, CaKeyboardEventT:: }, |
|---|
| 875 | //{ WXK_NUMPAD_TAB, CaKeyboardEventT:: }, |
|---|
| 876 | { WXK_NUMPAD_ENTER, CaKeyboardEventT::CK_NUMPADENTER }, |
|---|
| 877 | //{ WXK_NUMPAD_F1, CaKeyboardEventT:: }, |
|---|
| 878 | //{ WXK_NUMPAD_F2, CaKeyboardEventT:: }, |
|---|
| 879 | //{ WXK_NUMPAD_F3, CaKeyboardEventT:: }, |
|---|
| 880 | //{ WXK_NUMPAD_F4, CaKeyboardEventT:: }, |
|---|
| 881 | //{ WXK_NUMPAD_HOME, CaKeyboardEventT:: }, |
|---|
| 882 | //{ WXK_NUMPAD_LEFT, CaKeyboardEventT:: }, |
|---|
| 883 | //{ WXK_NUMPAD_UP, CaKeyboardEventT:: }, |
|---|
| 884 | //{ WXK_NUMPAD_RIGHT, CaKeyboardEventT:: }, |
|---|
| 885 | //{ WXK_NUMPAD_DOWN, CaKeyboardEventT:: }, |
|---|
| 886 | //{ WXK_NUMPAD_PAGEUP, CaKeyboardEventT:: }, |
|---|
| 887 | //{ WXK_NUMPAD_PAGEDOWN, CaKeyboardEventT:: }, |
|---|
| 888 | //{ WXK_NUMPAD_END, CaKeyboardEventT:: }, |
|---|
| 889 | //{ WXK_NUMPAD_BEGIN, CaKeyboardEventT:: }, |
|---|
| 890 | //{ WXK_NUMPAD_INSERT, CaKeyboardEventT:: }, |
|---|
| 891 | //{ WXK_NUMPAD_DELETE, CaKeyboardEventT:: }, |
|---|
| 892 | //{ WXK_NUMPAD_EQUAL, CaKeyboardEventT:: }, |
|---|
| 893 | { WXK_NUMPAD_MULTIPLY, CaKeyboardEventT::CK_MULTIPLY }, |
|---|
| 894 | { WXK_NUMPAD_ADD, CaKeyboardEventT::CK_ADD }, |
|---|
| 895 | { WXK_NUMPAD_SEPARATOR, CaKeyboardEventT::CK_NUMPADCOMMA }, |
|---|
| 896 | { WXK_NUMPAD_SUBTRACT, CaKeyboardEventT::CK_SUBTRACT }, |
|---|
| 897 | { WXK_NUMPAD_DECIMAL, CaKeyboardEventT::CK_DECIMAL }, |
|---|
| 898 | { WXK_NUMPAD_DIVIDE, CaKeyboardEventT::CK_DIVIDE }, |
|---|
| 899 | { WXK_WINDOWS_LEFT, CaKeyboardEventT::CK_LWIN }, |
|---|
| 900 | { WXK_WINDOWS_RIGHT, CaKeyboardEventT::CK_RWIN }, |
|---|
| 901 | //{ WXK_WINDOWS_MENU , CaKeyboardEventT:: }, |
|---|
| 902 | //{ WXK_COMMAND, CaKeyboardEventT:: }, |
|---|
| 903 | //{ (wxKeyCode)0, (CaKeyboardEventT::KeyT)0 } |
|---|
| 904 | }; |
|---|
| 905 | |
|---|
| 906 | |
|---|
| 907 | void MainCanvasT::OnKeyDown(wxKeyEvent& KE) |
|---|
| 908 | { |
|---|
| 909 | if (m_InitState!=INIT_SUCCESS) return; |
|---|
| 910 | |
|---|
| 911 | switch (KE.GetKeyCode()) |
|---|
| 912 | { |
|---|
| 913 | case WXK_F1: |
|---|
| 914 | { |
|---|
| 915 | // Activate the in-game console GUI (it's "F1" now, not CK_GRAVE ("^", accent grave) any longer). |
|---|
| 916 | extern ConVarT Options_ServerGameName; |
|---|
| 917 | cf::GuiSys::GuiI* ConsoleGui=cf::GuiSys::GuiMan->Find("Games/"+Options_ServerGameName.GetValueString()+"/GUIs/Console.cgui"); |
|---|
| 918 | |
|---|
| 919 | // ConsoleGui should be the same as in Initialize(), but could be NULL on file not found, parse error, etc. |
|---|
| 920 | if (ConsoleGui!=NULL && !ConsoleGui->GetIsActive()) |
|---|
| 921 | { |
|---|
| 922 | ConsoleGui->Activate(); |
|---|
| 923 | cf::GuiSys::GuiMan->BringToFront(ConsoleGui); |
|---|
| 924 | |
|---|
| 925 | static bool InitialHelpMsgPrinted=false; |
|---|
| 926 | |
|---|
| 927 | if (!InitialHelpMsgPrinted) |
|---|
| 928 | { |
|---|
| 929 | Console->Print("\n"); |
|---|
| 930 | Console->Print("Welcome to the Cafu console!\n"); |
|---|
| 931 | Console->Print("Enter help() to obtain more information.\n"); |
|---|
| 932 | Console->Print("\n"); |
|---|
| 933 | InitialHelpMsgPrinted=true; |
|---|
| 934 | } |
|---|
| 935 | |
|---|
| 936 | // Handled the key. |
|---|
| 937 | return; |
|---|
| 938 | } |
|---|
| 939 | |
|---|
| 940 | // Let the active GUI handle the key below (e.g. for closing the console again). |
|---|
| 941 | break; |
|---|
| 942 | } |
|---|
| 943 | |
|---|
| 944 | case WXK_F5: |
|---|
| 945 | { |
|---|
| 946 | TakeScreenshot(); |
|---|
| 947 | return; |
|---|
| 948 | } |
|---|
| 949 | |
|---|
| 950 | case WXK_F11: |
|---|
| 951 | { |
|---|
| 952 | if (!wxGetApp().IsCustomVideoMode()) |
|---|
| 953 | { |
|---|
| 954 | // Switching full-screen mode with F11 only makes sense if we didn't set a custom video mode (screen resolution). |
|---|
| 955 | // See AppCafuT::OnInit() for more details. |
|---|
| 956 | m_Parent->ShowFullScreen(!m_Parent->IsFullScreen()); |
|---|
| 957 | } |
|---|
| 958 | return; |
|---|
| 959 | } |
|---|
| 960 | } |
|---|
| 961 | |
|---|
| 962 | // Look for the pressed keys keycode in the table and translate it to a CaKeyCode if found. |
|---|
| 963 | for (int KeyCodeNr=0; KeyCodes[KeyCodeNr].wxKC!=0; KeyCodeNr++) |
|---|
| 964 | { |
|---|
| 965 | if (KeyCodes[KeyCodeNr].wxKC==KE.GetKeyCode()) |
|---|
| 966 | { |
|---|
| 967 | CaKeyboardEventT KeyboardEvent; |
|---|
| 968 | |
|---|
| 969 | KeyboardEvent.Type=CaKeyboardEventT::CKE_KEYDOWN; |
|---|
| 970 | KeyboardEvent.Key =KeyCodes[KeyCodeNr].CaKC; |
|---|
| 971 | |
|---|
| 972 | cf::GuiSys::GuiMan->ProcessDeviceEvent(KeyboardEvent); |
|---|
| 973 | // We should probably return here if the GuiMan handled the key, break (only when unhandled) for calling KE.Skip() otherwise. |
|---|
| 974 | break; |
|---|
| 975 | } |
|---|
| 976 | } |
|---|
| 977 | |
|---|
| 978 | KE.Skip(); |
|---|
| 979 | } |
|---|
| 980 | |
|---|
| 981 | |
|---|
| 982 | void MainCanvasT::OnKeyUp(wxKeyEvent& KE) |
|---|
| 983 | { |
|---|
| 984 | if (m_InitState!=INIT_SUCCESS) return; |
|---|
| 985 | |
|---|
| 986 | // Look for the released keys keycode in the table and translate it to a CaKeyCode if found. |
|---|
| 987 | for (int KeyCodeNr=0; KeyCodes[KeyCodeNr].wxKC!=0; KeyCodeNr++) |
|---|
| 988 | { |
|---|
| 989 | if (KeyCodes[KeyCodeNr].wxKC==KE.GetKeyCode()) |
|---|
| 990 | { |
|---|
| 991 | CaKeyboardEventT KeyboardEvent; |
|---|
| 992 | |
|---|
| 993 | KeyboardEvent.Type=CaKeyboardEventT::CKE_KEYUP; |
|---|
| 994 | KeyboardEvent.Key =KeyCodes[KeyCodeNr].CaKC; |
|---|
| 995 | |
|---|
| 996 | cf::GuiSys::GuiMan->ProcessDeviceEvent(KeyboardEvent); |
|---|
| 997 | return; |
|---|
| 998 | } |
|---|
| 999 | } |
|---|
| 1000 | |
|---|
| 1001 | KE.Skip(); |
|---|
| 1002 | } |
|---|
| 1003 | |
|---|
| 1004 | |
|---|
| 1005 | void MainCanvasT::OnKeyChar(wxKeyEvent& KE) |
|---|
| 1006 | { |
|---|
| 1007 | if (m_InitState!=INIT_SUCCESS) return; |
|---|
| 1008 | |
|---|
| 1009 | CaKeyboardEventT KeyboardEvent; |
|---|
| 1010 | |
|---|
| 1011 | KeyboardEvent.Type=CaKeyboardEventT::CKE_CHAR; |
|---|
| 1012 | KeyboardEvent.Key =KE.GetKeyCode(); |
|---|
| 1013 | |
|---|
| 1014 | cf::GuiSys::GuiMan->ProcessDeviceEvent(KeyboardEvent); |
|---|
| 1015 | } |
|---|