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