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