Added focus tracking in X11 and Windows classes, added new confined mouse mode (#7162)

This commit is contained in:
Ilija Boshkov 2017-01-25 19:21:41 +01:00 committed by Rémi Verschelde
parent 4c28f35b2c
commit 1005a56e5a
7 changed files with 78 additions and 34 deletions

View File

@ -38,7 +38,7 @@ Input *Input::get_singleton() {
} }
void Input::set_mouse_mode(MouseMode p_mode) { void Input::set_mouse_mode(MouseMode p_mode) {
ERR_FAIL_INDEX(p_mode,3); ERR_FAIL_INDEX(p_mode,4);
OS::get_singleton()->set_mouse_mode((OS::MouseMode)p_mode); OS::get_singleton()->set_mouse_mode((OS::MouseMode)p_mode);
} }
@ -87,6 +87,7 @@ void Input::_bind_methods() {
BIND_CONSTANT( MOUSE_MODE_VISIBLE ); BIND_CONSTANT( MOUSE_MODE_VISIBLE );
BIND_CONSTANT( MOUSE_MODE_HIDDEN ); BIND_CONSTANT( MOUSE_MODE_HIDDEN );
BIND_CONSTANT( MOUSE_MODE_CAPTURED ); BIND_CONSTANT( MOUSE_MODE_CAPTURED );
BIND_CONSTANT( MOUSE_MODE_CONFINED );
ADD_SIGNAL( MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "connected")) ); ADD_SIGNAL( MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "connected")) );
} }

View File

@ -47,7 +47,8 @@ public:
enum MouseMode { enum MouseMode {
MOUSE_MODE_VISIBLE, MOUSE_MODE_VISIBLE,
MOUSE_MODE_HIDDEN, MOUSE_MODE_HIDDEN,
MOUSE_MODE_CAPTURED MOUSE_MODE_CAPTURED,
MOUSE_MODE_CONFINED
}; };
void set_mouse_mode(MouseMode p_mode); void set_mouse_mode(MouseMode p_mode);

View File

@ -131,7 +131,8 @@ public:
enum MouseMode { enum MouseMode {
MOUSE_MODE_VISIBLE, MOUSE_MODE_VISIBLE,
MOUSE_MODE_HIDDEN, MOUSE_MODE_HIDDEN,
MOUSE_MODE_CAPTURED MOUSE_MODE_CAPTURED,
MOUSE_MODE_CONFINED
}; };
virtual void set_mouse_mode(MouseMode p_mode); virtual void set_mouse_mode(MouseMode p_mode);

View File

