1 /* NetHack may be freely redistributed. See license for details. */
2
3 #include <ctype.h>
4 #include "SDL.h"
5
6 extern "C" {
7 #include "hack.h"
8 }
9
10 #include "vulture_win.h"
11 #include "vulture_sdl.h"
12 #include "vulture_mou.h"
13 #include "vulture_main.h"
14 #include "vulture_opt.h"
15
16 #include "winclass/button.h"
17 #include "winclass/enhancebutton.h"
18 #include "winclass/inputdialog.h"
19 #include "winclass/levelwin.h"
20
21 #define V_EVENTSTACK_SIZE 32
22
23
24 /******************************************************************************
25 * globals vars
26 ******************************************************************************/
27
28 SDL_Rect *vulture_invrects = NULL;
29 int vulture_invrects_num = 0;
30 int vulture_invrects_max = 0;
31
32
33 /*********************************/
34
35 vulture_window_graphics vulture_winelem; /* contains borders, background, etc */
36
37 int vulture_windows_inited = 0;
38 int vulture_suppress_helpmsg;
39 int vulture_winid_map = 0;
40 int vulture_winid_minimap = 0;
41 int vulture_whatis_singleshot = 0;
42
43 vulture_event * vulture_eventstack = NULL;
44 int vulture_eventstack_top;
45
46
47 /******************************************************************************
48 * function pre-declarations
49 ******************************************************************************/
50
51 static int vulture_event_dispatcher_core(SDL_Event * event, void * result, window * topwin);
52 static int vulture_handle_event(window * topwin, window * win,
53 void * result, SDL_Event * event, int * redraw);
54
55
56 /******************************
57 * High-level window functions
58 ******************************/
vulture_messagebox(std::string message)59 void vulture_messagebox(std::string message)
60 {
61 mainwin *win;
62 int dummy;
63
64 win = new mainwin(ROOTWIN);
65 win->set_caption(message);
66
67 new button(win, "Continue", 1, '\0');
68
69 win->layout();
70
71 vulture_event_dispatcher(&dummy, V_RESPOND_INT, win);
72
73 delete win;
74 }
75
76
vulture_get_input(int force_x,int force_y,const char * ques,char * input)77 int vulture_get_input(int force_x, int force_y, const char *ques, char *input)
78 {
79 int response;
80 inputdialog *win = new inputdialog(ROOTWIN, ques, 256, force_x, force_y);
81
82 /* get input */
83 vulture_event_dispatcher(&response, V_RESPOND_INT, win);
84
85 /* copy result into input */
86 win->copy_input(input);
87
88 /* clean up */
89 delete win;
90
91 return response;
92 }
93
94
95
96 /******************************
97 * Event handling functions
98 ******************************/
99
vulture_event_dispatcher(void * result,int resulttype,window * topwin)100 void vulture_event_dispatcher(void * result, int resulttype, window * topwin)
101 {
102 vulture_event * queued_event;
103 int event_result = V_EVENT_UNHANDLED;
104 point mouse;
105 SDL_Event event;
106 int redraw;
107 window *win;
108
109 /* first, check whether we have an autoresponse queued */
110 while ( (queued_event = vulture_eventstack_get()))
111 {
112 if (queued_event->rtype == resulttype || queued_event->rtype == V_RESPOND_ANY)
113 {
114 /* suppress some messages during automatic actions */
115 vulture_suppress_helpmsg = 1;
116
117 if (resulttype == V_RESPOND_POSKEY)
118 *(vulture_event*)result = *queued_event;
119 else if (resulttype == V_RESPOND_CHARACTER)
120 *(char*)result = (char)queued_event->num;
121 else
122 *(int*)result = queued_event->num;
123 return;
124 }
125 }
126
127 /* this block will take us out of singleshot-whatis mode (triggered by the context menu) */
128 if (vulture_whatis_singleshot && resulttype == V_RESPOND_POSKEY)
129 {
130 ((vulture_event*)result)->num = ' ';
131 vulture_whatis_singleshot = 0;
132 return;
133 }
134
135 /* check whether we want to draw the "enhance" icon */
136 if (enhancebtn)
137 enhancebtn->check_enhance();
138
139 /* nothing queued, do normal event processing */
140 if (!topwin)
141 topwin = ROOTWIN;
142
143 if (!vulture_whatis_singleshot)
144 /* no need to suppress messages now... */
145 vulture_suppress_helpmsg = 0;
146
147 /* kill the tooltip */
148 vulture_mouse_invalidate_tooltip(1);
149
150 /* fake a mousemotion event, to make the window set its preferred cursor before we draw */
151 memset(&event, 0, sizeof(event));
152 event.type = SDL_MOUSEMOTION;
153 mouse = vulture_get_mouse_pos();
154 win = topwin->get_window_from_point(mouse);
155 vulture_handle_event(topwin, win, result, &event, &redraw);
156
157 /* draw windows, if necessary */
158 topwin->draw_windows();
159 vulture_mouse_draw();
160 vulture_refresh_window_region();
161 vulture_mouse_refresh();
162 vulture_mouse_restore_bg();
163
164 while (event_result != V_EVENT_HANDLED_FINAL)
165 {
166 /* Get next event OR wait 100ms */
167 vulture_wait_event(&event, 100);
168
169 event_result = vulture_event_dispatcher_core(&event, result, topwin);
170
171 SDL_Delay(20);
172 }
173 }
174
175
176
vulture_event_dispatcher_nonblocking(void * result,window * topwin)177 int vulture_event_dispatcher_nonblocking(void * result, window * topwin)
178 {
179 SDL_Event event;
180 int event_result = V_EVENT_UNHANDLED;
181
182 if (!topwin)
183 topwin = ROOTWIN;
184
185 /* kill the tooltip */
186 vulture_mouse_invalidate_tooltip(1);
187
188 /* draw windows, if necessary */
189 topwin->draw_windows();
190 vulture_mouse_draw();
191 vulture_refresh_window_region();
192 vulture_mouse_refresh();
193 vulture_mouse_restore_bg();
194
195 while (event_result != V_EVENT_HANDLED_FINAL)
196 {
197 if (!vulture_poll_event(&event))
198 return 0;
199
200 event_result = vulture_event_dispatcher_core(&event, result, topwin);
201 }
202
203 return 1;
204 }
205
206
vulture_event_dispatcher_core(SDL_Event * event,void * result,window * topwin)207 static int vulture_event_dispatcher_core(SDL_Event * event, void * result, window * topwin)
208 {
209 window *win, *win_old;
210 int event_result = V_EVENT_UNHANDLED;
211 int redraw = 0, hovertime_prev = 0;
212 static int hovertime = 0;
213 point mouse, mouse_old = vulture_get_mouse_prev_pos();
214
215 if (event->type == SDL_TIMEREVENT) {
216 hovertime_prev = hovertime;
217 hovertime += 150; /* 100 ms event timeout + 20 ms delay after the last event */
218 event->user.code = hovertime;
219 } else {
220 hovertime = 0;
221 event->user.code = 0;
222 }
223
224 /* keyboard events are always given to topwin, because mouse cursor
225 * position has nothing to do with keyboard input */
226 if (event->type == SDL_KEYDOWN) {
227 event_result = topwin->event_handler(topwin, result, event);
228 if (event_result == V_EVENT_HANDLED_REDRAW || event_result == V_EVENT_UNHANDLED_REDRAW)
229 redraw = 1;
230 }
231 else {
232 /* find out what window the mouse is over now */
233 mouse = vulture_get_mouse_pos();
234 win = topwin->get_window_from_point(mouse);
235
236 /* All other events belong to the window under the mouse cursor */
237 if (event->type == SDL_MOUSEMOTION) {
238 /* delete tooltip; if the mouse gets moved somewhere interesting
239 * the window it is over will set up a new tooltip */
240 vulture_mouse_invalidate_tooltip(0);
241
242 /* notify the window the mouse got moved out of */
243 win_old = topwin->get_window_from_point(mouse_old);
244 if (win_old && win != win_old && win_old != win->parent) {
245 event->type = SDL_MOUSEMOVEOUT;
246 event_result = vulture_handle_event(topwin, win_old, result, event, &redraw);
247 event->type = SDL_MOUSEMOTION;
248 }
249 }
250
251 /* the mouse might be outside the window of interest (topwin).
252 * if so win is NULL and we can go wait for the next event now... */
253 if (!win)
254 return V_EVENT_UNHANDLED;
255
256 event_result = vulture_handle_event(topwin, win, result, event, &redraw);
257 }
258
259 if (redraw)
260 topwin->draw_windows();
261
262 if (redraw || event->type != SDL_TIMEREVENT ||
263 (hovertime > HOVERTIMEOUT && hovertime_prev < HOVERTIMEOUT))
264 vulture_mouse_draw();
265
266 /* refresh all regions (except mouse & tt) needing a refresh here,
267 * do NOT do so in draw() functions */
268 if (redraw || vulture_invrects_num)
269 vulture_refresh_window_region();
270
271 if (redraw || event->type != SDL_TIMEREVENT ||
272 (hovertime > HOVERTIMEOUT && hovertime_prev < HOVERTIMEOUT))
273 {
274 vulture_mouse_refresh();
275 vulture_mouse_restore_bg();
276 }
277
278 return event_result;
279 }
280
281
282 /* takes an event and passes it each window in the win->parent->...->topwin
283 * chain until one of the windows handles the event or until the event is
284 * rejected by topwin */
vulture_handle_event(window * topwin,window * win,void * result,SDL_Event * event,int * redraw)285 static int vulture_handle_event(window *topwin, window *win,
286 void *result, SDL_Event *event, int *redraw)
287 {
288 int event_result = V_EVENT_UNHANDLED;
289 window * winptr = win;
290
291 while (event_result < V_EVENT_HANDLED_NOREDRAW)
292 {
293 event_result = winptr->event_handler(win, result, event);
294 if (event_result == V_EVENT_HANDLED_REDRAW ||
295 event_result == V_EVENT_UNHANDLED_REDRAW)
296 *redraw = 1;
297
298 if (winptr == topwin)
299 break;
300
301 /* try this window's parent next */
302 winptr = winptr->parent;
303 }
304
305 return event_result;
306 }
307
308
309
310 /* push an event onto the eventstack */
vulture_eventstack_add(int num,int x,int y,int rtype)311 void vulture_eventstack_add(int num, int x, int y, int rtype)
312 {
313 if (!vulture_eventstack)
314 {
315 vulture_eventstack = (vulture_event *)malloc(V_EVENTSTACK_SIZE * sizeof(vulture_event));
316 vulture_eventstack_top = -1;
317 }
318
319 vulture_eventstack_top++;
320 if (vulture_eventstack_top >= V_EVENTSTACK_SIZE)
321 {
322 printf("WARNING: eventstack full.\n");
323 vulture_eventstack_top = V_EVENTSTACK_SIZE - 1;
324 return;
325 }
326
327 vulture_eventstack[vulture_eventstack_top].num = num;
328 vulture_eventstack[vulture_eventstack_top].x = x;
329 vulture_eventstack[vulture_eventstack_top].y = y;
330 vulture_eventstack[vulture_eventstack_top].rtype = rtype;
331 }
332
333
334 /* pop an event off the eventstack */
vulture_eventstack_get(void)335 vulture_event * vulture_eventstack_get(void)
336 {
337 if (!vulture_eventstack)
338 return NULL;
339
340 /* stack empty? */
341 if (vulture_eventstack_top >= V_EVENTSTACK_SIZE || vulture_eventstack_top < 0)
342 return NULL;
343
344 vulture_eventstack_top--;
345
346 return &vulture_eventstack[vulture_eventstack_top+1];
347 }
348
349
vulture_eventstack_destroy(void)350 void vulture_eventstack_destroy(void)
351 {
352 if (vulture_eventstack)
353 free(vulture_eventstack);
354 }
355
356
vulture_invalidate_region(int x,int y,int w,int h)357 void vulture_invalidate_region(int x , int y, int w, int h)
358 {
359 int i;
360
361 /* look at known invalid rects */
362 for (i = 0; i < vulture_invrects_num; i++)
363 {
364 if (x > vulture_invrects[i].x &&
365 y > vulture_invrects[i].y &&
366 x + w < vulture_invrects[i].x + vulture_invrects[i].w &&
367 y + h < vulture_invrects[i].y + vulture_invrects[i].h)
368 /* new invalid region is fully inside an already known one */
369 return;
370
371 else if (x < vulture_invrects[i].x &&
372 y < vulture_invrects[i].y &&
373 x + w > vulture_invrects[i].x + vulture_invrects[i].w &&
374 y + h > vulture_invrects[i].y + vulture_invrects[i].h)
375 /* old invalid region is fully inside new one;
376 * stop searching here and reuse this entry in the array */
377 break;
378 }
379
380 if (i >= vulture_invrects_max)
381 {
382 vulture_invrects = (SDL_Rect *)realloc(vulture_invrects, (vulture_invrects_max + 16) * sizeof(SDL_Rect));
383 vulture_invrects_max += 16;
384 }
385
386 if (i == vulture_invrects_num)
387 vulture_invrects_num++;
388
389 vulture_invrects[i].x = x;
390 vulture_invrects[i].y = y;
391 vulture_invrects[i].w = w;
392 vulture_invrects[i].h = h;
393 }
394
395
396 /* refresh invalid regions */
vulture_refresh_window_region(void)397 void vulture_refresh_window_region(void)
398 {
399 int x1 = 9999, y1 = 9999, x2 = 0, y2 = 0;
400 int i;
401
402 /* find the bounding rectangla around all invalid rects */
403 for (i = 0; i < vulture_invrects_num; i++)
404 {
405 x1 = (x1 < vulture_invrects[i].x) ? x1 : vulture_invrects[i].x;
406 y1 = (y1 < vulture_invrects[i].y) ? y1 : vulture_invrects[i].y;
407
408 x2 = (x2 > (vulture_invrects[i].x + vulture_invrects[i].w)) ?
409 x2 : (vulture_invrects[i].x + vulture_invrects[i].w);
410 y2 = (y2 > (vulture_invrects[i].y + vulture_invrects[i].h)) ?
411 y2 : (vulture_invrects[i].y + vulture_invrects[i].h);
412 }
413
414 /* refresh the bounding rect */
415 if (x1 < x2 && y1 < y2)
416 vulture_refresh_region(x1, y1, x2, y2);
417
418 /* there are now 0 invalid rects */
419 if (vulture_invrects)
420 free(vulture_invrects);
421
422 vulture_invrects = NULL;
423 vulture_invrects_num = 0;
424 vulture_invrects_max = 0;
425 }
426
427
428 /* resize the vulture application window to the given width and height */
vulture_win_resize(int width,int height)429 void vulture_win_resize(int width, int height)
430 {
431 window *current, *topwin;
432 SDL_Event event;
433 vulture_event dummy;
434 bool descend = true;
435
436 if ( !ROOTWIN ) return;
437
438 current = topwin = ROOTWIN;
439
440 event.type = SDL_VIDEORESIZE;
441 event.resize.w = width;
442 event.resize.h = height;
443
444 current->event_handler(current, &dummy, &event);
445
446 do {
447 current = current->walk_winlist(&descend);
448
449 current->event_handler(current, &dummy, &event);
450
451 /* recalc absolute position */
452 if (current->v_type != V_WINTYPE_NONE && current->parent) {
453 current->abs_x = current->parent->abs_x + current->x;
454 current->abs_y = current->parent->abs_y + current->y;
455 }
456 }
457 while (current != topwin);
458
459 /* redraw everything */
460 levwin->force_redraw();
461
462 topwin->draw_windows();
463 vulture_refresh_window_region();
464 }
465
466
467 #ifdef VULTURE_NETHACK
468 /* show a main menu with common options when the user presses esc */
vulture_show_mainmenu()469 void vulture_show_mainmenu()
470 {
471 int winid, n;
472 anything any;
473 menu_item *selected;
474
475 winid = vulture_create_nhwindow(NHW_MENU);
476 vulture_start_menu(winid);
477
478 any.a_int = 1;
479 vulture_add_menu(winid, NO_GLYPH, &any, 'h', 0, ATR_BOLD,
480 "Help", MENU_UNSELECTED);
481 any.a_int = 2;
482 vulture_add_menu(winid, NO_GLYPH, &any, 'O', 0, ATR_BOLD,
483 "Options", MENU_UNSELECTED);
484 any.a_int = 3;
485 vulture_add_menu(winid, NO_GLYPH, &any, 'I', 0, ATR_BOLD,
486 "Interface options", MENU_UNSELECTED);
487 any.a_int = 4;
488 vulture_add_menu(winid, NO_GLYPH, &any, 'S', 0, ATR_BOLD,
489 "Save & Quit", MENU_UNSELECTED);
490 any.a_int = 5;
491 vulture_add_menu(winid, NO_GLYPH, &any, 'Q', 0, ATR_BOLD,
492 "Quit", MENU_UNSELECTED);
493
494 vulture_end_menu(winid, "Main menu");
495 n = vulture_select_menu(winid, PICK_ONE, &selected);
496 vulture_destroy_nhwindow(winid);
497
498 if (n < 1)
499 return;
500
501 switch(selected[0].item.a_int)
502 {
503 case 1: dohelp(); break;
504 case 2: doset(); break;
505 case 3: vulture_iface_opts(); break;
506 case 4: dosave(); break;
507 case 5: done2(); break;
508 }
509 }
510 #endif
511