Subversion Repositories AndroidProjects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1452 chris 1
#region License
2
//
3
// The Open Toolkit Library License
4
//
5
// Copyright (c) 2006 - 2009 the Open Toolkit library.
6
//
7
// Permission is hereby granted, free of charge, to any person obtaining a copy
8
// of this software and associated documentation files (the "Software"), to deal
9
// in the Software without restriction, including without limitation the rights to 
10
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11
// the Software, and to permit persons to whom the Software is furnished to do
12
// so, subject to the following conditions:
13
//
14
// The above copyright notice and this permission notice shall be included in all
15
// copies or substantial portions of the Software.
16
//
17
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
// OTHER DEALINGS IN THE SOFTWARE.
25
//
26
#endregion
27
 
28
using System;
29
using System.Collections.Generic;
30
using System.ComponentModel;
31
using System.Diagnostics;
32
using System.Drawing;
33
using System.Threading;
34
using OpenTK.Graphics;
35
using OpenTK.Input;
36
using OpenTK.Platform;
37
 
38
namespace OpenTK
39
{
40
    /// <summary>
41
    /// The GameWindow class contains cross-platform methods to create and render on an OpenGL
42
    /// window, handle input and load resources.
43
    /// </summary>
44
    /// <remarks>
45
    /// GameWindow contains several events you can hook or override to add your custom logic:
46
    /// <list>
47
    /// <item>
48
    /// OnLoad: Occurs after creating the OpenGL context, but before entering the main loop.
49
    /// Override to load resources.
50
    /// </item>
51
    /// <item>
52
    /// OnUnload: Occurs after exiting the main loop, but before deleting the OpenGL context.
53
    /// Override to unload resources.
54
    /// </item>
55
    /// <item>
56
    /// OnResize: Occurs whenever GameWindow is resized. You should update the OpenGL Viewport
57
    /// and Projection Matrix here.
58
    /// </item>
59
    /// <item>
60
    /// OnUpdateFrame: Occurs at the specified logic update rate. Override to add your game
61
    /// logic.
62
    /// </item>
63
    /// <item>
64
    /// OnRenderFrame: Occurs at the specified frame render rate. Override to add your
65
    /// rendering code.
66
    /// </item>
67
    /// </list>
68
    /// Call the Run() method to start the application's main loop. Run(double, double) takes two
69
    /// parameters that
70
    /// specify the logic update rate, and the render update rate.
71
    /// </remarks>
72
    public class GameWindow : NativeWindow, IGameWindow, IDisposable
73
    {
74
        #region --- Fields ---
75
 
76
        object exit_lock = new object();
77
 
78
        IGraphicsContext glContext;
79
 
80
        bool isExiting = false;
81
 
82
        double update_period, render_period;
83
        double target_update_period, target_render_period;
84
        // TODO: Implement these:
85
        double update_time, render_time;
86
        VSyncMode vsync;
87
 
88
        Stopwatch update_watch = new Stopwatch(), render_watch = new Stopwatch();
89
        double next_render = 0.0, next_update = 0.0;
90
        FrameEventArgs update_args = new FrameEventArgs();
91
        FrameEventArgs render_args = new FrameEventArgs();
92
 
93
        #endregion
94
 
95
        #region --- Contructors ---
96
 
97
        #region public GameWindow()
98
 
99
        /// <summary>Constructs a new GameWindow with sensible default attributes.</summary>
100
        public GameWindow()
101
            : this(640, 480, GraphicsMode.Default, "OpenTK Game Window", 0, DisplayDevice.Default) { }
102
 
103
        #endregion
104
 
105
        #region public GameWindow(int width, int height)
106
 
107
        /// <summary>Constructs a new GameWindow with the specified attributes.</summary>
108
        /// <param name="width">The width of the GameWindow in pixels.</param>
109
        /// <param name="height">The height of the GameWindow in pixels.</param>
110
        public GameWindow(int width, int height)
111
            : this(width, height, GraphicsMode.Default, "OpenTK Game Window", 0, DisplayDevice.Default) { }
112
 
113
        #endregion
114
 
115
        #region public GameWindow(int width, int height, GraphicsMode mode)
116
 
117
        /// <summary>Constructs a new GameWindow with the specified attributes.</summary>
118
        /// <param name="width">The width of the GameWindow in pixels.</param>
119
        /// <param name="height">The height of the GameWindow in pixels.</param>
120
        /// <param name="mode">The OpenTK.Graphics.GraphicsMode of the GameWindow.</param>
121
        public GameWindow(int width, int height, GraphicsMode mode)
122
            : this(width, height, mode, "OpenTK Game Window", 0, DisplayDevice.Default) { }
123
 
124
        #endregion
125
 
126
        #region public GameWindow(int width, int height, GraphicsMode mode, string title)
127
 
128
        /// <summary>Constructs a new GameWindow with the specified attributes.</summary>
129
        /// <param name="width">The width of the GameWindow in pixels.</param>
130
        /// <param name="height">The height of the GameWindow in pixels.</param>
131
        /// <param name="mode">The OpenTK.Graphics.GraphicsMode of the GameWindow.</param>
132
        /// <param name="title">The title of the GameWindow.</param>
133
        public GameWindow(int width, int height, GraphicsMode mode, string title)
134
            : this(width, height, mode, title, 0, DisplayDevice.Default) { }
135
 
136
        #endregion
137
 
138
        #region public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options)
139
 
140
        /// <summary>Constructs a new GameWindow with the specified attributes.</summary>
141
        /// <param name="width">The width of the GameWindow in pixels.</param>
142
        /// <param name="height">The height of the GameWindow in pixels.</param>
143
        /// <param name="mode">The OpenTK.Graphics.GraphicsMode of the GameWindow.</param>
144
        /// <param name="title">The title of the GameWindow.</param>
145
        /// <param name="options">GameWindow options regarding window appearance and behavior.</param>
146
        public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options)
147
            : this(width, height, mode, title, options, DisplayDevice.Default) { }
148
 
149
        #endregion
150
 
151
        #region public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device)
152
 
153
        /// <summary>Constructs a new GameWindow with the specified attributes.</summary>
154
        /// <param name="width">The width of the GameWindow in pixels.</param>
155
        /// <param name="height">The height of the GameWindow in pixels.</param>
156
        /// <param name="mode">The OpenTK.Graphics.GraphicsMode of the GameWindow.</param>
157
        /// <param name="title">The title of the GameWindow.</param>
158
        /// <param name="options">GameWindow options regarding window appearance and behavior.</param>
159
        /// <param name="device">The OpenTK.Graphics.DisplayDevice to construct the GameWindow in.</param>
160
        public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device)
