1 	#include <stdlib.h>
2 #include <string.h>
3 #include <SDL_keyboard.h>
4 #ifndef WINDOWS
5 #include <SDL_syswm.h>
6 #endif
7 
8 #include "elconfig.h"
9 #include "events.h"
10 #include "context_menu.h"
11 #include "gamewin.h"
12 #include "gl_init.h"
13 #include "interface.h"
14 #include "particles.h"
15 #include "pathfinder.h"
16 #include "paste.h"
17 #include "pm_log.h"
18 #include "update.h"
19 #ifdef PAWN
20 #include "pawn/elpawn.h"
21 #endif
22 #include "textures.h"
23 
24 #include "actor_scripts.h"
25 
26 #ifdef OSX
27 int osx_right_mouse_cam = 0;
28 #endif
29 
30 static const int min_fps = 3;
31 
enter_minimised_state(void)32 static void enter_minimised_state(void)
33 {
34 	max_fps = min_fps;
35 	//printf("entered minimised\n");
36 }
37 
leave_minimised_state(void)38 static void leave_minimised_state(void)
39 {
40 	max_fps = limit_fps;
41 	update_all_actors(0);
42 	//printf("left minimised\n");
43 }
44 
45 // Convert from utf-8 to unicode, generated from the table here:
46 // https://www.utf8-chartable.de/unicode-utf8-table.pl?htmlent=1
utf8_to_unicode(const char text[32])47 static Uint8 utf8_to_unicode(const char text[32])
48 {
49 	if ((Uint8)text[1] == 0)
50 	{
51 		if (((Uint8)text[0] >= 0x20) && ((Uint8)text[0] < 0x7f))
52 			return (Uint8)text[0];
53 		else
54 			return 0;
55 	}
56 	else if ((Uint8)text[0] == 0xc2)
57 	{
58 		switch((Uint8)text[1])
59 		{
60 			case 0xa0: return 0xa0; // no-break space
61 			case 0xa1: return 0xa1; // inverted exclamation mark
62 			case 0xa2: return 0xa2; // cent sign
63 			case 0xa3: return 0xa3; // pound sign
64 			case 0xa4: return 0xa4; // currency sign
65 			case 0xa5: return 0xa5; // yen sign
66 			case 0xa6: return 0xa6; // broken bar
67 			case 0xa7: return 0xa7; // section sign
68 			case 0xa8: return 0xa8; // diaeresis
69 			case 0xa9: return 0xa9; // copyright sign
70 			case 0xaa: return 0xaa; // feminine ordinal indicator
71 			case 0xab: return 0xab; // left-pointing double angle quotation mark
72 			case 0xac: return 0xac; // not sign
73 			case 0xad: return 0xad; // soft hyphen
74 			case 0xae: return 0xae; // registered sign
75 			case 0xaf: return 0xaf; // macron
76 			case 0xb0: return 0xb0; // degree sign
77 			case 0xb1: return 0xb1; // plus-minus sign
78 			case 0xb2: return 0xb2; // superscript two
79 			case 0xb3: return 0xb3; // superscript three
80 			case 0xb4: return 0xb4; // acute accent
81 			case 0xb5: return 0xb5; // micro sign
82 			case 0xb6: return 0xb6; // pilcrow sign
83 			case 0xb7: return 0xb7; // middle dot
84 			case 0xb8: return 0xb8; // cedilla
85 			case 0xb9: return 0xb9; // superscript one
86 			case 0xba: return 0xba; // masculine ordinal indicator
87 			case 0xbb: return 0xbb; // right-pointing double angle quotation mark
88 			case 0xbc: return 0xbc; // vulgar fraction one quarter
89 			case 0xbd: return 0xbd; // vulgar fraction one half
90 			case 0xbe: return 0xbe; // vulgar fraction three quarters
91 			case 0xbf: return 0xbf; // inverted question mark
92 			default: return 0;
93 		}
94 	}
95 	else if ((Uint8)text[0] == 0xc3)
96 	{
97 		switch((Uint8)text[1])
98 		{
99 			case 0x80: return 0xc0; // latin capital letter a with grave
100 			case 0x81: return 0xc1; // latin capital letter a with acute
101 			case 0x82: return 0xc2; // latin capital letter a with circumflex
102 			case 0x83: return 0xc3; // latin capital letter a with tilde
103 			case 0x84: return 0xc4; // latin capital letter a with diaeresis
104 			case 0x85: return 0xc5; // latin capital letter a with ring above
105 			case 0x86: return 0xc6; // latin capital letter ae
106 			case 0x87: return 0xc7; // latin capital letter c with cedilla
107 			case 0x88: return 0xc8; // latin capital letter e with grave
108 			case 0x89: return 0xc9; // latin capital letter e with acute
109 			case 0x8a: return 0xca; // latin capital letter e with circumflex
110 			case 0x8b: return 0xcb; // latin capital letter e with diaeresis
111 			case 0x8c: return 0xcc; // latin capital letter i with grave
112 			case 0x8d: return 0xcd; // latin capital letter i with acute
113 			case 0x8e: return 0xce; // latin capital letter i with circumflex
114 			case 0x8f: return 0xcf; // latin capital letter i with diaeresis
115 			case 0x90: return 0xd0; // latin capital letter eth
116 			case 0x91: return 0xd1; // latin capital letter n with tilde
117 			case 0x92: return 0xd2; // latin capital letter o with grave
118 			case 0x93: return 0xd3; // latin capital letter o with acute
119 			case 0x94: return 0xd4; // latin capital letter o with circumflex
120 			case 0x95: return 0xd5; // latin capital letter o with tilde
121 			case 0x96: return 0xd6; // latin capital letter o with diaeresis
122 			case 0x97: return 0xd7; // multiplication sign
123 			case 0x98: return 0xd8; // latin capital letter o with stroke
124 			case 0x99: return 0xd9; // latin capital letter u with grave
125 			case 0x9a: return 0xda; // latin capital letter u with acute
126 			case 0x9b: return 0xdb; // latin capital letter u with circumflex
127 			case 0x9c: return 0xdc; // latin capital letter u with diaeresis
128 			case 0x9d: return 0xdd; // latin capital letter y with acute
129 			case 0x9e: return 0xde; // latin capital letter thorn
130 			case 0x9f: return 0xdf; // latin small letter sharp s
131 			case 0xa0: return 0xe0; // latin small letter a with grave
132 			case 0xa1: return 0xe1; // latin small letter a with acute
133 			case 0xa2: return 0xe2; // latin small letter a with circumflex
134 			case 0xa3: return 0xe3; // latin small letter a with tilde
135 			case 0xa4: return 0xe4; // latin small letter a with diaeresis
136 			case 0xa5: return 0xe5; // latin small letter a with ring above
137 			case 0xa6: return 0xe6; // latin small letter ae
138 			case 0xa7: return 0xe7; // latin small letter c with cedilla
139 			case 0xa8: return 0xe8; // latin small letter e with grave
140 			case 0xa9: return 0xe9; // latin small letter e with acute
141 			case 0xaa: return 0xea; // latin small letter e with circumflex
142 			case 0xab: return 0xeb; // latin small letter e with diaeresis
143 			case 0xac: return 0xec; // latin small letter i with grave
144 			case 0xad: return 0xed; // latin small letter i with acute
145 			case 0xae: return 0xee; // latin small letter i with circumflex
146 			case 0xaf: return 0xef; // latin small letter i with diaeresis
147 			case 0xb0: return 0xf0; // latin small letter eth
148 			case 0xb1: return 0xf1; // latin small letter n with tilde
149 			case 0xb2: return 0xf2; // latin small letter o with grave
150 			case 0xb3: return 0xf3; // latin small letter o with acute
151 			case 0xb4: return 0xf4; // latin small letter o with circumflex
152 			case 0xb5: return 0xf5; // latin small letter o with tilde
153 			case 0xb6: return 0xf6; // latin small letter o with diaeresis
154 			case 0xb7: return 0xf7; // division sign
155 			case 0xb8: return 0xf8; // latin small letter o with stroke
156 			case 0xb9: return 0xf9; // latin small letter u with grave
157 			case 0xba: return 0xfa; // latin small letter u with acute
158 			case 0xbb: return 0xfb; // latin small letter u with circumflex
159 			case 0xbc: return 0xfc; // latin small letter u with diaeresis
160 			case 0xbd: return 0xfd; // latin small letter y with acute
161 			case 0xbe: return 0xfe; // latin small letter thorn
162 			case 0xbf: return 0xff; // latin small letter y with diaeresis
163 			default: return 0;
164 		}
165 	}
166 	else
167 		return 0;
168 }
169 
170 // Make sure minimised and restored window state is noticed
171 // On windows as least, the minimise event is sometimes not seen
172 // Called from the 500 ms timer in draw_scene()
check_minimised_or_restore_window(void)173 void check_minimised_or_restore_window(void)
174 {
175 	Uint32 flags = SDL_GetWindowFlags(el_gl_window);
176 	if (flags & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED))
177 	{
178 		if (max_fps != min_fps)
179 			enter_minimised_state();
180 	}
181 	else if (flags & (SDL_WINDOW_SHOWN | SDL_WINDOW_MAXIMIZED))
182 	{
183 		if (max_fps != limit_fps)
184 			leave_minimised_state();
185 	}
186 }
187 
HandleEvent(SDL_Event * event)188 int HandleEvent (SDL_Event *event)
189 {
190 	int done = 0;
191 	int mouse_delta_x = 0;
192 	int mouse_delta_y = 0;
193 	Uint32 flags = KMOD_NONE;
194 	SDL_Keymod  mod_key_status = 0;
195 	Uint8 unicode = '\0';
196 	static Uint32 last_loss = 0;
197 	static Uint32 last_gain = 0;
198 	static Uint32 last_SDL_KEYDOWN_timestamp = 0;
199 	static Uint32 last_SDL_KEYDOWN_return_value = 0;
200 	static int el_input_focus = 1;
201 
202 	if (event->type == SDL_FIRSTEVENT) return 0;
203 
204 	mod_key_status = SDL_GetModState();
205 	flags |= (mod_key_status & KMOD_SHIFT) | (mod_key_status & KMOD_ALT) | (mod_key_status & KMOD_CTRL);
206 
207 	switch( event->type )
208 	{
209 
210 #if !defined(WINDOWS) && !defined(OSX)
211 		case SDL_SYSWMEVENT:
212 			if (event->syswm.msg->msg.x11.event.type == SelectionNotify)
213 				finishpaste(event->syswm.msg->msg.x11.event.xselection);
214 			else if (event->syswm.msg->msg.x11.event.type == SelectionRequest)
215 				process_copy(&event->syswm.msg->msg.x11.event.xselectionrequest);
216 			break;
217 #endif
218 
219 		case SDL_WINDOWEVENT:
220 			switch (event->window.event) {
221 				case SDL_WINDOWEVENT_HIDDEN:
222 				case SDL_WINDOWEVENT_MINIMIZED:
223 					if (clear_mod_keys_on_focus)
224 						last_loss = SDL_GetTicks();
225 					if (max_fps != min_fps)
226 						enter_minimised_state();
227 					break;
228 				case SDL_WINDOWEVENT_SHOWN:
229 				case SDL_WINDOWEVENT_EXPOSED:
230 				case SDL_WINDOWEVENT_MAXIMIZED:
231 				case SDL_WINDOWEVENT_RESTORED:
232 					if (last_loss && ((SDL_GetTicks() - last_loss) > 250))
233 					{
234 						last_loss = 0;
235 						SDL_SetModState(KMOD_NONE);
236 					}
237 					last_gain = SDL_GetTicks();
238 					if (max_fps != limit_fps)
239 						leave_minimised_state();
240 					break;
241 				case SDL_WINDOWEVENT_LEAVE:
242 				case SDL_WINDOWEVENT_FOCUS_LOST:
243 					if (clear_mod_keys_on_focus)
244 						last_loss = SDL_GetTicks();
245 					el_input_focus = 0;
246 					break;
247 				case SDL_WINDOWEVENT_ENTER:
248 				case SDL_WINDOWEVENT_FOCUS_GAINED:
249 					if (last_loss && ((SDL_GetTicks() - last_loss) > 250))
250 					{
251 						last_loss = 0;
252 						SDL_SetModState(KMOD_NONE);
253 					}
254 					last_gain = SDL_GetTicks();
255 					el_input_focus = 1;
256 					break;
257 				case SDL_WINDOWEVENT_SIZE_CHANGED:
258 				{
259 					Uint32 old_window_width = window_width, old_window_height = window_height;
260 					//printf("\nSDL_WINDOWEVENT_SIZE_CHANGED old=%dx%d new=%dx%d\n", old_window_width, old_window_height, event->window.data1, event->window.data2);
261 					update_window_size_and_scale();
262 					resize_all_root_windows(old_window_width, window_width, old_window_height, window_height);
263 					break;
264 				}
265 				default:
266 					//printf("untrapped SDL_WINDOWEVENT %x\n", event->window.event);
267 					break;
268 
269 			}
270 			break;
271 
272 		case SDL_TEXTEDITING:
273 			//printf("SDL_TEXTEDITING text=[%s] start=%d len=%d\n", (unsigned char *)event->edit.text, event->edit.start, event->edit.length);
274 			break;
275 
276 		case SDL_TEXTINPUT:
277 			if (afk_time) // if enabled...
278 				last_action_time = cur_time;  // reset the AFK timer
279 			cm_post_show_check(1); // forces any context menu to close
280 			unicode = utf8_to_unicode(event->text.text);
281 			//printf("SDL_TEXTINPUT text=[%s] len=%lu,%lu timestamp=%u\n", (unsigned char *)event->text.text, sizeof(event->text.text), strlen(event->text.text), event->key.timestamp);
282 			//printf("UTF-8 udf8=(%x,%x) unicode=%x\n", event->text.text[0], event->text.text[1], unicode);
283 			if (unicode)
284 			{
285 				if (((event->key.timestamp - last_SDL_KEYDOWN_timestamp) > 10) || (last_SDL_KEYDOWN_return_value == -1))
286 					keypress_in_windows (mouse_x, mouse_y, SDLK_UNKNOWN, unicode, KMOD_NONE);
287 			}
288 			break;
289 
290 		case SDL_KEYDOWN:
291 			// Don't let the modifiers GUI, ALT, CTRL and SHIFT change the state if only the key pressed
292 			if ((event->key.keysym.sym != SDLK_LSHIFT) && (event->key.keysym.sym != SDLK_RSHIFT) &&
293 				(event->key.keysym.sym != SDLK_LCTRL) && (event->key.keysym.sym != SDLK_RCTRL) &&
294 				(event->key.keysym.sym != SDLK_LALT) && (event->key.keysym.sym != SDLK_RALT) &&
295 				(event->key.keysym.sym != SDLK_LGUI) && (event->key.keysym.sym != SDLK_RGUI))
296 			{
297 				if (afk_time) // if enabled...
298 					last_action_time = cur_time;  // reset the AFK timer
299 				cm_post_show_check(1); // forces any context menu to close
300 			}
301 			// Don't use a TAB key dangling from system window switching.  By default this would toggle the map window.
302 			if (last_gain && (event->key.keysym.sym == SDLK_TAB) && ((SDL_GetTicks() - last_gain) < 50))
303 				break;
304 			last_gain = 0;
305 			//printf("SDL_KEYDOWN keycode=%u,[%s] mod=%u timestamp=%u\n", event->key.keysym.sym, SDL_GetKeyName(event->key.keysym.sym), event->key.keysym.mod, event->key.timestamp);
306 			last_SDL_KEYDOWN_timestamp = event->key.timestamp;
307 			last_SDL_KEYDOWN_return_value = keypress_in_windows (mouse_x, mouse_y, event->key.keysym.sym, 0, event->key.keysym.mod);
308 			//printf("SDL_KEYDOWN result=%d\n", last_SDL_KEYDOWN_return_value);
309 			break;
310 
311 		case SDL_QUIT:
312 		case SDL_WINDOWEVENT_CLOSE:
313 			//printf("SDL_QUIT\n");
314 			done = 1;
315 			break;
316 
317 		case SDL_MOUSEBUTTONDOWN:
318 		case SDL_MOUSEBUTTONUP:
319 			// make sure the mouse button is our window, or else we ignore it
320 			//Checking if we have keyboard focus for a mouse click is wrong, but SDL doesn't care to tell us we have mouse focus when someone alt-tabs back in and the mouse was within bounds of both other and EL windows. Blech.
321 			if(event->button.x >= window_width || event->button.y >= window_height || !((SDL_GetWindowFlags(el_gl_window) & SDL_WINDOW_MOUSE_FOCUS) || el_input_focus))
322 			{
323 				break;
324 			}
325 
326 			if (afk_time && event->type == SDL_MOUSEBUTTONDOWN)
327 				last_action_time = cur_time;	// Set the latest events - don't make mousemotion set the afk_time... (if you prefer that mouse motion sets/resets the afk_time, then move this one step below...
328 
329 			// fallthrough
330 		case SDL_MOUSEMOTION:
331 		case SDL_MOUSEWHEEL:
332 			if (have_mouse)
333 			{
334 				mouse_x = window_width/2;
335 				mouse_y = window_height/2;
336 				highdpi_scale(&mouse_x, &mouse_y);
337 
338 				mouse_delta_x= event->motion.xrel;
339 				mouse_delta_y= event->motion.yrel;
340 				highdpi_scale(&mouse_delta_x, &mouse_delta_y);
341 			}
342 			else if(event->type==SDL_MOUSEMOTION)
343 			{
344 				mouse_x = event->motion.x;
345 				mouse_y = event->motion.y;
346 				highdpi_scale(&mouse_x, &mouse_y);
347 
348 				mouse_delta_x = event->motion.xrel;
349 				mouse_delta_y = event->motion.yrel;
350 				highdpi_scale(&mouse_delta_x, &mouse_delta_y);
351 			}
352 			else if(event->type==SDL_MOUSEWHEEL)
353 			{
354 				SDL_GetMouseState(&mouse_x, &mouse_y);
355 				highdpi_scale(&mouse_x, &mouse_y);
356 			}
357 			else
358 			{
359 #ifdef NEW_CURSOR
360 				if (sdl_cursors)
361 				{
362 #endif // NEW_CURSOR
363 					mouse_x= event->button.x;
364 					mouse_y= event->button.y;
365 					highdpi_scale(&mouse_x, &mouse_y);
366 #ifdef NEW_CURSOR
367 				}
368 #endif // NEW_CURSOR
369 				mouse_delta_x= mouse_delta_y= 0;
370 			}
371 
372 			if (event->type == SDL_MOUSEBUTTONDOWN)
373 			{
374 				if (event->button.button == SDL_BUTTON_LEFT)
375 					left_click++;
376 				else if (event->button.button == SDL_BUTTON_RIGHT)
377 					right_click++;
378 				else if (event->button.button == SDL_BUTTON_MIDDLE)
379 					middle_click++;
380 			}
381 			else
382 			{
383 				if (event->type == SDL_MOUSEMOTION && (event->motion.state & SDL_BUTTON_LMASK))
384 				{
385 					left_click++;
386 				}
387 				else
388 				{
389 					if (left_click) end_drag_windows();
390 					left_click = 0;
391 				}
392 
393 				if (event->type == SDL_MOUSEMOTION && (event->motion.state & SDL_BUTTON_RMASK))
394 				{
395 					right_click++;
396 #ifdef OSX
397 					if (osx_right_mouse_cam)
398 					{
399 						have_mouse = 1;
400 					}
401 #endif
402 				}
403 				else
404 				{
405 					right_click = 0;
406 #ifdef OSX
407 					if (osx_right_mouse_cam)
408 					{
409 						have_mouse = 0;
410 					}
411 #endif
412 				}
413 
414 				if (event->type == SDL_MOUSEMOTION && ((event->motion.state & SDL_BUTTON_MMASK) || (mod_key_status & KMOD_GUI)))
415 				{
416 					middle_click++;
417 				}
418 				else
419 				{
420 					middle_click = 0;
421 				}
422 			}
423 
424 			if ((SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MMASK) || have_mouse)
425 			{
426 				camera_rotation_speed = camera_rotation_speed*0.5 + normal_camera_rotation_speed * mouse_delta_x*0.00025;
427 				camera_tilt_speed = camera_tilt_speed*0.5 + normal_camera_rotation_speed * mouse_delta_y*0.00025;
428 				camera_rotation_deceleration = normal_camera_deceleration*1E-3;
429 				camera_tilt_deceleration = normal_camera_deceleration*1E-3;
430 
431 				if (camera_rotation_speed > 1.0)
432 					camera_rotation_speed = 1.0;
433 				else if (camera_rotation_speed < -1.0)
434 					camera_rotation_speed = -1.0;
435 
436 				// the following variables have to be removed!
437 				camera_rotation_duration = 0;
438 				camera_tilt_duration = 0;
439 				if (fol_cam && !fol_cam_behind)
440 				{
441 					hold_camera += camera_kludge - last_kludge;
442 					last_kludge = camera_kludge;
443 				}
444 			}
445 
446 			if (left_click) flags |= ELW_LEFT_MOUSE;
447 			if (middle_click || (mod_key_status & KMOD_GUI)) flags |= ELW_MID_MOUSE;
448 			if (right_click) flags |= ELW_RIGHT_MOUSE;
449 			if (event->type == SDL_MOUSEWHEEL)
450 			{
451 				if (event->wheel.y > 0)
452 					flags |= ELW_WHEEL_UP;
453 				else if (event->wheel.y < 0)
454 					flags |= ELW_WHEEL_DOWN;
455 			}
456 
457 			if ( left_click == 1 || right_click == 1 || middle_click == 1 || (flags & (ELW_WHEEL_UP | ELW_WHEEL_DOWN) ) )
458 			{
459 				click_in_windows (mouse_x, mouse_y, flags);
460 			}
461 			if (left_click >= 1)
462 			{
463 				if (drag_windows (mouse_x, mouse_y, mouse_delta_x, mouse_delta_y) >= 0)
464 				{
465 					/* clicking title forces any context menu to close */
466 					cm_post_show_check(1);
467 					return done;
468 				}
469 				if (drag_in_windows (mouse_x, mouse_y, flags, mouse_delta_x, mouse_delta_y) >= 0)
470 				{
471 					return done;
472 				}
473 			}
474 			break;
475 
476 		case SDL_USEREVENT:
477 			switch(event->user.code){
478 			case	EVENT_MOVEMENT_TIMER:
479 				pf_move();
480 				break;
481 			case	EVENT_UPDATE_PARTICLES:
482 				update_particles();
483 				break;
484 
485 			case    EVENT_UPDATES_DOWNLOADED:
486 				handle_update_download((struct http_get_struct *)event->user.data1);
487 				break;
488 
489 			case    EVENT_DOWNLOAD_COMPLETE:
490 				handle_file_download((struct http_get_struct *)event->user.data1);
491 				break;
492 
493 #ifdef PAWN
494 			case 	EVENT_PAWN_TIMER:
495 				handle_pawn_timers ();
496 				break;
497 #endif
498 #ifdef	CUSTOM_UPDATE
499 			case    EVENT_CUSTOM_UPDATE_COMPLETE:
500 				unload_actor_texture_cache();
501 				break;
502 #endif	/* CUSTOM_UPDATE */
503 			}
504 			break;
505 
506 		default:
507 			//printf("untrapped event %x\n", event->type);
508 			break;
509 
510 	}
511 
512 	return(done);
513 }
514