fna-workbench

fna-workbench Commit Details


Date:2016-01-14 09:38:57 (9 years 7 months ago)
Author:Ethan Lee
Branch:master
Commit:04bf5baceaed32b63ddba8bd237961d91d5ef3d7
Parents: 5a0cd5e43e8f75f5f379a5aea78a7adefe9f53ce
Message:Welcome to FNA, FNAPlatform!

Changes:

File differences

FNA.csproj
157157
158158
159159
160
160161
161162
162163
163164
164165
165
166166
167167
168168
......
336336
337337
338338
339
339
340340
341341
342342
<Compile Include="src\Design\Vector4Converter.cs" />
<Compile Include="src\DisplayOrientation.cs" />
<Compile Include="src\DrawableGameComponent.cs" />
<Compile Include="src\FNAPlatform.cs" />
<Compile Include="src\FrameworkDispatcher.cs" />
<Compile Include="src\Game.cs" />
<Compile Include="src\GameComponent.cs" />
<Compile Include="src\GameComponentCollection.cs" />
<Compile Include="src\GameComponentCollectionEventArgs.cs" />
<Compile Include="src\GamePlatform.cs" />
<Compile Include="src\GameServiceContainer.cs" />
<Compile Include="src\GameTime.cs" />
<Compile Include="src\GameWindow.cs" />
<Compile Include="src\SDL2\Input\SDL2_GamePad.cs" />
<Compile Include="src\SDL2\Input\SDL2_KeyboardUtil.cs" />
<Compile Include="src\SDL2\Input\SDL2_Mouse.cs" />
<Compile Include="src\SDL2\SDL2_GamePlatform.cs" />
<Compile Include="src\SDL2\SDL2_FNAPlatform.cs" />
<Compile Include="src\SDL2\SDL2_GameWindow.cs" />
<Compile Include="src\Storage\StorageContainer.cs" />
<Compile Include="src\Storage\StorageDevice.cs" />
Makefile
123123
124124
125125
126
126127
127128
128129
129130
130131
131
132132
133133
134134
......
302302
303303
304304
305
305
306306
307307
308308
src/Design/Vector4Converter.cs \
src/DisplayOrientation.cs \
src/DrawableGameComponent.cs \
src/FNAPlatform.cs \
src/FrameworkDispatcher.cs \
src/Game.cs \
src/GameComponent.cs \
src/GameComponentCollection.cs \
src/GameComponentCollectionEventArgs.cs \
src/GamePlatform.cs \
src/GameServiceContainer.cs \
src/GameTime.cs \
src/GameWindow.cs \
src/SDL2/Input/SDL2_GamePad.cs \
src/SDL2/Input/SDL2_KeyboardUtil.cs \
src/SDL2/Input/SDL2_Mouse.cs \
src/SDL2/SDL2_GamePlatform.cs \
src/SDL2/SDL2_FNAPlatform.cs \
src/SDL2/SDL2_GameWindow.cs \
src/Storage/StorageContainer.cs \
src/Storage/StorageDevice.cs \
src/FNAPlatform.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
#region License
/* FNA - XNA4 Reimplementation for Desktop Platforms
* Copyright 2009-2016 Ethan Lee and the MonoGame Team
*
* Released under the Microsoft Public License.
* See LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion
namespace Microsoft.Xna.Framework
{
internal static class FNAPlatform
{
#region Static Constructor
static FNAPlatform()
{
/* I suspect you may have an urge to put an #if in here for new
* FNAPlatform implementations.
*
* DON'T.
*
* Determine this at runtime, or load dynamically.
* No amount of whining will get me to budge on this.
* -flibit
*/
// Environment.GetEnvironmentVariable("FNA_PLATFORM_BACKEND");
Init =SDL2_FNAPlatform.Init;
Dispose =SDL2_FNAPlatform.Dispose;
BeforeInitialize =SDL2_FNAPlatform.BeforeInitialize;
RunLoop =SDL2_FNAPlatform.RunLoop;
SetPresentationInterval =SDL2_FNAPlatform.SetPresentationInterval;
GetGraphicsAdapters =SDL2_FNAPlatform.GetGraphicsAdapters;
GetKeyFromScancode =SDL2_FNAPlatform.GetKeyFromScancode;
OnIsMouseVisibleChanged =SDL2_FNAPlatform.OnIsMouseVisibleChanged;
GetStorageRoot =SDL2_FNAPlatform.GetStorageRoot;
IsStoragePathConnected =SDL2_FNAPlatform.IsStoragePathConnected;
Log =SDL2_FNAPlatform.Log;
ShowRuntimeError =SDL2_FNAPlatform.ShowRuntimeError;
TextureDataFromStream =SDL2_FNAPlatform.TextureDataFromStream;
SavePNG =SDL2_FNAPlatform.SavePNG;
}
#endregion
#region Public Static Methods
public delegate void InitFunc(Game game);
public static InitFunc Init;
public delegate void DisposeFunc(Game game);
public static DisposeFunc Dispose;
public delegate void BeforeInitializeFunc();
public static BeforeInitializeFunc BeforeInitialize;
public delegate void RunLoopFunc(Game game);
public static RunLoopFunc RunLoop;
public delegate void SetPresentationIntervalFunc(PresentInterval interval);
public static SetPresentationIntervalFunc SetPresentationInterval;
public delegate GraphicsAdapter[] GetGraphicsAdaptersFunc();
public static GetGraphicsAdaptersFunc GetGraphicsAdapters;
public delegate Keys GetKeyFromScancodeFunc(Keys scancode);
public static GetKeyFromScancodeFunc GetKeyFromScancode;
public delegate void OnIsMouseVisibleChangedFunc(bool visible);
public static OnIsMouseVisibleChangedFunc OnIsMouseVisibleChanged;
public delegate string GetStorageRootFunc();
public static GetStorageRootFunc GetStorageRoot;
public delegate bool IsStoragePathConnectedFunc(string path);
public static IsStoragePathConnectedFunc IsStoragePathConnected;
public delegate void LogFunc(string message);
public static LogFunc Log;
public delegate void ShowRuntimeErrorFunc(string title, string message);
public static ShowRuntimeErrorFunc ShowRuntimeError;
public delegate void TextureDataFromStreamFunc(
Stream stream,
out int width,
out int height,
out byte[] pixels,
int reqWidth = -1,
int reqHeight = -1,
bool zoom = false
);
public static TextureDataFromStreamFunc TextureDataFromStream;
public delegate void SavePNGFunc(
Stream stream,
int width,
int height,
int imgWidth,
int imgHeight,
byte[] data
);
public static SavePNGFunc SavePNG;
#endregion
}
}
src/Game.cs
109109
110110
111111
112
112
113113
114114
115115
......
213213
214214
215215
216
217
218216
219217
220218
......
288286
289287
290288
291
292
289
293290
294291
295292
......
351348
352349
353350
354
355
356
357
358
359
360
351
352
361353
362354
363355
......
423415
424416
425417
426
427
428
429
430
431418
432419
433420
......
456443
457444
458445
459
446
460447
461448
462449
......
777764
778765
779766
780
767
781768
782769
783770
......
788775
789776
790777
791
778
792779
793780
794781
......
834821
835822
836823
837
824
838825
839826
840827
if (_isMouseVisible != value)
{
_isMouseVisible = value;
Platform.OnIsMouseVisibleChanged(value);
FNAPlatform.OnIsMouseVisibleChanged(value);
}
}
}
internal static Game Instance = null;
internal GamePlatform Platform;
internal bool RunApplication;
#endregion
_components = new GameComponentCollection();
_content = new ContentManager(_services);
Platform = GamePlatform.Create(this);
_services.AddService(typeof(GamePlatform), Platform);
FNAPlatform.Init(this);
AudioDevice.Initialize();
AudioDevice.Dispose();
if (Platform != null)
{
_services.RemoveService(typeof(GamePlatform));
Platform.Dispose();
Platform = null;
Mouse.WindowHandle = IntPtr.Zero;
}
FNAPlatform.Dispose(this);
Mouse.WindowHandle = IntPtr.Zero;
ContentTypeReaderManager.ClearTypeCreators();
}
public void RunOneFrame()
{
if (Platform == null)
{
return;
}
if (!_initialized)
{
DoInitialize();
BeginRun();
_gameTimer = Stopwatch.StartNew();
Platform.RunLoop();
FNAPlatform.RunLoop(this);
EndRun();
{
if (exception is NoAudioHardwareException)
{
Platform.ShowRuntimeError(
FNAPlatform.ShowRuntimeError(
Window.Title,
"Could not find a suitable audio device. " +
" Verify that a sound card is\ninstalled," +
}
if (exception is NoSuitableGraphicsDeviceException)
{
Platform.ShowRuntimeError(
FNAPlatform.ShowRuntimeError(
Window.Title,
"Could not find a suitable graphics device." +
" More information:\n\n" + exception.Message
graphicsDeviceManager.CreateDevice();
}
Platform.BeforeInitialize();
FNAPlatform.BeforeInitialize();
Initialize();
/* We need to do this after virtual Initialize(...) is called.
src/GamePlatform.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
#region License
/* FNA - XNA4 Reimplementation for Desktop Platforms
* Copyright 2009-2016 Ethan Lee and the MonoGame Team
*
* Released under the Microsoft Public License.
* See LICENSE for details.
*/
#endregion
#region Using Statements
using System;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion
namespace Microsoft.Xna.Framework
{
abstract class GamePlatform : IDisposable
{
#region Public Properties
/// <summary>
/// Gets the Game instance that owns this GamePlatform instance.
/// </summary>
public Game Game
{
get;
private set;
}
public string OSVersion
{
get;
private set;
}
#endregion
#region Protected Properties
protected bool IsDisposed
{
get;
private set;
}
#endregion
#region Protected Constructor
protected GamePlatform(Game game, string osVersion)
{
if (game == null)
{
throw new ArgumentNullException("game");
}
Game = game;
OSVersion = osVersion;
IsDisposed = false;
}
#endregion
#region Deconstructor
~GamePlatform()
{
Dispose(false);
}
#endregion
#region Public Methods
/// <summary>
/// Log the specified Message.
/// </summary>
/// <param name='Message'>
/// The string to print to the log.
/// </param>
public abstract void Log(string Message);
/// <summary>
/// Gives derived classes an opportunity to do work before any
/// components are initialized. Note that the base implementation sets
/// IsActive to true, so derived classes should either call the base
/// implementation or set IsActive to true by their own means.
/// </summary>
public abstract void BeforeInitialize();
/// <summary>
/// When implemented in a derived class, starts the run loop and blocks
/// until it has ended.
/// </summary>
public abstract void RunLoop();
public abstract void OnIsMouseVisibleChanged(bool visible);
public abstract void ShowRuntimeError(
String title,
String message
);
public abstract GraphicsAdapter[] GetGraphicsAdapters();
public abstract void SetPresentationInterval(PresentInterval interval);
public abstract void TextureDataFromStream(
Stream stream,
out int width,
out int height,
out byte[] pixels,
int reqWidth = -1,
int reqHeight = -1,
bool zoom = false
);
public abstract void SavePNG(
Stream stream,
int width,
int height,
int imgWidth,
int imgHeight,
byte[] data
);
public abstract Keys GetKeyFromScancode(Keys scancode);
public abstract string GetStorageRoot();
public abstract bool IsStoragePathConnected(string path);
#endregion
#region Public Static Methods
public static GamePlatform Create(Game game)
{
/* I suspect you may have an urge to put an #if in here for new
* GamePlatform implementations.
*
* DON'T.
*
* Determine this at runtime, or load dynamically.
* No amount of whining will get me to budge on this.
* -flibit
*/
return new SDL2_GamePlatform(game);
}
#endregion
#region IDisposable implementation
/// <summary>
/// Performs application-defined tasks associated with freeing,
/// releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed)
{
IsDisposed = true;
}
}
#endregion
}
}
src/Graphics/GraphicsAdapter.cs
143143
144144
145145
146
146
147147
148148
149149
if (adapters == null)
{
adapters = new ReadOnlyCollection<GraphicsAdapter>(
Game.Instance.Platform.GetGraphicsAdapters()
FNAPlatform.GetGraphicsAdapters()
);
}
return adapters;
src/Graphics/Texture2D.cs
228228
229229
230230
231
231
232232
233233
234234
......
317317
318318
319319
320
320
321321
322322
323323
// Get the Texture2D pixels
byte[] data = new byte[Width * Height * GetFormatSize(Format)];
GetData(data);
Game.Instance.Platform.SavePNG(
FNAPlatform.SavePNG(
stream,
width,
height,
int requestedHeight = -1,
bool zoom = false
) {
Game.Instance.Platform.TextureDataFromStream(
FNAPlatform.TextureDataFromStream(
stream,
out width,
out height,
src/GraphicsDeviceManager.cs
261261
262262
263263
264
264
265265
266266
267267
);
// Apply the PresentInterval.
game.Platform.SetPresentationInterval(
FNAPlatform.SetPresentationInterval(
SynchronizeWithVerticalRetrace ?
GraphicsDevice.PresentationParameters.PresentationInterval :
PresentInterval.Immediate
src/Input/Keyboard.cs
5353
5454
5555
56
56
5757
5858
5959
public static Keys GetKeyFromScancodeEXT(Keys scancode)
{
return Game.Instance.Platform.GetKeyFromScancode(scancode);
return FNAPlatform.GetKeyFromScancode(scancode);
}
#endregion
src/SDL2/Input/SDL2_GamePad.cs
183183
184184
185185
186
186
187187
188188
189189
190190
191191
192
192
193193
194194
195195
......
216216
217217
218218
219
219
220220
221221
222222
......
227227
228228
229229
230
230
231231
232232
233233
......
238238
239239
240240
241
241
242242
243243
244244
......
272272
273273
274274
275
275
276276
277277
278278
}
if (INTERNAL_haptics[which] != IntPtr.Zero)
{
if (Game.Instance.Platform.OSVersion.Equals("Mac OS X") &&
if (SDL2_FNAPlatform.OSVersion.Equals("Mac OS X") &&
SDL.SDL_HapticEffectSupported(INTERNAL_haptics[which], ref INTERNAL_leftRightMacHackEffect) == 1)
{
INTERNAL_hapticTypes[which] = HapticType.LeftRightMacHack;
SDL.SDL_HapticNewEffect(INTERNAL_haptics[which], ref INTERNAL_leftRightMacHackEffect);
}
else if (!Game.Instance.Platform.OSVersion.Equals("Mac OS X") &&
else if (!SDL2_FNAPlatform.OSVersion.Equals("Mac OS X") &&
SDL.SDL_HapticEffectSupported(INTERNAL_haptics[which], ref INTERNAL_leftRightEffect) == 1)
{
INTERNAL_hapticTypes[which] = HapticType.LeftRight;
resChar,
resChar.Length
);
if (Game.Instance.Platform.OSVersion.Equals("Linux"))
if (SDL2_FNAPlatform.OSVersion.Equals("Linux"))
{
result.Append((char) resChar[8]);
result.Append((char) resChar[9]);
result.Append((char) resChar[18]);
result.Append((char) resChar[19]);
}
else if (Game.Instance.Platform.OSVersion.Equals("Mac OS X"))
else if (SDL2_FNAPlatform.OSVersion.Equals("Mac OS X"))
{
result.Append((char) resChar[0]);
result.Append((char) resChar[1]);
result.Append((char) resChar[18]);
result.Append((char) resChar[19]);
}
else if (Game.Instance.Platform.OSVersion.Equals("Windows"))
else if (SDL2_FNAPlatform.OSVersion.Equals("Windows"))
{
bool isXInput = true;
foreach (byte b in resChar)
INTERNAL_guids[which] = result.ToString();
// Initialize light bar
if (Game.Instance.Platform.OSVersion.Equals("Linux") &&
if (SDL2_FNAPlatform.OSVersion.Equals("Linux") &&
INTERNAL_guids[which].Equals("4c05c405"))
{
// Get all of the individual PS4 LED instances
src/SDL2/SDL2_FNAPlatform.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
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
#region License
/* FNA - XNA4 Reimplementation for Desktop Platforms
* Copyright 2009-2016 Ethan Lee and the MonoGame Team
*
* Released under the Microsoft Public License.
* See LICENSE for details.
*/
#endregion
#region USE_SCANCODES Option
// #define USE_SCANCODES
/* XNA Keys are based on keycodes, rather than scancodes.
*
* With SDL2 you can actually pick between SDL_Keycode and SDL_Scancode, but
* scancodes will not be accurate to XNA4. The benefit is that scancodes will
* essentially ignore "foreign" keyboard layouts, making default keyboard
* layouts work out of the box everywhere (unless the actual symbol for the keys
* matters in your game).
*
* At the same time, the TextInputEXT extension will still read the actual chars
* correctly, so you can (mostly) have your cake and eat it too if you don't
* care about your bindings menu not making a lot of sense on foreign layouts.
* -flibit
*/
#endregion
#region Using Statements
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using SDL2;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion
namespace Microsoft.Xna.Framework
{
internal static class SDL2_FNAPlatform
{
#region Public Static Constants
public static readonly string OSVersion = SDL.SDL_GetPlatform();
#endregion
#region Public Static Methods
public static void Init(Game game)
{
/* SDL2 might complain if an OS that uses SDL_main has not actually
* used SDL_main by the time you initialize SDL2.
* The only platform that is affected is Windows, but we can skip
* their WinMain. This was only added to prevent iOS from exploding.
* -flibit
*/
SDL.SDL_SetMainReady();
// This _should_ be the first real SDL call we make...
SDL.SDL_Init(
SDL.SDL_INIT_VIDEO |
SDL.SDL_INIT_JOYSTICK |
SDL.SDL_INIT_GAMECONTROLLER |
SDL.SDL_INIT_HAPTIC
);
// Set any hints to match XNA4 behavior...
string hint = SDL.SDL_GetHint(SDL.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS);
if (String.IsNullOrEmpty(hint))
{
SDL.SDL_SetHint(
SDL.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
"1"
);
}
// If available, load the SDL_GameControllerDB
string mappingsDB = Path.Combine(
TitleContainer.Location,
"gamecontrollerdb.txt"
);
if (File.Exists(mappingsDB))
{
SDL.SDL_GameControllerAddMappingsFromFile(
mappingsDB
);
}
// Set and initialize the SDL2 window
bool forceES2 = Environment.GetEnvironmentVariable(
"FNA_OPENGL_FORCE_ES2"
) == "1";
bool forceCoreProfile = Environment.GetEnvironmentVariable(
"FNA_OPENGL_FORCE_CORE_PROFILE"
) == "1";
game.Window = new SDL2_GameWindow(
forceES2 ||
OSVersion.Equals("Emscripten") ||
OSVersion.Equals("Android") ||
OSVersion.Equals("iOS"),
forceCoreProfile
);
// Disable the screensaver.
SDL.SDL_DisableScreenSaver();
// We hide the mouse cursor by default.
SDL.SDL_ShowCursor(0);
}
public static void Dispose(Game game)
{
if (game.Window != null)
{
/* Some window managers might try to minimize the window as we're
* destroying it. This looks pretty stupid and could cause problems,
* so set this hint right before we destroy everything.
* -flibit
*/
SDL.SDL_SetHintWithPriority(
SDL.SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS,
"0",
SDL.SDL_HintPriority.SDL_HINT_OVERRIDE
);
SDL.SDL_DestroyWindow(game.Window.Handle);
game.Window = null;
}
// This _should_ be the last SDL call we make...
SDL.SDL_Quit();
}
public static void RunLoop(Game game)
{
SDL.SDL_ShowWindow(game.Window.Handle);
// Which display did we end up on?
int displayIndex = SDL.SDL_GetWindowDisplayIndex(
game.Window.Handle
);
// OSX has some fancy fullscreen features, let's use them!
bool osxUseSpaces;
if (OSVersion.Equals("Mac OS X"))
{
string hint = SDL.SDL_GetHint(SDL.SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES);
osxUseSpaces = (String.IsNullOrEmpty(hint) || hint.Equals("1"));
}
else
{
osxUseSpaces = false;
}
// Active Key List
List<Keys> keys = new List<Keys>();
/* Setup Text Input Control Character Arrays
* (Only 4 control keys supported at this time)
*/
bool[] INTERNAL_TextInputControlDown = new bool[4];
int[] INTERNAL_TextInputControlRepeat = new int[4];
bool INTERNAL_TextInputSuppress = false;
SDL.SDL_Event evt;
while (game.RunApplication)
{
while (SDL.SDL_PollEvent(out evt) == 1)
{
// Keyboard
if (evt.type == SDL.SDL_EventType.SDL_KEYDOWN)
{
#if USE_SCANCODES
Keys key = SDL2_KeyboardUtil.ToXNA(evt.key.keysym.scancode);
#else
Keys key = SDL2_KeyboardUtil.ToXNA(evt.key.keysym.sym);
#endif
if (!keys.Contains(key))
{
keys.Add(key);
if (key == Keys.Back)
{
INTERNAL_TextInputControlDown[0] = true;
INTERNAL_TextInputControlRepeat[0] = Environment.TickCount + 400;
TextInputEXT.OnTextInput((char) 8); // Backspace
}
else if (key == Keys.Tab)
{
INTERNAL_TextInputControlDown[1] = true;
INTERNAL_TextInputControlRepeat[1] = Environment.TickCount + 400;
TextInputEXT.OnTextInput((char) 9); // Tab
}
else if (key == Keys.Enter)
{
INTERNAL_TextInputControlDown[2] = true;
INTERNAL_TextInputControlRepeat[2] = Environment.TickCount + 400;
TextInputEXT.OnTextInput((char) 13); // Enter
}
else if (keys.Contains(Keys.LeftControl) && key == Keys.V)
{
INTERNAL_TextInputControlDown[3] = true;
INTERNAL_TextInputControlRepeat[3] = Environment.TickCount + 400;
TextInputEXT.OnTextInput((char) 22); // Control-V (Paste)
INTERNAL_TextInputSuppress = true;
}
}
}
else if (evt.type == SDL.SDL_EventType.SDL_KEYUP)
{
#if USE_SCANCODES
Keys key = SDL2_KeyboardUtil.ToXNA(evt.key.keysym.scancode);
#else
Keys key = SDL2_KeyboardUtil.ToXNA(evt.key.keysym.sym);
#endif
if (keys.Remove(key))
{
if (key == Keys.Back)
{
INTERNAL_TextInputControlDown[0] = false;
}
else if (key == Keys.Tab)
{
INTERNAL_TextInputControlDown[1] = false;
}
else if (key == Keys.Enter)
{
INTERNAL_TextInputControlDown[2] = false;
}
else if ((!keys.Contains(Keys.LeftControl) && INTERNAL_TextInputControlDown[3]) || key == Keys.V)
{
INTERNAL_TextInputControlDown[3] = false;
INTERNAL_TextInputSuppress = false;
}
}
}
// Mouse Input
else if (evt.type == SDL.SDL_EventType.SDL_MOUSEMOTION)
{
Mouse.INTERNAL_IsWarped = false;
}
else if (evt.type == SDL.SDL_EventType.SDL_MOUSEWHEEL)
{
// 120 units per notch. Because reasons.
Mouse.INTERNAL_MouseWheel += evt.wheel.y * 120;
}
// Various Window Events...
else if (evt.type == SDL.SDL_EventType.SDL_WINDOWEVENT)
{
// Window Focus
if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED)
{
game.IsActive = true;
if (!osxUseSpaces)
{
// If we alt-tab away, we lose the 'fullscreen desktop' flag on some WMs
SDL.SDL_SetWindowFullscreen(
game.Window.Handle,
game.GraphicsDevice.PresentationParameters.IsFullScreen ?
(uint) SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP :
0
);
}
// Disable the screensaver when we're back.
SDL.SDL_DisableScreenSaver();
}
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_LOST)
{
game.IsActive = false;
if (!osxUseSpaces)
{
SDL.SDL_SetWindowFullscreen(game.Window.Handle, 0);
}
// Give the screensaver back, we're not that important now.
SDL.SDL_EnableScreenSaver();
}
// Window Resize
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESIZED)
{
Mouse.INTERNAL_WindowWidth = evt.window.data1;
Mouse.INTERNAL_WindowHeight = evt.window.data2;
// Should be called on user resize only, NOT ApplyChanges!
((SDL2_GameWindow) game.Window).INTERNAL_ClientSizeChanged();
}
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED)
{
Mouse.INTERNAL_WindowWidth = evt.window.data1;
Mouse.INTERNAL_WindowHeight = evt.window.data2;
// Need to reset the graphics device any time the window size changes
GraphicsDeviceManager gdm = game.Services.GetService(
typeof(IGraphicsDeviceService)
) as GraphicsDeviceManager;
// FIXME: gdm == null? -flibit
if (gdm.IsFullScreen)
{
GraphicsDevice device = game.GraphicsDevice;
gdm.INTERNAL_ResizeGraphicsDevice(
device.GLDevice.Backbuffer.Width,
device.GLDevice.Backbuffer.Height
);
}
else
{
gdm.INTERNAL_ResizeGraphicsDevice(
evt.window.data1,
evt.window.data2
);
}
}
// Window Move
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MOVED)
{
/* Apparently if you move the window to a new
* display, a GraphicsDevice Reset occurs.
* -flibit
*/
int newIndex = SDL.SDL_GetWindowDisplayIndex(
game.Window.Handle
);
if (newIndex != displayIndex)
{
displayIndex = newIndex;
game.GraphicsDevice.Reset(
game.GraphicsDevice.PresentationParameters,
GraphicsAdapter.Adapters[displayIndex]
);
}
}
// Mouse Focus
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_ENTER)
{
SDL.SDL_DisableScreenSaver();
}
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_LEAVE)
{
SDL.SDL_EnableScreenSaver();
}
}
// Controller device management
else if (evt.type == SDL.SDL_EventType.SDL_CONTROLLERDEVICEADDED)
{
GamePad.INTERNAL_AddInstance(evt.cdevice.which);
}
else if (evt.type == SDL.SDL_EventType.SDL_CONTROLLERDEVICEREMOVED)
{
GamePad.INTERNAL_RemoveInstance(evt.cdevice.which);
}
// Text Input
else if (evt.type == SDL.SDL_EventType.SDL_TEXTINPUT && !INTERNAL_TextInputSuppress)
{
string text;
// Based on the SDL2# LPUtf8StrMarshaler
unsafe
{
byte* endPtr = evt.text.text;
while (*endPtr != 0)
{
endPtr++;
}
byte[] bytes = new byte[endPtr - evt.text.text];
Marshal.Copy((IntPtr) evt.text.text, bytes, 0, bytes.Length);
text = System.Text.Encoding.UTF8.GetString(bytes);
}
if (text.Length > 0)
{
TextInputEXT.OnTextInput(text[0]);
}
}
// Quit
else if (evt.type == SDL.SDL_EventType.SDL_QUIT)
{
game.RunApplication = false;
break;
}
}
// Text Input Controls Key Handling
if (INTERNAL_TextInputControlDown[0] && INTERNAL_TextInputControlRepeat[0] <= Environment.TickCount)
{
TextInputEXT.OnTextInput((char) 8);
}
if (INTERNAL_TextInputControlDown[1] && INTERNAL_TextInputControlRepeat[1] <= Environment.TickCount)
{
TextInputEXT.OnTextInput((char) 9);
}
if (INTERNAL_TextInputControlDown[2] && INTERNAL_TextInputControlRepeat[2] <= Environment.TickCount)
{
TextInputEXT.OnTextInput((char) 13);
}
if (INTERNAL_TextInputControlDown[3] && INTERNAL_TextInputControlRepeat[3] <= Environment.TickCount)
{
TextInputEXT.OnTextInput((char) 22);
}
Keyboard.SetKeys(keys);
game.Tick();
}
// We out.
game.Exit();
}
public static void BeforeInitialize()
{
// We want to initialize the controllers ASAP!
SDL.SDL_Event[] evt = new SDL.SDL_Event[1];
SDL.SDL_PumpEvents(); // Required to get OSX device events this early.
while (SDL.SDL_PeepEvents(
evt,
1,
SDL.SDL_eventaction.SDL_GETEVENT,
SDL.SDL_EventType.SDL_CONTROLLERDEVICEADDED,
SDL.SDL_EventType.SDL_CONTROLLERDEVICEADDED
) == 1) {
GamePad.INTERNAL_AddInstance(evt[0].cdevice.which);
}
}
public static void SetPresentationInterval(PresentInterval interval)
{
if (interval == PresentInterval.Default || interval == PresentInterval.One)
{
if (OSVersion.Equals("Mac OS X"))
{
// Apple is a big fat liar about swap_control_tear. Use stock VSync.
SDL.SDL_GL_SetSwapInterval(1);
}
else
{
if (SDL.SDL_GL_SetSwapInterval(-1) != -1)
{
System.Console.WriteLine("Using EXT_swap_control_tear VSync!");
}
else
{
System.Console.WriteLine("EXT_swap_control_tear unsupported. Fall back to standard VSync.");
SDL.SDL_ClearError();
SDL.SDL_GL_SetSwapInterval(1);
}
}
}
else if (interval == PresentInterval.Immediate)
{
SDL.SDL_GL_SetSwapInterval(0);
}
else if (interval == PresentInterval.Two)
{
SDL.SDL_GL_SetSwapInterval(2);
}
else
{
throw new Exception("Unrecognized PresentInterval!");
}
}
public static GraphicsAdapter[] GetGraphicsAdapters()
{
SDL.SDL_DisplayMode filler = new SDL.SDL_DisplayMode();
GraphicsAdapter[] adapters = new GraphicsAdapter[SDL.SDL_GetNumVideoDisplays()];
for (int i = 0; i < adapters.Length; i += 1)
{
List<DisplayMode> modes = new List<DisplayMode>();
int numModes = SDL.SDL_GetNumDisplayModes(i);
for (int j = 0; j < numModes; j += 1)
{
SDL.SDL_GetDisplayMode(i, j, out filler);
// Check for dupes caused by varying refresh rates.
bool dupe = false;
foreach (DisplayMode mode in modes)
{
if (filler.w == mode.Width && filler.h == mode.Height)
{
dupe = true;
}
}
if (!dupe)
{
modes.Add(
new DisplayMode(
filler.w,
filler.h,
SurfaceFormat.Color // FIXME: Assumption!
)
);
}
}
SDL.SDL_GetCurrentDisplayMode(i, out filler);
adapters[i] = new GraphicsAdapter(
new DisplayMode(
filler.w,
filler.h,
SurfaceFormat.Color // FIXME: Assumption!
),
new DisplayModeCollection(modes),
SDL.SDL_GetDisplayName(i)
);
}
return adapters;
}
public static Keys GetKeyFromScancode(Keys scancode)
{
return SDL2_KeyboardUtil.KeyFromScancode(scancode);
}
public static void OnIsMouseVisibleChanged(bool visible)
{
SDL.SDL_ShowCursor(visible ? 1 : 0);
}
public static string GetStorageRoot()
{
if (OSVersion.Equals("Windows"))
{
return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"SavedGames"
);
}
if (OSVersion.Equals("Mac OS X"))
{
string osConfigDir = Environment.GetEnvironmentVariable("HOME");
if (String.IsNullOrEmpty(osConfigDir))
{
return "."; // Oh well.
}
osConfigDir += "/Library/Application Support";
return osConfigDir;
}
if (OSVersion.Equals("Linux"))
{
// Assuming a non-OSX Unix platform will follow the XDG. Which it should.
string osConfigDir = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
if (String.IsNullOrEmpty(osConfigDir))
{
osConfigDir = Environment.GetEnvironmentVariable("HOME");
if (String.IsNullOrEmpty(osConfigDir))
{
return ".";// Oh well.
}
osConfigDir += "/.local/share";
}
return osConfigDir;
}
throw new Exception("StorageDevice: Platform.OSVersion not handled!");
}
public static bool IsStoragePathConnected(string path)
{
if (OSVersion.Equals("Linux") ||
OSVersion.Equals("Mac OS X"))
{
/* Linux and Mac use locally connected storage in the user's
* home location, which should always be "connected".
*/
return true;
}
if (OSVersion.Equals("Windows"))
{
try
{
return new DriveInfo(path).IsReady;
}
catch
{
// The storageRoot path is invalid / has been removed.
return false;
}
}
throw new Exception("StorageDevice: Platform.OSVersion not handled!");
}
public static void Log(string Message)
{
Console.WriteLine(Message);
}
public static void ShowRuntimeError(string title, string message)
{
SDL.SDL_ShowSimpleMessageBox(
SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR,
title,
message,
IntPtr.Zero
);
}
public static void TextureDataFromStream(
Stream stream,
out int width,
out int height,
out byte[] pixels,
int reqWidth = -1,
int reqHeight = -1,
bool zoom = false
) {
// Load the Stream into an SDL_RWops*
byte[] mem = new byte[stream.Length];
GCHandle handle = GCHandle.Alloc(mem, GCHandleType.Pinned);
stream.Read(mem, 0, mem.Length);
IntPtr rwops = SDL.SDL_RWFromMem(mem, mem.Length);
// Load the SDL_Surface* from RWops, get the image data
IntPtr surface = SDL_image.IMG_Load_RW(rwops, 1);
handle.Free();
if (surface == IntPtr.Zero)
{
// File not found, supported, etc.
width = 0;
height = 0;
pixels = null;
return;
}
surface = INTERNAL_convertSurfaceFormat(surface);
// Image scaling, if applicable
if (reqWidth != -1 && reqHeight != -1)
{
// Get the file surface dimensions now...
int rw;
int rh;
unsafe
{
SDL_Surface* surPtr = (SDL_Surface*) surface;
rw = surPtr->w;
rh = surPtr->h;
}
// Calculate the image scale factor
bool scaleWidth;
if (zoom)
{
scaleWidth = rw < rh;
}
else
{
scaleWidth = rw > rh;
}
float scale;
if (scaleWidth)
{
scale = reqWidth / (float) rw;
}
else
{
scale = reqHeight / (float) rh;
}
// Calculate the scaled image size, crop if zoomed
int resultWidth;
int resultHeight;
SDL.SDL_Rect crop = new SDL.SDL_Rect();
if (zoom)
{
resultWidth = reqWidth;
resultHeight = reqHeight;
if (scaleWidth)
{
crop.x = 0;
crop.w = rw;
crop.y = (int) (rh / 2 - (reqHeight / scale) / 2);
crop.h = (int) (reqHeight / scale);
}
else
{
crop.y = 0;
crop.h = rh;
crop.x = (int) (rw / 2 - (reqWidth / scale) / 2);
crop.w = (int) (reqWidth / scale);
}
}
else
{
resultWidth = (int) (rw * scale);
resultHeight = (int) (rh * scale);
}
// Alloc surface, blit!
IntPtr newSurface = SDL.SDL_CreateRGBSurface(
0,
resultWidth,
resultHeight,
32,
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000
);
SDL.SDL_SetSurfaceBlendMode(
surface,
SDL.SDL_BlendMode.SDL_BLENDMODE_NONE
);
if (zoom)
{
SDL.SDL_BlitScaled(
surface,
ref crop,
newSurface,
IntPtr.Zero
);
}
else
{
SDL.SDL_BlitScaled(
surface,
IntPtr.Zero,
newSurface,
IntPtr.Zero
);
}
SDL.SDL_FreeSurface(surface);
surface = newSurface;
}
// Copy surface data to output managed byte array
unsafe
{
SDL_Surface* surPtr = (SDL_Surface*) surface;
width = surPtr->w;
height = surPtr->h;
pixels = new byte[width * height * 4]; // MUST be SurfaceFormat.Color!
Marshal.Copy(surPtr->pixels, pixels, 0, pixels.Length);
}
SDL.SDL_FreeSurface(surface);
/* Ensure that the alpha pixels are... well, actual alpha.
* You think this looks stupid, but be assured: Your paint program is
* almost certainly even stupider.
* -flibit
*/
for (int i = 0; i < pixels.Length; i += 4)
{
if (pixels[i + 3] == 0)
{
pixels[i] = 0;
pixels[i + 1] = 0;
pixels[i + 2] = 0;
}
}
}
public static void SavePNG(
Stream stream,
int width,
int height,
int imgWidth,
int imgHeight,
byte[] data
) {
// Create an SDL_Surface*, write the pixel data
IntPtr surface = SDL.SDL_CreateRGBSurface(
0,
imgWidth,
imgHeight,
32,
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000
);
SDL.SDL_LockSurface(surface);
unsafe
{
SDL_Surface* surPtr = (SDL_Surface*) surface;
Marshal.Copy(
data,
0,
surPtr->pixels,
data.Length
);
}
SDL.SDL_UnlockSurface(surface);
data = null; // We're done with the original pixel data.
// Blit to a scaled surface of the size we want, if needed.
if (width != imgWidth || height != imgHeight)
{
IntPtr scaledSurface = SDL.SDL_CreateRGBSurface(
0,
width,
height,
32,
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000
);
SDL.SDL_BlitScaled(
surface,
IntPtr.Zero,
scaledSurface,
IntPtr.Zero
);
SDL.SDL_FreeSurface(surface);
surface = scaledSurface;
}
// Create an SDL_RWops*, save PNG to RWops
const int pngHeaderSize = 41;
const int pngFooterSize = 57;
byte[] pngOut = new byte[
(width * height * 4) +
pngHeaderSize +
pngFooterSize +
256 // FIXME: Arbitrary zlib data padding for low-res images
]; // Max image size
IntPtr dst = SDL.SDL_RWFromMem(pngOut, pngOut.Length);
SDL_image.IMG_SavePNG_RW(surface, dst, 1);
SDL.SDL_FreeSurface(surface); // We're done with the surface.
// Get PNG size, write to Stream
int size = (
(pngOut[33] << 24) |
(pngOut[34] << 16) |
(pngOut[35] << 8) |
(pngOut[36])
) + pngHeaderSize + pngFooterSize;
stream.Write(pngOut, 0, size);
}
#endregion
#region Private Static SDL_Surface Interop
[StructLayout(LayoutKind.Sequential)]
private struct SDL_Surface
{
#pragma warning disable 0169
UInt32 flags;
public IntPtr format;
public Int32 w;
public Int32 h;
Int32 pitch;
public IntPtr pixels;
IntPtr userdata;
Int32 locked;
IntPtr lock_data;
SDL.SDL_Rect clip_rect;
IntPtr map;
Int32 refcount;
#pragma warning restore 0169
}
private static unsafe IntPtr INTERNAL_convertSurfaceFormat(IntPtr surface)
{
IntPtr result = surface;
unsafe
{
SDL_Surface* surPtr = (SDL_Surface*) surface;
SDL.SDL_PixelFormat* pixelFormatPtr = (SDL.SDL_PixelFormat*) surPtr->format;
// SurfaceFormat.Color is SDL_PIXELFORMAT_ABGR8888
if (pixelFormatPtr->format != SDL.SDL_PIXELFORMAT_ABGR8888)
{
// Create a properly formatted copy, free the old surface
result = SDL.SDL_ConvertSurfaceFormat(surface, SDL.SDL_PIXELFORMAT_ABGR8888, 0);
SDL.SDL_FreeSurface(surface);
}
}
return result;
}
#endregion
}
}
src/SDL2/SDL2_GamePlatform.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
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
#region License
/* FNA - XNA4 Reimplementation for Desktop Platforms
* Copyright 2009-2016 Ethan Lee and the MonoGame Team
*
* Released under the Microsoft Public License.
* See LICENSE for details.
*/
#endregion
#region USE_SCANCODES Option
// #define USE_SCANCODES
/* XNA Keys are based on keycodes, rather than scancodes.
*
* With SDL2 you can actually pick between SDL_Keycode and SDL_Scancode, but
* scancodes will not be accurate to XNA4. The benefit is that scancodes will
* essentially ignore "foreign" keyboard layouts, making default keyboard
* layouts work out of the box everywhere (unless the actual symbol for the keys
* matters in your game).
*
* At the same time, the TextInputEXT extension will still read the actual chars
* correctly, so you can (mostly) have your cake and eat it too if you don't
* care about your bindings menu not making a lot of sense on foreign layouts.
* -flibit
*/
#endregion
#region Using Statements
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using SDL2;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion
namespace Microsoft.Xna.Framework
{
class SDL2_GamePlatform : GamePlatform
{
#region Public Constructor
public SDL2_GamePlatform(Game game) : base(game, SDL.SDL_GetPlatform())
{
/* SDL2 might complain if an OS that uses SDL_main has not actually
* used SDL_main by the time you initialize SDL2.
* The only platform that is affected is Windows, but we can skip
* their WinMain. This was only added to prevent iOS from exploding.
* -flibit
*/
SDL.SDL_SetMainReady();
// This _should_ be the first real SDL call we make...
SDL.SDL_Init(
SDL.SDL_INIT_VIDEO |
SDL.SDL_INIT_JOYSTICK |
SDL.SDL_INIT_GAMECONTROLLER |
SDL.SDL_INIT_HAPTIC
);
// Set any hints to match XNA4 behavior...
string hint = SDL.SDL_GetHint(SDL.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS);
if (String.IsNullOrEmpty(hint))
{
SDL.SDL_SetHint(
SDL.SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
"1"
);
}
// If available, load the SDL_GameControllerDB
string mappingsDB = Path.Combine(
TitleContainer.Location,
"gamecontrollerdb.txt"
);
if (File.Exists(mappingsDB))
{
SDL.SDL_GameControllerAddMappingsFromFile(
mappingsDB
);
}
// Set and initialize the SDL2 window
bool forceES2 = Environment.GetEnvironmentVariable(
"FNA_OPENGL_FORCE_ES2"
) == "1";
bool forceCoreProfile = Environment.GetEnvironmentVariable(
"FNA_OPENGL_FORCE_CORE_PROFILE"
) == "1";
game.Window = new SDL2_GameWindow(
forceES2 ||
OSVersion.Equals("Emscripten") ||
OSVersion.Equals("Android") ||
OSVersion.Equals("iOS"),
forceCoreProfile
);
// Disable the screensaver.
SDL.SDL_DisableScreenSaver();
// We hide the mouse cursor by default.
SDL.SDL_ShowCursor(0);
}
#endregion
#region Public GamePlatform Methods
public override void RunLoop()
{
SDL.SDL_ShowWindow(Game.Window.Handle);
// Which display did we end up on?
int displayIndex = SDL.SDL_GetWindowDisplayIndex(
Game.Window.Handle
);
// OSX has some fancy fullscreen features, let's use them!
bool osxUseSpaces;
if (OSVersion.Equals("Mac OS X"))
{
string hint = SDL.SDL_GetHint(SDL.SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES);
osxUseSpaces = (String.IsNullOrEmpty(hint) || hint.Equals("1"));
}
else
{
osxUseSpaces = false;
}
// Active Key List
List<Keys> keys = new List<Keys>();
/* Setup Text Input Control Character Arrays
* (Only 4 control keys supported at this time)
*/
bool[] INTERNAL_TextInputControlDown = new bool[4];
int[] INTERNAL_TextInputControlRepeat = new int[4];
bool INTERNAL_TextInputSuppress = false;
SDL.SDL_Event evt;
while (Game.RunApplication)
{
while (SDL.SDL_PollEvent(out evt) == 1)
{
// Keyboard
if (evt.type == SDL.SDL_EventType.SDL_KEYDOWN)
{
#if USE_SCANCODES
Keys key = SDL2_KeyboardUtil.ToXNA(evt.key.keysym.scancode);
#else
Keys key = SDL2_KeyboardUtil.ToXNA(evt.key.keysym.sym);
#endif
if (!keys.Contains(key))
{
keys.Add(key);
if (key == Keys.Back)
{
INTERNAL_TextInputControlDown[0] = true;
INTERNAL_TextInputControlRepeat[0] = Environment.TickCount + 400;
TextInputEXT.OnTextInput((char) 8); // Backspace
}
else if (key == Keys.Tab)
{
INTERNAL_TextInputControlDown[1] = true;
INTERNAL_TextInputControlRepeat[1] = Environment.TickCount + 400;
TextInputEXT.OnTextInput((char) 9); // Tab
}
else if (key == Keys.Enter)
{
INTERNAL_TextInputControlDown[2] = true;
INTERNAL_TextInputControlRepeat[2] = Environment.TickCount + 400;
TextInputEXT.OnTextInput((char) 13); // Enter
}
else if (keys.Contains(Keys.LeftControl) && key == Keys.V)
{
INTERNAL_TextInputControlDown[3] = true;
INTERNAL_TextInputControlRepeat[3] = Environment.TickCount + 400;
TextInputEXT.OnTextInput((char) 22); // Control-V (Paste)
INTERNAL_TextInputSuppress = true;
}
}
}
else if (evt.type == SDL.SDL_EventType.SDL_KEYUP)
{
#if USE_SCANCODES
Keys key = SDL2_KeyboardUtil.ToXNA(evt.key.keysym.scancode);
#else
Keys key = SDL2_KeyboardUtil.ToXNA(evt.key.keysym.sym);
#endif
if (keys.Remove(key))
{
if (key == Keys.Back)
{
INTERNAL_TextInputControlDown[0] = false;
}
else if (key == Keys.Tab)
{
INTERNAL_TextInputControlDown[1] = false;
}
else if (key == Keys.Enter)
{
INTERNAL_TextInputControlDown[2] = false;
}
else if ((!keys.Contains(Keys.LeftControl) && INTERNAL_TextInputControlDown[3]) || key == Keys.V)
{
INTERNAL_TextInputControlDown[3] = false;
INTERNAL_TextInputSuppress = false;
}
}
}
// Mouse Input
else if (evt.type == SDL.SDL_EventType.SDL_MOUSEMOTION)
{
Mouse.INTERNAL_IsWarped = false;
}
else if (evt.type == SDL.SDL_EventType.SDL_MOUSEWHEEL)
{
// 120 units per notch. Because reasons.
Mouse.INTERNAL_MouseWheel += evt.wheel.y * 120;
}
// Various Window Events...
else if (evt.type == SDL.SDL_EventType.SDL_WINDOWEVENT)
{
// Window Focus
if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED)
{
Game.IsActive = true;
if (!osxUseSpaces)
{
// If we alt-tab away, we lose the 'fullscreen desktop' flag on some WMs
SDL.SDL_SetWindowFullscreen(
Game.Window.Handle,
Game.GraphicsDevice.PresentationParameters.IsFullScreen ?
(uint) SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP :
0
);
}
// Disable the screensaver when we're back.
SDL.SDL_DisableScreenSaver();
}
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_LOST)
{
Game.IsActive = false;
if (!osxUseSpaces)
{
SDL.SDL_SetWindowFullscreen(Game.Window.Handle, 0);
}
// Give the screensaver back, we're not that important now.
SDL.SDL_EnableScreenSaver();
}
// Window Resize
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_RESIZED)
{
Mouse.INTERNAL_WindowWidth = evt.window.data1;
Mouse.INTERNAL_WindowHeight = evt.window.data2;
// Should be called on user resize only, NOT ApplyChanges!
((SDL2_GameWindow) Game.Window).INTERNAL_ClientSizeChanged();
}
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_SIZE_CHANGED)
{
Mouse.INTERNAL_WindowWidth = evt.window.data1;
Mouse.INTERNAL_WindowHeight = evt.window.data2;
// Need to reset the graphics device any time the window size changes
GraphicsDeviceManager gdm = Game.Services.GetService(
typeof(IGraphicsDeviceService)
) as GraphicsDeviceManager;
// FIXME: gdm == null? -flibit
if (gdm.IsFullScreen)
{
GraphicsDevice device = Game.GraphicsDevice;
gdm.INTERNAL_ResizeGraphicsDevice(
device.GLDevice.Backbuffer.Width,
device.GLDevice.Backbuffer.Height
);
}
else
{
gdm.INTERNAL_ResizeGraphicsDevice(
evt.window.data1,
evt.window.data2
);
}
}
// Window Move
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_MOVED)
{
/* Apparently if you move the window to a new
* display, a GraphicsDevice Reset occurs.
* -flibit
*/
int newIndex = SDL.SDL_GetWindowDisplayIndex(
Game.Window.Handle
);
if (newIndex != displayIndex)
{
displayIndex = newIndex;
Game.GraphicsDevice.Reset(
Game.GraphicsDevice.PresentationParameters,
GraphicsAdapter.Adapters[displayIndex]
);
}
}
// Mouse Focus
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_ENTER)
{
SDL.SDL_DisableScreenSaver();
}
else if (evt.window.windowEvent == SDL.SDL_WindowEventID.SDL_WINDOWEVENT_LEAVE)
{
SDL.SDL_EnableScreenSaver();
}
}
// Controller device management
else if (evt.type == SDL.SDL_EventType.SDL_CONTROLLERDEVICEADDED)
{
GamePad.INTERNAL_AddInstance(evt.cdevice.which);
}
else if (evt.type == SDL.SDL_EventType.SDL_CONTROLLERDEVICEREMOVED)
{
GamePad.INTERNAL_RemoveInstance(evt.cdevice.which);
}
// Text Input
else if (evt.type == SDL.SDL_EventType.SDL_TEXTINPUT && !INTERNAL_TextInputSuppress)
{
string text;
// Based on the SDL2# LPUtf8StrMarshaler
unsafe
{
byte* endPtr = evt.text.text;
while (*endPtr != 0)
{
endPtr++;
}
byte[] bytes = new byte[endPtr - evt.text.text];
Marshal.Copy((IntPtr) evt.text.text, bytes, 0, bytes.Length);
text = System.Text.Encoding.UTF8.GetString(bytes);
}
if (text.Length > 0)
{
TextInputEXT.OnTextInput(text[0]);
}
}
// Quit
else if (evt.type == SDL.SDL_EventType.SDL_QUIT)
{
Game.RunApplication = false;
break;
}
}
// Text Input Controls Key Handling
if (INTERNAL_TextInputControlDown[0] && INTERNAL_TextInputControlRepeat[0] <= Environment.TickCount)
{
TextInputEXT.OnTextInput((char) 8);
}
if (INTERNAL_TextInputControlDown[1] && INTERNAL_TextInputControlRepeat[1] <= Environment.TickCount)
{
TextInputEXT.OnTextInput((char) 9);
}
if (INTERNAL_TextInputControlDown[2] && INTERNAL_TextInputControlRepeat[2] <= Environment.TickCount)
{
TextInputEXT.OnTextInput((char) 13);
}
if (INTERNAL_TextInputControlDown[3] && INTERNAL_TextInputControlRepeat[3] <= Environment.TickCount)
{
TextInputEXT.OnTextInput((char) 22);
}
Keyboard.SetKeys(keys);
Game.Tick();
}
// We out.
Game.Exit();
}
public override void BeforeInitialize()
{
// We want to initialize the controllers ASAP!
SDL.SDL_Event[] evt = new SDL.SDL_Event[1];
SDL.SDL_PumpEvents(); // Required to get OSX device events this early.
while (SDL.SDL_PeepEvents(
evt,
1,
SDL.SDL_eventaction.SDL_GETEVENT,
SDL.SDL_EventType.SDL_CONTROLLERDEVICEADDED,
SDL.SDL_EventType.SDL_CONTROLLERDEVICEADDED
) == 1) {
GamePad.INTERNAL_AddInstance(evt[0].cdevice.which);
}
}
public override void Log(string Message)
{
Console.WriteLine(Message);
}
public override void ShowRuntimeError(string title, string message)
{
SDL.SDL_ShowSimpleMessageBox(
SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR,
title,
message,
Game.Window.Handle
);
}
public override GraphicsAdapter[] GetGraphicsAdapters()
{
SDL.SDL_DisplayMode filler = new SDL.SDL_DisplayMode();
GraphicsAdapter[] adapters = new GraphicsAdapter[SDL.SDL_GetNumVideoDisplays()];
for (int i = 0; i < adapters.Length; i += 1)
{
List<DisplayMode> modes = new List<DisplayMode>();
int numModes = SDL.SDL_GetNumDisplayModes(i);
for (int j = 0; j < numModes; j += 1)
{
SDL.SDL_GetDisplayMode(i, j, out filler);
// Check for dupes caused by varying refresh rates.
bool dupe = false;
foreach (DisplayMode mode in modes)
{
if (filler.w == mode.Width && filler.h == mode.Height)
{
dupe = true;
}
}
if (!dupe)
{
modes.Add(
new DisplayMode(
filler.w,
filler.h,
SurfaceFormat.Color // FIXME: Assumption!
)
);
}
}
SDL.SDL_GetCurrentDisplayMode(i, out filler);
adapters[i] = new GraphicsAdapter(
new DisplayMode(
filler.w,
filler.h,
SurfaceFormat.Color // FIXME: Assumption!
),
new DisplayModeCollection(modes),
SDL.SDL_GetDisplayName(i)
);
}
return adapters;
}
public override void SetPresentationInterval(PresentInterval interval)
{
if (interval == PresentInterval.Default || interval == PresentInterval.One)
{
if (OSVersion.Equals("Mac OS X"))
{
// Apple is a big fat liar about swap_control_tear. Use stock VSync.
SDL.SDL_GL_SetSwapInterval(1);
}
else
{
if (SDL.SDL_GL_SetSwapInterval(-1) != -1)
{
System.Console.WriteLine("Using EXT_swap_control_tear VSync!");
}
else
{
System.Console.WriteLine("EXT_swap_control_tear unsupported. Fall back to standard VSync.");
SDL.SDL_ClearError();
SDL.SDL_GL_SetSwapInterval(1);
}
}
}
else if (interval == PresentInterval.Immediate)
{
SDL.SDL_GL_SetSwapInterval(0);
}
else if (interval == PresentInterval.Two)
{
SDL.SDL_GL_SetSwapInterval(2);
}
else
{
throw new Exception("Unrecognized PresentInterval!");
}
}
public override void TextureDataFromStream(
Stream stream,
out int width,
out int height,
out byte[] pixels,
int reqWidth = -1,
int reqHeight = -1,
bool zoom = false
) {
// Load the Stream into an SDL_RWops*
byte[] mem = new byte[stream.Length];
GCHandle handle = GCHandle.Alloc(mem, GCHandleType.Pinned);
stream.Read(mem, 0, mem.Length);
IntPtr rwops = SDL.SDL_RWFromMem(mem, mem.Length);
// Load the SDL_Surface* from RWops, get the image data
IntPtr surface = SDL_image.IMG_Load_RW(rwops, 1);
handle.Free();
if (surface == IntPtr.Zero)
{
// File not found, supported, etc.
width = 0;
height = 0;
pixels = null;
return;
}
surface = INTERNAL_convertSurfaceFormat(surface);
// Image scaling, if applicable
if (reqWidth != -1 && reqHeight != -1)
{
// Get the file surface dimensions now...
int rw;
int rh;
unsafe
{
SDL_Surface* surPtr = (SDL_Surface*) surface;
rw = surPtr->w;
rh = surPtr->h;
}
// Calculate the image scale factor
bool scaleWidth;
if (zoom)
{
scaleWidth = rw < rh;
}
else
{
scaleWidth = rw > rh;
}
float scale;
if (scaleWidth)
{
scale = reqWidth / (float) rw;
}
else
{
scale = reqHeight / (float) rh;
}
// Calculate the scaled image size, crop if zoomed
int resultWidth;
int resultHeight;
SDL.SDL_Rect crop = new SDL.SDL_Rect();
if (zoom)
{
resultWidth = reqWidth;
resultHeight = reqHeight;
if (scaleWidth)
{
crop.x = 0;
crop.w = rw;
crop.y = (int) (rh / 2 - (reqHeight / scale) / 2);
crop.h = (int) (reqHeight / scale);
}
else
{
crop.y = 0;
crop.h = rh;
crop.x = (int) (rw / 2 - (reqWidth / scale) / 2);
crop.w = (int) (reqWidth / scale);
}
}
else
{
resultWidth = (int) (rw * scale);
resultHeight = (int) (rh * scale);
}
// Alloc surface, blit!
IntPtr newSurface = SDL.SDL_CreateRGBSurface(
0,
resultWidth,
resultHeight,
32,
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000
);
SDL.SDL_SetSurfaceBlendMode(
surface,
SDL.SDL_BlendMode.SDL_BLENDMODE_NONE
);
if (zoom)
{
SDL.SDL_BlitScaled(
surface,
ref crop,
newSurface,
IntPtr.Zero
);
}
else
{
SDL.SDL_BlitScaled(
surface,
IntPtr.Zero,
newSurface,
IntPtr.Zero
);
}
SDL.SDL_FreeSurface(surface);
surface = newSurface;
}
// Copy surface data to output managed byte array
unsafe
{
SDL_Surface* surPtr = (SDL_Surface*) surface;
width = surPtr->w;
height = surPtr->h;
pixels = new byte[width * height * 4]; // MUST be SurfaceFormat.Color!
Marshal.Copy(surPtr->pixels, pixels, 0, pixels.Length);
}
SDL.SDL_FreeSurface(surface);
/* Ensure that the alpha pixels are... well, actual alpha.
* You think this looks stupid, but be assured: Your paint program is
* almost certainly even stupider.
* -flibit
*/
for (int i = 0; i < pixels.Length; i += 4)
{
if (pixels[i + 3] == 0)
{
pixels[i] = 0;
pixels[i + 1] = 0;
pixels[i + 2] = 0;
}
}
}
public override void SavePNG(
Stream stream,
int width,
int height,
int imgWidth,
int imgHeight,
byte[] data
) {
// Create an SDL_Surface*, write the pixel data
IntPtr surface = SDL.SDL_CreateRGBSurface(
0,
imgWidth,
imgHeight,
32,
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000
);
SDL.SDL_LockSurface(surface);
unsafe
{
SDL_Surface* surPtr = (SDL_Surface*) surface;
Marshal.Copy(
data,
0,
surPtr->pixels,
data.Length
);
}
SDL.SDL_UnlockSurface(surface);
data = null; // We're done with the original pixel data.
// Blit to a scaled surface of the size we want, if needed.
if (width != imgWidth || height != imgHeight)
{
IntPtr scaledSurface = SDL.SDL_CreateRGBSurface(
0,
width,
height,
32,
0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000
);
SDL.SDL_BlitScaled(
surface,
IntPtr.Zero,
scaledSurface,
IntPtr.Zero
);
SDL.SDL_FreeSurface(surface);
surface = scaledSurface;
}
// Create an SDL_RWops*, save PNG to RWops
const int pngHeaderSize = 41;
const int pngFooterSize = 57;
byte[] pngOut = new byte[
(width * height * 4) +
pngHeaderSize +
pngFooterSize +
256 // FIXME: Arbitrary zlib data padding for low-res images
]; // Max image size
IntPtr dst = SDL.SDL_RWFromMem(pngOut, pngOut.Length);
SDL_image.IMG_SavePNG_RW(surface, dst, 1);
SDL.SDL_FreeSurface(surface); // We're done with the surface.
// Get PNG size, write to Stream
int size = (
(pngOut[33] << 24) |
(pngOut[34] << 16) |
(pngOut[35] << 8) |
(pngOut[36])
) + pngHeaderSize + pngFooterSize;
stream.Write(pngOut, 0, size);
}
public override Keys GetKeyFromScancode(Keys scancode)
{
return SDL2_KeyboardUtil.KeyFromScancode(scancode);
}
public override string GetStorageRoot()
{
if (OSVersion.Equals("Windows"))
{
return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"SavedGames"
);
}
if (OSVersion.Equals("Mac OS X"))
{
string osConfigDir = Environment.GetEnvironmentVariable("HOME");
if (String.IsNullOrEmpty(osConfigDir))
{
return "."; // Oh well.
}
osConfigDir += "/Library/Application Support";
return osConfigDir;
}
if (OSVersion.Equals("Linux"))
{
// Assuming a non-OSX Unix platform will follow the XDG. Which it should.
string osConfigDir = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
if (String.IsNullOrEmpty(osConfigDir))
{
osConfigDir = Environment.GetEnvironmentVariable("HOME");
if (String.IsNullOrEmpty(osConfigDir))
{
return ".";// Oh well.
}
osConfigDir += "/.local/share";
}
return osConfigDir;
}
throw new Exception("StorageDevice: Platform.OSVersion not handled!");
}
public override bool IsStoragePathConnected(string path)
{
if (OSVersion.Equals("Linux") ||
OSVersion.Equals("Mac OS X"))
{
/* Linux and Mac use locally connected storage in the user's
* home location, which should always be "connected".
*/
return true;
}
if (OSVersion.Equals("Windows"))
{
try
{
return new DriveInfo(path).IsReady;
}
catch
{
// The storageRoot path is invalid / has been removed.
return false;
}
}
throw new Exception("StorageDevice: Platform.OSVersion not handled!");
}
public override void OnIsMouseVisibleChanged(bool visible)
{
SDL.SDL_ShowCursor(visible ? 1 : 0);
}
#endregion
#region Protected GamePlatform Methods
protected override void Dispose(bool disposing)
{
if (!IsDisposed)
{
if (Game.Window != null)
{
/* Some window managers might try to minimize the window as we're
* destroying it. This looks pretty stupid and could cause problems,
* so set this hint right before we destroy everything.
* -flibit
*/
SDL.SDL_SetHintWithPriority(
SDL.SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS,
"0",
SDL.SDL_HintPriority.SDL_HINT_OVERRIDE
);
SDL.SDL_DestroyWindow(Game.Window.Handle);
Game.Window = null;
}
// This _should_ be the last SDL call we make...
SDL.SDL_Quit();
}
base.Dispose(disposing);
}
#endregion
#region Private Static SDL_Surface Interop
[StructLayout(LayoutKind.Sequential)]
private struct SDL_Surface
{
#pragma warning disable 0169
UInt32 flags;
public IntPtr format;
public Int32 w;
public Int32 h;
Int32 pitch;
public IntPtr pixels;
IntPtr userdata;
Int32 locked;
IntPtr lock_data;
SDL.SDL_Rect clip_rect;
IntPtr map;
Int32 refcount;
#pragma warning restore 0169
}
private static unsafe IntPtr INTERNAL_convertSurfaceFormat(IntPtr surface)
{
IntPtr result = surface;
unsafe
{
SDL_Surface* surPtr = (SDL_Surface*) surface;
SDL.SDL_PixelFormat* pixelFormatPtr = (SDL.SDL_PixelFormat*) surPtr->format;
// SurfaceFormat.Color is SDL_PIXELFORMAT_ABGR8888
if (pixelFormatPtr->format != SDL.SDL_PIXELFORMAT_ABGR8888)
{
// Create a properly formatted copy, free the old surface
result = SDL.SDL_ConvertSurfaceFormat(surface, SDL.SDL_PIXELFORMAT_ABGR8888, 0);
SDL.SDL_FreeSurface(surface);
}
}
return result;
}
#endregion
}
}
src/Storage/StorageDevice.cs
5555
5656
5757
58
58
5959
6060
6161
......
9393
9494
9595
96
96
9797
9898
9999
{
get
{
return Game.Instance.Platform.IsStoragePathConnected(storageRoot);
return FNAPlatform.IsStoragePathConnected(storageRoot);
}
}
#region Private Static Variables
private static readonly string storageRoot = Game.Instance.Platform.GetStorageRoot();
private static readonly string storageRoot = FNAPlatform.GetStorageRoot();
#endregion

Archive Download the corresponding diff file

Branches

Number of commits:
Page rendered in 1.95262s using 13 queries.