161
            : this(width, height, mode, title, options, device, 1, 0, GraphicsContextFlags.Default)
162
        { }
163
 
164
        #endregion
165
 
166
        #region public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device, int major, int minor, GraphicsContextFlags flags)
167
 
168
        /// <summary>Constructs a new GameWindow with the specified attributes.</summary>
169
        /// <param name="width">The width of the GameWindow in pixels.</param>
170
        /// <param name="height">The height of the GameWindow in pixels.</param>
171
        /// <param name="mode">The OpenTK.Graphics.GraphicsMode of the GameWindow.</param>
172
        /// <param name="title">The title of the GameWindow.</param>
173
        /// <param name="options">GameWindow options regarding window appearance and behavior.</param>
174
        /// <param name="device">The OpenTK.Graphics.DisplayDevice to construct the GameWindow in.</param>
175
        /// <param name="major">The major version for the OpenGL GraphicsContext.</param>
176
        /// <param name="minor">The minor version for the OpenGL GraphicsContext.</param>
177
        /// <param name="flags">The GraphicsContextFlags version for the OpenGL GraphicsContext.</param>
178
        public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device,
179
            int major, int minor, GraphicsContextFlags flags)
180
            : this(width, height, mode, title, options, device, major, minor, flags, null)
181
        { }
182
 
183
        #endregion
184
 
185
        #region public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device, int major, int minor, GraphicsContextFlags flags, IGraphicsContext sharedContext)
186
 
187
        /// <summary>Constructs a new GameWindow with the specified attributes.</summary>
188
        /// <param name="width">The width of the GameWindow in pixels.</param>
189
        /// <param name="height">The height of the GameWindow in pixels.</param>
190
        /// <param name="mode">The OpenTK.Graphics.GraphicsMode of the GameWindow.</param>
191
        /// <param name="title">The title of the GameWindow.</param>
192
        /// <param name="options">GameWindow options regarding window appearance and behavior.</param>
193
        /// <param name="device">The OpenTK.Graphics.DisplayDevice to construct the GameWindow in.</param>
194
        /// <param name="major">The major version for the OpenGL GraphicsContext.</param>
195
        /// <param name="minor">The minor version for the OpenGL GraphicsContext.</param>
196
        /// <param name="flags">The GraphicsContextFlags version for the OpenGL GraphicsContext.</param>
197
        /// <param name="sharedContext">An IGraphicsContext to share resources with.</param>
198
        public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device,
199
                          int major, int minor, GraphicsContextFlags flags, IGraphicsContext sharedContext)
