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