added dos timer and double-esc to quit
[retroray] / src / modern / main.c
1 /*
2 RetroRay - integrated standalone vintage modeller/renderer
3 Copyright (C) 2023  John Tsiombikas <nuclear@mutantstargoat.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <assert.h>
21 #include "miniglut.h"
22 #include "app.h"
23 #include "rtk.h"
24 #include "logger.h"
25
26 static void display(void);
27 static void reshape(int x, int y);
28 static void keydown(unsigned char key, int x, int y);
29 static void keyup(unsigned char key, int x, int y);
30 static void skeydown(int key, int x, int y);
31 static void skeyup(int key, int x, int y);
32 static void mouse(int bn, int st, int x, int y);
33 static void motion(int x, int y);
34 static int translate_skey(int key);
35
36 #if defined(__unix__) || defined(unix)
37 #include <GL/glx.h>
38 static Display *xdpy;
39 static Window xwin;
40
41 static void (*glx_swap_interval_ext)();
42 static void (*glx_swap_interval_sgi)();
43 #endif
44 #ifdef _WIN32
45 #include <windows.h>
46 static PROC wgl_swap_interval_ext;
47 #endif
48
49 static rtk_rect rband;
50
51
52 int main(int argc, char **argv)
53 {
54         glutInit(&argc, argv);
55         glutInitWindowSize(640, 480);
56         glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
57         glutCreateWindow("RetroRay");
58
59         glutDisplayFunc(display);
60         glutReshapeFunc(reshape);
61         glutKeyboardFunc(keydown);
62         glutKeyboardUpFunc(keyup);
63         glutSpecialFunc(skeydown);
64         glutSpecialUpFunc(skeyup);
65         glutMouseFunc(mouse);
66         glutMotionFunc(motion);
67         glutPassiveMotionFunc(motion);
68         glutSpaceballMotionFunc(app_sball_motion);
69         glutSpaceballRotateFunc(app_sball_rotate);
70         glutSpaceballButtonFunc(app_sball_button);
71
72 #if defined(__unix__) || defined(unix)
73         xdpy = glXGetCurrentDisplay();
74         xwin = glXGetCurrentDrawable();
75
76         if(!(glx_swap_interval_ext = glXGetProcAddress((unsigned char*)"glXSwapIntervalEXT"))) {
77                 glx_swap_interval_sgi = glXGetProcAddress((unsigned char*)"glXSwapIntervalSGI");
78         }
79 #endif
80 #ifdef _WIN32
81         wgl_swap_interval_ext = wglGetProcAddress("wglSwapIntervalEXT");
82 #endif
83
84         win_width = glutGet(GLUT_WINDOW_WIDTH);
85         win_height = glutGet(GLUT_WINDOW_HEIGHT);
86         win_aspect = (float)win_width / win_height;
87
88         init_logger();
89         add_log_stream(stdout);
90
91         if(app_init() == -1) {
92                 return 1;
93         }
94         atexit(app_shutdown);
95         glutMainLoop();
96         return 0;
97 }
98
99 unsigned long get_msec(void)
100 {
101         return (unsigned long)glutGet(GLUT_ELAPSED_TIME);
102 }
103
104 void app_redisplay(int x, int y, int w, int h)
105 {
106         /*dbgmsg("fakeupd: %d,%d (%dx%d)\n", x, y, w, h);*/
107         glutPostRedisplay();
108 }
109
110 void app_swap_buffers(void)
111 {
112         glMatrixMode(GL_MODELVIEW);
113         glLoadIdentity();
114         glMatrixMode(GL_PROJECTION);
115         glPushMatrix();
116         glLoadIdentity();
117
118         glRasterPos2i(-1, 1);
119         glPixelZoom(1, -1);
120         glEnable(GL_ALPHA_TEST);
121         glAlphaFunc(GL_GREATER, 0.5f);
122         glDrawPixels(win_width, win_height, GL_BGRA, GL_UNSIGNED_BYTE, framebuf);
123         glDisable(GL_ALPHA_TEST);
124
125         if(rband.width | rband.height) {
126                 glOrtho(0, win_width, win_height, 0, -1, 1);
127
128                 glPushAttrib(GL_ENABLE_BIT);
129                 glDisable(GL_DEPTH_TEST);
130                 glDisable(GL_LIGHTING);
131
132                 glEnable(GL_COLOR_LOGIC_OP);
133                 glLogicOp(GL_XOR);
134
135                 glBegin(GL_LINE_LOOP);
136                 glColor3f(1, 1, 1);
137                 glVertex2f(rband.x, rband.y);
138                 glVertex2f(rband.x + rband.width, rband.y);
139                 glVertex2f(rband.x + rband.width, rband.y + rband.height);
140                 glVertex2f(rband.x, rband.y + rband.height);
141                 glEnd();
142
143                 glPopAttrib();
144         }
145
146         glPopMatrix();
147         glMatrixMode(GL_MODELVIEW);
148
149         glutSwapBuffers();
150         assert(glGetError() == GL_NO_ERROR);
151 }
152
153 void app_quit(void)
154 {
155         exit(0);
156 }
157
158 void app_resize(int x, int y)
159 {
160         if(x == win_width && y == win_height) return;
161
162         glutReshapeWindow(x, y);
163 }
164
165 void app_fullscreen(int fs)
166 {
167         static int prev_w, prev_h;
168
169         if(fs == -1) {
170                 fs = !fullscr;
171         }
172
173         if(fs == fullscr) return;
174
175         if(fs) {
176                 prev_w = glutGet(GLUT_WINDOW_WIDTH);
177                 prev_h = glutGet(GLUT_WINDOW_HEIGHT);
178                 glutFullScreen();
179         } else {
180                 glutReshapeWindow(prev_w, prev_h);
181         }
182         fullscr = fs;
183 }
184
185 #if defined(__unix__) || defined(unix)
186 void app_vsync(int vsync)
187 {
188         vsync = vsync ? 1 : 0;
189         if(glx_swap_interval_ext) {
190                 glx_swap_interval_ext(xdpy, xwin, vsync);
191         } else if(glx_swap_interval_sgi) {
192                 glx_swap_interval_sgi(vsync);
193         }
194 }
195 #endif
196 #ifdef WIN32
197 void app_vsync(int vsync)
198 {
199         if(wgl_swap_interval_ext) {
200                 wgl_swap_interval_ext(vsync ? 1 : 0);
201         }
202 }
203 #endif
204
205 void app_rband(int x, int y, int w, int h)
206 {
207         rband.x = x;
208         rband.y = y;
209         rband.width = w;
210         rband.height = h;
211
212         glutPostRedisplay();
213 }
214
215
216 static void display(void)
217 {
218         app_display();
219         app_swap_buffers();
220 }
221
222 static void reshape(int x, int y)
223 {
224         app_reshape(x, y);
225 }
226
227 static void keydown(unsigned char key, int x, int y)
228 {
229         modkeys = glutGetModifiers();
230         app_keyboard(key, 1);
231 }
232
233 static void keyup(unsigned char key, int x, int y)
234 {
235         app_keyboard(key, 0);
236 }
237
238 static void skeydown(int key, int x, int y)
239 {
240         int k;
241         modkeys = glutGetModifiers();
242         if((k = translate_skey(key)) >= 0) {
243                 app_keyboard(k, 1);
244         }
245 }
246
247 static void skeyup(int key, int x, int y)
248 {
249         int k = translate_skey(key);
250         if(k >= 0) {
251                 app_keyboard(k, 0);
252         }
253 }
254
255 static void mouse(int bn, int st, int x, int y)
256 {
257         modkeys = glutGetModifiers();
258         app_mouse(bn - GLUT_LEFT_BUTTON, st == GLUT_DOWN, x, y);
259 }
260
261 static void motion(int x, int y)
262 {
263         app_motion(x, y);
264 }
265
266 static int translate_skey(int key)
267 {
268         switch(key) {
269         case GLUT_KEY_LEFT:
270                 return KEY_LEFT;
271         case GLUT_KEY_UP:
272                 return KEY_UP;
273         case GLUT_KEY_RIGHT:
274                 return KEY_RIGHT;
275         case GLUT_KEY_DOWN:
276                 return KEY_DOWN;
277         case GLUT_KEY_PAGE_UP:
278                 return KEY_PGUP;
279         case GLUT_KEY_PAGE_DOWN:
280                 return KEY_PGDN;
281         case GLUT_KEY_HOME:
282                 return KEY_HOME;
283         case GLUT_KEY_END:
284                 return KEY_END;
285         case GLUT_KEY_INSERT:
286                 return KEY_INS;
287         default:
288                 if(key >= GLUT_KEY_F1 && key <= GLUT_KEY_F12) {
289                         return key - GLUT_KEY_F1 + KEY_F1;
290                 }
291         }
292
293         return -1;
294 }