200
            : base(width, height, title, options,
201
                   mode == null ? GraphicsMode.Default : mode,
202
                   device == null ? DisplayDevice.Default : device)
203
        {
204
            try
205
            {
206
                glContext = new GraphicsContext(mode == null ? GraphicsMode.Default : mode, WindowInfo, major, minor, flags);
207
                glContext.MakeCurrent(WindowInfo);
208
                (glContext as IGraphicsContextInternal).LoadAll();
209
 
210
                VSync = VSyncMode.On;
211
 
212
                //glWindow.WindowInfoChanged += delegate(object sender, EventArgs e) { OnWindowInfoChangedInternal(e); };
213
            }
214
            catch (Exception e)
215
            {
216
                Debug.Print(e.ToString());
217
                base.Dispose();
218
                throw;
219
            }
220
        }
221
 
222
        #endregion
223
 
224
        #endregion
225
 
226
        #region --- Public Members ---
227
 
228
        #region Methods
229
 
230
        #region Dispose
231
 
232
        /// <summary>
233
        /// Disposes of the GameWindow, releasing all resources consumed by it.
234
        /// </summary>
235
        public override void Dispose()
236
        {
237
            try
238
            {
239
                Dispose(true);
240
            }
241
            finally
242
            {
243
                try
244
                {
245
                    if (glContext != null)
246
                    {
247
                        glContext.Dispose();
248
                        glContext = null;
249
                    }
250
                }
251
                finally
252
                {
253
                    base.Dispose();
254
                }
255
            }
256
            GC.SuppressFinalize(this);
257
        }
258
 
259
        #endregion
260
 
261
        #region Exit
262
 
263
        /// <summary>
264
        /// Closes the GameWindow. Equivalent to <see cref="NativeWindow.Close"/> method.
265
        /// </summary>
266
        /// <remarks>
267
        /// <para>Override if you are not using <see cref="GameWindow.Run()"/>.</para>
268
        /// <para>If you override this method, place a call to base.Exit(), to ensure proper OpenTK shutdown.</para>
269
        /// </remarks>
270
        public virtual void Exit()
271
        {
272
            Close();
273
        }
274
 
275
        #endregion
276
 
277
        #region MakeCurrent
278
 
279
        /// <summary>
280
        /// Makes the GraphicsContext current on the calling thread.
281
        /// </summary>
282
        public void MakeCurrent()
283
        {
284
            EnsureUndisposed();
285
            Context.MakeCurrent(WindowInfo);
286
        }
287
 
288
        #endregion
289
 
290
        #region OnClose
291
 
292
        /// <summary>
293
        /// Called when the NativeWindow is about to close.
294
        /// </summary>
295
        /// <param name="e">
296
        /// The <see cref="System.ComponentModel.CancelEventArgs" /> for this event.
297
        /// Set e.Cancel to true in order to stop the GameWindow from closing.</param>
298
        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
299
        {
300
            base.OnClosing(e);
301
            if (!e.Cancel)
302
            {
303
                isExiting = true;
304
                OnUnloadInternal(EventArgs.Empty);
305
            }
306
        }
307
 
308
 
309
        #endregion
310
 
311
        #region OnLoad
312
 
313
        /// <summary>
314
        /// Called after an OpenGL context has been established, but before entering the main loop.
315
        /// </summary>
316
        /// <param name="e">Not used.</param>
317
        protected virtual void OnLoad(EventArgs e)
318
        {
319
            if (Load != null) Load(this, e);
320
        }
321
 
322
        #endregion
323
 
324
        #region OnUnload
325
 
326
        /// <summary>
327
        /// Called after GameWindow.Exit was called, but before destroying the OpenGL context.
328
        /// </summary>
329
        /// <param name="e">Not used.</param>
330
        protected virtual void OnUnload(EventArgs e)
331
        {
332
            if (Unload != null) Unload(this, e);
333
        }
334
 
335
        #endregion
336
 
337
        #region public void Run()
338
 
339
        /// <summary>
340
        /// Enters the game loop of the GameWindow using the maximum update rate.
341
        /// </summary>
342
        /// <seealso cref="Run(double)"/>
343
        public void Run()
344
        {
345
            Run(0.0, 0.0);
346
        }
347
 
348
        #endregion
349
 
350
        #region public void Run(double updateFrequency)
351
 
352
        /// <summary>
353
        /// Enters the game loop of the GameWindow using the specified update rate.
354
        /// maximum possible render frequency.
355
        /// </summary>
356
        public void Run(double updateRate)
357
        {
358
            Run(updateRate, 0.0);
359
        }
360
 
361
        #endregion
362
 
