Skip to content

Commit

Permalink
Create turn notifier for when the game is running for Windows (#6682)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Azzurite authored May 6, 2022
1 parent 822ce5e commit ceeb547
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 5 deletions.
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
}
3 changes: 3 additions & 0 deletions core/src/com/unciv/ui/worldscreen/WorldScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
}

Expand Down
3 changes: 2 additions & 1 deletion desktop/src/com/unciv/app/desktop/DesktopLauncher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
)

Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -15,4 +17,9 @@ class PlatformSpecificHelpersDesktop : GeneralPlatformSpecificHelpers {
false
}
}

override fun notifyTurnStarted() {
turnNotifier.turnStarted()
}

}

0 comments on commit ceeb547

Please sign in to comment.