1 module rpui.platform_sdl;
2 
3 version(rpuiSdl2):
4 
5 import std.conv;
6 import std.array;
7 import std.string;
8 
9 import derelict.sdl2.sdl;
10 import derelict.sdl2.ttf;
11 import derelict.sdl2.image;
12 
13 import gapi.opengl;
14 
15 import rpui.events;
16 import rpui.events_observer;
17 import rpui.input;
18 import rpui.platform;
19 
20 extern(C) void platformShowSystemCursor() {
21     SDL_ShowCursor(SDL_ENABLE);
22 }
23 
24 extern(C) void platformHideSystemCursor() {
25     SDL_ShowCursor(SDL_DISABLE);
26 }
27 
28 extern(C) void platformSetMousePosition(void* window, in int x, in int y) {
29     SDL_WarpMouseInWindow(cast(SDL_Window*) window, x, y);
30 }
31 
32 extern(C) float platformGetTicks() {
33     return SDL_GetTicks();
34 }
35 
36 extern(C) bool platformEventLoop(void* window, EventsObserver events) {
37     SDL_Event event;
38 
39     while (SDL_PollEvent(&event)) {
40         if (event.type == SDL_QUIT) {
41             return false;
42         }
43         else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
44             events.notify(WindowResizeEvent(event.window.data1, event.window.data2));
45         }
46         else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_EXPOSED) {
47             events.notify(WindowExposedEvent());
48         }
49         else if (event.type == SDL_MOUSEMOTION) {
50             const button = createMouseButtonFromSdlState(event.motion.state);
51             events.notify(MouseMoveEvent(event.motion.x, event.motion.y, button));
52         }
53         else if (event.type == SDL_MOUSEBUTTONDOWN) {
54             const button = createMouseButtonFromButtonSdlEvent(event);
55             events.notify(MouseDownEvent(event.button.x, event.button.y, button));
56 
57             switch (event.button.clicks) {
58                 case 2:
59                     events.notify(DblClickEvent(event.button.x, event.button.y, button));
60                     break;
61 
62                 case 3:
63                     events.notify(TripleClickEvent(event.button.x, event.button.y, button));
64                     break;
65 
66                 default:
67                     // Ignore
68             }
69         }
70         else if (event.type == SDL_MOUSEBUTTONUP) {
71             const button = createMouseButtonFromButtonSdlEvent(event);
72             events.notify(MouseUpEvent(event.button.x, event.button.y, button));
73         }
74         else if (event.type == SDL_MOUSEWHEEL) {
75             events.notify(MouseWheelEvent(event.wheel.x, event.wheel.y));
76         }
77         else if (event.type == SDL_TEXTINPUT) {
78             const ch = fromStringz(dtext(event.text.text[0..4]).ptr)[0];
79             events.notify(TextEnteredEvent(ch));
80         }
81         else if (event.type == SDL_KEYDOWN) {
82             try {
83                 const key = to!(KeyCode)(to!(int)(event.key.keysym.sym));
84                 setKeyPressed(key, true);
85                 events.notify(KeyPressedEvent(key));
86             } catch (Exception e) {
87             }
88         }
89         else if (event.type == SDL_KEYUP) {
90             try {
91                 const key = to!(KeyCode)(to!(int)(event.key.keysym.sym));
92                 events.notify(KeyReleasedEvent(key));
93                 setKeyPressed(key, false);
94             } catch (Exception e) {
95             }
96         }
97     }
98 
99     return true;
100 }
101 
102 extern(C) void platformSwapWindow(void* window) {
103     SDL_GL_SwapWindow(cast(SDL_Window*) window);
104 }
105 
106 extern(C) void platformGapiDeleteContext(void* context) {
107     SDL_GL_DeleteContext(context);
108 }
109 
110 extern(C) void platformDestroyWindow(void* window) {
111     SDL_DestroyWindow(cast(SDL_Window*) window);
112 }
113 
114 extern(C) void platformShutdown() {
115     SDL_Quit();
116     TTF_Quit();
117 }
118 
119 extern(C) void platformInit() {
120     // Load shared libraries
121     DerelictGL3.load();
122     DerelictSDL2.load();
123     DerelictSDL2Image.load();
124     DerelictSDL2TTF.load();
125 
126     // Init
127     if (SDL_Init(SDL_INIT_VIDEO) < 0)
128         throw new Error("Failed to init SDL");
129 
130     if (TTF_Init() < 0)
131         throw new Error("Failed to init SDL TTF");
132 
133     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
134     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
135     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
136     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
137     SDL_GL_SetSwapInterval(2);
138 }
139 
140 extern(C) Window platformCreateWindow(in string title, uint width, uint height) {
141     auto window = SDL_CreateWindow(
142         toStringz(title),
143         SDL_WINDOWPOS_CENTERED,
144         SDL_WINDOWPOS_CENTERED,
145         width,
146         height,
147         SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
148     );
149 
150     if (window == null)
151         throw new Error("SDL Error: " ~ to!string(SDL_GetError()));
152 
153     auto glContext = SDL_GL_CreateContext(window);
154 
155     if (glContext == null)
156         throw new Error("SDL Error: " ~ to!string(SDL_GetError()));
157 
158     SDL_GL_SwapWindow(window);
159     DerelictGL3.reload();
160 
161     return Window(window, glContext);
162 }
163 
164 extern(C) void platformSetClipboardTextUtf8(in string text) {
165     SDL_SetClipboardText(toStringz(text));
166 }
167 
168 extern(C) void platformSetClipboardTextUtf32(in dstring text) {
169     // writeln("PLATFORM COPY: ", data.charString);
170 
171     SDL_SetClipboardText(toStringz(to!string(text)));
172 }
173 
174 extern(C) string platformGetClipboardTextUtf8() {
175     return to!string(fromStringz(SDL_GetClipboardText()));
176 }
177 
178 extern(C) dstring platformGetClipboardTextUtf32() {
179     return to!dstring(fromStringz(SDL_GetClipboardText()));
180 }
181 
182 extern(C) bool hasClipboardText() {
183     return cast(bool) SDL_HasClipboardText();
184 }