@ -254,6 +254,25 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) // Check For Windows Messages switch (uMsg) // Check For Windows Messages
{ {
case WM_SETFOCUS:
{
window_has_focus = true;
// Re-capture cursor if we're in one of the capture modes
if (mouse_mode==MOUSE_MODE_CAPTURED || mouse_mode==MOUSE_MODE_CONFINED) {
SetCapture(hWnd);
}
break;
}
case WM_KILLFOCUS:
{
window_has_focus = false;
// Release capture if we're in one of the capture modes
if (mouse_mode==MOUSE_MODE_CAPTURED || mouse_mode==MOUSE_MODE_CONFINED) {
ReleaseCapture();
}
break;
}
case WM_ACTIVATE: // Watch For Window Activate Message case WM_ACTIVATE: // Watch For Window Activate Message
{ {
minimized = HIWORD(wParam) != 0; minimized = HIWORD(wParam) != 0;
@ -266,19 +285,17 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam) {
alt_mem=false; alt_mem=false;
control_mem=false; control_mem=false;
shift_mem=false; shift_mem=false;
if (mouse_mode==MOUSE_MODE_CAPTURED) { if (mouse_mode==MOUSE_MODE_CAPTURED || mouse_mode==MOUSE_MODE_CONFINED) {
RECT clipRect; RECT clipRect;
GetClientRect(hWnd, &clipRect); GetClientRect(hWnd, &clipRect);
ClientToScreen(hWnd, (POINT*) &clipRect.left); ClientToScreen(hWnd, (POINT*) &clipRect.left);
ClientToScreen(hWnd, (POINT*) &clipRect.right); ClientToScreen(hWnd, (POINT*) &clipRect.right);
ClipCursor(&clipRect); ClipCursor(&clipRect);
SetCapture(hWnd); SetCapture(hWnd);
} }
} else { } else {
main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
alt_mem=false; alt_mem=false;
}; };
return 0; // Return To The Message Loop return 0; // Return To The Message Loop
@ -345,6 +362,9 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam) {
} }
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
if (!window_has_focus && mouse_mode==MOUSE_MODE_CAPTURED)
break;
/* /*
LPARAM extra = GetMessageExtraInfo(); LPARAM extra = GetMessageExtraInfo();
if (IsPenEvent(extra)) { if (IsPenEvent(extra)) {
@ -410,7 +430,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam) {
mm.relative_y=mm.y-old_y; mm.relative_y=mm.y-old_y;
old_x=mm.x; old_x=mm.x;
old_y=mm.y; old_y=mm.y;
if (main_loop) if (window_has_focus && main_loop)
input->parse_input_event(event); input->parse_input_event(event);
@ -714,9 +734,8 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam) {
joypad->probe_joypads(); joypad->probe_joypads();
} break; } break;
case WM_SETCURSOR: { case WM_SETCURSOR: {
if(LOWORD(lParam) == HTCLIENT) { if(LOWORD(lParam) == HTCLIENT) {
if(mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED) { if(window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED)) {
//Hide the cursor //Hide the cursor
if(hCursor == NULL) if(hCursor == NULL)
hCursor = SetCursor(NULL); hCursor = SetCursor(NULL);
@ -948,7 +967,7 @@ void OS_Windows::initialize(const VideoMode& p_desired,int p_video_driver,int p_
main_loop=NULL; main_loop=NULL;
outside=true; outside=true;
window_has_focus=true;
WNDCLASSEXW wc; WNDCLASSEXW wc;
video_mode=p_desired; video_mode=p_desired;
@ -1326,16 +1345,16 @@ void OS_Windows::set_mouse_mode(MouseMode p_mode) {
if (mouse_mode==p_mode) if (mouse_mode==p_mode)
return; return;
mouse_mode=p_mode; mouse_mode=p_mode;
if (p_mode==MOUSE_MODE_CAPTURED) { if (mouse_mode==MOUSE_MODE_CAPTURED || mouse_mode==MOUSE_MODE_CONFINED) {
RECT clipRect; RECT clipRect;
GetClientRect(hWnd, &clipRect); GetClientRect(hWnd, &clipRect);
ClientToScreen(hWnd, (POINT*) &clipRect.left); ClientToScreen(hWnd, (POINT*) &clipRect.left);
ClientToScreen(hWnd, (POINT*) &clipRect.right); ClientToScreen(hWnd, (POINT*) &clipRect.right);
ClipCursor(&clipRect); ClipCursor(&clipRect);
SetCapture(hWnd);
center=Point2i(video_mode.width/2,video_mode.height/2); center=Point2i(video_mode.width/2,video_mode.height/2);
POINT pos = { (int) center.x, (int) center.y }; POINT pos = { (int) center.x, (int) center.y };
ClientToScreen(hWnd, &pos); ClientToScreen(hWnd, &pos);
if (mouse_mode==MOUSE_MODE_CAPTURED)
SetCursorPos(pos.x, pos.y); SetCursorPos(pos.x, pos.y);
} else { } else {
ReleaseCapture(); ReleaseCapture();

View File

@ -118,6 +118,7 @@ class OS_Windows : public OS {
bool control_mem; bool control_mem;
bool meta_mem; bool meta_mem;
bool force_quit; bool force_quit;
bool window_has_focus;
uint32_t last_button_state; uint32_t last_button_state;
CursorShape cursor_shape; CursorShape cursor_shape;

View File

@ -450,6 +450,8 @@ void OS_X11::initialize(const VideoMode& p_desired,int p_video_driver,int p_audi
physics_2d_server->init(); physics_2d_server->init();
input = memnew( InputDefault ); input = memnew( InputDefault );
window_has_focus = true; // Set focus to true at init
#ifdef JOYDEV_ENABLED #ifdef JOYDEV_ENABLED
joypad = memnew( JoypadLinux(input)); joypad = memnew( JoypadLinux(input));
#endif #endif
@ -518,17 +520,21 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) {
if (p_mode==mouse_mode) if (p_mode==mouse_mode)
return; return;
if (mouse_mode==MOUSE_MODE_CAPTURED) if (mouse_mode==MOUSE_MODE_CAPTURED || mouse_mode==MOUSE_MODE_CONFINED)
XUngrabPointer(x11_display, CurrentTime); XUngrabPointer(x11_display, CurrentTime);
if (mouse_mode!=MOUSE_MODE_VISIBLE && p_mode==MOUSE_MODE_VISIBLE)
XUndefineCursor(x11_display,x11_window); // The only modes that show a cursor are VISIBLE and CONFINED
if (p_mode!=MOUSE_MODE_VISIBLE && mouse_mode==MOUSE_MODE_VISIBLE) { bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED);
XDefineCursor(x11_display,x11_window,null_cursor);
if (showCursor) {
XUndefineCursor(x11_display,x11_window); // show cursor
} else {
XDefineCursor(x11_display,x11_window,null_cursor); // hide cursor
} }
mouse_mode=p_mode; mouse_mode=p_mode;
if (mouse_mode==MOUSE_MODE_CAPTURED) { if (mouse_mode==MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
while(true) { while(true) {
//flush pending motion events //flush pending motion events
@ -1254,6 +1260,10 @@ void OS_X11::process_xevents() {
do_mouse_warp=false; do_mouse_warp=false;
// Is the current mouse mode one where it needs to be grabbed.
bool mouse_mode_grab = mouse_mode==MOUSE_MODE_CAPTURED || mouse_mode==MOUSE_MODE_CONFINED;
while (XPending(x11_display) > 0) { while (XPending(x11_display) > 0) {
XEvent event; XEvent event;
XNextEvent(x11_display, &event); XNextEvent(x11_display, &event);
@ -1272,24 +1282,29 @@ void OS_X11::process_xevents() {
minimized = (visibility->state == VisibilityFullyObscured); minimized = (visibility->state == VisibilityFullyObscured);
} break; } break;
case LeaveNotify: { case LeaveNotify: {
if (main_loop && !mouse_mode_grab)
if (main_loop && mouse_mode!=MOUSE_MODE_CAPTURED)
main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT); main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
if (input) if (input)
input->set_mouse_in_window(false); input->set_mouse_in_window(false);
} break; } break;
case EnterNotify: { case EnterNotify: {
if (main_loop && !mouse_mode_grab)
if (main_loop && mouse_mode!=MOUSE_MODE_CAPTURED)
main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER); main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
if (input) if (input)
input->set_mouse_in_window(true); input->set_mouse_in_window(true);
} break; } break;
case FocusIn: case FocusIn:
minimized = false; minimized = false;
window_has_focus = true;
main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
if (mouse_mode==MOUSE_MODE_CAPTURED) { if (mouse_mode_grab) {
// Show and update the cursor if confined and the window regained focus.
if (mouse_mode==MOUSE_MODE_CONFINED)
XUndefineCursor(x11_display, x11_window);
else if (mouse_mode==MOUSE_MODE_CAPTURED) // or re-hide it in captured mode
XDefineCursor(x11_display, x11_window, null_cursor);
XGrabPointer( XGrabPointer(
x11_display, x11_window, True, x11_display, x11_window, True,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask, ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
@ -1298,9 +1313,14 @@ void OS_X11::process_xevents() {
break; break;
case FocusOut: case FocusOut:
window_has_focus = false;
main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
if (mouse_mode==MOUSE_MODE_CAPTURED) { if (mouse_mode_grab) {
//dear X11, I try, I really try, but you never work, you do whathever you want. //dear X11, I try, I really try, but you never work, you do whathever you want.
if (mouse_mode==MOUSE_MODE_CAPTURED) {
// Show the cursor if we're in captured mode so it doesn't look weird.
XUndefineCursor(x11_display, x11_window);
}
XUngrabPointer(x11_display, CurrentTime); XUngrabPointer(x11_display, CurrentTime);
} }
break; break;
@ -1343,7 +1363,6 @@ void OS_X11::process_xevents() {
mouse_event.mouse_button.pressed=(event.type==ButtonPress); mouse_event.mouse_button.pressed=(event.type==ButtonPress);
if (event.type==ButtonPress && event.xbutton.button==1) { if (event.type==ButtonPress && event.xbutton.button==1) {
uint64_t diff = get_ticks_usec()/1000 - last_click_ms; uint64_t diff = get_ticks_usec()/1000 - last_click_ms;
@ -1377,7 +1396,6 @@ void OS_X11::process_xevents() {
// PLEASE DO ME A FAVOR AND DIE DROWNED IN A FECAL // PLEASE DO ME A FAVOR AND DIE DROWNED IN A FECAL
// MOUNTAIN BECAUSE THAT'S WHERE YOU BELONG. // MOUNTAIN BECAUSE THAT'S WHERE YOU BELONG.
while(true) { while(true) {
if (mouse_mode==MOUSE_MODE_CAPTURED && event.xmotion.x==current_videomode.width/2 && event.xmotion.y==current_videomode.height/2) { if (mouse_mode==MOUSE_MODE_CAPTURED && event.xmotion.x==current_videomode.width/2 && event.xmotion.y==current_videomode.height/2) {
//this is likely the warp event since it was warped here //this is likely the warp event since it was warped here
@ -1419,7 +1437,7 @@ void OS_X11::process_xevents() {
Point2i new_center = pos; Point2i new_center = pos;
pos = last_mouse_pos + ( pos - center ); pos = last_mouse_pos + ( pos - center );
center=new_center; center=new_center;
do_mouse_warp=true; do_mouse_warp=window_has_focus; // warp the cursor if we're focused in
#else #else
//Dear X11, thanks for making my life miserable //Dear X11, thanks for making my life miserable
@ -1462,7 +1480,10 @@ void OS_X11::process_xevents() {
last_mouse_pos=pos; last_mouse_pos=pos;
// printf("rel: %d,%d\n", rel.x, rel.y ); // printf("rel: %d,%d\n", rel.x, rel.y );
// Don't propagate the motion event unless we have focus
// this is so that the relative motion doesn't get messed up
// after we regain focus.
if (window_has_focus || !mouse_mode_grab)
input->parse_input_event( motion_event); input->parse_input_event( motion_event);
} break; } break;

View File

@ -134,7 +134,7 @@ class OS_X11 : public OS_Unix {
bool force_quit; bool force_quit;
bool minimized; bool minimized;
bool window_has_focus;
bool do_mouse_warp; bool do_mouse_warp;
const char *cursor_theme; const char *cursor_theme;