heed resolution options, and cross-version resizing
[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 "options.h"
24 #include "rtk.h"
25 #include "logger.h"
26
27 static void display(void);
28 static void reshape(int x, int y);
29 static void keydown(unsigned char key, int x, int y);
30 static void keyup(unsigned char key, int x, int y);
31 static void skeydown(int key, int x, int y);
32 static void skeyup(int key, int x, int y);
33 static void mouse(int bn, int st, int x, int y);
34 static void motion(int x, int y);
35 static int translate_skey(int key);
36
37 #if defined(__unix__) || defined(unix)
38 #include <GL/glx.h>
39 static Display *xdpy;
40 static Window xwin;
41
42 static void (*glx_swap_interval_ext)();
43 static void (*glx_swap_interval_sgi)();
44 #endif
45 #ifdef _WIN32
46 #include <windows.h>
47 static PROC wgl_swap_interval_ext;
48 #endif
49
50 static rtk_rect rband;
51
52
53 int main(int argc, char **argv)
54 {
55         glutInit(&argc, argv);
56
57         load_options(CFGFILE);
58
59         glutInitWindowSize(opt.xres, opt.yres);
60         glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
61         glutCreateWindow("RetroRay");
62
63         glutDisplayFunc(display);
64         glutReshapeFunc(reshape);
65         glutKeyboardFunc(keydown);
66         glutKeyboardUpFunc(keyup);
67         glutSpecialFunc(skeydown);
68         glutSpecialUpFunc(skeyup);
69         glutMouseFunc(mouse);
70         glutMotionFunc(motion);
71         glutPassiveMotionFunc(motion);
72         glutSpaceballMotionFunc(app_sball_motion);
73         glutSpaceballRotateFunc(app_sball_rotate);
74         glutSpaceballButtonFunc(app_sball_button);
75
76 #if defined(__unix__) || defined(unix)
77         xdpy = glXGetCurrentDisplay();
78         xwin = glXGetCurrentDrawable();
79
80         if(!(glx_swap_interval_ext = glXGetProcAddress((unsigned char*)"glXSwapIntervalEXT"))) {
81                 glx_swap_interval_sgi = glXGetProcAddress((unsigned char*)"glXSwapIntervalSGI");
82         }
83 #endif
84 #ifdef _WIN32
85         wgl_swap_interval_ext = wglGetProcAddress("wglSwapIntervalEXT");
86 #endif
87
88         win_width = glutGet(GLUT_WINDOW_WIDTH);
89         win_height = glutGet(GLUT_WINDOW_HEIGHT);
90         win_aspect = (float)win_width / win_height;
91
92         init_logger();
93         add_log_stream(stdout);
94
95         if(app_init() == -1) {
96                 return 1;
97         }
98         atexit(app_shutdown);
99         glutMainLoop();
100         return 0;
101 }
102
103 unsigned long get_msec(void)
104 {
105         return (unsigned long)glutGet(GLUT_ELAPSED_TIME);
106 }
107
108 void app_redisplay(int x, int y, int w, int h)
109 {
110         /*dbgmsg("fakeupd: %d,%d (%dx%d)\n", x, y, w, h);*/
111         glutPostRedisplay();
112 }
113
114 void app_swap_buffers(void)
115 {
116         glViewport(0, 0, win_width, win_height);
117
118         glMatrixMode(GL_MODELVIEW);
119         glLoadIdentity();
120         glMatrixMode(GL_PROJECTION);
121         glPushMatrix();
122         glLoadIdentity();
123         glOrtho(0, win_width, win_height, 0, -1, 1);
124
125         glRasterPos2i(0, 0);
126         glPixelZoom(1, -1);
127         glEnable(GL_ALPHA_TEST);
128         glAlphaFunc(GL_GREATER, 0.5f);
129         glDrawPixels(win_width, win_height, GL_BGRA, GL_UNSIGNED_BYTE, framebuf);
130         glDisable(GL_ALPHA_TEST);
131
132         glDisable(GL_DEPTH_TEST);
133         glDisable(GL_LIGHTING);
134         glBegin(GL_LINE_LOOP);
135         glColor3f(1, 0, 0);
136         glVertex2f(0, 0);
137         glVertex2f(win_width, 0);
138         glVertex2f(win_width, win_height);
139         glVertex2f(0, win_height);
140         glEnd();
141
142         if(rband.width | rband.height) {
143
144                 glPushAttrib(GL_ENABLE_BIT);
145                 glDisable(GL_DEPTH_TEST);
146                 glDisable(GL_LIGHTING);
147
148                 glEnable(GL_COLOR_LOGIC_OP);
149                 glLogicOp(GL_XOR);
150
151                 glBegin(GL_LINE_LOOP);
152                 glColor3f(1, 1, 1);
153                 glVertex2f(rband.x, rband.y);
154                 glVertex2f(rband.x + rband.width, rband.y);
155                 glVertex2f(rband.x + rband.width, rband.y + rband.height);
156                 glVertex2f(rband.x, rband.y + rband.height);
157                 glEnd();
158
159                 glPopAttrib();
160         }
161
162         glPopMatrix();
163         glMatrixMode(GL_MODELVIEW);
164
165         glutSwapBuffers();
166         assert(glGetError() == GL_NO_ERROR);
167 }
168
169 void app_quit(void)
170 {
171         exit(0);
172 }
173
174 void app_resize(int x, int y)
175 {
176         if(x == win_width && y == win_height) return;
177
178         glutReshapeWindow(x, y);
179 }
180
181 void app_fullscreen(int fs)
182 {
183         static int prev_w, prev_h;
184
185         if(fs == -1) {
186                 fs = !fullscr;
187         }
188
189         if(fs == fullscr) return;
190
191         if(fs) {
192                 prev_w = glutGet(GLUT_WINDOW_WIDTH);
193                 prev_h = glutGet(GLUT_WINDOW_HEIGHT);
194                 glutFullScreen();
195         } else {
196                 glutReshapeWindow(prev_w, prev_h);
197         }
198         fullscr = fs;
199 }
200
201 #if defined(__unix__) || defined(unix)
202 void app_vsync(int vsync)
203 {
204         vsync = vsync ? 1 : 0;
205         if(glx_swap_interval_ext) {
206                 glx_swap_interval_ext(xdpy, xwin, vsync);
207         } else if(glx_swap_interval_sgi) {
208                 glx_swap_interval_sgi(vsync);
209         }
210 }
211 #endif
212 #ifdef WIN32
213 void app_vsync(int vsync)
214 {
215         if(wgl_swap_interval_ext) {
216                 wgl_swap_interval_ext(vsync ? 1 : 0);
217         }
218 }
219 #endif
220
221 void app_rband(int x, int y, int w, int h)
222 {
223         rband.x = x;
224         rband.y = y;
225         rband.width = w;
226         rband.height = h;
227
228         glutPostRedisplay();
229 }
230
231
232 static void display(void)
233 {
234         app_display();
235         app_swap_buffers();
236 }
237
238 static void reshape(int x, int y)
239 {
240         app_reshape(x, y);
241 }
242
243 static void keydown(unsigned char key, int x, int y)
244 {
245         modkeys = glutGetModifiers();
246         app_keyboard(key, 1);
247 }
248
249 static void keyup(unsigned char key, int x, int y)
250 {
251         app_keyboard(key, 0);
252 }
253
254 static void skeydown(int key, int x, int y)
255 {
256         int k;
257         modkeys = glutGetModifiers();
258         if((k = translate_skey(key)) >= 0) {
259                 app_keyboard(k, 1);
260         }
261 }
262
263 static void skeyup(int key, int x, int y)
264 {
265         int k = translate_skey(key);
266         if(k >= 0) {
267                 app_keyboard(k, 0);
268         }
269 }
270
271 static void mouse(int bn, int st, int x, int y)
272 {
273         modkeys = glutGetModifiers();
274         app_mouse(bn - GLUT_LEFT_BUTTON, st == GLUT_DOWN, x, y);
275 }
276
277 static void motion(int x, int y)
278 {
279         app_motion(x, y);
280 }
281
282 static int translate_skey(int key)
283 {
284         switch(key) {
285         case GLUT_KEY_LEFT:
286                 return KEY_LEFT;
287         case GLUT_KEY_UP:
288                 return KEY_UP;
289         case GLUT_KEY_RIGHT:
290                 return KEY_RIGHT;
291         case GLUT_KEY_DOWN:
292                 return KEY_DOWN;
293         case GLUT_KEY_PAGE_UP:
294                 return KEY_PGUP;
295         case GLUT_KEY_PAGE_DOWN:
296                 return KEY_PGDN;
297         case GLUT_KEY_HOME:
298                 return KEY_HOME;
299         case GLUT_KEY_END:
300                 return KEY_END;
301         case GLUT_KEY_INSERT:
302                 return KEY_INS;
303         default:
304                 if(key >= GLUT_KEY_F1 && key <= GLUT_KEY_F12) {
305                         return key - GLUT_KEY_F1 + KEY_F1;
306                 }
307         }
308
309         return -1;
310 }