Unreal steals input from Game Center login on Mac

When building a game to integrate with macOS, our team needed a way to allow users who are not logged in with Apple’s Game Center to do so. Unreal supports this on iOS, but not on Mac. We were able to create a gamecenter login window with custom Objective-C++ GameKit code without too much hassle.

However, we still had a lingering issue where the unreal game would steal focus/input from the login window whenever it popped up, making the feature unusable for anyone who wished to log into Game Center with anything other than their main AppleID.

I have a solution for this incoming–just throwing it on Answerhub for posterity :slight_smile:

1 Like

I have to admit I do not fully understand why or how Unreal is stealing focus here, but we did find a way to workaround it nonetheless. Here’s how we got around it:

  1. Make sure the mouse cursor is enabled by your playercontroller while the Game Center login window is up. The rest of this guide will be pretty useless if unreal is stealing you player’s mouse from them.

  2. You’ll need to constantly tell the application to make any GameCenter login-related window the “key” window of the app instead of the game (in Mac apps, the key window is brought to the foreground and receives all input). We did this with this custom, blueprint-accessible Objective-C++ function:

    /**

    • Call this to move any GameCenter-related sign-in windows into the foreground.
    • Unreal has a nasty habit of snatching focus from them, so we call this function
    • over and over until the user authenticates with Game Center.
      /
      void UMacOS::ForegroundGKWindow() {
      #if PLATFORM_MAC
      NSArray<NSWindow
      >* windows = [NSApp windows];
      for (NSWindow* window in windows) {
      // check if it’s a GKPanel by converting the window to a string
      // and checking its name. This is very nasty but as far as I can see
      // there is no public GKPanel class I can import
      if([[NSString stringWithFormat:@"%@", window] containsString:@“GKPanel”]) {
      NSLog(@“FOUND GKPANEL: %@ | %d”, window, [window isKeyWindow]);
      if (![window isKeyWindow]) {
      // any window manipulation has to run on the main thread
      dispatch_async(dispatch_get_main_queue(), ^
      {
      [(NSPanel*)window makeKeyAndOrderFront:nil];
      });
      }
      }
      }
      #endif
      }

Finally, we rigged up some blueprint to call this function a few times every second, so that if the user clicks and the game steals their input, the focus is immediately put back in the GKPanel:

In our case, we just made this run on a repeating timer in the short-lived level that call our gamecenter login handlers. If you game has more complicated game center integration, you may need a more complicated solution for regularly calling ForegroundGKWindow

The downside to this solution is it’s pretty dependent on Game Center implementation details, so if Apple massively changes how GC works in an update, this could break. If anyone has a cleaner solution, please let us know!

I’ve discovered why (thanks to the great work of another engineer):

The offending code lies in SlateApplication.cpp, in the function RoutePointerUpEvent:

#if PLATFORM_MAC
	// Make sure the application and its front window are activated if user wasn't drag & dropping between windows
	if (PointerEvent.GetEffectingButton() == EKeys::LeftMouseButton && !bIsDragDropping)
	{
		TSharedPtr<SWindow> ActiveWindow = GetActiveTopLevelWindow();
		if (ActiveWindow.IsValid() && !ActiveWindow->GetNativeWindow()->IsForegroundWindow() && !ActiveWindow->GetNativeWindow()->IsMinimized())
		{
			FPlatformApplicationMisc::ActivateApplication();

			if (!ActiveWindow->IsVirtualWindow())
			{
				ActiveWindow->BringToFront(true);
			}
		}
		else if ([NSApp keyWindow] == nullptr)
		{
			FPlatformApplicationMisc::ActivateApplication();
		}
	}
#endif

Essentially it seems like what this code is doing is checking if the Unreal window is the current foreground window or not. If it isn’t, then it’ll automatically re-focus it by bringing it to the front, which is why clicking on the Game Center dialog focuses it on the window, then Unreal again.

My explanation could be wrong though since I don’t have a super deep understanding of Slate code, but commenting out this block seems to make it work.