363
        #region public void Run(double updates_per_second, double frames_per_second)
364
 
365
        /// <summary>
366
        /// Enters the game loop of the GameWindow updating and rendering at the specified frequency.
367
        /// </summary>
368
        /// <remarks>
369
        /// When overriding the default game loop you should call ProcessEvents()
370
        /// to ensure that your GameWindow responds to operating system events.
371
        /// <para>
372
        /// Once ProcessEvents() returns, it is time to call update and render the next frame.
373
        /// </para>
374
        /// </remarks>
375
        /// <param name="updates_per_second">The frequency of UpdateFrame events.</param>
376
        /// <param name="frames_per_second">The frequency of RenderFrame events.</param>
377
        public void Run(double updates_per_second, double frames_per_second)
378
        {
379
            EnsureUndisposed();
380
 
381
            try
382
            {
383
                if (updates_per_second < 0.0 || updates_per_second > 200.0)
384
                    throw new ArgumentOutOfRangeException("updates_per_second", updates_per_second,
385
                                                          "Parameter should be inside the range [0.0, 200.0]");
386
                if (frames_per_second < 0.0 || frames_per_second > 200.0)
387
                    throw new ArgumentOutOfRangeException("frames_per_second", frames_per_second,
388
                                                          "Parameter should be inside the range [0.0, 200.0]");
389
 
390
                TargetUpdateFrequency = updates_per_second;
391
                TargetRenderFrequency = frames_per_second;
392
 
393
                Visible = true;   // Make sure the GameWindow is visible.
394
                OnLoadInternal(EventArgs.Empty);
395
                OnResize(EventArgs.Empty);
396
 
397
                // On some platforms, ProcessEvents() does not return while the user is resizing or moving
398
                // the window. We can avoid this issue by raising UpdateFrame and RenderFrame events
399
                // whenever we encounter a size or move event.
400
                // Note: hack disabled. Threaded rendering isprovides a better solution to this issue.
401
                //Move += DispatchUpdateAndRenderFrame;
402
                //Resize += DispatchUpdateAndRenderFrame;
403
 
404
                Debug.Print("Entering main loop.");
405
                update_watch.Start();
406
                render_watch.Start();
407
                while (true)
408
                {
409
                    ProcessEvents();
410
                    if (Exists && !IsExiting)
411
                        DispatchUpdateAndRenderFrame(this, EventArgs.Empty);
412
                    else
413
                        return;
414
                }
415
            }
416
            finally
417
            {
418
                Move -= DispatchUpdateAndRenderFrame;
419
                Resize -= DispatchUpdateAndRenderFrame;
420
 
421
                if (Exists)
422
                {
423
                    // TODO: Should similar behaviour be retained, possibly on native window level?
424
                    //while (this.Exists)
425
                    //    ProcessEvents(false);
426
                }
427
            }
428
        }
429
 
430
        void DispatchUpdateAndRenderFrame(object sender, EventArgs e)
431
        {
432
            RaiseUpdateFrame(update_watch, ref next_update, update_args);
433
            RaiseRenderFrame(render_watch, ref next_render, render_args);
434
        }
435
 
436
        void RaiseUpdateFrame(Stopwatch update_watch, ref double next_update, FrameEventArgs update_args)
437
        {
438
            int num_updates = 0;
439
            double total_update_time = 0;
440
 
441
            // Cap the maximum time drift to 1 second (e.g. when the process is suspended).
442
            double time = update_watch.Elapsed.TotalSeconds;
443
            if (time <= 0)
444
                return;
445
            if (time > 1.0)
446
                time = 1.0;
447
 
448
            // Raise UpdateFrame events until we catch up with our target update rate.
449
            while (next_update - time <= 0 && time > 0)
450
            {
451
                next_update -= time;
452
                update_args.Time = time;
453
                OnUpdateFrameInternal(update_args);
454
                time = update_time = update_watch.Elapsed.TotalSeconds - time;
455
                // Stopwatches are not accurate over long time periods.
456
                // We accumulate the total elapsed time into the time variable
457
                // while reseting the Stopwatch frequently.
458
                update_watch.Reset();
459
                update_watch.Start();
460
 
461
                // Don't schedule a new update more than 1 second in the future.
462
                // Sometimes the hardware cannot keep up with updates
463
                // (e.g. when the update rate is too high, or the UpdateFrame processing
464
                // is too costly). This cap ensures  we can catch up in a reasonable time
465
                // once the load becomes lighter.
466
                next_update += TargetUpdatePeriod;
467
                next_update = Math.Max(next_update, -1.0);
468
 
469
                total_update_time += update_time;
470
 
471
                // Allow up to 10 consecutive UpdateFrame events to prevent the
472
                // application from "hanging" when the hardware cannot keep up
473
                // with the requested update rate.
474
                if (++num_updates >= 10 || TargetUpdateFrequency == 0.0)
475
                    break;
476
            }
477
 
478
            // Calculate statistics 
479
            if (num_updates > 0)
480
            {
481
                update_period = total_update_time / (double)num_updates;
482
            }
483
        }
