From 1cb68f19a7258f3c4bd08f881b953ff7cba9d017 Mon Sep 17 00:00:00 2001 From: dtookey Date: Tue, 8 Aug 2023 09:01:47 -0400 Subject: [PATCH] cleaning up desktop interactions. need to move things into individual files --- .../kotlin/controllers/DesktopController.kt | 164 +++++++++++++++++- .../controllers/DesktopControllerTest.kt | 2 +- 2 files changed, 158 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/controllers/DesktopController.kt b/src/main/kotlin/controllers/DesktopController.kt index 74c9ba6..056676e 100644 --- a/src/main/kotlin/controllers/DesktopController.kt +++ b/src/main/kotlin/controllers/DesktopController.kt @@ -84,14 +84,149 @@ interface DesktopController { } } -class WindowsDesktopController : DesktopController { +interface OSProxy{ + fun getActiveWindowName(): String +} +/** + * Windows implementation of [DesktopController]. + * + * This class provides methods to interact with the desktop on Windows + * by calling Win32 APIs. + * + * It implements the [DesktopController] interface to provide desktop + * functionality like getting the mouse pointer location on Windows. + */ +class WindowsDesktopController : DesktopController, OSProxy { + + companion object{ + /** + * Converts a native byte buffer to a String. + * + * This takes a byte array [byteBuffer] containing text from a native Win32 call, + * converts it to a String using JNA, and trims whitespace characters. + * + * Usage example: + * + * ``` + * val buffer = ByteArray(256) + * GetWindowTextA(hwnd, buffer, buffer.size) // Win32 call + * + * val windowTitle = nativeByteBufferToString(buffer) + * println(windowTitle) // Print title string + * ``` + * + * @param byteBuffer Byte array containing text from a native call + * @return The native text as a String + */ + private fun nativeByteBufferToString(byteBuffer: ByteArray): String{ + // I guess this prunes anything that isn't on the ascii table? + val wText = Native.toString(byteBuffer).trim { it <= ' ' } + return wText + } + } + + /** + * Interface for calling Windows User32 API functions. + * + * This defines an interface extending StdCallLibrary to call native + * Windows User32 library functions like EnumWindows, GetWindowTextA etc. + * + * Classes can implement this interface to make direct calls to the + * User32 DLL on Windows. + */ internal interface User32 : StdCallLibrary { + + /** + * Interface for a Windows callback function to enumerate windows. + * + * This extends the StdCallLibrary.StdCallCallback to define a callback + * method that will be invoked by the Windows API EnumWindows function. + * + * The callback method accepts a window handle (HWND) and a user-defined + * pointer, and returns a Boolean indicating whether to continue enumeration. + * + * Usage example: + * ``` + * val callback = object : WNDENUMPROC { + * override fun callback(hWnd: Pointer?, arg: Pointer?): Boolean { + * // Check if hWnd matches target window + * if (matchesTarget(hWnd)) { + * // Found target window, stop enumeration + * return false + * } + * // Keep enumerating + * return true + * } + * } + * ``` + * + * @param hWnd Window handle (HWND) for the current enumerated window. + * @param arg User-defined data pointer passed to EnumWindows. + * @return True to continue enumerating windows, false to stop. + */ interface WNDENUMPROC : StdCallLibrary.StdCallCallback { fun callback(hWnd: Pointer?, arg: Pointer?): Boolean } + /** + * Enumerates windows on the system. + * + * This calls the Windows API EnumWindows function to enumerate all top-level windows. + * + * For each window, it calls the provided [WNDENUMPROC] callback function, + * passing the window handle [hWnd] and user-defined [userData] pointer. + * + * Enumeration can be stopped by returning false from the callback. + * + * Usage example: + * + * ``` + * val windows = mutableListOf() + * + * val callback = object : WNDENUMPROC { + * override fun callback(hWnd: Pointer?, arg: Pointer?): Boolean { + * windows.add(hWnd) // Add hWnd to list + * return true // Continue enumerating + * } + * } + * + * EnumWindows(callback, null) // Get all top-level windows + * + * println(windows) // Print list of HWNDs + * ``` + * + * @param lpEnumFunc The [WNDENUMPROC] callback to call for each window. + * @param userData Optional user-defined data to pass to the callback. + * @return True if successful, false otherwise. + */ fun EnumWindows(lpEnumFunc: WNDENUMPROC?, userData: Pointer?): Boolean + + /** + * Gets the title text of the specified window. + * + * This calls the Win32 API GetWindowTextA function to get the title + * text for the window handle [hWnd]. + * + * The window text is copied into the [lpString] buffer up to [nMaxCount] characters. + * + * Usage example: + * + * ``` + * val buffer = ByteArray(256) + * val hwnd = getWindowHandle() // get some HWND + * + * GetWindowTextA(hwnd, buffer, buffer.size) + * + * val windowTitle = String(buffer) + * println(windowTitle) + * ``` + * + * @param hWnd The window handle (HWND). + * @param lpString The buffer to receive the window text. + * @param nMaxCount The maximum number of characters to copy to the buffer. + * @return The length of the window text (excluding null-terminator). + */ fun GetWindowTextA(hWnd: Pointer?, lpString: ByteArray?, nMaxCount: Int): Int fun GetForegroundWindow(): Pointer? @@ -103,13 +238,28 @@ class WindowsDesktopController : DesktopController { } - fun getCurrentlyActiveWindowName(): String { + /** + * Gets the title of the active/foreground window. + * + * This calls Win32 APIs to get the handle of the foreground window, + * then gets its title text. + * + * Usage example: + * + * ``` + * val activeWindowName = getActiveWindowName() + * + * println(activeWindowName) // Prints foreground window title + * ``` + * + * @return The title text of the current foreground window. + */ + override fun getActiveWindowName(): String { val user32 = User32.INSTANCE + val textBuffer = ByteArray(512) - val windowText = ByteArray(512) - val hWnd = user32.GetForegroundWindow() - user32.GetWindowTextA(hWnd, windowText, 512) - val wText = Native.toString(windowText).trim { it <= ' ' } //i have no idea what this does - return wText + user32.GetWindowTextA(user32.GetForegroundWindow(), textBuffer, 512) + + return nativeByteBufferToString(textBuffer) } } \ No newline at end of file diff --git a/src/test/kotlin/controllers/DesktopControllerTest.kt b/src/test/kotlin/controllers/DesktopControllerTest.kt index a1df84a..422b7f3 100644 --- a/src/test/kotlin/controllers/DesktopControllerTest.kt +++ b/src/test/kotlin/controllers/DesktopControllerTest.kt @@ -64,6 +64,6 @@ class DesktopControllerTest { @Test fun devTest(){ val c = WindowsDesktopController() - println("TEXT: ${ c.getCurrentlyActiveWindowName()}") + println("TEXT: ${ c.getActiveWindowName()}") } } \ No newline at end of file