432
432
433
433
if (sapp_mouse_locked()) { ... }
434
434
435
- On native platforms, the sapp_lock_mouse() and sapp_mouse_locked()
436
- functions work as expected (mouse lock is activated or deactivated
437
- immediately when sapp_lock_mouse() is called, and sapp_mouse_locked()
438
- also immediately returns the new state after sapp_lock_mouse()
439
- is called.
435
+ Note that mouse-lock state may not change immediately after sapp_lock_mouse(true/false)
436
+ is called, instead on some platforms the actual state switch may be delayed
437
+ to the end of the current frame or even to a later frame.
440
438
441
- On the web platform, sapp_lock_mouse() and sapp_mouse_locked() behave
442
- differently, as dictated by the limitations of the HTML5 Pointer Lock API:
439
+ The mouse may also be unlocked automatically without calling sapp_lock_mouse(false),
440
+ most notably when the application window becomes inactive.
441
+
442
+ On the web platform there are further restrictions to be aware of, caused
443
+ by the limitations of the HTML5 Pointer Lock API:
443
444
444
445
- sapp_lock_mouse(true) can be called at any time, but it will
445
446
only take effect in a 'short-lived input event handler of a specific
491
492
}
492
493
}
493
494
495
+ For a 'first person shooter mouse' the following code inside the sokol-app event handler
496
+ is recommended somewhere in your frame callback:
497
+
498
+ if (!sapp_mouse_locked()) {
499
+ sapp_lock_mouse(true);
500
+ }
501
+
494
502
CLIPBOARD SUPPORT
495
503
=================
496
504
Applications can send and receive UTF-8 encoded text data from and to the
@@ -2631,18 +2639,24 @@ typedef struct {
2631
2639
HICON small_icon ;
2632
2640
HCURSOR cursors [_SAPP_MOUSECURSOR_NUM ];
2633
2641
UINT orig_codepage ;
2634
- LONG mouse_locked_x , mouse_locked_y ;
2635
- bool mouse_locked_pos_valid ;
2636
2642
RECT stored_window_rect ; // used to restore window pos/size when toggling fullscreen => windowed
2637
2643
bool is_win10_or_greater ;
2638
2644
bool in_create_window ;
2639
2645
bool iconified ;
2640
- bool mouse_tracked ;
2641
- uint8_t mouse_capture_mask ;
2642
2646
_sapp_win32_dpi_t dpi ;
2643
- bool raw_input_mousepos_valid ;
2644
- LONG raw_input_mousepos_x ;
2645
- LONG raw_input_mousepos_y ;
2647
+ struct {
2648
+ struct {
2649
+ LONG pos_x , pos_y ;
2650
+ bool pos_valid ;
2651
+ } lock ;
2652
+ struct {
2653
+ LONG pos_x , pos_y ;
2654
+ bool pos_valid ;
2655
+ } raw_input ;
2656
+ bool requested_lock ;
2657
+ bool tracked ;
2658
+ uint8_t capture_mask ;
2659
+ } mouse ;
2646
2660
uint8_t raw_input_data [256 ];
2647
2661
} _sapp_win32_t ;
2648
2662
@@ -7194,16 +7208,16 @@ _SOKOL_PRIVATE void _sapp_win32_update_cursor(sapp_mouse_cursor cursor, bool sho
7194
7208
}
7195
7209
7196
7210
_SOKOL_PRIVATE void _sapp_win32_capture_mouse (uint8_t btn_mask ) {
7197
- if (0 == _sapp .win32 .mouse_capture_mask ) {
7211
+ if (0 == _sapp .win32 .mouse . capture_mask ) {
7198
7212
SetCapture (_sapp .win32 .hwnd );
7199
7213
}
7200
- _sapp .win32 .mouse_capture_mask |= btn_mask ;
7214
+ _sapp .win32 .mouse . capture_mask |= btn_mask ;
7201
7215
}
7202
7216
7203
7217
_SOKOL_PRIVATE void _sapp_win32_release_mouse (uint8_t btn_mask ) {
7204
- if (0 != _sapp .win32 .mouse_capture_mask ) {
7205
- _sapp .win32 .mouse_capture_mask &= ~btn_mask ;
7206
- if (0 == _sapp .win32 .mouse_capture_mask ) {
7218
+ if (0 != _sapp .win32 .mouse . capture_mask ) {
7219
+ _sapp .win32 .mouse . capture_mask &= ~btn_mask ;
7220
+ if (0 == _sapp .win32 .mouse . capture_mask ) {
7207
7221
ReleaseCapture ();
7208
7222
}
7209
7223
}
@@ -7214,76 +7228,107 @@ _SOKOL_PRIVATE bool _sapp_win32_is_foreground_window(void) {
7214
7228
}
7215
7229
7216
7230
_SOKOL_PRIVATE void _sapp_win32_lock_mouse (bool lock ) {
7217
- if (lock == _sapp .mouse .locked ) {
7218
- return ;
7231
+ _sapp .win32 .mouse .requested_lock = lock ;
7232
+ }
7233
+
7234
+ _SOKOL_PRIVATE void _sapp_win32_do_lock_mouse (void ) {
7235
+ _sapp .mouse .locked = true;
7236
+
7237
+ // hide mouse cursor (NOTE: this maintains a hidden counter, but since
7238
+ // only mouse-lock uses ShowCursor this doesn't matter)
7239
+ ShowCursor (FALSE);
7240
+
7241
+ // reset dx/dy and release any active mouse capture
7242
+ _sapp .mouse .dx = 0.0f ;
7243
+ _sapp .mouse .dy = 0.0f ;
7244
+ _sapp_win32_release_mouse (0xFF );
7245
+
7246
+ // store current mouse position so that it can be restored when unlocked
7247
+ POINT pos ;
7248
+ if (GetCursorPos (& pos )) {
7249
+ _sapp .win32 .mouse .lock .pos_valid = true;
7250
+ _sapp .win32 .mouse .lock .pos_x = pos .x ;
7251
+ _sapp .win32 .mouse .lock .pos_y = pos .y ;
7252
+ } else {
7253
+ _sapp .win32 .mouse .lock .pos_valid = false;
7254
+ }
7255
+
7256
+ // while mouse is locked, restrict cursor movement to the client
7257
+ // rectangle so that we don't loose any mouse movement events
7258
+ RECT client_rect ;
7259
+ GetClientRect (_sapp .win32 .hwnd , & client_rect );
7260
+ POINT mid_point ;
7261
+ mid_point .x = (client_rect .right - client_rect .left ) / 2 ;
7262
+ mid_point .y = (client_rect .bottom - client_rect .top ) / 2 ;
7263
+ ClientToScreen (_sapp .win32 .hwnd , & mid_point );
7264
+ RECT clip_rect ;
7265
+ clip_rect .left = clip_rect .right = mid_point .x ;
7266
+ clip_rect .top = clip_rect .bottom = mid_point .y ;
7267
+ ClipCursor (& clip_rect );
7268
+
7269
+ // enable raw input for mouse, starts sending WM_INPUT messages to WinProc (see GLFW)
7270
+ const RAWINPUTDEVICE rid = {
7271
+ 0x01 , // usUsagePage: HID_USAGE_PAGE_GENERIC
7272
+ 0x02 , // usUsage: HID_USAGE_GENERIC_MOUSE
7273
+ 0 , // dwFlags
7274
+ _sapp .win32 .hwnd // hwndTarget
7275
+ };
7276
+ if (!RegisterRawInputDevices (& rid , 1 , sizeof (rid ))) {
7277
+ _SAPP_ERROR (WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK );
7219
7278
}
7279
+ // in case the raw mouse device only supports absolute position reporting,
7280
+ // we need to skip the dx/dy compution for the first WM_INPUT event
7281
+ _sapp .win32 .mouse .raw_input .pos_valid = false;
7282
+ }
7283
+
7284
+ _SOKOL_PRIVATE void _sapp_win32_do_unlock_mouse (void ) {
7285
+ _sapp .mouse .locked = false;
7286
+
7287
+ // make mouse cursor visible
7288
+ ShowCursor (TRUE);
7289
+
7290
+ // reset dx/dy and release any active mouse capture
7220
7291
_sapp .mouse .dx = 0.0f ;
7221
7292
_sapp .mouse .dy = 0.0f ;
7222
7293
_sapp_win32_release_mouse (0xFF );
7223
- if (lock ) {
7224
- // don't allow locking the mouse unless we're the active window
7225
- if (!_sapp_win32_is_foreground_window ()) {
7226
- return ;
7227
- }
7228
7294
7229
- _sapp .mouse .locked = true;
7230
- /* store the current mouse position, so it can be restored when unlocked */
7231
- POINT pos ;
7232
- BOOL res = GetCursorPos (& pos );
7233
- if (res ) {
7234
- _sapp .win32 .mouse_locked_x = pos .x ;
7235
- _sapp .win32 .mouse_locked_y = pos .y ;
7236
- _sapp .win32 .mouse_locked_pos_valid = true;
7295
+ // disable raw input for mouse
7296
+ const RAWINPUTDEVICE rid = { 0x01 , 0x02 , RIDEV_REMOVE , NULL };
7297
+ if (!RegisterRawInputDevices (& rid , 1 , sizeof (rid ))) {
7298
+ _SAPP_ERROR (WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK );
7299
+ }
7237
7300
7238
- /* while the mouse is locked, make the mouse cursor invisible and
7239
- confine the mouse movement to a small rectangle inside our window
7240
- (so that we don't miss any mouse up events)
7241
- */
7242
- RECT client_rect = {
7243
- _sapp .win32 .mouse_locked_x ,
7244
- _sapp .win32 .mouse_locked_y ,
7245
- _sapp .win32 .mouse_locked_x ,
7246
- _sapp .win32 .mouse_locked_y
7247
- };
7248
- ClipCursor (& client_rect );
7249
- } else {
7250
- _sapp .win32 .mouse_locked_pos_valid = false;
7251
- }
7301
+ // unrestrict mouse movement
7302
+ ClipCursor (NULL );
7252
7303
7253
- /* make the mouse cursor invisible, this will stack with sapp_show_mouse() */
7254
- ShowCursor (FALSE);
7304
+ // restore the 'pre-locked' mouse position
7305
+ if (_sapp .win32 .mouse .lock .pos_valid ) {
7306
+ SetCursorPos (_sapp .win32 .mouse .lock .pos_x , _sapp .win32 .mouse .lock .pos_y );
7307
+ _sapp .win32 .mouse .lock .pos_valid = false;
7308
+ }
7309
+ }
7255
7310
7256
- /* enable raw input for mouse, starts sending WM_INPUT messages to WinProc (see GLFW) */
7257
- const RAWINPUTDEVICE rid = {
7258
- 0x01 , // usUsagePage: HID_USAGE_PAGE_GENERIC
7259
- 0x02 , // usUsage: HID_USAGE_GENERIC_MOUSE
7260
- 0 , // dwFlags
7261
- _sapp .win32 .hwnd // hwndTarget
7262
- };
7263
- if (!RegisterRawInputDevices (& rid , 1 , sizeof (rid ))) {
7264
- _SAPP_ERROR (WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_LOCK );
7265
- }
7266
- /* in case the raw mouse device only supports absolute position reporting,
7267
- we need to skip the dx/dy compution for the first WM_INPUT event
7268
- */
7269
- _sapp .win32 .raw_input_mousepos_valid = false;
7270
- } else {
7271
- _sapp .mouse .locked = false;
7272
- /* disable raw input for mouse */
7273
- const RAWINPUTDEVICE rid = { 0x01 , 0x02 , RIDEV_REMOVE , NULL };
7274
- if (!RegisterRawInputDevices (& rid , 1 , sizeof (rid ))) {
7275
- _SAPP_ERROR (WIN32_REGISTER_RAW_INPUT_DEVICES_FAILED_MOUSE_UNLOCK );
7311
+ _SOKOL_PRIVATE void _sapp_win32_update_mouse_lock (void ) {
7312
+ // mouse lock can only be active when we're the active window
7313
+ if (!_sapp_win32_is_foreground_window ()) {
7314
+ // unlock mouse if currently locked
7315
+ if (_sapp .mouse .locked ) {
7316
+ _sapp_win32_do_unlock_mouse ();
7276
7317
}
7318
+ return ;
7319
+ }
7277
7320
7278
- /* let the mouse roam freely again */
7279
- ClipCursor (NULL );
7280
- ShowCursor (TRUE);
7321
+ // nothing to do if requested lock state matches current lock state
7322
+ const bool lock = _sapp .win32 .mouse .requested_lock ;
7323
+ if (lock == _sapp .mouse .locked ) {
7324
+ return ;
7325
+ }
7281
7326
7282
- /* restore the 'pre-locked' mouse position */
7283
- if (_sapp . win32 . mouse_locked_pos_valid ) {
7284
- SetCursorPos ( _sapp . win32 . mouse_locked_x , _sapp . win32 . mouse_locked_y );
7285
- _sapp . win32 . mouse_locked_pos_valid = false;
7286
- }
7327
+ // otherwise change into desired state
7328
+ if (lock ) {
7329
+ _sapp_win32_do_lock_mouse ( );
7330
+ } else {
7331
+ _sapp_win32_do_unlock_mouse ();
7287
7332
}
7288
7333
}
7289
7334
@@ -7582,8 +7627,8 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM
7582
7627
case WM_MOUSEMOVE :
7583
7628
if (!_sapp .mouse .locked ) {
7584
7629
_sapp_win32_mouse_update (lParam );
7585
- if (!_sapp .win32 .mouse_tracked ) {
7586
- _sapp .win32 .mouse_tracked = true;
7630
+ if (!_sapp .win32 .mouse . tracked ) {
7631
+ _sapp .win32 .mouse . tracked = true;
7587
7632
TRACKMOUSEEVENT tme ;
7588
7633
_sapp_clear (& tme , sizeof (tme ));
7589
7634
tme .cbSize = sizeof (tme );
@@ -7617,13 +7662,13 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM
7617
7662
*/
7618
7663
LONG new_x = raw_mouse_data -> data .mouse .lLastX ;
7619
7664
LONG new_y = raw_mouse_data -> data .mouse .lLastY ;
7620
- if (_sapp .win32 .raw_input_mousepos_valid ) {
7621
- _sapp .mouse .dx = (float ) (new_x - _sapp .win32 .raw_input_mousepos_x );
7622
- _sapp .mouse .dy = (float ) (new_y - _sapp .win32 .raw_input_mousepos_y );
7665
+ if (_sapp .win32 .mouse . raw_input . pos_valid ) {
7666
+ _sapp .mouse .dx = (float ) (new_x - _sapp .win32 .mouse . raw_input . pos_x );
7667
+ _sapp .mouse .dy = (float ) (new_y - _sapp .win32 .mouse . raw_input . pos_y );
7623
7668
}
7624
- _sapp .win32 .raw_input_mousepos_x = new_x ;
7625
- _sapp .win32 .raw_input_mousepos_y = new_y ;
7626
- _sapp .win32 .raw_input_mousepos_valid = true;
7669
+ _sapp .win32 .mouse . raw_input . pos_x = new_x ;
7670
+ _sapp .win32 .mouse . raw_input . pos_y = new_y ;
7671
+ _sapp .win32 .mouse . raw_input . pos_valid = true;
7627
7672
}
7628
7673
else {
7629
7674
/* mouse reports movement delta (this seems to be the common case) */
@@ -7638,7 +7683,7 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM
7638
7683
if (!_sapp .mouse .locked ) {
7639
7684
_sapp .mouse .dx = 0.0f ;
7640
7685
_sapp .mouse .dy = 0.0f ;
7641
- _sapp .win32 .mouse_tracked = false;
7686
+ _sapp .win32 .mouse . tracked = false;
7642
7687
_sapp_win32_mouse_event (SAPP_EVENTTYPE_MOUSE_LEAVE , SAPP_MOUSEBUTTON_INVALID );
7643
7688
}
7644
7689
break ;
@@ -8133,12 +8178,8 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) {
8133
8178
if (_sapp .quit_requested ) {
8134
8179
PostMessage (_sapp .win32 .hwnd , WM_CLOSE , 0 , 0 );
8135
8180
}
8136
- // unlock mouse if window doesn't have focus
8137
- if (_sapp .mouse .locked ) {
8138
- if (!_sapp_win32_is_foreground_window ()) {
8139
- _sapp_win32_lock_mouse (false);
8140
- }
8141
- }
8181
+ // update mouse-lock state
8182
+ _sapp_win32_update_mouse_lock ();
8142
8183
}
8143
8184
_sapp_call_cleanup ();
8144
8185
0 commit comments