484
 
485
        void RaiseRenderFrame(Stopwatch render_watch, ref double next_render, FrameEventArgs render_args)
486
        {
487
            // Cap the maximum time drift to 1 second (e.g. when the process is suspended).
488
            double time = render_watch.Elapsed.TotalSeconds;
489
            if (time > 1.0)
490
                time = 1.0;
491
            if (time <= 0)
492
                return;
493
            double time_left = next_render - time;
494
 
495
            if (time_left <= 0.0)
496
            {
497
                // Schedule next render event. The 1 second cap ensures
498
                // the process does not appear to hang.
499
                next_render = time_left + TargetRenderPeriod;
500
                if (next_render < -1.0)
501
                    next_render = -1.0;
502
 
503
                render_watch.Reset();
504
                render_watch.Start();
505
 
506
                if (time > 0)
507
                {
508
                    // Todo: revisit this code. Maybe check average framerate instead?
509
                    // Note: VSyncMode.Adaptive enables vsync by default. The code below
510
                    // is supposed to disable vsync if framerate becomes too low (half of target
511
                    // framerate in the current approach) and reenable once the framerate
512
                    // rises again.
513
                    // Note 2: calling Context.VSync = true repeatedly seems to cause jitter on
514
                    // some configurations. If possible, we should avoid repeated calls.
515
                    // Note 3: we may not read/write the VSync property without a current context.
516
                    // This may come to pass if the user has moved rendering to his own thread.
517
                    if (Context.IsCurrent && VSync == VSyncMode.Adaptive && TargetRenderPeriod != 0)
518
                    {
519
                        // Check if we have enough time for a vsync
520
                        if (RenderTime > 2.0 * TargetRenderPeriod)
521
                            Context.VSync = false;
522
                        else
523
                            Context.VSync = true;
524
                    }
525
 
526
                    render_period = render_args.Time = time;
527
                    OnRenderFrameInternal(render_args);
528
                    render_time = render_watch.Elapsed.TotalSeconds;
529
                }
530
            }
531
        }
532
 
533
        #endregion
534
 
535
        #region SwapBuffers
536
 
537
        /// <summary>
538
        /// Swaps the front and back buffer, presenting the rendered scene to the user.
539
        /// </summary>
540
        public void SwapBuffers()
541
        {
542
            EnsureUndisposed();
543
            this.Context.SwapBuffers();
544
        }
545
 
546
        #endregion
547
 
548
        #endregion
549
 
550
        #region Properties
551
 
552
        #region Context
553
 
554
        /// <summary>
555
        /// Returns the opengl IGraphicsContext associated with the current GameWindow.
556
        /// </summary>
557
        public IGraphicsContext Context
558
        {
559
            get
560
            {
561
                EnsureUndisposed();
562
                return glContext;
563
            }
564
        }
565
 
566
        #endregion
567
 
568
        #region IsExiting
569
 
570
        /// <summary>
571
        /// Gets a value indicating whether the shutdown sequence has been initiated
572
        /// for this window, by calling GameWindow.Exit() or hitting the 'close' button.
573
        /// If this property is true, it is no longer safe to use any OpenTK.Input or
574
        /// OpenTK.Graphics.OpenGL functions or properties.
575
        /// </summary>
576
        public bool IsExiting
577
        {
578
            get
579
            {
580
                EnsureUndisposed();
581
                return isExiting;
582
            }
583
        }
584
 
585
        #endregion
586
 
587
        #region Joysticks
588
 
589
        /// <summary>
590
        /// Gets a readonly IList containing all available OpenTK.Input.JoystickDevices.
591
        /// </summary>
592
        public IList<JoystickDevice> Joysticks
593
        {
594
            get { return InputDriver.Joysticks; }
595
        }
596
 
597
        #endregion
598
 
599
        #region Keyboard
600
 
601
        /// <summary>
602
        /// Gets the primary Keyboard device, or null if no Keyboard exists.
603
        /// </summary>
604
        public KeyboardDevice Keyboard
605
        {
606
            get { return InputDriver.Keyboard.Count > 0 ? InputDriver.Keyboard[0] : null; }
607
        }
608
 
609
        #endregion
610
 
611
        #region Mouse
612
 
