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 | } |