Create turn notifier for when the game is running for Windows (#6682)

* Create turn notifier for when the game is running for Windows

If playing on Desktop, you often put the game into background, but still want to know if it's your turn. A standard Windows function for that is `FlashWindow` from winuser.h, which is implemented here

* Fix: Use the window from the listener instead of the static one from libGDL

* Only notify if it's the turn of the player that is playing

* Always notify spectators of the next players' turn

* Refactor: Move notifier into GeneralPlatformSpecificHelpers

* Only load Windows DLL when we're actually on Windows
This commit is contained in:
Timo T 2022-05-06 07:42:30 +02:00 committed by GitHub
parent 822ce5e041
commit ceeb547fb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 88 additions and 5 deletions

View File

@ -71,6 +71,9 @@ project(":desktop") {
"implementation"("com.github.MinnDevelopment:java-discord-rpc:v2.0.1")
"implementation"("net.java.dev.jna:jna:5.11.0")
"implementation"("net.java.dev.jna:jna-platform:5.11.0")
// For server-side
"implementation"("io.ktor:ktor-server-core:1.6.8")

View File

@ -11,7 +11,12 @@ interface GeneralPlatformSpecificHelpers {
* @param allow `true`: allow all orientations (follows sensor as limited by OS settings)
* `false`: allow only landscape orientations (both if supported, otherwise default landscape only)
*/
fun allowPortrait(allow: Boolean)
fun allowPortrait(allow: Boolean) {}
fun isInternetConnected(): Boolean
/**
* Notifies the user that it's their turn while the game is running
*/
fun notifyTurnStarted() {}
}

View File

@ -363,6 +363,9 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas
// stuff has changed and the "waiting for X" will now show the correct civ
stopMultiPlayerRefresher()
latestGame.isUpToDate = true
if (viewingCiv.civName == latestGame.currentPlayer || viewingCiv.civName == Constants.spectator) {
game.platformSpecificHelper?.notifyTurnStarted()
}
postCrashHandlingRunnable { createNewWorldScreen(latestGame) }
}

View File

@ -46,13 +46,14 @@ internal object DesktopLauncher {
UniqueDocsWriter().write()
}
val platformSpecificHelper = PlatformSpecificHelpersDesktop(config)
val desktopParameters = UncivGameParameters(
versionFromJar,
cancelDiscordEvent = { discordTimer?.cancel() },
fontImplementation = NativeFontDesktop(Fonts.ORIGINAL_FONT_SIZE.toInt(), settings.fontFamily),
customSaveLocationHelper = CustomSaveLocationHelperDesktop(),
crashReportSysInfo = CrashReportSysInfoDesktop(),
platformSpecificHelper = PlatformSpecificHelpersDesktop(),
platformSpecificHelper = platformSpecificHelper,
audioExceptionHelper = HardenGdxAudio()
)

View File

@ -0,0 +1,64 @@
package com.unciv.app.desktop
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Window
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3WindowAdapter
import com.sun.jna.Native
import com.sun.jna.Pointer
import com.sun.jna.platform.win32.User32
import com.sun.jna.platform.win32.WinNT
import com.sun.jna.platform.win32.WinUser
import org.lwjgl.glfw.GLFWNativeWin32
class MultiplayerTurnNotifierDesktop: Lwjgl3WindowAdapter() {
companion object {
val user32: User32? = try {
if (System.getProperty("os.name")?.contains("Windows") == true) {
Native.load(User32::class.java)
} else {
null
}
} catch (e: UnsatisfiedLinkError) {
println("Error while initializing turn notifier: " + e.message)
null
}
}
private var window: Lwjgl3Window? = null
private var hasFocus: Boolean = true
override fun created(window: Lwjgl3Window?) {
this.window = window
}
override fun focusLost() {
hasFocus = false
}
override fun focusGained() {
hasFocus = true
}
fun turnStarted() {
flashWindow()
}
/**
* See https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-flashwindowex
*
* We should've used FlashWindow instead of FlashWindowEx, but for some reason the former has no binding in Java's User32
*/
private fun flashWindow() {
try {
if (user32 == null || window == null || hasFocus) return
val flashwinfo = WinUser.FLASHWINFO()
val hwnd = GLFWNativeWin32.glfwGetWin32Window(window!!.windowHandle)
flashwinfo.hWnd = WinNT.HANDLE(Pointer.createConstant(hwnd))
flashwinfo.dwFlags = 3 // FLASHW_ALL
flashwinfo.uCount = 3
user32.FlashWindowEx(flashwinfo)
} catch (e: Throwable) {
/** try to ignore even if we get an [Error], just log it */
println("Error while notifying the user of their turn: " + e.message)
}
}
}

View File

@ -1,11 +1,13 @@
package com.unciv.app.desktop
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration
import com.unciv.ui.utils.GeneralPlatformSpecificHelpers
import java.net.InetAddress
class PlatformSpecificHelpersDesktop : GeneralPlatformSpecificHelpers {
override fun allowPortrait(allow: Boolean) {
// No need to do anything
class PlatformSpecificHelpersDesktop(config: Lwjgl3ApplicationConfiguration) : GeneralPlatformSpecificHelpers {
val turnNotifier = MultiplayerTurnNotifierDesktop()
init {
config.setWindowListener(turnNotifier);
}
override fun isInternetConnected(): Boolean {
@ -15,4 +17,9 @@ class PlatformSpecificHelpersDesktop : GeneralPlatformSpecificHelpers {
false
}
}
override fun notifyTurnStarted() {
turnNotifier.turnStarted()
}
}