613
        /// <summary>
614
        /// Gets the primary Mouse device, or null if no Mouse exists.
615
        /// </summary>
616
        public MouseDevice Mouse
617
        {
618
            get { return InputDriver.Mouse.Count > 0 ? InputDriver.Mouse[0] : null; }
619
        }
620
 
621
        #endregion
622
 
623
        #region --- GameWindow Timing ---
624
 
625
        // TODO: Disabled because it is not reliable enough. Use vsync as a workaround.
626
 
627
        //#region public bool AllowSleep
628
 
629
        //public bool AllowSleep
630
        //{
631
        //    get { return allow_sleep; }
632
        //    set { allow_sleep = value; }
633
        //}
634
 
635
        //#endregion
636
 
637
        #region RenderFrequency
638
 
639
        /// <summary>
640
        /// Gets a double representing the actual frequency of RenderFrame events, in hertz (i.e. fps or frames per second).
641
        /// </summary>
642
        public double RenderFrequency
643
        {
644
            get
645
            {
646
                EnsureUndisposed();
647
                if (render_period == 0.0)
648
                    return 1.0;
649
                return 1.0 / render_period;
650
            }
651
        }
652
 
653
        #endregion
654
 
655
        #region RenderPeriod
656
 
657
        /// <summary>
658
        /// Gets a double representing the period of RenderFrame events, in seconds.
659
        /// </summary>
660
        public double RenderPeriod
661
        {
662
            get
663
            {
664
                EnsureUndisposed();
665
                return render_period;
666
            }
667
        }
668
 
669
        #endregion
670
 
671
        #region RenderTime
672
 
673
        /// <summary>
674
        /// Gets a double representing the time spent in the RenderFrame function, in seconds.
675
        /// </summary>
676
        public double RenderTime
677
        {
678
            get
679
            {
680
                EnsureUndisposed();
681
                return render_time;
682
            }
683
            protected set
684
            {
685
                EnsureUndisposed();
686
                render_time = value;
687
            }
688
        }
689
 
690
        #endregion
691
 
692
        #region TargetRenderFrequency
693
 
694
        /// <summary>
695
        /// Gets or sets a double representing the target render frequency, in hertz.
696
        /// </summary>
697
        /// <remarks>
698
        /// <para>A value of 0.0 indicates that RenderFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para>
699
        /// <para>Values lower than 1.0Hz are clamped to 1.0Hz. Values higher than 200.0Hz are clamped to 200.0Hz.</para>
700
        /// </remarks>
701
        public double TargetRenderFrequency
702
        {
703
            get
704
            {
705
                EnsureUndisposed();
706
                if (TargetRenderPeriod == 0.0)
707
                    return 0.0;
708
                return 1.0 / TargetRenderPeriod;
709
            }
710
            set
711
            {
712
                EnsureUndisposed();
713
                if (value < 1.0)
714
                {
715
                    TargetRenderPeriod = 0.0;
716
                }
717
                else if (value <= 200.0)
718
                {
719
                    TargetRenderPeriod = 1.0 / value;
720
                }
721
                else Debug.Print("Target render frequency clamped to 200.0Hz."); // TODO: Where is it actually performed?
722
            }
723
        }
724
 
725
        #endregion
726
 
727
        #region TargetRenderPeriod
728
 
729
        /// <summary>
730
        /// Gets or sets a double representing the target render period, in seconds.
731
        /// </summary>
732
        /// <remarks>
733
        /// <para>A value of 0.0 indicates that RenderFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para>
734
        /// <para>Values lower than 0.005 seconds (200Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0.</para>
735
        /// </remarks>
736
        public double TargetRenderPeriod
737
        {
738
            get
739
            {
740
                EnsureUndisposed();
741
                return target_render_period;
742
            }
743
            set
744
            {
745
                EnsureUndisposed();
746
                if (value <= 0.005)
747
                {
748
                    target_render_period = 0.0;
749
                }
750
                else if (value <= 1.0)
751
                {
752
                    target_render_period = value;
753
                }
754
                else Debug.Print("Target render period clamped to 1.0 seconds.");
755
            }
756
        }
757
 
758
        #endregion
759
 
760
        #region TargetUpdateFrequency
761
 
762
        /// <summary>
763
        /// Gets or sets a double representing the target update frequency, in hertz.
764
        /// </summary>
765
        /// <remarks>
766
        /// <para>A value of 0.0 indicates that UpdateFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para>
767
        /// <para>Values lower than 1.0Hz are clamped to 1.0Hz. Values higher than 200.0Hz are clamped to 200.0Hz.</para>
768
        /// </remarks>
769
        public double TargetUpdateFrequency
