Skip to content

Instantly share code, notes, and snippets.

@RChehowski
Created October 10, 2019 20:17

Revisions

  1. RChehowski created this gist Oct 10, 2019.
    101 changes: 101 additions & 0 deletions WindowsFlash.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,101 @@
    package com.vizor;

    import com.sun.jna.Pointer;
    import com.sun.jna.platform.win32.User32;
    import com.sun.jna.platform.win32.WinNT.HANDLE;
    import com.sun.jna.platform.win32.WinUser;
    import javafx.stage.Stage;

    import java.lang.invoke.MethodHandle;
    import java.lang.invoke.MethodHandles;
    import java.lang.invoke.MethodType;
    import java.util.List;
    import java.util.Optional;

    import static com.sun.jna.platform.win32.WinUser.FLASHW_ALL;
    import static com.sun.jna.platform.win32.WinUser.FLASHW_TIMERNOFG;

    public class WindowsFlash
    {
    /**
    * This check greatly increases the probability to retrieve exactly the same window that you've required
    * since the JavaFX stage title must be exactly the same string as the one from the glass window.
    * Otherwise we might get the wrong window with exactly the same name.
    *
    * @param stageWindowName JavaFX Stage window name.
    * @param glassWindowName Native glass window name.
    *
    * @return Whether the two strings are identical.
    */
    @SuppressWarnings("StringEquality")
    private static boolean stringsAreIdentical(final String stageWindowName, final String glassWindowName)
    {
    return stageWindowName == glassWindowName;
    }

    /**
    * Returns the JavaFX Stage native window handle for Windows.
    *
    * @param stage The JavaFX stage.
    *
    * @return An optional value containing the native window handle.
    */
    private static Optional<HANDLE> getStageHandle(final Stage stage)
    {
    try
    {
    final Class<?> glassWindowClass = Class.forName("com.sun.glass.ui.Window");

    final MethodHandle getTitleMethodHandle = MethodHandles.lookup()
    .findVirtual(glassWindowClass, "getTitle", MethodType.methodType(String.class));

    final MethodHandle getNativeHandleMethodHandle = MethodHandles.lookup()
    .findVirtual(glassWindowClass, "getNativeHandle", MethodType.methodType(long.class));

    final MethodHandle getWindows = MethodHandles.lookup()
    .findStatic(glassWindowClass, "getWindows", MethodType.methodType(List.class));

    // Iterate through glass windows and find the right one
    final List<?> glassWindows = (List<?>) getWindows.invoke();
    for (final Object glassWindow : glassWindows)
    {
    final String stageWindowName = stage.getTitle();
    final String glassWindowName = (String) getTitleMethodHandle.invoke(glassWindow);

    // Check strings identity, don't compare them
    if (stringsAreIdentical(stageWindowName, glassWindowName))
    {
    final long nativeHandle = (long)getNativeHandleMethodHandle.invoke(glassWindow);
    return Optional.of(new HANDLE(new Pointer(nativeHandle)));
    }
    }

    // Unable to find the correct window
    return Optional.empty();
    }
    catch (Throwable throwable)
    {
    // This may be a NPE, a reflective operation exception or a security exception
    return Optional.empty();
    }
    }

    /**
    * Flashes the specified window. It does not change the active state of the window.
    * Silently does nothing if any error occurred.
    *
    * @param stage JavaFX stage to flash.
    */
    public static void flashWindowIcon(final Stage stage)
    {
    getStageHandle(stage).ifPresent(windowHandle -> {
    WinUser.FLASHWINFO info = new WinUser.FLASHWINFO();
    info.hWnd = windowHandle;
    info.dwFlags = FLASHW_ALL | FLASHW_TIMERNOFG;
    info.uCount = Integer.MAX_VALUE;
    info.dwTimeout = 0;

    User32.INSTANCE.FlashWindowEx(info);
    });
    }
    }