Skip to content

Commit

Permalink
Merge pull request #21 from marad/window_inspector
Browse files Browse the repository at this point in the history
Window inspector
  • Loading branch information
marad authored Nov 24, 2023
2 parents 9887d6f + e51aacd commit c4ca170
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 31 deletions.
10 changes: 9 additions & 1 deletion src/main/kotlin/gh/marad/tiler/app/internal/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import gh.marad.tiler.app.AppFacade
import gh.marad.tiler.common.TilerCommand
import gh.marad.tiler.config.ConfigFacade
import gh.marad.tiler.config.Hotkey
import gh.marad.tiler.help.WindowInspector
import gh.marad.tiler.os.OsFacade
import gh.marad.tiler.tiler.TilerFacade
import kotlinx.coroutines.*
Expand All @@ -19,6 +20,7 @@ import java.awt.TrayIcon
class App(val config: ConfigFacade, val os: OsFacade, val tiler: TilerFacade, val actions: ActionsFacade) : AppFacade {
private val logger = LoggerFactory.getLogger(App::class.java)
private val commandChannel = Channel<List<TilerCommand>>(100)
private val windowInspector = WindowInspector(os)

@Suppress("UNUSED_VARIABLE")
override suspend fun start() {
Expand All @@ -29,7 +31,8 @@ class App(val config: ConfigFacade, val os: OsFacade, val tiler: TilerFacade, va
commandChannel.send(tiler.initializeWithOpenWindows())
val evenHandler = BroadcastingEventHandler(
TilerWindowEventHandler(tiler, config.getFilteringRules(), os, commandChannel),
RestoreWindowsOnExitEventHandler(os)
RestoreWindowsOnExitEventHandler(os),
windowInspector
)

coroutineScope {
Expand Down Expand Up @@ -93,6 +96,11 @@ class App(val config: ConfigFacade, val os: OsFacade, val tiler: TilerFacade, va
})

val popupMenu = java.awt.PopupMenu()
popupMenu.add(MenuItem("Window inspector")).addActionListener {
windowInspector.isVisible = true
}
popupMenu.addSeparator()

if (config.getConfigPath() != null) {
popupMenu.add(MenuItem("Edit config")).addActionListener {
val editor = ProcessBuilder(config.configEditorPath(), config.getConfigPath())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ class FilteringRules {
companion object {
val CoreRules = arrayOf(
Rule.ignoreIf { it.isPopup },
Rule.ignoreIf { it.windowName == "Tiler Window Inspector" },
Rule.ignoreIf { it.exeName == "idea64.exe" && it.windowName.isBlank() },
Rule.ignoreIf { it.exeName == "PowerToys.MeasureToolUI.exe" || it.exeName == "PowerToys.Settings.exe" },
Rule.ignoreIf { it.windowName == "PopupMessageWindow" }
Rule.ignoreIf { it.windowName == "PopupMessageWindow" },
Rule.ignoreIf { it.exeName == "explorer.exe" },
Rule.manageIf { it.exeName == "explorer.exe" && it.className == "CabinetWClass" }
)
}
}
56 changes: 56 additions & 0 deletions src/main/kotlin/gh/marad/tiler/help/WindowInspector.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package gh.marad.tiler.help

import gh.marad.tiler.common.Window
import gh.marad.tiler.os.OsFacade
import gh.marad.tiler.os.WindowEventHandler
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.Font
import javax.swing.*

class WindowInspector(private val osFacade: OsFacade) : JFrame("Tiler Window Inspector"), WindowEventHandler {
private val btn = JButton("Inspect next window")
private val pane = JEditorPane()
private var capturing = false
init {
val uiFont = Font.getFont("Segoe UI")
font = uiFont
btn.font = uiFont
pane.font = uiFont
pane.isEditable = false
isAlwaysOnTop = true
defaultCloseOperation = WindowConstants.HIDE_ON_CLOSE
preferredSize = Dimension(600, 400)
contentPane.add(btn, BorderLayout.NORTH)
contentPane.add(pane, BorderLayout.CENTER)
pack()

btn.addActionListener {
pane.text = "Activate a window to inspect..."
capturing = true
}
pane.text = osFacade.windowDebugInfo(osFacade.activeWindow())
}

override suspend fun windowActivated(window: Window) {
if (capturing) {
pane.text = osFacade.windowDebugInfo(window)
capturing = false
}
}

override suspend fun windowAppeared(window: Window) { }
override suspend fun windowDisappeared(window: Window) { }
override suspend fun windowMinimized(window: Window) { }
override suspend fun windowRestored(window: Window) { }
override suspend fun windowMovedOrResized(window: Window) { }
}

fun main() {
val osFacade = OsFacade.createWindowsFacade()
val win = WindowInspector(osFacade)
win.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
win.isVisible = true

OsFacade.createWindowsFacade().startEventHandling(win)
}
15 changes: 2 additions & 13 deletions src/main/kotlin/gh/marad/tiler/os/internal/Functions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fun generateEventProcedure(eventHandler: WindowEventHandler): WinUser.WinEventPr

val window = Window(hwnd)

if(shouldIgnoreEvent(window, event, hwnd, idObject)) {
if(shouldIgnoreEvent(window, idObject)) {
return@WinEventProc
}

Expand Down Expand Up @@ -55,19 +55,8 @@ fun generateEventProcedure(eventHandler: WindowEventHandler): WinUser.WinEventPr

fun shouldIgnoreEvent(
window: Window,
event: WinDef.DWORD?,
hwnd: WinDef.HWND,
idObject: WinDef.LONG?
): Boolean {
val isNotForWindow = idObject != OBJID_WINDOW
val isNotAWindow = !window.isWindow()
val isCloaked = DwmApi.isCloaked(hwnd)
val isWindowsTooltip = window.getRealClassName() == "Xaml_WindowedPopupClass"
val isAToolWindow = window.getExStyle().toolWindow()

return isNotForWindow ||
isNotAWindow ||
isCloaked ||
isWindowsTooltip ||
isAToolWindow
return isNotForWindow || !isInterestingWindow(window)
}
25 changes: 15 additions & 10 deletions src/main/kotlin/gh/marad/tiler/os/internal/winapi/Functions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,28 @@ fun listWindows(): List<Window> {
val windows = mutableListOf<Window>()
u32.EnumWindows({ handle: HWND, data: Pointer? ->
val win = Window(handle)
val isNotCloaked = !DwmApi.isCloaked(handle)
val isNotAToolWindow = !win.getExStyle().toolWindow()
val isNotATaskManager = win.getRealClassName() != "TaskManagerWindow"

if (
isNotAToolWindow
&& isNotCloaked
&& isNotATaskManager
&& win.isWindow()
) {
if (isInterestingWindow(win)) {
windows.add(win)
}
true
}, null)
return windows
}

fun isInterestingWindow(win: Window): Boolean {
val isNotCloaked = !DwmApi.isCloaked(win.handle)
val isNotAToolWindow = !win.getExStyle().toolWindow()
val isNotATaskManager = win.getRealClassName() != "TaskManagerWindow"
val isNotAWindowsTooltop = win.getRealClassName() != "Xaml_WindowedPopupClass"

return isNotAToolWindow
&& isNotCloaked
&& isNotATaskManager
&& isNotAWindowsTooltop
&& win.isWindow()
&& win.getTitle().isNotEmpty()
}

fun windowsUnderCursor(): List<Window> {
val cursor = POINT()
u32.GetCursorPos(cursor)
Expand Down
7 changes: 1 addition & 6 deletions src/main/kotlin/gh/marad/tiler/tiler/internal/Tiler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,7 @@ class Tiler(

override fun removeWindow(window: Window): List<TilerCommand> {
viewManager.currentView().removeWindow(window.id)
val windowToActivate = viewManager.currentView().windowToActivate()
return if (!viewManager.currentView().hasWindow(os.activeWindow().id) && windowToActivate != null) {
retile() + ActivateWindow(windowToActivate)
} else {
retile()
}
return retile()
}

override fun moveWindow(window: TilerWindow, viewId: Int): List<TilerCommand> {
Expand Down

0 comments on commit c4ca170

Please sign in to comment.