770
        {
771
            get
772
            {
773
                EnsureUndisposed();
774
                if (TargetUpdatePeriod == 0.0)
775
                    return 0.0;
776
                return 1.0 / TargetUpdatePeriod;
777
            }
778
            set
779
            {
780
                EnsureUndisposed();
781
                if (value < 1.0)
782
                {
783
                    TargetUpdatePeriod = 0.0;
784
                }
785
                else if (value <= 200.0)
786
                {
787
                    TargetUpdatePeriod = 1.0 / value;
788
                }
789
                else Debug.Print("Target update frequency clamped to 200.0Hz."); // TODO: Where is it actually performed?
790
            }
791
        }
792
 
793
        #endregion
794
 
795
        #region TargetUpdatePeriod
796
 
797
        /// <summary>
798
        /// Gets or sets a double representing the target update period, in seconds.
799
        /// </summary>
800
        /// <remarks>
801
        /// <para>A value of 0.0 indicates that UpdateFrame events are generated at the maximum possible frequency (i.e. only limited by the hardware's capabilities).</para>
802
        /// <para>Values lower than 0.005 seconds (200Hz) are clamped to 0.0. Values higher than 1.0 seconds (1Hz) are clamped to 1.0.</para>
803
        /// </remarks>
804
        public double TargetUpdatePeriod
805
        {
806
            get
807
            {
808
                EnsureUndisposed();
809
                return target_update_period;
810
            }
811
            set
812
            {
813
                EnsureUndisposed();
814
                if (value <= 0.005)
815
                {
816
                    target_update_period = 0.0;
817
                }
818
                else if (value <= 1.0)
819
                {
820
                    target_update_period = value;
821
                }
822
                else Debug.Print("Target update period clamped to 1.0 seconds."); // TODO: Where is it actually performed?
823
            }
824
        }
825
 
826
        #endregion
827
 
828
        #region UpdateFrequency
829
 
830
        /// <summary>
831
        /// Gets a double representing the frequency of UpdateFrame events, in hertz.
832
        /// </summary>
833
        public double UpdateFrequency
834
        {
835
            get
836
            {
837
                EnsureUndisposed();
838
                if (update_period == 0.0)
839
                    return 1.0;
840
                return 1.0 / update_period;
841
            }
842
        }
843
 
844
        #endregion
845
 
846
        #region UpdatePeriod
847
 
848
        /// <summary>
849
        /// Gets a double representing the period of UpdateFrame events, in seconds.
850
        /// </summary>
851
        public double UpdatePeriod
852
        {
853
            get
854
            {
855
                EnsureUndisposed();
856
                return update_period;
857
            }
858
        }
859
 
860
        #endregion
861
 
862
        #region UpdateTime
863
 
864
        /// <summary>
865
        /// Gets a double representing the time spent in the UpdateFrame function, in seconds.
866
        /// </summary>
867
        public double UpdateTime
868
        {
869
            get
870
            {
871
                EnsureUndisposed();
872
                return update_time;
873
            }
874
        }
875
 
876
        #endregion
877
 
878
        #endregion
879
 
880
        #region VSync
881
 
882
        /// <summary>
883
        /// Gets or sets the VSyncMode.
884
        /// </summary>
885
        public VSyncMode VSync
886
        {
887
            get
888
            {
889
                EnsureUndisposed();
890
                GraphicsContext.Assert();
891
                return vsync;
892
            }
893
            set
894
            {
895
                EnsureUndisposed();
896
                GraphicsContext.Assert();
897
                Context.VSync = (vsync = value) != VSyncMode.Off;
898
            }
899
        }
900
 
901
        #endregion
902
 
903
        #region WindowState
904
 
905
        /// <summary>
906
        /// Gets or states the state of the NativeWindow.
907
        /// </summary>
908
        public override WindowState WindowState
909
        {
910
            get
911
            {
912
                return base.WindowState;
913
            }
914
            set
915
            {
916
                base.WindowState = value;
917
                Debug.Print("Updating Context after setting WindowState to {0}", value);
918
 
919
                if (Context != null)
920
                    Context.Update(WindowInfo);
921
            }
922
        }
923
        #endregion
924
 
925
        #endregion
926
 
927
        #region Events
928
 
929
        /// <summary>
930
        /// Occurs before the window is displayed for the first time.
931
        /// </summary>
932
        public event EventHandler<EventArgs> Load;
933
 
934
        /// <summary>
935
        /// Occurs when it is time to render a frame.
936
        /// </summary>
937
        public event EventHandler<FrameEventArgs> RenderFrame;
938
 
939
        /// <summary>
