1 using System; 2 using System.ComponentModel; 3 using System.Globalization; 4 using System.Linq; 5 using DavyKager; 6 using LibRender2.Screens; 7 using OpenBve.Input; 8 using OpenBveApi.Hosts; 9 using OpenBveApi.Interface; 10 using OpenBveApi.Runtime; 11 using OpenTK; 12 using OpenTK.Input; 13 using OpenTK.Graphics.OpenGL; 14 using TrainManager; 15 using ButtonState = OpenTK.Input.ButtonState; 16 17 namespace OpenBve 18 { 19 internal static partial class MainLoop 20 { 21 internal enum QuitMode 22 { 23 ContinueGame = 0, 24 QuitProgram = 1, 25 ExitToMenu = 2 26 } 27 // declarations 28 internal static bool LimitFramerate = false; 29 internal static QuitMode Quit = QuitMode.ContinueGame; 30 /// <summary>BlockKeyRepeat should be set to 'true' whilst processing a KeyUp or KeyDown event.</summary> 31 internal static bool BlockKeyRepeat; 32 /// <summary>The current simulation time-factor</summary> 33 internal static int TimeFactor = 1; 34 35 internal static double timeSinceLastMouseEvent; 36 37 internal static formMain.MainDialogResult currentResult; 38 // internal static formRouteInformation RouteInformationForm; 39 // internal static Thread RouteInfoThread; 40 // internal static bool RouteInfoActive 41 // { 42 // get 43 // { 44 // return RouteInformationForm != null && RouteInformationForm.IsHandleCreated && RouteInformationForm.Visible; 45 // } 46 // } 47 48 49 // internal static AppDomain RouteInfoFormDomain; 50 51 private static double kioskModeTimer; 52 StartLoopEx(formMain.MainDialogResult result)53 internal static void StartLoopEx(formMain.MainDialogResult result) 54 { 55 Program.Sounds.Initialize(Program.CurrentHost, Interface.CurrentOptions.SoundRange); 56 if (Program.CurrentHost.Platform == HostPlatform.MicrosoftWindows) 57 { 58 Tolk.Load(); 59 string name = Tolk.DetectScreenReader(); 60 if (!string.IsNullOrEmpty(name)) 61 { 62 Interface.CurrentOptions.ScreenReaderAvailable = true; 63 Program.FileSystem.AppendToLogFile("Supported screen reader driver " + name + " initialised."); 64 } 65 else 66 { 67 Program.FileSystem.AppendToLogFile("No supported screen reader found."); 68 } 69 } 70 71 //Process extra command line arguments supplied 72 if (result.InitialStation != null) 73 { 74 //We have supplied a station name or index to the loader 75 Program.CurrentRoute.InitialStationName = result.InitialStation; 76 } 77 if (result.StartTime != default(double)) 78 { 79 Program.CurrentRoute.InitialStationTime = result.StartTime; 80 } 81 82 Game.InitialAIDriver = result.AIDriver; 83 Game.InitialReversedConsist = result.ReverseConsist; 84 if (result.FullScreen) 85 { 86 Interface.CurrentOptions.FullscreenMode = true; 87 } 88 if (result.Width != default(double) && result.Height != default(double)) 89 { 90 if (Interface.CurrentOptions.FullscreenMode) 91 { 92 Interface.CurrentOptions.FullscreenWidth = result.Width; 93 Interface.CurrentOptions.FullscreenHeight = result.Height; 94 95 } 96 else 97 { 98 Interface.CurrentOptions.WindowWidth = result.Width; 99 Interface.CurrentOptions.WindowHeight = result.Height; 100 } 101 } 102 if (Interface.CurrentOptions.IsUseNewRenderer) 103 { 104 Program.FileSystem.AppendToLogFile("Using openGL 3.0 (new) renderer"); 105 } 106 else 107 { 108 Program.FileSystem.AppendToLogFile("Using openGL 1.2 (old) renderer"); 109 } 110 if (Interface.CurrentOptions.FullscreenMode) 111 { 112 Program.FileSystem.AppendToLogFile("Initialising full-screen game window of size " + Interface.CurrentOptions.FullscreenWidth + " x " + Interface.CurrentOptions.FullscreenHeight); 113 } 114 else 115 { 116 Program.FileSystem.AppendToLogFile("Initialising game window of size " + Interface.CurrentOptions.WindowWidth + " x " + Interface.CurrentOptions.WindowHeight); 117 } 118 Screen.Initialize(); 119 currentResult = result; 120 Program.currentGameWindow.Closing += OpenTKQuit; 121 Program.currentGameWindow.Run(); 122 } 123 124 // -------------------------------- 125 126 // repeats 127 128 129 // private static void ThreadProc() 130 // { 131 // RouteInformationForm = new formRouteInformation(); 132 // Application.Run(RouteInformationForm); 133 // } 134 OpenTKQuit(object sender, CancelEventArgs e)135 private static void OpenTKQuit(object sender, CancelEventArgs e) 136 { 137 Quit = QuitMode.QuitProgram; 138 } 139 140 /******************** 141 PROCESS EVENTS 142 ********************/ 143 // 144 // MOUSE EVENTS 145 // 146 147 /// <summary>The current mouse state</summary> 148 internal static MouseState currentMouseState, previousMouseState; 149 150 internal static bool MouseGrabEnabled = false; 151 internal static bool MouseGrabIgnoreOnce = false; 152 internal static OpenBveApi.Math.Vector2 MouseGrabTarget = new OpenBveApi.Math.Vector2(0.0, 0.0); 153 154 /// <summary>Called when a mouse button is pressed</summary> 155 /// <param name="sender">The sender</param> 156 /// <param name="e">The button arguments</param> mouseDownEvent(object sender, MouseButtonEventArgs e)157 internal static void mouseDownEvent(object sender, MouseButtonEventArgs e) 158 { 159 timeSinceLastMouseEvent = 0; 160 if (e.Button == MouseButton.Right) 161 { 162 MouseGrabEnabled = !MouseGrabEnabled; 163 MouseGrabIgnoreOnce = true; 164 } 165 if (e.Button == MouseButton.Left) 166 { 167 // if currently in a menu, forward the click to the menu system 168 if (Program.Renderer.CurrentInterface == InterfaceType.Menu) 169 { 170 Game.Menu.ProcessMouseDown(e.X, e.Y); 171 } 172 else if (Program.Renderer.CurrentInterface == InterfaceType.Normal) 173 { 174 Program.Renderer.Touch.TouchCheck(new Vector2(e.X, e.Y)); 175 } 176 } 177 } 178 mouseUpEvent(object sender, MouseButtonEventArgs e)179 internal static void mouseUpEvent(object sender, MouseButtonEventArgs e) 180 { 181 timeSinceLastMouseEvent = 0; 182 if (e.Button == MouseButton.Left) 183 { 184 if (Program.Renderer.CurrentInterface == InterfaceType.Normal) 185 { 186 Program.Renderer.Touch.LeaveCheck(new Vector2(e.X, e.Y)); 187 } 188 } 189 } 190 191 /// <summary>Called when a mouse button is released</summary> 192 /// <param name="sender">The sender</param> 193 /// <param name="e">The button arguments</param> mouseMoveEvent(object sender, MouseMoveEventArgs e)194 internal static void mouseMoveEvent(object sender, MouseMoveEventArgs e) 195 { 196 timeSinceLastMouseEvent = 0; 197 // if currently in a menu, forward the click to the menu system 198 if (Program.Renderer.CurrentInterface == InterfaceType.Menu) 199 { 200 Game.Menu.ProcessMouseMove(e.X, e.Y); 201 } 202 } 203 204 /// <summary>Called when the state of the mouse wheel changes</summary> 205 /// <param name="sender">The sender</param> 206 /// <param name="e">The button arguments</param> mouseWheelEvent(object sender, MouseWheelEventArgs e)207 internal static void mouseWheelEvent(object sender, MouseWheelEventArgs e) 208 { 209 timeSinceLastMouseEvent = 0; 210 if (Program.Renderer.CurrentInterface == InterfaceType.Menu) 211 { 212 Game.Menu.ProcessMouseScroll(e.Delta); 213 } 214 } 215 UpdateMouse(double TimeElapsed)216 internal static void UpdateMouse(double TimeElapsed) 217 { 218 if (Program.Renderer.CurrentInterface != InterfaceType.Menu) 219 { 220 timeSinceLastMouseEvent += TimeElapsed; 221 } 222 else 223 { 224 timeSinceLastMouseEvent = 0; //Always show the mouse in the menu 225 } 226 227 if (Interface.CurrentOptions.CursorHideDelay > 0 && timeSinceLastMouseEvent > Interface.CurrentOptions.CursorHideDelay) 228 { 229 Program.currentGameWindow.CursorVisible = false; 230 } 231 else 232 { 233 Program.currentGameWindow.CursorVisible = true; 234 } 235 236 if (MainLoop.MouseGrabEnabled) 237 { 238 double factor; 239 if (Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior | Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead) 240 { 241 factor = 1.0; 242 } 243 else 244 { 245 factor = 3.0; 246 } 247 248 Program.Renderer.Camera.AlignmentDirection.Yaw += factor * MouseGrabTarget.X; 249 Program.Renderer.Camera.AlignmentDirection.Pitch -= factor * MouseGrabTarget.Y; 250 MouseGrabTarget = OpenBveApi.Math.Vector2.Null; 251 } 252 } 253 254 // 255 // KEYBOARD EVENTS 256 // 257 private static KeyboardModifier CurrentKeyboardModifier = KeyboardModifier.None; 258 ProcessKeyboard()259 internal static void ProcessKeyboard() 260 { 261 if (Interface.CurrentOptions.UseJoysticks) 262 { 263 for (int k = 0; k < Program.Joysticks.AttachedJoysticks.Count; k++) 264 { 265 Guid guid = Program.Joysticks.AttachedJoysticks.ElementAt(k).Key; 266 Program.Joysticks.AttachedJoysticks[guid].Poll(); 267 } 268 } 269 if (Program.Renderer.CurrentInterface == InterfaceType.Menu && Game.Menu.IsCustomizingControl()) 270 { 271 if (Interface.CurrentOptions.UseJoysticks) 272 { 273 for (int k = 0; k < Program.Joysticks.AttachedJoysticks.Count; k++) 274 { 275 Guid guid = Program.Joysticks.AttachedJoysticks.ElementAt(k).Key; 276 int axes = Program.Joysticks.AttachedJoysticks[guid].AxisCount(); 277 for (int i = 0; i < axes; i++) 278 { 279 double aa = Program.Joysticks.AttachedJoysticks[guid].GetAxis(i); 280 if (aa < -0.75) 281 { 282 Game.Menu.SetControlJoyCustomData(guid, JoystickComponent.Axis, i, -1); 283 return; 284 } 285 if (aa > 0.75) 286 { 287 Game.Menu.SetControlJoyCustomData(guid, JoystickComponent.Axis, i, 1); 288 return; 289 } 290 } 291 int buttons = Program.Joysticks.AttachedJoysticks[guid].ButtonCount(); 292 for (int i = 0; i < buttons; i++) 293 { 294 if (Program.Joysticks.AttachedJoysticks[guid].GetButton(i) == ButtonState.Pressed) 295 { 296 Game.Menu.SetControlJoyCustomData(guid, JoystickComponent.Button, i, 1); 297 return; 298 } 299 } 300 int hats = Program.Joysticks.AttachedJoysticks[guid].HatCount(); 301 for (int i = 0; i < hats; i++) 302 { 303 JoystickHatState hat = Program.Joysticks.AttachedJoysticks[guid].GetHat(i); 304 if (hat.Position != HatPosition.Centered) 305 { 306 Game.Menu.SetControlJoyCustomData(guid, JoystickComponent.Hat, i, (int)hat.Position); 307 return; 308 } 309 } 310 } 311 } 312 return; 313 } 314 if (MouseGrabEnabled) 315 { 316 previousMouseState = currentMouseState; 317 currentMouseState = Mouse.GetState(); 318 if (previousMouseState != currentMouseState) 319 { 320 if (MouseGrabIgnoreOnce) 321 { 322 MouseGrabIgnoreOnce = false; 323 } 324 else if (MouseGrabEnabled) 325 { 326 MouseGrabTarget = new OpenBveApi.Math.Vector2(currentMouseState.X - previousMouseState.X, currentMouseState.Y - previousMouseState.Y); 327 } 328 } 329 } 330 331 //Traverse the controls array 332 for (int i = 0; i < Interface.CurrentControls.Length; i++) 333 { 334 Guid currentDevice = Interface.CurrentControls[i].Device; 335 //Check to see if our device is currently available 336 switch (Interface.CurrentControls[i].Method) 337 { 338 case ControlMethod.Joystick: 339 if (Program.Joysticks.AttachedJoysticks.Count == 0 || !Program.Joysticks.AttachedJoysticks[Interface.CurrentControls[i].Device].IsConnected()) 340 { 341 //Not currently connected 342 continue; 343 } 344 break; 345 case ControlMethod.RailDriver: 346 if (!Program.Joysticks.AttachedJoysticks.ContainsKey(AbstractRailDriver.Guid)) 347 { 348 //Not currently connected 349 continue; 350 } 351 currentDevice = AbstractRailDriver.Guid; 352 break; 353 default: 354 //Not a joystick / RD 355 continue; 356 } 357 358 359 switch (Interface.CurrentControls[i].Component) 360 { 361 case JoystickComponent.Axis: 362 var axisState = Program.Joysticks.GetAxis(currentDevice, Interface.CurrentControls[i].Element); 363 if (axisState.ToString(CultureInfo.InvariantCulture) != Interface.CurrentControls[i].LastState) 364 { 365 Interface.CurrentControls[i].LastState = axisState.ToString(CultureInfo.InvariantCulture); 366 if (Interface.CurrentControls[i].InheritedType == Translations.CommandType.AnalogHalf) 367 { 368 if (Math.Sign(axisState) == Math.Sign(Interface.CurrentControls[i].Direction)) 369 { 370 axisState = Math.Abs(axisState); 371 if (axisState < Interface.CurrentOptions.JoystickAxisThreshold) 372 { 373 Interface.CurrentControls[i].AnalogState = 0.0; 374 } 375 else if (Interface.CurrentOptions.JoystickAxisThreshold != 1.0) 376 { 377 Interface.CurrentControls[i].AnalogState = (axisState - Interface.CurrentOptions.JoystickAxisThreshold) / (1.0 - Interface.CurrentOptions.JoystickAxisThreshold); 378 } 379 else 380 { 381 Interface.CurrentControls[i].AnalogState = 1.0; 382 } 383 } 384 } 385 else if (Interface.CurrentControls[i].InheritedType == Translations.CommandType.AnalogFull) 386 { 387 axisState *= (float)Interface.CurrentControls[i].Direction; 388 if (axisState > -Interface.CurrentOptions.JoystickAxisThreshold & axisState < Interface.CurrentOptions.JoystickAxisThreshold) 389 { 390 Interface.CurrentControls[i].AnalogState = 0.0; 391 } 392 else if (Interface.CurrentOptions.JoystickAxisThreshold != 1.0) 393 { 394 if (axisState < 0.0) 395 { 396 Interface.CurrentControls[i].AnalogState = (axisState + Interface.CurrentOptions.JoystickAxisThreshold) / (1.0 - Interface.CurrentOptions.JoystickAxisThreshold); 397 } 398 else if (axisState > 0.0) 399 { 400 Interface.CurrentControls[i].AnalogState = (axisState - Interface.CurrentOptions.JoystickAxisThreshold) / (1.0 - Interface.CurrentOptions.JoystickAxisThreshold); 401 } 402 else 403 { 404 Interface.CurrentControls[i].AnalogState = 0.0; 405 } 406 } 407 else 408 { 409 Interface.CurrentControls[i].AnalogState = Math.Sign(axisState); 410 } 411 } 412 else 413 { 414 if (Math.Sign(axisState) == Math.Sign(Interface.CurrentControls[i].Direction)) 415 { 416 axisState = Math.Abs(axisState); 417 if (axisState < Interface.CurrentOptions.JoystickAxisThreshold) 418 { 419 axisState = 0.0f; 420 } 421 else if (Interface.CurrentOptions.JoystickAxisThreshold != 1.0) 422 { 423 axisState = (float)((axisState - Interface.CurrentOptions.JoystickAxisThreshold) / (1.0 - Interface.CurrentOptions.JoystickAxisThreshold)); 424 } 425 else 426 { 427 axisState = 1.0f; 428 } 429 if (Interface.CurrentControls[i].DigitalState == DigitalControlState.Released | Interface.CurrentControls[i].DigitalState == DigitalControlState.ReleasedAcknowledged) 430 { 431 if (axisState > 0.67) Interface.CurrentControls[i].DigitalState = DigitalControlState.Pressed; 432 } 433 else 434 { 435 if (axisState < 0.33) Interface.CurrentControls[i].DigitalState = DigitalControlState.Released; 436 } 437 } 438 } 439 } 440 break; 441 case JoystickComponent.Button: 442 //Load the current state 443 var buttonState = Program.Joysticks.GetButton(currentDevice, Interface.CurrentControls[i].Element); 444 //Test whether the state is the same as the last frame 445 if (buttonState.ToString() != Interface.CurrentControls[i].LastState) 446 { 447 if (buttonState == ButtonState.Pressed) 448 { 449 Interface.CurrentControls[i].AnalogState = 1.0; 450 Interface.CurrentControls[i].DigitalState = DigitalControlState.Pressed; 451 AddControlRepeat(i); 452 } 453 else 454 { 455 Interface.CurrentControls[i].AnalogState = 0.0; 456 Interface.CurrentControls[i].DigitalState = DigitalControlState.Released; 457 RemoveControlRepeat(i); 458 } 459 //Store the state 460 Interface.CurrentControls[i].LastState = buttonState.ToString(); 461 } 462 break; 463 case JoystickComponent.Hat: 464 //Load the current state 465 var hatState = Program.Joysticks.GetHat(currentDevice, Interface.CurrentControls[i].Element).Position; 466 //Test if the state is the same as last frame 467 if (hatState.ToString() != Interface.CurrentControls[i].LastState) 468 { 469 if ((int)hatState == Interface.CurrentControls[i].Direction) 470 { 471 Interface.CurrentControls[i].AnalogState = 1.0; 472 Interface.CurrentControls[i].DigitalState = DigitalControlState.Pressed; 473 AddControlRepeat(i); 474 } 475 else 476 { 477 Interface.CurrentControls[i].AnalogState = 0.0; 478 Interface.CurrentControls[i].DigitalState = DigitalControlState.Released; 479 RemoveControlRepeat(i); 480 } 481 //Store the state 482 Interface.CurrentControls[i].LastState = hatState.ToString(); 483 } 484 break; 485 } 486 } 487 } 488 489 // save camera setting SaveCameraSettings()490 internal static void SaveCameraSettings() 491 { 492 switch (Program.Renderer.Camera.CurrentMode) 493 { 494 case CameraViewMode.Interior: 495 case CameraViewMode.InteriorLookAhead: 496 TrainManagerBase.PlayerTrain.Cars[TrainManagerBase.PlayerTrain.CameraCar].InteriorCamera = Program.Renderer.Camera.Alignment; 497 break; 498 case CameraViewMode.Exterior: 499 Program.Renderer.Camera.SavedExterior = Program.Renderer.Camera.Alignment; 500 break; 501 case CameraViewMode.Track: 502 case CameraViewMode.FlyBy: 503 case CameraViewMode.FlyByZooming: 504 Program.Renderer.Camera.SavedTrack = Program.Renderer.Camera.Alignment; 505 break; 506 } 507 } 508 509 // restore camera setting RestoreCameraSettings()510 internal static void RestoreCameraSettings() 511 { 512 switch (Program.Renderer.Camera.CurrentMode) 513 { 514 case CameraViewMode.Interior: 515 case CameraViewMode.InteriorLookAhead: 516 Program.Renderer.Camera.Alignment = TrainManagerBase.PlayerTrain.Cars[TrainManagerBase.PlayerTrain.CameraCar].InteriorCamera; 517 break; 518 case CameraViewMode.Exterior: 519 Program.Renderer.Camera.Alignment = Program.Renderer.Camera.SavedExterior; 520 break; 521 case CameraViewMode.Track: 522 case CameraViewMode.FlyBy: 523 case CameraViewMode.FlyByZooming: 524 Program.Renderer.Camera.Alignment = Program.Renderer.Camera.SavedTrack; 525 Program.Renderer.CameraTrackFollower.UpdateAbsolute(Program.Renderer.Camera.SavedTrack.TrackPosition, true, false); 526 Program.Renderer.Camera.Alignment.TrackPosition = Program.Renderer.CameraTrackFollower.TrackPosition; 527 break; 528 } 529 Program.Renderer.Camera.Alignment.Zoom = 0.0; 530 Program.Renderer.Camera.VerticalViewingAngle = Program.Renderer.Camera.OriginalVerticalViewingAngle; 531 } 532 533 534 #if DEBUG 535 536 /// <summary>Checks whether an OpenGL error has occured this frame</summary> 537 /// <param name="Location">The location of the caller (The main loop or the loading screen loop)</param> CheckForOpenGlError(string Location)538 internal static void CheckForOpenGlError(string Location) { 539 var error = GL.GetError(); 540 if (error != ErrorCode.NoError) { 541 string message = Location + ": "; 542 switch (error) { 543 case ErrorCode.InvalidEnum: 544 message += "GL_INVALID_ENUM"; 545 break; 546 case ErrorCode.InvalidValue: 547 message += "GL_INVALID_VALUE"; 548 break; 549 case ErrorCode.InvalidOperation: 550 message += "GL_INVALID_OPERATION"; 551 break; 552 case ErrorCode.StackOverflow: 553 message += "GL_STACK_OVERFLOW"; 554 break; 555 case ErrorCode.StackUnderflow: 556 message += "GL_STACK_UNDERFLOW"; 557 break; 558 case ErrorCode.OutOfMemory: 559 message += "GL_OUT_OF_MEMORY"; 560 break; 561 case ErrorCode.TableTooLargeExt: 562 message += "GL_TABLE_TOO_LARGE"; 563 break; 564 default: 565 message += error.ToString(); 566 break; 567 } 568 throw new InvalidOperationException(message); 569 } 570 } 571 #endif 572 } 573 } 574