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         const scaleX = event.originalWidth / event.width;
64         const scaleY = event.originalHeight / event.height;
65         glViewport(0, 0, event.width * scaleX, event.height * scaleY);
66     }
67 
68     private void initPlatform() {
69         platformInit();
70 
71         auto window = platformCreateWindow(
72             "RPUI",
73             windowData.viewportWidth,
74             windowData.viewportHeight
75         );
76 
77         windowData.window = window.handle;
78         windowData.glContext = window.gapiContext;
79 
80         isPlatformInit = true;
81     }
82 
83     private void initGL() {
84         glDisable(GL_CULL_FACE);
85         glDisable(GL_MULTISAMPLE);
86         glDisable(GL_DEPTH_TEST);
87         glEnable(GL_BLEND);
88         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
89         glClearColor(150.0f/255.0f, 150.0f/255.0f, 150.0f/255.0f, 0);
90         glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
91     }
92 
93     private void mainLoop() {
94         bool running = true;
95         int width;
96         int height;
97         int originalWidth;
98         int originalHeight;
99 
100         SDL_GetWindowSize(cast(SDL_Window*) windowData.window, &width, &height);
101         SDL_GL_GetDrawableSize(cast(SDL_Window*) windowData.window, &originalWidth, &originalHeight);
102 
103         events.notify(
104             WindowResizeEvent(
105                 width,
106                 height,
107                 originalWidth,
108                 originalHeight
109             )
110         );
111 
112         while (running) {
113             SDL_GetWindowSize(cast(SDL_Window*) windowData.window, &width, &height);
114 
115             windowData.viewportWidth = width;
116             windowData.viewportHeight = height;
117 
118             times.current = platformGetTicks();
119             running = platformEventLoop(windowData.window, events);
120             render();
121         }
122     }
123 
124     private void render() {
125         if (times.current >= times.last + times.part) {
126             times.delta = (times.current - times.last) / 1000.0f;
127             onProgress(ProgressEvent(times.delta));
128             times.last = times.current;
129             glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
130             onRender();
131             platformSwapWindow(windowData.window);
132         }
133     }
134 
135     override void onWindowExposed(in WindowExposedEvent event) {
136         // TODO(Andrey): test on windows
137         // glViewport(0, 0, windowData.viewportWidth, windowData.viewportHeight);
138         int width;
139         int height;
140         SDL_GetWindowSize(cast(SDL_Window*) windowData.window, &width, &height);
141 
142         windowData.viewportWidth = width;
143         windowData.viewportHeight = height;
144 
145         onProgress(ProgressEvent(0));
146         glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
147         onRender();
148         platformSwapWindow(windowData.window);
149     }
150 
151     void onRender() {
152     }
153 
154     void onCreate() {
155     }
156 
157     void onDestroy() {
158     }
159 
160     void onProgress(in ProgressEvent event) {
161     }
162 }