940
        /// Occurs before the window is destroyed.
941
        /// </summary>
942
        public event EventHandler<EventArgs> Unload;
943
 
944
        /// <summary>
945
        /// Occurs when it is time to update a frame.
946
        /// </summary>
947
        public event EventHandler<FrameEventArgs> UpdateFrame;
948
 
949
        #endregion
950
 
951
        #endregion
952
 
953
        #region --- Protected Members ---
954
 
955
        #region Dispose
956
 
957
        /// <summary>
958
        /// Override to add custom cleanup logic.
959
        /// </summary>
960
        /// <param name="manual">True, if this method was called by the application; false if this was called by the finalizer thread.</param>
961
        protected virtual void Dispose(bool manual) { }
962
 
963
        #endregion
964
 
965
        #region OnRenderFrame
966
 
967
        /// <summary>
968
        /// Called when the frame is rendered.
969
        /// </summary>
970
        /// <param name="e">Contains information necessary for frame rendering.</param>
971
        /// <remarks>
972
        /// Subscribe to the <see cref="RenderFrame"/> event instead of overriding this method.
973
        /// </remarks>
974
        protected virtual void OnRenderFrame(FrameEventArgs e)
975
        {
976
            if (RenderFrame != null) RenderFrame(this, e);
977
        }
978
 
979
        #endregion
980
 
981
        #region OnUpdateFrame
982
 
983
        /// <summary>
984
        /// Called when the frame is updated.
985
        /// </summary>
986
        /// <param name="e">Contains information necessary for frame updating.</param>
987
        /// <remarks>
988
        /// Subscribe to the <see cref="UpdateFrame"/> event instead of overriding this method.
989
        /// </remarks>
990
        protected virtual void OnUpdateFrame(FrameEventArgs e)
991
        {
992
            if (UpdateFrame != null) UpdateFrame(this, e);
993
        }
994
 
995
        #endregion
996
 
997
        #region OnWindowInfoChanged
998
 
999
        /// <summary>
1000
        /// Called when the WindowInfo for this GameWindow has changed.
1001
        /// </summary>
1002
        /// <param name="e">Not used.</param>
1003
        protected virtual void OnWindowInfoChanged(EventArgs e) { }
1004
 
1005
        #endregion
1006
 
1007
        #region OnResize
1008
 
1009
        protected override void OnResize(EventArgs e)
1010
        {
1011
            base.OnResize(e);
1012
            glContext.Update(base.WindowInfo);
1013
        }
1014
 
1015
        #endregion
1016
 
1017
        #endregion
1018
 
1019
        #region --- Private Members ---
1020
 
1021
        #region OnLoadInternal
1022
 
1023
        private void OnLoadInternal(EventArgs e)
1024
        {
1025
            OnLoad(e);
1026
        }
1027
 
1028
        #endregion
1029
 
1030
        #region OnRenderFrameInternal
1031
 
1032
        private void OnRenderFrameInternal(FrameEventArgs e) { if (Exists && !isExiting) OnRenderFrame(e); }
1033
 
1034
        #endregion
1035
 
1036
        #region OnUnloadInternal
1037
 
1038
        private void OnUnloadInternal(EventArgs e) { OnUnload(e); }
1039
 
1040
        #endregion
1041
 
1042
        #region OnUpdateFrameInternal
1043
 
1044
        private void OnUpdateFrameInternal(FrameEventArgs e) { if (Exists && !isExiting) OnUpdateFrame(e); }
1045
 
1046
        #endregion
1047
 
1048
        #region OnWindowInfoChangedInternal
1049
 
1050
        private void OnWindowInfoChangedInternal(EventArgs e)
1051
        {
1052
            glContext.MakeCurrent(WindowInfo);
1053
 
1054
            OnWindowInfoChanged(e);
1055
        }
1056
 
1057
        #endregion
1058
 
1059
        #endregion
1060
    }
1061
 
1062
    #region public enum VSyncMode
1063
 
1064
    /// <summary>
1065
    /// Enumerates available VSync modes.
1066
    /// </summary>
1067
    public enum VSyncMode
1068
    {
1069
        /// <summary>
1070
        /// Vsync disabled.
1071
        /// </summary>
1072
        Off = 0,
1073
        /// <summary>
1074
        /// VSync enabled.
1075
        /// </summary>
1076
        On,
1077
        /// <summary>
1078
        /// VSync enabled, unless framerate falls below one half of target framerate.
1079
        /// If no target framerate is specified, this behaves exactly like <see cref="VSyncMode.On"/>.
1080
        /// </summary>
1081
        Adaptive,
1082
    }
1083
 
1084
    #endregion
1085
}