2
0
Эх сурвалжийг харах

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

Ilija Boshkov 8 жил өмнө
parent
commit
1005a56e5a

+ 2 - 1
core/os/input.cpp

@@ -38,7 +38,7 @@ Input *Input::get_singleton() {
 }
 
 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);
 }
 
@@ -87,6 +87,7 @@ void Input::_bind_methods() {
 	BIND_CONSTANT( MOUSE_MODE_VISIBLE );
 	BIND_CONSTANT( MOUSE_MODE_HIDDEN );
 	BIND_CONSTANT( MOUSE_MODE_CAPTURED );
+	BIND_CONSTANT( MOUSE_MODE_CONFINED );
 
 	ADD_SIGNAL( MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "connected")) );
 }

+ 2 - 1
core/os/input.h

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

+ 2 - 1
core/os/os.h

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

+ 30 - 11
platform/windows/os_windows.cpp

@@ -254,6 +254,25 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM	wParam,	LPARAM	lParam) {
 
 		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
 		{
 			minimized = HIWORD(wParam) != 0;
@@ -266,19 +285,17 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM	wParam,	LPARAM	lParam) {
 				alt_mem=false;
 				control_mem=false;
 				shift_mem=false;
-				if (mouse_mode==MOUSE_MODE_CAPTURED) {
+				if (mouse_mode==MOUSE_MODE_CAPTURED || mouse_mode==MOUSE_MODE_CONFINED) {
 					RECT clipRect;
 					GetClientRect(hWnd, &clipRect);
 					ClientToScreen(hWnd, (POINT*) &clipRect.left);
 					ClientToScreen(hWnd, (POINT*) &clipRect.right);
 					ClipCursor(&clipRect);
 					SetCapture(hWnd);
-
 				}
 			} else {
 				main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
 				alt_mem=false;
-
 			};
 
 			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();
 			if (IsPenEvent(extra)) {
@@ -376,7 +396,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM	wParam,	LPARAM	lParam) {
 			mm.button_mask|=(wParam&MK_XBUTTON2)?(1<<6):0;*/
 			mm.x=GET_X_LPARAM(lParam);
 			mm.y=GET_Y_LPARAM(lParam);
-
+			
 			if (mouse_mode==MOUSE_MODE_CAPTURED) {
 
 				Point2i c(video_mode.width/2,video_mode.height/2);
@@ -410,7 +430,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM	wParam,	LPARAM	lParam) {
 			mm.relative_y=mm.y-old_y;
 			old_x=mm.x;
 			old_y=mm.y;
-			if (main_loop)
+			if (window_has_focus && main_loop)
 				input->parse_input_event(event);
 
 
@@ -714,9 +734,8 @@ LRESULT OS_Windows::WndProc(HWND hWnd,UINT uMsg, WPARAM	wParam,	LPARAM	lParam) {
 			joypad->probe_joypads();
 		} break;
 		case WM_SETCURSOR: {
-
 			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
 					if(hCursor == 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;
     outside=true;
-
+	window_has_focus=true;
 	WNDCLASSEXW	wc;
 
 	video_mode=p_desired;
@@ -1326,17 +1345,17 @@ void OS_Windows::set_mouse_mode(MouseMode p_mode) {
 	if (mouse_mode==p_mode)
 		return;
 	mouse_mode=p_mode;
-	if (p_mode==MOUSE_MODE_CAPTURED) {
+	if (mouse_mode==MOUSE_MODE_CAPTURED || mouse_mode==MOUSE_MODE_CONFINED) {
 		RECT clipRect;
 		GetClientRect(hWnd, &clipRect);
 		ClientToScreen(hWnd, (POINT*) &clipRect.left);
 		ClientToScreen(hWnd, (POINT*) &clipRect.right);
 		ClipCursor(&clipRect);
-		SetCapture(hWnd);
 		center=Point2i(video_mode.width/2,video_mode.height/2);
 		POINT pos = { (int) center.x, (int) center.y };
 		ClientToScreen(hWnd, &pos);
-		SetCursorPos(pos.x, pos.y);
+		if (mouse_mode==MOUSE_MODE_CAPTURED)
+			SetCursorPos(pos.x, pos.y);
 	} else {
 		ReleaseCapture();
 		ClipCursor(NULL);

+ 1 - 0
platform/windows/os_windows.h

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

+ 40 - 19
platform/x11/os_x11.cpp

@@ -450,6 +450,8 @@ void OS_X11::initialize(const VideoMode& p_desired,int p_video_driver,int p_audi
 	physics_2d_server->init();
 
 	input = memnew( InputDefault );
+
+	window_has_focus = true; // Set focus to true at init
 #ifdef JOYDEV_ENABLED
 	joypad = memnew( JoypadLinux(input));
 #endif
@@ -518,17 +520,21 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) {
 	if (p_mode==mouse_mode)
 		return;
 
-	if (mouse_mode==MOUSE_MODE_CAPTURED)
+	if (mouse_mode==MOUSE_MODE_CAPTURED || mouse_mode==MOUSE_MODE_CONFINED)
 		XUngrabPointer(x11_display, CurrentTime);
-	if (mouse_mode!=MOUSE_MODE_VISIBLE && p_mode==MOUSE_MODE_VISIBLE)
-		XUndefineCursor(x11_display,x11_window);
-	if (p_mode!=MOUSE_MODE_VISIBLE && mouse_mode==MOUSE_MODE_VISIBLE) {
-		XDefineCursor(x11_display,x11_window,null_cursor);
+
+	// The only modes that show a cursor are VISIBLE and CONFINED
+	bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED);
+
+	if (showCursor) {
+		XUndefineCursor(x11_display,x11_window); // show cursor
+	} else {
+		XDefineCursor(x11_display,x11_window,null_cursor); // hide cursor
 	}
 
 	mouse_mode=p_mode;
 
-	if (mouse_mode==MOUSE_MODE_CAPTURED) {
+	if (mouse_mode==MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
 
 		while(true) {
 			//flush pending motion events
@@ -1254,6 +1260,10 @@ void OS_X11::process_xevents() {
 
 	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) {
 		XEvent event;
 		XNextEvent(x11_display, &event);
@@ -1272,35 +1282,45 @@ void OS_X11::process_xevents() {
 			minimized = (visibility->state == VisibilityFullyObscured);
 		} break;
 		case LeaveNotify: {
-
-			if (main_loop && mouse_mode!=MOUSE_MODE_CAPTURED)
+			if (main_loop && !mouse_mode_grab)
 				main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
 			if (input)
 				input->set_mouse_in_window(false);
 
 		} break;
 		case EnterNotify: {
-
-			if (main_loop && mouse_mode!=MOUSE_MODE_CAPTURED)
+			if (main_loop && !mouse_mode_grab)
 				main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
 			if (input)
 				input->set_mouse_in_window(true);
 		} break;
 		case FocusIn:
 			minimized = false;
+			window_has_focus = true;
 			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(
-						x11_display, x11_window, True,
+      			x11_display, x11_window, True,
 						ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
 						GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime);
 			}
 			break;
 
 		case FocusOut:
+			window_has_focus = false;
 			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.
+				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);
 			}
 			break;
@@ -1320,7 +1340,7 @@ void OS_X11::process_xevents() {
 
 			/* exit in case of a mouse button press */
 			last_timestamp=event.xbutton.time;
-			if (mouse_mode==MOUSE_MODE_CAPTURED) {
+			if (mouse_mode == MOUSE_MODE_CAPTURED) {
 				event.xbutton.x=last_mouse_pos.x;
 				event.xbutton.y=last_mouse_pos.y;
 			}
@@ -1343,7 +1363,6 @@ void OS_X11::process_xevents() {
 
 			mouse_event.mouse_button.pressed=(event.type==ButtonPress);
 
-
 			if (event.type==ButtonPress && event.xbutton.button==1) {
 
 				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
 			// MOUNTAIN BECAUSE THAT'S WHERE YOU BELONG.
 
-
 			while(true) {
 				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
@@ -1419,7 +1437,7 @@ void OS_X11::process_xevents() {
 				Point2i new_center = pos;
 				pos = last_mouse_pos + ( pos - center );
 				center=new_center;
-				do_mouse_warp=true;
+				do_mouse_warp=window_has_focus; // warp the cursor if we're focused in
 #else
 				//Dear X11, thanks for making my life miserable
 
@@ -1462,8 +1480,11 @@ void OS_X11::process_xevents() {
 			last_mouse_pos=pos;
 
 			// printf("rel: %d,%d\n", rel.x, rel.y );
-
-			input->parse_input_event( motion_event);
+			// 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);
 
 		} break;
 		case KeyPress:

+ 1 - 1
platform/x11/os_x11.h

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