-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSDLApp.cs
158 lines (136 loc) · 6.46 KB
/
SDLApp.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
using SDL2;
using System;
using System.Runtime.InteropServices;
namespace SDLBase
{
public class SDLApp
{
private int resX = 512;
private int resY = 512;
private string title = "Test App";
private IntPtr window;
private IntPtr windowSurface;
private IntPtr renderer;
private Bitmap screen;
private bool exit = false;
public SDLApp(int resX, int resY, string title)
{
this.resX = resX;
this.resY = resY;
this.title = title;
}
public bool Initialize()
{
// Initialize all modules of SDL
if (SDL.SDL_Init(SDL.SDL_INIT_EVERYTHING) != 0)
{
Console.WriteLine("Can't initialize SDL!");
return false;
}
// Create a window, centered on the default screen, with the given resolution
window = SDL.SDL_CreateWindow("SDL Base Application", SDL.SDL_WINDOWPOS_CENTERED, SDL.SDL_WINDOWPOS_CENTERED, resX, resY, 0);
if (window == IntPtr.Zero)
{
Console.WriteLine("Can't create window or renderer!");
return false;
}
// Get the window surface - We'll need this to copy our data at the end
windowSurface = SDL.SDL_GetWindowSurface(window);
var surface = Marshal.PtrToStructure<SDL.SDL_Surface>(windowSurface);
var pixeLFormat = Marshal.PtrToStructure<SDL.SDL_PixelFormat>(surface.format);
var formatName = SDL.SDL_GetPixelFormatName(pixeLFormat.format);
Console.WriteLine($"Window surface format = {formatName}");
// Create a software based renderer. We could create an accelerated one for a series of reasons,
// but for our test case we want to have full control over the pixels
renderer = SDL.SDL_CreateSoftwareRenderer(windowSurface);
// Create buffer for the screen
screen = new Bitmap(resX, resY);
return true;
}
public void Shutdown()
{
// Shutdown SDL
SDL.SDL_Quit();
}
public void Run(Action mainLoopFunction)
{
// Set exit to true to exit application
exit = false;
while (!exit)
{
// Handle all events in the qeueu
SDL.SDL_Event evt;
while (SDL.SDL_PollEvent(out evt) != 0)
{
switch (evt.type)
{
// Quit event => Pressing the Close button on a window or Alt-F4: in this case, just set the exit flag to true
case SDL.SDL_EventType.SDL_QUIT:
exit = true;
break;
// Key presses
case SDL.SDL_EventType.SDL_KEYDOWN:
if ((evt.key.keysym.sym == SDL.SDL_Keycode.SDLK_ESCAPE) && ((evt.key.keysym.mod & SDL.SDL_Keymod.KMOD_SHIFT) != 0))
{
// If Shift-ESC is pressed, set the exit flag to true to exit the application
exit = true;
}
break;
}
}
// Run the main loop function (where the actual application is running)
mainLoopFunction();
// Copy the contents of the screen buffer to the actual main screen
CopyToSurface(screen, windowSurface);
// Update the window (takes the contents of the window surface and transfer it to the screen)
SDL.SDL_UpdateWindowSurface(window);
}
}
// Copy surface => This is intended to copy the image on the Bitmap (usually the screen buffer) to the
// main window.
// This has to be unsafe since it works with memory copies and such, which is considered unsafe code by C#
unsafe void CopyToSurface(Bitmap src, IntPtr dest)
{
// Get the window surface data. This is information like the size of the surface, and it's pitch (horizontal line size in bytes).
var surfaceData = (SDL.SDL_Surface)System.Runtime.InteropServices.Marshal.PtrToStructure(dest, typeof(SDL.SDL_Surface));
// Get destination data position
var destData = surfaceData.pixels;
// If the pitch size is the same as the width of the screen bitmap (times 4 bytes, RGBA), then the buffers are a 1 to 1 match and I can
// copy everything at the same time. Otherwise I have to do it line by line
if (surfaceData.pitch == src.width * 4)
{
// Fixed will pin a certain area location in place while the block is run: we need to do this to make sure the C#
// runtime doesn't move the memory around while we're copying the buffer
fixed (byte* srcData = &src.data[0])
{
// Copy the buffer on the bitmap screen to the target location
Buffer.MemoryCopy(srcData, destData.ToPointer(), src.width * src.height * 4, src.width * src.height * 4);
}
}
else
{
// Same as in the previous block, but it has to copy the buffer line by line, since pitch is different than
// buffer width
// Fixed will pin a certain area location in place while the block is run: we need to do this to make sure the C#
// runtime doesn't move the memory around while we're copying the buffer
fixed (byte* srcData = &src.data[0])
{
// Start from the first line
byte* srcLine = srcData;
// For each line in the source data
for (int y = 0; y < src.height; y++)
{
// Copy a whole line
Buffer.MemoryCopy(srcLine, destData.ToPointer(), src.width * 4, src.width * 4);
// Move down one line on both buffers
srcLine = srcLine + src.width;
destData = destData + surfaceData.pitch;
}
}
}
}
// This function returns the screen to be used by the main loop
public Bitmap GetScreen() => screen;
}
}