Skip to content

Commit

Permalink
walk backwards algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
seanbudd committed Dec 9, 2022
1 parent f8fb374 commit 452babc
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 27 deletions.
67 changes: 41 additions & 26 deletions source/utils/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import typing
from typing import (
Callable,
List,
Optional,
Set,
)
Expand Down Expand Up @@ -220,13 +221,13 @@ def _isObjectAboveLockScreen(obj: "NVDAObjects.NVDAObject") -> bool:
if lockAppModule is None:
_checkWindowsForAppModules()
lockAppModule = _findLockAppModule()
if lockAppModule is None:
# lockAppModule not running/registered by NVDA yet
log.debug(
"lockAppModule not detected when Windows is locked. "
"Cannot detect if object is in lock app, considering object as safe. "
)
return True
if lockAppModule is None:
# lockAppModule not running/registered by NVDA yet
log.debug(
"lockAppModule not detected when Windows is locked. "
"Cannot detect if object is in lock app, considering object as safe. "
)
return True

from NVDAObjects.window import Window
if not isinstance(obj, Window):
Expand All @@ -236,7 +237,9 @@ def _isObjectAboveLockScreen(obj: "NVDAObjects.NVDAObject") -> bool:
# must be a window to get its HWNDVal
return True

return _isObjectAboveLockScreenCheckZOrder(obj.windowHandle, lockAppModule.processID)
topLevelWindowHandle = winUser.getAncestor(obj.windowHandle, winUser.GA_ROOT)
# TODO: time this
return _isObjectAboveLockScreenCheckZOrder(topLevelWindowHandle, lockAppModule.processID)


def _isObjectAboveLockScreenCheckZOrder(objWindowHandle: int, lockAppModuleProcessId: int) -> bool:
Expand All @@ -255,12 +258,12 @@ def _isWindowLockApp(hwnd: winUser.HWNDVal) -> bool:

try:
return _isWindowAboveWindowMatchesCond(objWindowHandle, _isWindowLockApp)
except _WindowNotFoundError:
except _UnexpectedWindowCountError:
log.debugWarning("Couldn't find lock screen")
return True


class _WindowNotFoundError(Exception):
class _UnexpectedWindowCountError(Exception):
"""
Raised when a window which matches the expected condition
is not found by _isWindowAboveWindowMatchesCond
Expand All @@ -269,26 +272,38 @@ class _WindowNotFoundError(Exception):


def _isWindowAboveWindowMatchesCond(
targetWindow: winUser.HWNDVal,
window: winUser.HWNDVal,
matchCond: Callable[[winUser.HWNDVal], bool]
) -> bool:
""" Returns True if targetWindow is above a window that matches the match condition.
"""
aboveWindow = winUser.getWindow(targetWindow, winUser.GW_HWNDPREV)
belowWindow = winUser.getWindow(targetWindow, winUser.GW_HWNDNEXT)
while (
aboveWindow != winUser.GW_RESULT_NOT_FOUND
or belowWindow != winUser.GW_RESULT_NOT_FOUND
):
if matchCond(belowWindow): # target window is above the found window
return True
elif matchCond(aboveWindow): # found window is above the target window
return False
if aboveWindow != winUser.GW_RESULT_NOT_FOUND:
aboveWindow = winUser.getWindow(aboveWindow, winUser.GW_HWNDPREV)
if belowWindow != winUser.GW_RESULT_NOT_FOUND:
belowWindow = winUser.getWindow(belowWindow, winUser.GW_HWNDNEXT)
raise _WindowNotFoundError(f"Window not found matching condition {matchCond}")
desktopWindow = winUser.getDesktopWindow()
topLevelWindow = winUser.getTopWindow(desktopWindow)
bottomWindow = winUser.getWindow(topLevelWindow, winUser.GW_HWNDLAST)
currentWindow = bottomWindow
currentIndex = 0 # 0 is the last/lowest window
window1Indexes: List[int] = []
window2Indexes: List[int] = []
while currentWindow != winUser.GW_RESULT_NOT_FOUND:
if currentWindow == window:
window1Indexes.append(currentIndex)
if matchCond(currentWindow):
window2Indexes.append(currentIndex)
currentWindow = winUser.getWindow(currentWindow, winUser.GW_HWNDPREV)
currentIndex += 1
if len(window1Indexes) != 1 or len(window2Indexes) == 0:
raise _UnexpectedWindowCountError(
"Windows found\n"
f" - window 1 indexes: {window1Indexes} (expects len 1)\n"
f" - window 2 indexes: {window2Indexes} (expects len >= 1)\n"
)
lowestWin2Window = min(window2Indexes)
highestWin1Window = max(window1Indexes)
if highestWin1Window >= lowestWin2Window:
# this means it is above the lockscreen
return True
else:
return False


_hasSessionLockStateUnknownWarningBeenGiven = False
Expand Down
6 changes: 5 additions & 1 deletion source/winUser.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,14 @@ class GUITHREADINFO(Structure):
GWL_STYLE=-16
GWL_EXSTYLE=-20

# getWindow: TODO turn to enum
# getWindow relationship parameters: TODO turn to enum
GW_HWNDFIRST = 0
GW_HWNDLAST = 1
GW_HWNDNEXT=2
GW_HWNDPREV=3
GW_OWNER=4

# getWindow results
GW_RESULT_NOT_FOUND = 0
"""
When GetWindow returns 0, it means the window has not been found.
Expand Down

0 comments on commit 452babc

Please sign in to comment.