Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /* |
2 | * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>. |
||
3 | * It is copyright by its individual contributors, as recorded in the |
||
4 | * project's Git history. See COPYING.txt at the top level for license |
||
5 | * terms and a link to the Git history. |
||
6 | */ |
||
7 | /* |
||
8 | * |
||
9 | * SDL mouse driver |
||
10 | * |
||
11 | */ |
||
12 | |||
13 | #include <string.h> |
||
14 | #include <SDL.h> |
||
15 | |||
16 | #include "maths.h" |
||
17 | #include "timer.h" |
||
18 | #include "event.h" |
||
19 | #include "window.h" |
||
20 | #include "mouse.h" |
||
21 | #include "playsave.h" |
||
22 | #include "dxxerror.h" |
||
23 | #include "args.h" |
||
24 | #include "gr.h" |
||
25 | |||
26 | namespace dcx { |
||
27 | |||
28 | namespace { |
||
29 | |||
30 | struct flushable_mouseinfo |
||
31 | { |
||
32 | int delta_x, delta_y, delta_z; |
||
33 | int z; |
||
34 | }; |
||
35 | |||
36 | struct mouseinfo : flushable_mouseinfo |
||
37 | { |
||
38 | int x,y; |
||
39 | int cursor_enabled; |
||
40 | fix64 cursor_time; |
||
41 | std::array<fix64, MOUSE_MAX_BUTTONS> time_lastpressed; |
||
42 | }; |
||
43 | |||
44 | } |
||
45 | |||
46 | static mouseinfo Mouse; |
||
47 | |||
48 | d_event_mousebutton::d_event_mousebutton(const event_type etype, const unsigned b) : |
||
49 | d_event{etype}, button(b) |
||
50 | { |
||
51 | } |
||
52 | |||
53 | void mouse_init(void) |
||
54 | { |
||
55 | Mouse = {}; |
||
56 | } |
||
57 | |||
58 | void mouse_close(void) |
||
59 | { |
||
60 | SDL_ShowCursor(SDL_ENABLE); |
||
61 | } |
||
62 | |||
63 | static window_event_result maybe_send_z_move(const unsigned button) |
||
64 | { |
||
65 | short dz; |
||
66 | if (button == MBTN_Z_UP) |
||
67 | { |
||
68 | Mouse.delta_z += Z_SENSITIVITY; |
||
69 | Mouse.z += Z_SENSITIVITY; |
||
70 | dz = Z_SENSITIVITY; |
||
71 | } |
||
72 | else if (button == MBTN_Z_DOWN) |
||
73 | { |
||
74 | Mouse.delta_z -= Z_SENSITIVITY; |
||
75 | Mouse.z -= Z_SENSITIVITY; |
||
76 | dz = -1*Z_SENSITIVITY; |
||
77 | } |
||
78 | else |
||
79 | return window_event_result::ignored; |
||
80 | const d_event_mouse_moved event{EVENT_MOUSE_MOVED, 0, 0, dz}; |
||
81 | return event_send(event); |
||
82 | } |
||
83 | |||
84 | static window_event_result send_singleclick(const bool pressed, const unsigned button) |
||
85 | { |
||
86 | const d_event_mousebutton event{pressed ? EVENT_MOUSE_BUTTON_DOWN : EVENT_MOUSE_BUTTON_UP, button}; |
||
87 | con_printf(CON_DEBUG, "Sending event EVENT_MOUSE_BUTTON_%s, button %d, coords %d,%d,%d", |
||
88 | pressed ? "DOWN" : "UP", event.button, Mouse.x, Mouse.y, Mouse.z); |
||
89 | return event_send(event); |
||
90 | } |
||
91 | |||
92 | static window_event_result maybe_send_doubleclick(const fix64 now, const unsigned button) |
||
93 | { |
||
94 | auto &when = Mouse.time_lastpressed[button]; |
||
95 | const auto then = when; |
||
96 | when = now; |
||
97 | if (now > then + F1_0/5) |
||
98 | return window_event_result::ignored; |
||
99 | const d_event_mousebutton event{EVENT_MOUSE_DOUBLE_CLICKED, button}; |
||
100 | con_printf(CON_DEBUG, "Sending event EVENT_MOUSE_DOUBLE_CLICKED, button %d, coords %d,%d", button, Mouse.x, Mouse.y); |
||
101 | return event_send(event); |
||
102 | } |
||
103 | |||
104 | window_event_result mouse_button_handler(const SDL_MouseButtonEvent *const mbe) |
||
105 | { |
||
106 | window_event_result highest_result(window_event_result::ignored); |
||
107 | |||
108 | if (unlikely(CGameArg.CtlNoMouse)) |
||
109 | return window_event_result::ignored; |
||
110 | // to bad, SDL buttons use a different mapping as descent expects, |
||
111 | // this is at least true and tested for the first three buttons |
||
112 | static const std::array<int, 17> button_remap{{ |
||
113 | MBTN_LEFT, |
||
114 | MBTN_MIDDLE, |
||
115 | MBTN_RIGHT, |
||
116 | MBTN_Z_UP, |
||
117 | MBTN_Z_DOWN, |
||
118 | MBTN_PITCH_BACKWARD, |
||
119 | MBTN_PITCH_FORWARD, |
||
120 | MBTN_BANK_LEFT, |
||
121 | MBTN_BANK_RIGHT, |
||
122 | MBTN_HEAD_LEFT, |
||
123 | MBTN_HEAD_RIGHT, |
||
124 | MBTN_11, |
||
125 | MBTN_12, |
||
126 | MBTN_13, |
||
127 | MBTN_14, |
||
128 | MBTN_15, |
||
129 | MBTN_16 |
||
130 | }}; |
||
131 | const unsigned button_idx = mbe->button - 1; // -1 since SDL seems to start counting at 1 |
||
132 | if (unlikely(button_idx >= button_remap.size())) |
||
133 | return window_event_result::ignored; |
||
134 | |||
135 | const auto now = timer_query(); |
||
136 | const auto button = button_remap[button_idx]; |
||
137 | const auto mbe_state = mbe->state; |
||
138 | Mouse.cursor_time = now; |
||
139 | |||
140 | const auto pressed = mbe_state != SDL_RELEASED; |
||
141 | if (pressed) { |
||
142 | highest_result = maybe_send_z_move(button); |
||
143 | } |
||
144 | highest_result = std::max(send_singleclick(pressed, button), highest_result); |
||
145 | //Double-click support |
||
146 | if (pressed) |
||
147 | { |
||
148 | highest_result = std::max(maybe_send_doubleclick(now, button), highest_result); |
||
149 | } |
||
150 | |||
151 | return highest_result; |
||
152 | } |
||
153 | |||
154 | window_event_result mouse_motion_handler(const SDL_MouseMotionEvent *const mme) |
||
155 | { |
||
156 | Mouse.cursor_time = timer_query(); |
||
157 | Mouse.x += mme->xrel; |
||
158 | Mouse.y += mme->yrel; |
||
159 | |||
160 | // z handled in mouse_button_handler |
||
161 | const d_event_mouse_moved event{EVENT_MOUSE_MOVED, mme->xrel, mme->yrel, 0}; |
||
162 | |||
163 | //con_printf(CON_DEBUG, "Sending event EVENT_MOUSE_MOVED, relative motion %d,%d,%d", |
||
164 | // event.dx, event.dy, event.dz); |
||
165 | return event_send(event); |
||
166 | } |
||
167 | |||
168 | void mouse_flush() // clears all mice events... |
||
169 | { |
||
170 | // event_poll(); |
||
171 | static_cast<flushable_mouseinfo &>(Mouse) = {}; |
||
172 | SDL_GetMouseState(&Mouse.x, &Mouse.y); // necessary because polling only gives us the delta. |
||
173 | } |
||
174 | |||
175 | //======================================================================== |
||
176 | void mouse_get_pos( int *x, int *y, int *z ) |
||
177 | { |
||
178 | //event_poll(); // Have to assume this is called in event_process, because event_poll can cause a window to close (depending on what the user does) |
||
179 | *x=Mouse.x; |
||
180 | *y=Mouse.y; |
||
181 | *z=Mouse.z; |
||
182 | } |
||
183 | |||
184 | window_event_result mouse_in_window(window *wind) |
||
185 | { |
||
186 | auto &canv = window_get_canvas(*wind); |
||
187 | return (static_cast<unsigned>(Mouse.x) - canv.cv_bitmap.bm_x <= canv.cv_bitmap.bm_w) && |
||
188 | (static_cast<unsigned>(Mouse.y) - canv.cv_bitmap.bm_y <= canv.cv_bitmap.bm_h) ? window_event_result::handled : window_event_result::ignored; |
||
189 | } |
||
190 | |||
191 | void mouse_get_delta( int *dx, int *dy, int *dz ) |
||
192 | { |
||
193 | *dz = Mouse.delta_z; |
||
194 | Mouse.delta_x = 0; |
||
195 | Mouse.delta_y = 0; |
||
196 | Mouse.delta_z = 0; |
||
197 | SDL_GetRelativeMouseState(dx, dy); |
||
198 | } |
||
199 | |||
200 | template <bool noactivate> |
||
201 | static void mouse_change_cursor() |
||
202 | { |
||
203 | Mouse.cursor_enabled = (!noactivate && !CGameArg.CtlNoMouse && !CGameArg.CtlNoCursor); |
||
204 | if (!Mouse.cursor_enabled) |
||
205 | SDL_ShowCursor(SDL_DISABLE); |
||
206 | } |
||
207 | |||
208 | void mouse_enable_cursor() |
||
209 | { |
||
210 | mouse_change_cursor<false>(); |
||
211 | } |
||
212 | |||
213 | void mouse_disable_cursor() |
||
214 | { |
||
215 | mouse_change_cursor<true>(); |
||
216 | } |
||
217 | |||
218 | // If we want to display/hide cursor, do so if not already and also hide it automatically after some time. |
||
219 | void mouse_cursor_autohide() |
||
220 | { |
||
221 | static fix64 hidden_time = 0; |
||
222 | |||
223 | const auto is_showing = SDL_ShowCursor(SDL_QUERY); |
||
224 | int result; |
||
225 | if (Mouse.cursor_enabled) |
||
226 | { |
||
227 | const auto now = timer_query(); |
||
228 | const auto cursor_time = Mouse.cursor_time; |
||
229 | const auto recent_cursor_time = cursor_time + (F1_0*2) >= now; |
||
230 | if (is_showing) |
||
231 | { |
||
232 | if (recent_cursor_time) |
||
233 | return; |
||
234 | hidden_time = now; |
||
235 | result = SDL_DISABLE; |
||
236 | } |
||
237 | else |
||
238 | { |
||
239 | if (!(recent_cursor_time && hidden_time + (F1_0/2) < now)) |
||
240 | return; |
||
241 | result = SDL_ENABLE; |
||
242 | } |
||
243 | } |
||
244 | else |
||
245 | { |
||
246 | if (!is_showing) |
||
247 | return; |
||
248 | result = SDL_DISABLE; |
||
249 | } |
||
250 | SDL_ShowCursor(result); |
||
251 | } |
||
252 | |||
253 | } |