forked from chris-hydon/ox-g1-surface
/
SurfaceApp.cs
319 lines (268 loc) · 10 KB
/
SurfaceApp.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
using System;
using Microsoft.Surface;
using Microsoft.Surface.Core;
using Microsoft.Xna.Framework;
namespace SurfaceTower
{
/// <summary>
/// This class provides all of the routine stuff that the Surface requires for every application.
/// </summary>
public abstract class SurfaceApp : Game
{
protected readonly GraphicsDeviceManager graphics;
protected ContactTarget contactTarget;
protected bool applicationLoadCompleteSignalled;
protected UserOrientation currentOrientation = UserOrientation.Bottom;
protected Matrix screenTransform = Matrix.Identity;
protected Matrix inverted;
// application state: Activated, Previewed, Deactivated,
// start in Activated state
protected bool isApplicationActivated = true;
protected bool isApplicationPreviewed;
/// <summary>
/// The graphics device manager for the application.
/// </summary>
protected GraphicsDeviceManager Graphics
{
get { return graphics; }
}
/// <summary>
/// The target receiving all surface input for the application.
/// </summary>
protected ContactTarget ContactTarget
{
get { return contactTarget; }
}
/// <summary>
/// Default constructor.
/// </summary>
public SurfaceApp()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
#region Initialization
/// <summary>
/// Moves and sizes the window to cover the input surface.
/// </summary>
private void SetWindowOnSurface()
{
System.Diagnostics.Debug.Assert(Window.Handle != System.IntPtr.Zero,
"Window initialization must be complete before SetWindowOnSurface is called");
if (Window.Handle == System.IntPtr.Zero)
return;
// We don't want to run in full-screen mode because we need
// overlapped windows, so instead run in windowed mode
// and resize to take up the whole surface with no border.
// Make sure the graphics device has the correct back buffer size.
InteractiveSurface interactiveSurface = InteractiveSurface.DefaultInteractiveSurface;
if (interactiveSurface != null)
{
graphics.PreferredBackBufferWidth = interactiveSurface.Width;
graphics.PreferredBackBufferHeight = interactiveSurface.Height;
graphics.ApplyChanges();
// Remove the border and position the window.
Program.RemoveBorder(Window.Handle);
Program.PositionWindow(Window);
}
}
/// <summary>
/// Initializes the surface input system. This should be called after any window
/// initialization is done, and should only be called once.
/// </summary>
private void InitializeSurfaceInput()
{
System.Diagnostics.Debug.Assert(Window.Handle != System.IntPtr.Zero,
"Window initialization must be complete before InitializeSurfaceInput is called");
if (Window.Handle == System.IntPtr.Zero)
return;
System.Diagnostics.Debug.Assert(contactTarget == null,
"Surface input already initialized");
if (contactTarget != null)
return;
// Create a target for surface input.
contactTarget = new ContactTarget(Window.Handle, EventThreadChoice.OnBackgroundThread);
contactTarget.EnableInput();
}
/// <summary>
/// Reset the application's orientation and transform based on the current launcher orientation.
/// </summary>
private void ResetOrientation()
{
UserOrientation newOrientation = ApplicationLauncher.Orientation;
if (newOrientation == currentOrientation) { return; }
currentOrientation = newOrientation;
if (currentOrientation == UserOrientation.Top)
{
screenTransform = inverted;
}
else
{
screenTransform = Matrix.Identity;
}
}
#endregion
#region Overridden Game Methods
/// <summary>
/// Allows the app to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
SetWindowOnSurface();
InitializeSurfaceInput();
// Set the application's orientation based on the current launcher orientation
currentOrientation = ApplicationLauncher.Orientation;
// Subscribe to surface application activation events
ApplicationLauncher.ApplicationActivated += OnApplicationActivated;
ApplicationLauncher.ApplicationPreviewed += OnApplicationPreviewed;
ApplicationLauncher.ApplicationDeactivated += OnApplicationDeactivated;
// Setup the UI to transform if the UI is rotated.
// Create a rotation matrix to orient the screen so it is viewed correctly
// when the user orientation is 180 degress different.
inverted = Matrix.CreateRotationZ(MathHelper.ToRadians(180)) *
Matrix.CreateTranslation(graphics.GraphicsDevice.Viewport.Width,
graphics.GraphicsDevice.Viewport.Height,
0);
if (currentOrientation == UserOrientation.Top)
{
screenTransform = inverted;
}
base.Initialize();
}
/// <summary>
/// Load your graphics content.
/// </summary>
protected override void LoadContent()
{
}
/// <summary>
/// Unload your graphics content.
/// </summary>
protected override void UnloadContent()
{
Content.Unload();
}
/// <summary>
/// Allows the app to run logic such as updating the world,
/// checking for collisions, gathering input and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (isApplicationActivated || isApplicationPreviewed)
{
if (isApplicationActivated)
{
ProcessContacts(gameTime, contactTarget.GetState());
}
DoUpdate(gameTime, isApplicationActivated);
}
base.Update(gameTime);
}
/// <summary>
/// This is called when the app should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
if (!applicationLoadCompleteSignalled)
{
// Dismiss the loading screen now that we are starting to draw
ApplicationLauncher.SignalApplicationLoadComplete();
applicationLoadCompleteSignalled = true;
}
DoRotate(gameTime, screenTransform.Equals(inverted));
DoDraw(gameTime);
base.Draw(gameTime);
}
#endregion
#region Application Event Handlers
/// <summary>
/// This is called when application has been activated.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnApplicationActivated(object sender, EventArgs e)
{
// Update application state.
isApplicationActivated = true;
isApplicationPreviewed = false;
// Orientaton can change between activations.
ResetOrientation();
}
/// <summary>
/// This is called when application is in preview mode.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnApplicationPreviewed(object sender, EventArgs e)
{
// Update application state.
isApplicationActivated = false;
isApplicationPreviewed = true;
}
/// <summary>
/// This is called when application has been deactivated.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnApplicationDeactivated(object sender, EventArgs e)
{
// Update application state.
isApplicationActivated = false;
isApplicationPreviewed = false;
}
#endregion
#region IDisposable
protected override void Dispose(bool disposing)
{
if (disposing)
{
// Release managed resources.
IDisposable graphicsDispose = graphics as IDisposable;
if (graphicsDispose != null)
{
graphicsDispose.Dispose();
}
if (contactTarget != null)
{
contactTarget.Dispose();
contactTarget = null;
}
}
// Release unmanaged Resources.
// Set large objects to null to facilitate garbage collection.
base.Dispose(disposing);
}
#endregion
#region Methods to override
/// <summary>
/// This is called when the app should draw itself, after Surface-specific methods have been called.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected abstract void DoDraw(GameTime gameTime);
/// <summary>
/// Called whenever the user interface may need rotating.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
/// <param name="inverted">True if the display should be inverted, false otherwise.</param>
protected abstract void DoRotate(GameTime gameTime, bool inverted);
/// <summary>
/// Allows the app to run logic such as updating the world, checking for collisions, gathering input
/// and playing audio. Called after the Surface-specific methods have been called.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
/// <param name="activated">True if the application is activated, false if in preview mode.</param>
protected abstract void DoUpdate(GameTime gameTime, bool activated);
/// <summary>
/// Called just before DoUpdate if contacts need to be processed.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
/// <param name="contacts">The collection of contacts that are currently active.</param>
protected abstract void ProcessContacts(GameTime gameTime, ReadOnlyContactCollection contacts);
#endregion
}
}