5a23d71bae879301eec304096250941fd33d329f
[vrlugburz] / tools / dunger / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <GL/glut.h>
5 #include <utk/cubertk.h>
6 #include <drawtext.h>
7 #include "level.h"
8 #include "lview.h"
9
10 static int init(void);
11 static void cleanup(void);
12 static void display(void);
13 static void reshape(int x, int y);
14 static void keyb(unsigned char key, int x, int y);
15 static void keyup(unsigned char key, int x, int y);
16 static void mouse(int bn, int st, int x, int y);
17 static void motion(int x, int y);
18
19 static void cb_new(utk_event *ev, void *data);
20 static void cb_new_ok(utk_event *ev, void *data);
21 static void cb_open(utk_event *ev, void *data);
22 static void cb_open_ok(utk_event *ev, void *data);
23 static void cb_save(utk_event *ev, void *data);
24 static void cb_save_ok(utk_event *ev, void *data);
25
26 static void cb_cancel(utk_event *ev, void *data);
27
28 static int parse_args(int argc, char **argv);
29
30
31 static void ucolor(int r, int g, int b, int a);
32 static void uclip(int x1, int y1, int x2, int y2);
33 static void uimage(int x, int y, const void *pix, int xsz, int ysz);
34 static void urect(int x1, int y1, int x2, int y2);
35 static void uline(int x1, int y1, int x2, int y2, int width);
36 static void utext(int x, int y, const char *txt, int sz);
37 static int utextspacing(void);
38 static int utextwidth(const char *txt, int sz);
39
40
41 int win_width, win_height;
42 int view_width, view_height;
43 float view_panx, view_pany, view_zoom = 1.0f;
44
45 static int bnstate[8];
46 int mousex, mousey, clickx, clicky;
47
48 static float uiscale = 1.0f;
49 #define UISPLIT 150
50 static int splitx;
51
52 #define FONTSZ  16
53 static struct dtx_font *uifont;
54 static utk_widget *uiroot, *uiwin_new;
55 static utk_widget *uigrab;
56 static utk_widget *cbox_newsz;
57
58 static struct level lvl;
59
60
61 int main(int argc, char **argv)
62 {
63         glutInit(&argc, argv);
64
65         if(parse_args(argc, argv) == -1) {
66                 return 1;
67         }
68
69         glutInitWindowSize(1280, 800);
70         glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_MULTISAMPLE);
71         glutCreateWindow("dunger");
72
73         win_width = glutGet(GLUT_WINDOW_WIDTH);
74         win_height = glutGet(GLUT_WINDOW_HEIGHT);
75
76         glutDisplayFunc(display);
77         glutReshapeFunc(reshape);
78         glutKeyboardFunc(keyb);
79         glutKeyboardUpFunc(keyup);
80         glutMouseFunc(mouse);
81         glutMotionFunc(motion);
82         glutPassiveMotionFunc(motion);
83
84         if(init() == -1) {
85                 return 1;
86         }
87         atexit(cleanup);
88
89         glutMainLoop();
90         return 0;
91 }
92
93
94 static int init(void)
95 {
96         int pad;
97         utk_widget *win, *hbox, *vbox;
98
99         glEnable(GL_MULTISAMPLE);
100
101         glClearColor(0.15, 0.15, 0.15, 1);
102
103         if(!(uifont = dtx_open_font("uifont.ttf", 0))) {
104                 fprintf(stderr, "failed to open uifont.ttf\n");
105                 return -1;
106         }
107         dtx_prepare_range(uifont, FONTSZ, ' ', 'z');
108         dtx_use_font(uifont, FONTSZ);
109
110         if(!(uiroot = utk_init(win_width / uiscale, win_height / uiscale))) {
111                 fprintf(stderr, "failed to initialized ubertk\n");
112                 return -1;
113         }
114         utk_set_color_func(ucolor);
115         utk_set_clip_func(uclip);
116         utk_set_image_func(uimage);
117         utk_set_rect_func(urect);
118         utk_set_line_func(uline);
119         utk_set_text_func(utext);
120         utk_set_text_spacing_func(utextspacing);
121         utk_set_text_width_func(utextwidth);
122
123         win = utk_vbox(uiroot, 0, UTK_DEF_SPACING);
124         utk_set_pos(win, 15, 15);
125         utk_button(win, "New", 0, 0, cb_new, 0);
126         utk_button(win, "Open ...", 0, 0, cb_open, 0);
127         utk_button(win, "Save ...", 0, 0, cb_save, 0);
128
129         uiwin_new = utk_window(uiroot, (win_width - 220) / 2, (win_height - 150) / 2,
130                         220, 150, "New level");
131         vbox = utk_vbox(uiwin_new, UTK_DEF_PADDING, UTK_DEF_SPACING);
132         {
133                 const char *items[] = {"small (16x16)", "medium (24x24)", "large (32x32)"};
134                 cbox_newsz = utk_combobox_items(vbox, items, sizeof items / sizeof *items, 0, 0);
135                 utk_select(cbox_newsz, 2);
136         }
137         hbox = utk_hbox(vbox, UTK_DEF_PADDING, UTK_DEF_SPACING);
138         utk_button(hbox, "OK", 0, 0, cb_new_ok, 0);
139         utk_button(hbox, "Cancel", 0, 0, cb_cancel, uiwin_new);
140         pad = utk_get_padding(uiwin_new);
141         utk_set_size(uiwin_new, utk_get_width(vbox) + pad * 2.0f, utk_get_height(vbox) + pad * 2.0f);
142
143
144         if(init_level(&lvl, 32, 32) == -1) {
145                 fprintf(stderr, "failed to create level\n");
146                 return -1;
147         }
148         if(init_lview(&lvl) == -1) {
149                 return -1;
150         }
151
152         splitx = UISPLIT * uiscale;
153         view_width = win_width - splitx;
154         view_height = win_height;
155
156         return 0;
157 }
158
159 static void cleanup(void)
160 {
161         destroy_lview();
162         destroy_level(&lvl);
163         dtx_close_font(uifont);
164         utk_close(uiroot);
165 }
166
167 static void display(void)
168 {
169         glClear(GL_COLOR_BUFFER_BIT);
170
171         glMatrixMode(GL_MODELVIEW);
172         glLoadIdentity();
173
174         /* draw view */
175
176         glMatrixMode(GL_PROJECTION);
177         glLoadIdentity();
178         glOrtho(0, view_width, 0, view_height, -1, 1);
179         glViewport(splitx, 0, view_width, view_height);
180
181         glBegin(GL_QUADS);
182         glColor3f(0.1, 0.1, 0.1);
183         glVertex2f(0, 0);
184         glVertex2f(view_width, 0);
185         glVertex2f(view_width, view_height);
186         glVertex2f(0, view_height);
187         glEnd();
188
189         draw_lview();
190
191         /* draw UI */
192
193         glMatrixMode(GL_PROJECTION);
194         glLoadIdentity();
195         glOrtho(0, win_width, win_height, 0, -1, 1);
196         glViewport(0, 0, win_width, win_height);
197
198         glBegin(GL_QUADS);
199         glColor3f(0.25, 0.25, 0.25);
200         glVertex2f(0, 0);
201         glVertex2f(splitx, 0);
202         glVertex2f(splitx, win_height);
203         glVertex2f(0, win_height);
204         glEnd();
205
206         glEnable(GL_BLEND);
207         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
208         utk_draw(uiroot);
209         glDisable(GL_BLEND);
210
211
212         glutSwapBuffers();
213 }
214
215 static void reshape(int x, int y)
216 {
217         win_width = x;
218         win_height = y;
219
220         if(uiroot) {
221                 utk_set_size(uiroot, x / uiscale, y / uiscale);
222         }
223
224         lview_viewport(splitx, 0, x - splitx, y);
225 }
226
227 static void keyb(unsigned char key, int x, int y)
228 {
229         switch(key) {
230         case 27:
231                 if(uigrab) {
232                         if(utk_is_dialog(uigrab)) {
233                                 utk_destroy_window(uigrab);
234                         } else {
235                                 utk_hide(uigrab);
236                         }
237                         uigrab = 0;
238                 } else {
239                         exit(0);
240                 }
241                 return;
242
243         default:
244                 break;
245         }
246
247         utk_keyboard_event(key, 1);
248         glutPostRedisplay();
249 }
250
251 static void keyup(unsigned char key, int x, int y)
252 {
253         utk_keyboard_event(key, 0);
254         glutPostRedisplay();
255 }
256
257 static void mouse(int bn, int st, int x, int y)
258 {
259         int bidx = bn - GLUT_LEFT_BUTTON;
260         int press = st == GLUT_DOWN;
261
262         bnstate[bidx] = press;
263         mousex = x;
264         mousey = y;
265
266         if(bn <= 2) {
267                 if(press) {
268                         clickx = x;
269                         clicky = y;
270                 } else {
271                         clickx = clicky = -1;
272                 }
273         } else if(bn == 3) {
274                 if(press) zoom_lview(0.1);
275         } else if(bn == 4) {
276                 if(press) zoom_lview(-0.1);
277         }
278
279         if(!uigrab) {
280                 lview_mbutton(bidx, press, x, y);
281         }
282
283         utk_mbutton_event(bidx, press, x / uiscale, y / uiscale);
284         glutPostRedisplay();
285 }
286
287 static void motion(int x, int y)
288 {
289         int dx, dy;
290         dx = x - mousex;
291         dy = y - mousey;
292         mousex = x;
293         mousey = y;
294
295         if(clickx >= splitx) {
296                 if(bnstate[1]) {
297                         pan_lview(-dx, dy);
298                 }
299         }
300
301         if(!uigrab) {
302                 lview_mouse(x, y);
303         }
304
305         utk_mmotion_event(x / uiscale, y / uiscale);
306         glutPostRedisplay();
307 }
308
309 static void cb_new(utk_event *ev, void *data)
310 {
311         utk_show(uiwin_new);
312         uigrab = uiwin_new;
313 }
314
315 static void cb_new_ok(utk_event *ev, void *data)
316 {
317         static int levsz[] = {16, 24, 32};
318         int sz;
319         struct level newlvl;
320
321         sz = levsz[utk_get_selected(cbox_newsz)];
322
323         if(init_level(&newlvl, sz, sz) == -1) {
324                 utk_message_dialog("failed to create new level", UTK_MSG_TYPE_ERROR,
325                                 UTK_MSG_BN_OK, cb_cancel, 0);
326                 return;
327         }
328
329         destroy_level(&lvl);
330         destroy_lview();
331
332         lvl = newlvl;
333         init_lview(&lvl);
334
335         utk_hide(uiwin_new);
336         uigrab = 0;
337 }
338
339 static void cb_open(utk_event *ev, void *data)
340 {
341         uigrab = utk_file_dialog(UTK_FILE_DIALOG_OPEN, 0, "Level file (*.lvl) [.lvl]", 0, cb_open_ok, 0);
342 }
343
344 static void cb_open_ok(utk_event *ev, void *data)
345 {
346         char *errmsg = 0;
347         const char *path;
348
349         utk_widget *dlg = utk_event_widget(ev);
350         path = utk_file_dialog_path(dlg);
351         printf("selected: %s\n", path);
352         if(load_level(&lvl, path) == -1) {
353                 errmsg = alloca(strlen(path) + 32);
354                 sprintf(errmsg, "Failed to load level: %s", path);
355         }
356         utk_destroy_window(dlg);
357         if(uigrab == dlg) uigrab = 0;
358
359         if(errmsg) {
360                 uigrab = utk_message_dialog(errmsg, UTK_MSG_TYPE_ERROR, UTK_MSG_BN_OK, cb_cancel, 0);
361         }
362 }
363
364 static void cb_save(utk_event *ev, void *data)
365 {
366         uigrab = utk_file_dialog(UTK_FILE_DIALOG_SAVE, 0, "Level file (*.lvl) [.lvl]", 0, cb_save_ok, 0);
367 }
368
369 static void cb_save_ok(utk_event *ev, void *data)
370 {
371         char *errmsg = 0;
372         const char *path;
373
374         utk_widget *dlg = utk_event_widget(ev);
375         path = utk_file_dialog_path(dlg);
376         printf("selected: %s\n", path);
377         if(save_level(&lvl, path) == -1) {
378                 errmsg = alloca(strlen(path) + 32);
379                 sprintf(errmsg, "Failed to save level file: %s", path);
380         }
381         utk_destroy_window(dlg);
382         if(uigrab == dlg) uigrab = 0;
383
384         if(errmsg) {
385                 uigrab = utk_message_dialog(errmsg, UTK_MSG_TYPE_ERROR, UTK_MSG_BN_OK, cb_cancel, 0);
386         }
387 }
388
389 static void cb_cancel(utk_event *ev, void *data)
390 {
391         if(!data) data = utk_get_window(utk_event_widget(ev));
392
393         if(utk_is_dialog(data)) {
394                 utk_destroy_window(data);
395         } else {
396                 utk_hide(data);
397         }
398         uigrab = 0;
399 }
400
401 static int parse_args(int argc, char **argv)
402 {
403         int i;
404
405         for(i=1; i<argc; i++) {
406                 if(argv[i][0] == '-') {
407                         if(strcmp(argv[i], "-uiscale") == 0) {
408                                 if(!argv[++i] || !(uiscale = atoi(argv[i]))) {
409                                         fprintf(stderr, "-uiscale should be followed by a positive number\n");
410                                         return -1;
411                                 }
412                         } else if(strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "-h") == 0) {
413                                 printf("Usage: %s [options]\n", argv[0]);
414                                 printf("Options:\n");
415                                 printf(" -uiscale <scale>: UI scale factor (default: 1)\n");
416                                 printf(" -h,-help: print usage and exit\n");
417                                 exit(0);
418                         } else {
419                                 fprintf(stderr, "unknown option: %s\n", argv[i]);
420                                 return -1;
421                         }
422                 } else {
423                         fprintf(stderr, "unexpected argument: %s\n", argv[i]);
424                         return -1;
425                 }
426         }
427         return 0;
428 }
429
430 /* --- ubertk callbacks --- */
431
432 static void ucolor(int r, int g, int b, int a)
433 {
434         glColor4ub(r, g, b, a);
435 }
436
437 static void uclip(int x1, int y1, int x2, int y2)
438 {
439         if(!(x1 | y1 | x2 | y2)) {
440                 glDisable(GL_SCISSOR_TEST);
441         } else {
442                 glEnable(GL_SCISSOR_TEST);
443         }
444
445         x1 *= uiscale;
446         y1 *= uiscale;
447         x2 *= uiscale;
448         y2 *= uiscale;
449
450         glScissor(x1, win_height - y2, x2 - x1, y2 - y1);
451 }
452
453 static void uimage(int x, int y, const void *pix, int xsz, int ysz)
454 {
455         glPixelZoom(1, -1);
456         glRasterPos2f(x * uiscale, y * uiscale);
457         glDrawPixels(xsz, ysz, GL_BGRA, GL_UNSIGNED_BYTE, pix);
458 }
459
460 static void urect(int x1, int y1, int x2, int y2)
461 {
462         glRectf(x1 * uiscale, y1 * uiscale, x2 * uiscale, y2 * uiscale);
463 }
464
465 static void uline(int x1, int y1, int x2, int y2, int width)
466 {
467         glLineWidth(width);
468         glBegin(GL_LINES);
469         glVertex2f(x1 * uiscale, y1 * uiscale);
470         glVertex2f(x2 * uiscale, y2 * uiscale);
471         glEnd();
472 }
473
474 static void utext(int x, int y, const char *txt, int sz)
475 {
476         glMatrixMode(GL_PROJECTION);
477         glPushMatrix();
478         glTranslatef(x * uiscale, (y - dtx_baseline()) * uiscale, 0);
479         glScalef(uiscale, -uiscale, 1);
480
481         dtx_string(txt);
482         dtx_flush();
483
484         glPopMatrix();
485 }
486
487 static int utextspacing(void)
488 {
489         return dtx_line_height();
490 }
491
492 static int utextwidth(const char *txt, int sz)
493 {
494         return dtx_string_width(txt);
495 }