1 module rpui.application;
2 
3 import std.conv;
4 import std.array;
5 import std.string;
6 
7 import derelict.sdl2.sdl;
8 
9 import gapi.opengl;
10 import rpui.events;
11 import rpui.events_observer;
12 import rpui.input;
13 import rpui.cursor;
14 import rpui.primitives;
15 import rpui.platform;
16 
17 abstract class Application : EventsListenerEmpty {
18     struct WindowData {
19         void* window;
20         void* glContext;
21         int viewportWidth = 1024;
22         int viewportHeight = 768;
23     }
24 
25     struct Times {
26         float current = 0f;
27         float delta = 0f;
28         float last = 0f;
29         immutable part = 1_000.0 / 60.0;
30     }
31 
32     protected EventsObserver events;
33     protected CursorManager cursorManager;
34 
35     protected WindowData windowData;
36     private Times times;
37     private bool isPlatformInit = false;
38 
39     this() {
40         events = new EventsObserver();
41         cursorManager = new CursorManager();
42         events.subscribe(this);
43     }
44 
45     ~this() {
46         if (isPlatformInit) {
47             platformGapiDeleteContext(windowData.glContext);
48             platformDestroyWindow(windowData.window);
49             platformShutdown();
50         }
51     }
52 
53     final void run() {
54         initPlatform();
55         initGL();
56         onCreate();
57         mainLoop();
58     }
59 
60     override void onWindowResize(in WindowResizeEvent event) {
61         windowData.viewportWidth = event.width;
62         windowData.viewportHeight = event.height;
63         glViewport(0, 0, event.width, event.height);
64     }
65 
66     private void initPlatform() {
67         platformInit();
68 
69         auto window = platformCreateWindow(
70             "RPUI",
71             windowData.viewportWidth,
72             windowData.viewportHeight
73         );
74 
75         windowData.window = window.handle;
76         windowData.glContext = window.gapiContext;
77 
78         isPlatformInit = true;
79     }
80 
81     private void initGL() {
82         glDisable(GL_CULL_FACE);
83         glDisable(GL_MULTISAMPLE);
84         glDisable(GL_DEPTH_TEST);
85         glEnable(GL_BLEND);
86         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
87         glClearColor(150.0f/255.0f, 150.0f/255.0f, 150.0f/255.0f, 0);
88         glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
89     }
90 
91     private void mainLoop() {
92         bool running = true;
93         events.notify(WindowResizeEvent(windowData.viewportWidth, windowData.viewportHeight));
94 
95         while (running) {
96             int width;
97             int height;
98             SDL_GetWindowSize(cast(SDL_Window*) windowData.window, &width, &height);
99 
100             windowData.viewportHeight = width;
101             windowData.viewportHeight = height;
102 
103             times.current = platformGetTicks();
104             running = platformEventLoop(windowData.window, events);
105             render();
106         }
107     }
108 
109     private void render() {
110         if (times.current >= times.last + times.part) {
111             times.delta = (times.current - times.last) / 1000.0f;
112             onProgress(ProgressEvent(times.delta));
113             times.last = times.current;
114             glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
115             onRender();
116             platformSwapWindow(windowData.window);
117         }
118     }
119 
120     override void onWindowExposed(in WindowExposedEvent event) {
121         // TODO(Andrey): test on windows
122         // glViewport(0, 0, windowData.viewportWidth, windowData.viewportHeight);
123         int width;
124         int height;
125         SDL_GetWindowSize(cast(SDL_Window*) windowData.window, &width, &height);
126 
127         windowData.viewportHeight = width;
128         windowData.viewportHeight = height;
129 
130         onProgress(ProgressEvent(0));
131         glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
132         onRender();
133         platformSwapWindow(windowData.window);
134     }
135 
136     void onRender() {
137     }
138 
139     void onCreate() {
140     }
141 
142     void onDestroy() {
143     }
144 
145     void onProgress(in ProgressEvent event) {
146     }
147 }