Repository: Kalmat/PyWinCtl (language: python)

Issue:
Type-checking and mark package as typed
Mark pywinctl as typed using the `py.typed` marker. See https://peps.python.org/pep-0561/
This allows static type checkers like pyright, mypy, pytype, pycharm, etc. to use inline types instead of looking for a non-existent type-stub.

I've added Github actions to validate typing using pyright and mypy.
Checking all python versions *may* be overkill. Testing only the oldest supported may be enough.
CI validation: https://github.com/Avasam/PyWinCtl/pull/1

And finally I've fixed any typing issue from basic type-checking.


Discussion / hints:
Hi!
First off, thank you so much for your help and interest!!
Excuse my ignorance... what is this all about? I mean, what are mypy, pyright and type-checking; and what are they useful for (I read the PEP-0561 article, but wasn't able to fully understand, sorry)? Should I merge it now or should I wait until all work is done (it says "work in progress")?
Thank you again!
It is work in progress, so no need to merge now. I would also highly suggest reviewing the changes to ensure they all make sense to you, why a change had to be made, and to ensure I respected the intent of certain logic.

For example, when a `None` window title on MacOS is about to be used it a way that would raise an error. Should we return early? Is it fine to consider it equivalent to an empty string? (it depends on the use-case and intent, since a `None` window title has a special meaning).

---

What is / Why type-checking?

Static type checking is about evaluating which type your variables should have at any given point. As well as classes and method signatures etc. It is **not** a linter (like Flake8, pylint, pycodestyle, etc.) or a formatter (like autopep8, isort, Black, pycln, etc.)
It allows you to, during development, ensure that you're using sane types, keep track of what your variables are, and not accidentally misuse them. Resulting in more resilient code, and a more truthful and complete API for libraries: 
```py
# Very simple examples
a = 0
a.lower()  # Errors at runtime. Type checkers will catch this.

b: str
b = 0 # type checkers won't accept this

c: str | int
c = 0  # This is fine
c = ""  # This is fine
```

---

What are the tools mentioned?

https://google.github.io/pytype/ is a very simple and minimal checker by google that only runs on linux.
http://mypy-lang.org/ is the "official" python type-checking tool. But is often falling behind, has edge-case bugs, or doesn't have full support.
https://github.com/microsoft/pyright is Eric Traut (Microsoft)'s python type checker written in TypeScript. Really good and powerful.
The PyCharm (by Jetbrain) editor comes with its own type-checking tool

---

What are stubs? (PEP 561). And typeshed?

Type stubs are `.pyi` files that contain no functional code, only type definitions.
They're usually third-party packages to type libraries that are missing typing definitions. They can also be useful to describe c-extensions. Typeshed is the official central python repository of 3rd-party type stubs that everyone can contribute to. Typeshed actively supports all 4 tools mentioned above.

`types-*` packages are third-party stubs uploaded by typeshed

Libraries have the option to mark their packages as "typed" using the `py.typed` marker file. When that's done, type-checkers stop looking for 3rd-party stubs, and instead trust the package to provide their own types. Removing the following error:
![image](https://user-images.githubusercontent.com/1350584/199075378-4e572fae-52e4-4cc3-bc3d-cf09e3a39ee0.png)
![image](https://user-images.githubusercontent.com/1350584/199075591-305fe77b-0345-466c-95a3-945bf851da1d.png)
It also automatically adds the following tag on PyPI:
![image](https://user-images.githubusercontent.com/1350584/199081286-e9d90f74-ed3a-4c98-a461-87f3f8f7a034.png)


---

Why these changes?

1. Adding the `py.typed` marker file is for the reason mentioned above. Most of this library already provides inline-typing. It is much better to leverage that, than create a third-party stub.
2. Added CI actions to run mypy and pyright. The 2 most popular (and best) type-checkers. To help validate that the types and signatures this library provide are sane. If this library is gonna mark itself as using inline-type, let's ensure the types are actually correct. It also helps ensure support for specific Python versions isn't broken.
3. Even in basic type-checking mode. Said tools found a couple issues. Like accessing potentially None values, unused imports, obsolete typing code, missing `| None` (used to be `Optional[]`) on parameters, comparisons with an always None value, methods exposing the wrong return type, etc.
  So I'm also fixing everything found said tools in their non-strict mode so that the CI can pass.
4. Updated dev requirements: mypy doesn't come bundled with typeshed (pyright does). So we need to specify the extra type-stubs explicitly. 

---

How to type-check locally?

On VSCode:
You can install the Python and Pylance extensions. Make sure that mypy is turned on in the settings.

On PyCharm:
It has it's own integrated type-checkers. There's mypy plugin(s?), but I don't use the editor so I can't vouch for any.

As a command line:
Mypy:
1. Install mypy: `pip install mypy`
2. `mypy .`

Pyright:
1. Install NodeJS
2. `npx pyright`
I still need to add NamedTuples to `types-pywin32` for the CI to pass (https://github.com/Avasam/PyWinCtl/pull/1). Other than that, ready for review..
Woooow!!! Sorry for making you write that awesome explanation. I'm merging the PR right now! I will have to test everything before uploading it to PyPi.

Thanks a LOT!!!!

Fix patch:
diff --git a/.github/workflows/type-checking.yml b/.github/workflows/type-checking.yml
index d0a7b92..7b21d5f 100644
--- a/.github/workflows/type-checking.yml
+++ b/.github/workflows/type-checking.yml
@@ -43,5 +43,5 @@ jobs:
           python-version: ${{ matrix.python-version }}
           cache: "pip"
           cache-dependency-path: "docs/requirements.txt"
-      - run: pip install -r "docs/requirements.txt" mypy
-      - run: mypy .
+      - run: pip install -r "docs/requirements.txt"
+      - run: mypy . --python-version=${{ matrix.python-version }}
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 29df914..ff08bde 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -4,10 +4,11 @@
 
 PyRect>=0.1
 types-setuptools>=65.5
+mypy >= 0.990
 
 # Windows
 pywin32>=302; sys_platform == 'win32'
-types-pywin32>=304.0.0.3
+types-pywin32>=304.0.0.4
 
 # Linux
 xlib>=0.21; sys_platform == 'linux'
diff --git a/pyrightconfig.json b/pyrightconfig.json
index 2a403be..7e1c034 100644
--- a/pyrightconfig.json
+++ b/pyrightconfig.json
@@ -1,22 +1,26 @@
 {
-  "typeCheckingMode": "basic",
+  // Oldest not EOL python version
+  "pythonVersion": "3.7",
+  "useLibraryCodeForTypes": true,
+  "extraPaths": [
+    "src/"
+  ],
+  "typeCheckingMode": "strict",
   // Extra strict. Optional options even in "strict" mode.
   "reportImplicitStringConcatenation": "error",
   "reportCallInDefaultInitializer": "error",
   "reportPropertyTypeMismatch": "error",
-  "reportUninitializedInstanceVariable": "error",
-  "reportUnnecessaryTypeIgnoreComment": "warning",
-  // External libraries may be missing stubs
-  "reportMissingTypeStubs": "warning",
-  // False positives with TYPE_CHECKING
+  // type: ignore comments are meant for mypy
+  "reportUnnecessaryTypeIgnoreComment": "information",
+  // False positives with TYPE_CHECKING and defered imports
   "reportImportCycles": "information",
-  // Extra runtime safety
-  "reportUnnecessaryComparison": "warning",
   // As a library, we're allowed to use our own privates
   "reportPrivateUsage": "none",
   // Supporting common misused parameters from users that don't type-check is ok
   "reportUnnecessaryIsInstance": "none",
-  // Keeping unused classes and functions for easier later reference
+  // Some private classes are reused in private modules
   "reportUnusedClass": "none",
+  // Keeping unused functions and variables for easier reference
+  "reportUnusedVariable": "none",
   "reportUnusedFunction": "none",
 }
diff --git a/setup.cfg b/setup.cfg
index 7d900db..8bb2787 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,13 +2,21 @@
 description_file = README.md
 
 [mypy]
-mypy_path = src/
-show_error_codes = True
-strict = False
-# Will be the default in mypy 0.990
-namespace_packages = True
+python_version = 3.7
+mypy_path = src/, typings/
+strict = True
+
+# Leverage type inference for function return type
+disallow_untyped_calls=False
+disallow_incomplete_defs=False
+disallow_untyped_defs=False
+
+# https://github.com/python/mypy/issues/8234 (post assert "type: ignore")
+# https://github.com/python/mypy/issues/8823 (version specific "type: ignore")
+warn_unused_ignores = False
+
 disable_error_code =
-  # Currently using non-strict typing
-  var-annotated,
-  # https://github.com/python/mypy/issues/6232
-  union-attr, attr-defined, assignment
+  # https://github.com/python/mypy/issues/6232 (redifinition with correct type)
+  attr-defined, assignment,
+  # https://github.com/python/mypy/issues/13975 (@property mistaken as Callable)
+  comparison-overlap, truthy-function
diff --git a/src/pywinctl/__init__.py b/src/pywinctl/__init__.py
index 40f0a04..da6d6ed 100644
--- a/src/pywinctl/__init__.py
+++ b/src/pywinctl/__init__.py
@@ -1,19 +1,58 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
+# pyright: reportUnknownMemberType=false
 from __future__ import annotations
 
-from typing import NamedTuple
-from abc import ABC, abstractproperty, abstractmethod
-
 import difflib
 import re
 import sys
 import threading
+from abc import ABC, abstractmethod
+from collections.abc import Callable
+from typing import Any, NamedTuple, cast
 
 import pyrect  # type: ignore[import]  # TODO: Create type stubs or add to base library
 
+if sys.platform == "darwin":
+    from ._pywinctl_macos import (MacOSNSWindow as NSWindow, MacOSWindow as Window, checkPermissions, getActiveWindow,
+                                  getActiveWindowTitle, getAllAppsNames, getAllAppsWindowsTitles, getAllScreens,
+                                  getAllTitles, getAllWindows, getAppsWithName, getMousePos, getScreenSize,
+                                  getTopWindowAt, getWindowsAt, getWindowsWithTitle, getWorkArea)
+
+elif sys.platform == "win32":
+    from ._pywinctl_win import (Win32Window as Window, checkPermissions, getActiveWindow, getActiveWindowTitle,
+                                getAllAppsNames, getAllAppsWindowsTitles, getAllScreens, getAllTitles, getAllWindows,
+                                getAppsWithName, getMousePos, getScreenSize, getTopWindowAt, getWindowsAt,
+                                getWindowsWithTitle, getWorkArea)
+
+elif sys.platform == "linux":
+    from ._pywinctl_linux import (LinuxWindow as Window, checkPermissions, getActiveWindow, getActiveWindowTitle,
+                                  getAllAppsNames, getAllAppsWindowsTitles, getAllScreens, getAllTitles, getAllWindows,
+                                  getAppsWithName, getMousePos, getScreenSize, getTopWindowAt, getWindowsAt,
+                                  getWindowsWithTitle, getWorkArea)
+
+else:
+    raise NotImplementedError('PyWinCtl currently does not support this platform. If you think you can help, please contribute! https://github.com/Kalmat/PyWinCtl')
+
+__all__ = [
+    "BaseWindow", "version", "Re",
+    # OS Specifics
+    "Window", "checkPermissions", "getActiveWindow", "getActiveWindowTitle", "getAllAppsNames",
+    "getAllAppsWindowsTitles", "getAllScreens", "getAllTitles", "getAllWindows", "getAppsWithName", "getMousePos",
+    "getScreenSize", "getTopWindowAt", "getWindowsAt", "getWindowsWithTitle", "getWorkArea",
+]
+# Mac only
+if sys.platform == "darwin":
+    __all__ += ["NSWindow"]
+
 __version__ = "0.0.39"
 
+class Box(NamedTuple):
+    left: int
+    top: int
+    width: int
+    height: int
+
 class Rect(NamedTuple):
     left: int
     top: int
@@ -29,13 +68,13 @@ class Size(NamedTuple):
     height: int
 
 
-def pointInRect(x, y, left, top, width, height):
+def pointInRect(x: float, y: float, left: float, top: float, width: float, height: float):
     """Returns ``True`` if the ``(x, y)`` point is within the box described
     by ``(left, top, width, height)``."""
     return left < x < left + width and top < y < top + height
 
 
-def version(numberOnly=True):
+def version(numberOnly: bool = True):
     """Returns the current version of PyWinCtl module, in the form ''x.x.xx'' as string"""
     return ("" if numberOnly else "PyWinCtl-")+__version__
 
@@ -57,19 +96,20 @@ class Re:
 
     IGNORECASE = re.IGNORECASE
 
-    _cond_dic = {
+    # Does not play well with static typing and current implementation of TypedDict
+    _cond_dic: dict[int, Callable[[str | re.Pattern[str], str, float], bool]] = {
         IS: lambda s1, s2, fl: s1 == s2,
-        CONTAINS: lambda s1, s2, fl: s1 in s2,
-        STARTSWITH: lambda s1, s2, fl: s2.startswith(s1),
-        ENDSWITH: lambda s1, s2, fl: s2.endswith(s1),
+        CONTAINS: lambda s1, s2, fl: s1 in s2, # type: ignore
+        STARTSWITH: lambda s1, s2, fl: s2.startswith(s1),  # type: ignore
+        ENDSWITH: lambda s1, s2, fl: s2.endswith(s1),  # type: ignore
         NOTIS: lambda s1, s2, fl: s1 != s2,
-        NOTCONTAINS: lambda s1, s2, fl: s1 not in s2,
-        NOTSTARTSWITH: lambda s1, s2, fl: not s2.startswith(s1),
-        NOTENDSWITH: lambda s1, s2, fl: not s2.endswith(s1),
-        MATCH: lambda s1, s2, fl: bool(s1.search(s2)),
-        NOTMATCH: lambda s1, s2, fl: not (bool(s1.search(s2))),
-        EDITDISTANCE: lambda s1, s2, fl: _levenshtein(s1, s2) >= fl,
-        DIFFRATIO: lambda s1, s2, fl: difflib.SequenceMatcher(None, s1, s2).ratio() * 100 >= fl
+        NOTCONTAINS: lambda s1, s2, fl: s1 not in s2,  # type: ignore
+        NOTSTARTSWITH: lambda s1, s2, fl: not s2.startswith(s1),  # type: ignore
+        NOTENDSWITH: lambda s1, s2, fl: not s2.endswith(s1),  # type: ignore
+        MATCH: lambda s1, s2, fl: bool(s1.search(s2)),  # type: ignore
+        NOTMATCH: lambda s1, s2, fl: not (bool(s1.search(s2))),  # type: ignore
+        EDITDISTANCE: lambda s1, s2, fl: _levenshtein(s1, s2) >= fl,  # type: ignore
+        DIFFRATIO: lambda s1, s2, fl: difflib.SequenceMatcher(None, s1, s2).ratio() * 100 >= fl  # type: ignore
     }
 
 
@@ -77,13 +117,13 @@ def _levenshtein(seq1: str, seq2: str) -> float:
     # https://stackabuse.com/levenshtein-distance-and-text-similarity-in-python/
     # Adapted to return a similarity percentage, which is easier to define
     # Removed numpy to reduce dependencies. This is likely slower, but titles are not too long
+    # SPed up filling in the top row
     size_x = len(seq1) + 1
     size_y = len(seq2) + 1
-    matrix = [[0 for y in range(size_y)] for x in range(size_x)]
+    matrix = [[0 for _y in range(size_y)] for _x in range(size_x)]
     for x in range(size_x):
         matrix[x][0] = x
-    for y in range(size_y):
-        matrix[0][y] = y
+    matrix[0] = list(range(0, size_y))
 
     for x in range(1, size_x):
         for y in range(1, size_y):
@@ -103,20 +143,21 @@ def _levenshtein(seq1: str, seq2: str) -> float:
     return (1 - dist / max(len(seq1), len(seq2))) * 100
 
 class BaseWindow(ABC):
-    @abstractproperty
+    @property
+    @abstractmethod
     def _rect(self) -> pyrect.Rect:
         raise NotImplementedError
 
     def _rectFactory(self, bounds: Rect | None = None):
 
-        def _onRead(attrName):
+        def _onRead(attrName: str):
             r = self._getWindowRect()
             rect._left = r.left               # Setting _left directly to skip the onRead.
             rect._top = r.top                 # Setting _top directly to skip the onRead.
             rect._width = r.right - r.left    # Setting _width directly to skip the onRead.
             rect._height = r.bottom - r.top   # Setting _height directly to skip the onRead.
 
-        def _onChange(oldBox, newBox):
+        def _onChange(oldBox: Box, newBox: Box):
             self._moveResizeTo(newBox.left, newBox.top, newBox.width, newBox.height)
 
         if bounds:
@@ -155,7 +196,7 @@ def getExtraFrameSize(self, includeBorder: bool = True) -> tuple[int, int, int,
         raise NotImplementedError
 
     @abstractmethod
-    def getClientFrame(self):
+    def getClientFrame(self) -> Rect:
         """
         Get the client area of window, as a Rect (x, y, right, bottom)
         Notice that scroll and status bars might be included, or not, depending on the application
@@ -272,12 +313,12 @@ def getAppName(self) -> str:
         raise NotImplementedError
 
     @abstractmethod
-    def getParent(self):
+    def getParent(self) -> object:
         """Returns the handle of the window parent"""
         raise NotImplementedError
 
     @abstractmethod
-    def getChildren(self) -> list[int]:
+    def getChildren(self) -> list[Any]:
         """
         Get the children handles of current window
 
@@ -286,18 +327,18 @@ def getChildren(self) -> list[int]:
         raise NotImplementedError
 
     @abstractmethod
-    def getHandle(self):
+    def getHandle(self) -> object:
         """Returns the handle of the window"""
         raise NotImplementedError
 
     @abstractmethod
-    def isParent(self, child) -> bool:
+    def isParent(self, child: Any) -> bool:
         """Returns ''True'' if the window is parent of the given window as input argument"""
         raise NotImplementedError
     isParentOf = isParent  # isParentOf is an alias of isParent method
 
     @abstractmethod
-    def isChild(self, parent) -> bool:
+    def isChild(self, parent: Any) -> bool:
         """Returns ''True'' if the window is child of the given window as input argument"""
         raise NotImplementedError
     isChildOf = isChild  # isParentOf is an alias of isParent method
@@ -307,226 +348,246 @@ def getDisplay(self) -> str:
         """Returns the name of the current window display (monitor)"""
         raise NotImplementedError
 
-    @abstractproperty
+    @property
+    @abstractmethod
     def isMinimized(self) -> bool:
         """Returns ''True'' if the window is currently minimized."""
         raise NotImplementedError
 
-    @abstractproperty
+    @property
+    @abstractmethod
     def isMaximized(self) -> bool:
         """Returns ''True'' if the window is currently maximized."""
         raise NotImplementedError
 
-    @abstractproperty
+    @property
+    @abstractmethod
     def isActive(self) -> bool:
         """Returns ''True'' if the window is currently the active, foreground window."""
         raise NotImplementedError
 
-    @abstractproperty
+    @property
+    @abstractmethod
     def title(self) -> str:
         """Returns the window title as a string."""
         raise NotImplementedError
 
     if sys.platform == "darwin":
-        @abstractproperty
+        @property
+        @abstractmethod
         def updatedTitle(self) -> str:
             """macOS Apple Script ONLY. Returns a similar window title from same app as a string."""
             raise NotImplementedError
 
-    @abstractproperty
+    @property
+    @abstractmethod
     def visible(self) -> bool:
         raise NotImplementedError
     isVisible = visible  # isVisible is an alias for the visible property.
 
-    @abstractproperty
+    @property
+    @abstractmethod
     def isAlive(self) -> bool:
         raise NotImplementedError
 
     # Wrappers for pyrect.Rect object's properties:
     @property
     def left(self):
-        return self._rect.left
+        return cast(float, self._rect.left)  # pyrect.Rect properties are potentially unknown
 
     @left.setter
-    def left(self, value):
+    def left(self, value: float):
         # import pdb; pdb.set_trace()
         self._rect.left  # Run rect's onRead to update the Rect object.
         self._rect.left = value
 
     @property
     def right(self):
-        return self._rect.right
+        return cast(float, self._rect.right)  # pyrect.Rect properties are potentially unknown
 
     @right.setter
-    def right(self, value):
+    def right(self, value: float):
         self._rect.right  # Run rect's onRead to update the Rect object.
         self._rect.right = value
 
     @property
     def top(self):
-        return self._rect.top
+        return cast(float, self._rect.top)  # pyrect.Rect properties are potentially unknown
 
     @top.setter
-    def top(self, value):
+    def top(self, value: float):
         self._rect.top  # Run rect's onRead to update the Rect object.
         self._rect.top = value
 
     @property
     def bottom(self):
-        return self._rect.bottom
+        return cast(float, self._rect.bottom)  # pyrect.Rect properties are potentially unknown
 
     @bottom.setter
-    def bottom(self, value):
+    def bottom(self, value: float):
         self._rect.bottom  # Run rect's onRead to update the Rect object.
         self._rect.bottom = value
 
     @property
     def topleft(self):
-        return self._rect.topleft
+        return cast(Point, self._rect.topleft) # pyrect.Rect.Point properties are potentially unknown
 
     @topleft.setter
-    def topleft(self, value):
+    def topleft(self, value: tuple[float, float]):
         self._rect.topleft  # Run rect's onRead to update the Rect object.
         self._rect.topleft = value
 
     @property
     def topright(self):
-        return self._rect.topright
+        return cast(Point, self._rect.topright) # pyrect.Rect.Point properties are potentially unknown
 
     @topright.setter
-    def topright(self, value):
+    def topright(self, value: tuple[float, float]):
         self._rect.topright  # Run rect's onRead to update the Rect object.
         self._rect.topright = value
 
     @property
     def bottomleft(self):
-        return self._rect.bottomleft
+        return cast(Point, self._rect.bottomleft) # pyrect.Rect.Point properties are potentially unknown
 
     @bottomleft.setter
-    def bottomleft(self, value):
+    def bottomleft(self, value: tuple[float, float]):
         self._rect.bottomleft  # Run rect's onRead to update the Rect object.
         self._rect.bottomleft = value
 
     @property
     def bottomright(self):
-        return self._rect.bottomright
+        return cast(Point, self._rect.bottomright) # pyrect.Rect.Point properties are potentially unknown
 
     @bottomright.setter
-    def bottomright(self, value):
+    def bottomright(self, value: tuple[float, float]):
         self._rect.bottomright  # Run rect's onRead to update the Rect object.
         self._rect.bottomright = value
 
     @property
     def midleft(self):
-        return self._rect.midleft
+        return cast(Point, self._rect.midleft) # pyrect.Rect.Point properties are potentially unknown
 
     @midleft.setter
-    def midleft(self, value):
+    def midleft(self, value: tuple[float, float]):
         self._rect.midleft  # Run rect's onRead to update the Rect object.
         self._rect.midleft = value
 
     @property
     def midright(self):
-        return self._rect.midright
+        return cast(Point, self._rect.midright) # pyrect.Rect.Point properties are potentially unknown
 
     @midright.setter
-    def midright(self, value):
+    def midright(self, value: tuple[float, float]):
         self._rect.midright  # Run rect's onRead to update the Rect object.
         self._rect.midright = value
 
     @property
     def midtop(self):
-        return self._rect.midtop
+        return cast(Point, self._rect.midtop) # pyrect.Rect.Point properties are potentially unknown
 
     @midtop.setter
-    def midtop(self, value):
+    def midtop(self, value: tuple[float, float]):
         self._rect.midtop  # Run rect's onRead to update the Rect object.
         self._rect.midtop = value
 
     @property
     def midbottom(self):
-        return self._rect.midbottom
+        return cast(Point, self._rect.midbottom) # pyrect.Rect.Point properties are potentially unknown
 
     @midbottom.setter
-    def midbottom(self, value):
+    def midbottom(self, value: tuple[float, float]):
         self._rect.midbottom  # Run rect's onRead to update the Rect object.
         self._rect.midbottom = value
 
     @property
     def center(self):
-        return self._rect.center
+        return cast(Point, self._rect.center) # pyrect.Rect.Point properties are potentially unknown
 
     @center.setter
-    def center(self, value):
+    def center(self, value: tuple[float, float]):
         self._rect.center  # Run rect's onRead to update the Rect object.
         self._rect.center = value
 
     @property
     def centerx(self):
-        return self._rect.centerx
+        return cast(float, self._rect.centerx)  # pyrect.Rect properties are potentially unknown
 
     @centerx.setter
-    def centerx(self, value):
+    def centerx(self, value: float):
         self._rect.centerx  # Run rect's onRead to update the Rect object.
         self._rect.centerx = value
 
     @property
     def centery(self):
-        return self._rect.centery
+        return cast(float, self._rect.centery)  # pyrect.Rect properties are potentially unknown
 
     @centery.setter
-    def centery(self, value):
+    def centery(self, value: float):
         self._rect.centery  # Run rect's onRead to update the Rect object.
         self._rect.centery = value
 
     @property
     def width(self):
-        return self._rect.width
+        return cast(float, self._rect.width)  # pyrect.Rect properties are potentially unknown
 
     @width.setter
-    def width(self, value):
+    def width(self, value: float):
         self._rect.width  # Run rect's onRead to update the Rect object.
         self._rect.width = value
 
     @property
     def height(self):
-        return self._rect.height
+        return cast(float, self._rect.height)  # pyrect.Rect properties are potentially unknown
 
     @height.setter
-    def height(self, value):
+    def height(self, value: float):
         self._rect.height  # Run rect's onRead to update the Rect object.
         self._rect.height = value
 
     @property
     def size(self):
-        return self._rect.size
+        return cast(Size, self._rect.size) # pyrect.Rect.Size properties are potentially unknown
 
     @size.setter
-    def size(self, value):
+    def size(self, value: tuple[float, float]):
         self._rect.size  # Run rect's onRead to update the Rect object.
         self._rect.size = value
 
     @property
     def area(self):
-        return self._rect.area
+        return cast(float, self._rect.area)  # pyrect.Rect properties are potentially unknown
 
     @area.setter
-    def area(self, value):
+    def area(self, value: float):
         self._rect.area  # Run rect's onRead to update the Rect object.
-        self._rect.area = value
+        self._rect.area = value # pyright: ignore[reportGeneralTypeIssues]  # FIXME: pyrect.Rect.area has no setter!
 
     @property
     def box(self):
-        return self._rect.box
+        return cast(Box, self._rect.box) # pyrect.Rect.Box properties are potentially unknown
 
     @box.setter
-    def box(self, value):
+    def box(self, value: Box):
         self._rect.box  # Run rect's onRead to update the Rect object.
         self._rect.box = value
 
 class _WinWatchDog(threading.Thread):
 
-    def __init__(self, win: BaseWindow, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB=None, isMaximizedCB=None, resizedCB=None, movedCB=None, changedTitleCB=None, changedDisplayCB=None, interval=0.3):
+    def __init__(
+        self,
+        win: BaseWindow,
+        isAliveCB: Callable[[bool], None] | None = None,
+        isActiveCB: Callable[[bool], None] | None = None,
+        isVisibleCB: Callable[[bool], None] | None = None,
+        isMinimizedCB: Callable[[bool], None] | None = None,
+        isMaximizedCB: Callable[[bool], None] | None = None,
+        resizedCB: Callable[[tuple[float, float]], None] | None = None,
+        movedCB: Callable[[tuple[float, float]], None] | None = None,
+        changedTitleCB: Callable[[str], None] | None = None,
+        changedDisplayCB: Callable[[str], None] | None = None,
+        interval: float = 0.3
+    ):
         threading.Thread.__init__(self)
         self._win = win
         self._interval = interval
@@ -619,7 +680,7 @@ def run(self):
                     visible = self._win.isVisible
                     if self._isVisible != visible:
                         self._isVisible = visible
-                        self._isVisibleCB(visible)
+                        self._isVisibleCB(visible)  # type: ignore[arg-type]  # mypy bug
 
                 if self._isMinimizedCB:
                     minimized = self._win.isMinimized
@@ -662,7 +723,18 @@ def run(self):
                 self.kill()
                 break
 
-    def updateCallbacks(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB=None, isMaximizedCB=None, resizedCB=None, movedCB=None, changedTitleCB=None, changedDisplayCB=None):
+    def updateCallbacks(
+        self,
+        isAliveCB: Callable[[bool], None] | None = None,
+        isActiveCB: Callable[[bool], None] | None = None,
+        isVisibleCB: Callable[[bool], None] | None = None,
+        isMinimizedCB: Callable[[bool], None] | None = None,
+        isMaximizedCB: Callable[[bool], None] | None = None,
+        resizedCB: Callable[[tuple[float, float]], None] | None = None,
+        movedCB: Callable[[tuple[float, float]], None] | None = None,
+        changedTitleCB: Callable[[str], None] | None = None,
+        changedDisplayCB: Callable[[str], None] | None = None
+    ):
 
         self._isAliveCB = isAliveCB
         self._isActiveCB = isActiveCB
@@ -676,48 +748,31 @@ def updateCallbacks(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isM
 
         self._getInitialValues()
 
-    def updateInterval(self, interval=0.3):
+    def updateInterval(self, interval: float = 0.3):
         self._interval = interval
 
-    def setTryToFind(self, tryToFind):
-        if sys.platform == "darwin" and type(self._win).__name__ == MacOSWindow.__name__:
-            self._tryToFind = tryToFind
+    def setTryToFind(self, tryToFind: bool):
+        if sys.platform == "darwin" and type(self._win).__name__ == Window.__name__:
+                self._tryToFind = tryToFind
 
     def kill(self):
         self._kill.set()
 
-    def restart(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB=None, isMaximizedCB=None, resizedCB=None, movedCB=None, changedTitleCB=None, changedDisplayCB=None, interval=0.3):
+    def restart(
+        self,
+        isAliveCB: Callable[[bool], None] | None  = None,
+        isActiveCB: Callable[[bool], None] | None  = None,
+        isVisibleCB: Callable[[bool], None] | None  = None,
+        isMinimizedCB: Callable[[bool], None] | None  = None,
+        isMaximizedCB: Callable[[bool], None] | None  = None,
+        resizedCB: Callable[[tuple[float, float]], None] | None  = None,
+        movedCB: Callable[[tuple[float, float]], None] | None  = None,
+        changedTitleCB: Callable[[str], None] | None  = None,
+        changedDisplayCB: Callable[[str], None] | None  = None,
+        interval: float = 0.3
+    ):
         self.kill()
         self._kill = threading.Event()
         self.updateCallbacks(isAliveCB, isActiveCB, isVisibleCB, isMinimizedCB, isMaximizedCB, resizedCB, movedCB, changedTitleCB, changedDisplayCB)
         self.updateInterval(interval)
         self.run()
-
-
-if sys.platform == "darwin":
-    from ._pywinctl_macos import (MacOSNSWindow, MacOSWindow, checkPermissions, getActiveWindow, getActiveWindowTitle,
-                                  getAllAppsNames, getAllAppsWindowsTitles, getAllScreens, getAllTitles, getAllWindows,
-                                  getAppsWithName, getMousePos, getScreenSize, getTopWindowAt, getWindowsAt,
-                                  getWindowsWithTitle, getWorkArea)
-
-    Window = MacOSWindow
-    NSWindow = MacOSNSWindow
-
-elif sys.platform == "win32":
-    from ._pywinctl_win import (Win32Window, checkPermissions, getActiveWindow, getActiveWindowTitle, getAllAppsNames,
-                                getAllAppsWindowsTitles, getAllScreens, getAllTitles, getAllWindows, getAppsWithName,
-                                getMousePos, getScreenSize, getTopWindowAt, getWindowsAt, getWindowsWithTitle,
-                                getWorkArea)
-
-    Window = Win32Window
-
-elif sys.platform == "linux":
-    from ._pywinctl_linux import (LinuxWindow, checkPermissions, getActiveWindow, getActiveWindowTitle, getAllAppsNames,
-                                  getAllAppsWindowsTitles, getAllScreens, getAllTitles, getAllWindows, getAppsWithName,
-                                  getMousePos, getScreenSize, getTopWindowAt, getWindowsAt, getWindowsWithTitle,
-                                  getWorkArea)
-
-    Window = LinuxWindow
-
-else:
-    raise NotImplementedError('PyWinCtl currently does not support this platform. If you think you can help, please contribute! https://github.com/Kalmat/PyWinCtl')
diff --git a/src/pywinctl/_pywinctl_linux.py b/src/pywinctl/_pywinctl_linux.py
index 71da838..f4b5e27 100644
--- a/src/pywinctl/_pywinctl_linux.py
+++ b/src/pywinctl/_pywinctl_linux.py
@@ -1,5 +1,6 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
+
 from __future__ import annotations
 import sys
 assert sys.platform == "linux"
@@ -10,27 +11,29 @@
 import re
 import subprocess
 import time
-import traceback
-from typing import Iterable, cast
-
-# TODO: Create basic type stubs for Xlib
-import Xlib.X  # type: ignore[import]
-import Xlib.display  # type: ignore[import]
-import Xlib.protocol  # type: ignore[import]
-import ewmh  # type: ignore[import]
 import tkinter as tk
-from Xlib.xobject.colormap import Colormap  # type: ignore[import]
-from Xlib.xobject.cursor import Cursor  # type: ignore[import]
-from Xlib.xobject.drawable import Drawable, Pixmap, Window  # type: ignore[import]
-from Xlib.xobject.fontable import Fontable, GC, Font  # type: ignore[import]
-from Xlib.xobject.resource import Resource  # type: ignore[import]
+import traceback
+from collections.abc import Callable
+from typing import Iterable
+from typing_extensions import TypedDict
+
+import ewmh
+# All of tehse errors will be fixed with a Xlib type-stub
+# pyright: reportUnknownMemberType=false, reportUnknownVariableType=false, reportUnknownArgumentType=false, reportGeneralTypeIssues=false, reportUnknownParameterType=false
+import Xlib.display  # type: ignore[import,reportMissingTypeStubs]
+import Xlib.error  # type: ignore[import,reportMissingTypeStubs]
+import Xlib.protocol  # type: ignore[import,reportMissingTypeStubs]
+import Xlib.X  # type: ignore[import,reportMissingTypeStubs]
+import Xlib.Xutil  # type: ignore[import,reportMissingTypeStubs]
+import Xlib.Xatom  # type: ignore[import,reportMissingTypeStubs]
 from pynput import mouse
+from Xlib.xobject.drawable import Window  # type: ignore[import,reportMissingTypeStubs]
 
-from pywinctl import pointInRect, BaseWindow, Rect, Point, Size, Re, _WinWatchDog
+from . import BaseWindow, Point, Re, Rect, Size, _WinWatchDog, pointInRect
 
 DISP = Xlib.display.Display()
 SCREEN = DISP.screen()
-ROOT = SCREEN.root
+ROOT: Window = SCREEN.root
 EWMH = ewmh.EWMH(_display=DISP, root=ROOT)
 
 # WARNING: Changes are not immediately applied, specially for hide/show (unmap/map)
@@ -97,7 +100,7 @@ def getActiveWindow():
     return None
 
 
-def getActiveWindowTitle() -> str:
+def getActiveWindowTitle():
     """
     Get the title of the currently active (focused) Window
 
@@ -110,15 +113,15 @@ def getActiveWindowTitle() -> str:
         return ""
 
 
-def __remove_bad_windows(windows: Iterable[Window]):
+def __remove_bad_windows(windows: Iterable[Window | None]):
     """
     :param clients: Xlib Windows
     :return: A generator of LinuxWindow that filters out BadWindows
     """
     for window in windows:
         try:
-            yield LinuxWindow(window)
-        except Xlib.error.XResourceError:  # pyright: ignore[reportGeneralTypeIssues]  # TODO: Create basic Xlib stub
+            yield LinuxWindow(window)  # type: ignore[arg-type,reportGeneralTypeIssues]  # We expect an error here
+        except Xlib.error.XResourceError:
             pass
 
 
@@ -127,7 +130,7 @@ def getAllWindows():
     Get the list of Window objects for all visible windows
     :return: list of Window objects
     """
-    windows = cast('list[Window]', EWMH.getClientListStacking())
+    windows = EWMH.getClientListStacking()
     return [window for window in __remove_bad_windows(windows)]
 
 
@@ -140,7 +143,7 @@ def getAllTitles() -> list[str]:
     return [window.title for window in getAllWindows()]
 
 
-def getWindowsWithTitle(title, app=(), condition=Re.IS, flags=0):
+def getWindowsWithTitle(title: str | re.Pattern[str], app: tuple[str, ...] | None = (), condition: int = Re.IS, flags: int = 0):
     """
     Get the list of window objects whose title match the given string with condition and flags.
     Use ''condition'' to delimit the search. Allowed values are stored in pywinctl.Re sub-class (e.g. pywinctl.Re.CONTAINS)
@@ -165,8 +168,8 @@ def getWindowsWithTitle(title, app=(), condition=Re.IS, flags=0):
     :param flags: (optional) specific flags to apply to condition. Defaults to 0 (no flags)
     :return: list of Window objects
     """
-    matches = []
-    if title and condition in Re._cond_dic.keys():
+    matches: list[LinuxWindow] = []
+    if title and condition in Re._cond_dic:
         lower = False
         if condition in (Re.MATCH, Re.NOTMATCH):
             title = re.compile(title, flags)
@@ -175,6 +178,8 @@ def getWindowsWithTitle(title, app=(), condition=Re.IS, flags=0):
                 flags = 90
         elif flags == Re.IGNORECASE:
             lower = True
+            if isinstance(title, re.Pattern):
+                title = title.pattern
             title = title.lower()
         for win in getAllWindows():
             if win.title and Re._cond_dic[condition](title, win.title.lower() if lower else win.title, flags)  \
@@ -189,10 +194,10 @@ def getAllAppsNames() -> list[str]:
 
     :return: list of names as strings
     """
-    return list(getAllAppsWindowsTitles().keys())
+    return list(getAllAppsWindowsTitles())
 
 
-def getAppsWithName(name, condition=Re.IS, flags=0):
+def getAppsWithName(name: str | re.Pattern[str], condition: int = Re.IS, flags: int = 0):
     """
     Get the list of app names which match the given string using the given condition and flags.
     Use ''condition'' to delimit the search. Allowed values are stored in pywinctl.Re sub-class (e.g. pywinctl.Re.CONTAINS)
@@ -216,8 +221,8 @@ def getAppsWithName(name, condition=Re.IS, flags=0):
     :param flags: (optional) specific flags to apply to condition. Defaults to 0 (no flags)
     :return: list of app names
     """
-    matches = []
-    if name and condition in Re._cond_dic.keys():
+    matches: list[str] = []
+    if name and condition in Re._cond_dic:
         lower = False
         if condition in (Re.MATCH, Re.NOTMATCH):
             name = re.compile(name, flags)
@@ -226,6 +231,8 @@ def getAppsWithName(name, condition=Re.IS, flags=0):
                 flags = 90
         elif flags == Re.IGNORECASE:
             lower = True
+            if isinstance(name, re.Pattern):
+                name = name.pattern
             name = name.lower()
         for title in getAllAppsNames():
             if title and Re._cond_dic[condition](name, title.lower() if lower else title, flags):
@@ -233,7 +240,7 @@ def getAppsWithName(name, condition=Re.IS, flags=0):
     return matches
 
 
-def getAllAppsWindowsTitles() -> dict:
+def getAllAppsWindowsTitles():
     """
     Get all visible apps names and their open windows titles
 
@@ -244,10 +251,10 @@ def getAllAppsWindowsTitles() -> dict:
 
     :return: python dictionary
     """
-    result = {}
+    result: dict[str, list[str]] = {}
     for win in getAllWindows():
         appName = win.getAppName()
-        if appName in result.keys():
+        if appName in result:
             result[appName].append(win.title)
         else:
             result[appName] = [win.title]
@@ -276,7 +283,7 @@ def getTopWindowAt(x: int, y: int):
     :param y: Y screen coordinate of the window
     :return: Window object or None
     """
-    windows = cast('list[Window]', EWMH.getClientListStacking())
+    windows = EWMH.getClientListStacking()
     for window in __remove_bad_windows(reversed(windows)):
         if pointInRect(x, y, window.left, window.top, window.width, window.height):
             return window
@@ -287,16 +294,16 @@ def getTopWindowAt(x: int, y: int):
 def _xlibGetAllWindows(parent: Window | None = None, title: str = ""):
     # Not using window class (get_wm_class())
 
-    parentWindow = parent or ROOT
-    allWindows = [parentWindow]
+    parent = parent or ROOT
+    allWindows = [parent]
 
-    def findit(hwnd):
+    def findit(hwnd: Window):
         query = hwnd.query_tree()
         for child in query.children:
             allWindows.append(child)
             findit(child)
 
-    findit(parentWindow)
+    findit(parent)
     if not title:
         return allWindows
     else:
@@ -335,9 +342,8 @@ def _getWindowAttributes(hWnd: Window):
     # https://gist.github.com/ssokolow/e7c9aae63fb7973e4d64cff969a78ae8
     # https://stackoverflow.com/questions/36188154/get-x11-window-caption-height
 
+    from ctypes import Structure, byref, c_int32, c_uint32, c_ulong, cdll
     from ctypes.util import find_library
-    from ctypes import (
-        Structure, byref, c_int32, c_uint32, c_ulong, cdll)
 
     x11 = find_library('X11')
     xlib = cdll.LoadLibrary(str(x11))
@@ -402,15 +408,10 @@ class LinuxWindow(BaseWindow):
     def _rect(self):
         return self.__rect
 
-    @_rect.setter
-    def _rect(self, value):
-        self.__rect = value
-
-    # hWnd used to be typed as: Cursor, Drawable, Pixmap, Resource, Fontable, GC, Colormap, Font
     def __init__(self, hWnd: Window):
         super().__init__()
-        self._hWnd = hWnd
-        self._parent = self._hWnd.query_tree().parent
+        self._hWnd: Window = hWnd
+        self._parent: Window = self._hWnd.query_tree().parent
         self.__rect = self._rectFactory()
         # self._saveWindowInitValues()  # Store initial Window parameters to allow reset and other actions
         self.watchdog = self._WatchDog(self)
@@ -431,7 +432,7 @@ def _getWindowRect(self) -> Rect:
             win = parent
         w = geom.width
         h = geom.height
-        # ww = DISP.create_resource_object('window', self._hWnd)
+        # ww = DISP.create_resource_object('window', self._hWnd.id)
         # ret = ww.translate_coords(self._hWnd, x, y)
         return Rect(x, y, x + w, y + h)
 
@@ -446,11 +447,8 @@ def getExtraFrameSize(self, includeBorder: bool = True) -> tuple[int, int, int,
         a = DISP.intern_atom("_GTK_FRAME_EXTENTS", True)
         if not a:
             a = DISP.intern_atom("_NET_FRAME_EXTENTS", True)
-        ret = self._hWnd.get_property(a, Xlib.X.AnyPropertyType, 0, 32)
-        if ret:
-            ret = ret.value
-        else:
-            ret = (0, 0, 0, 0)
+        get_property = self._hWnd.get_property(a, Xlib.X.AnyPropertyType, 0, 32)
+        ret: tuple[int, int, int, int] = get_property.value if get_property else (0, 0, 0, 0)
         borderWidth = 0
         if includeBorder:
             titleHeight, borderWidth = _getBorderSizes()
@@ -482,7 +480,7 @@ def getClientFrame(self):
     def __repr__(self):
         return '%s(hWnd=%s)' % (self.__class__.__name__, self._hWnd)
 
-    def __eq__(self, other):
+    def __eq__(self, other: object) -> bool:
         return isinstance(other, LinuxWindow) and self._hWnd == other._hWnd
 
     def close(self) -> bool:
@@ -507,7 +505,7 @@ def minimize(self, wait: bool = False) -> bool:
         """
         if not self.isMinimized:
             prop = DISP.intern_atom(WM_CHANGE_STATE, False)
-            data = (32, [Xlib.Xutil.IconicState, 0, 0, 0, 0])  # pyright: ignore[reportGeneralTypeIssues]  # Missing Xlib typing
+            data = (32, [Xlib.Xutil.IconicState, 0, 0, 0, 0])
             ev = Xlib.protocol.event.ClientMessage(window=self._hWnd.id, client_type=prop, data=data)
             mask = Xlib.X.SubstructureRedirectMask | Xlib.X.SubstructureNotifyMask
             ROOT.send_event(event=ev, event_mask=mask)
@@ -558,7 +556,7 @@ def show(self, wait: bool = False) -> bool:
         :param wait: set to ''True'' to wait until action is confirmed (in a reasonable time lap)
         :return: ''True'' if window showed
         """
-        win = DISP.create_resource_object('window', self._hWnd)
+        win = DISP.create_resource_object('window', self._hWnd.id)
         win.map()
         DISP.flush()
         win.map_sub_windows()
@@ -576,7 +574,7 @@ def hide(self, wait: bool = False) -> bool:
         :param wait: set to ''True'' to wait until action is confirmed (in a reasonable time lap)
         :return: ''True'' if window hidden
         """
-        win = DISP.create_resource_object('window', self._hWnd)
+        win = DISP.create_resource_object('window', self._hWnd.id)
         win.unmap_sub_windows()
         DISP.flush()
         win.unmap()
@@ -587,7 +585,7 @@ def hide(self, wait: bool = False) -> bool:
             time.sleep(WAIT_DELAY * retries)
         return not self._isMapped
 
-    def activate(self, wait: bool = False) -> bool:
+    def activate(self, wait: bool = False):
         """
         Activate this window and make it the foreground (focused) window
 
@@ -607,7 +605,7 @@ def activate(self, wait: bool = False) -> bool:
             time.sleep(WAIT_DELAY * retries)
         return self.isActive
 
-    def resize(self, widthOffset: int, heightOffset: int, wait: bool = False) -> bool:
+    def resize(self, widthOffset: int, heightOffset: int, wait: bool = False):
         """
         Resizes the window relative to its current size
 
@@ -618,14 +616,14 @@ def resize(self, widthOffset: int, heightOffset: int, wait: bool = False) -> boo
 
     resizeRel = resize  # resizeRel is an alias for the resize() method.
 
-    def resizeTo(self, newWidth: int, newHeight: int, wait: bool = False) -> bool:
+    def resizeTo(self, newWidth: int, newHeight: int, wait: bool = False):
         """
         Resizes the window to a new width and height
 
         :param wait: set to ''True'' to wait until action is confirmed (in a reasonable time lap)
         :return: ''True'' if window resized to the given size
         """
-        EWMH.setMoveResizeWindow(self._hWnd, x=self.left, y=self.top, w=newWidth, h=newHeight)
+        EWMH.setMoveResizeWindow(self._hWnd, x=int(self.left), y=int(self.top), w=newWidth, h=newHeight)
         EWMH.display.flush()
         retries = 0
         while wait and retries < WAIT_ATTEMPTS and (self.width != newWidth or self.height != newHeight):
@@ -633,7 +631,7 @@ def resizeTo(self, newWidth: int, newHeight: int, wait: bool = False) -> bool:
             time.sleep(WAIT_DELAY * retries)
         return self.width == newWidth and self.height == newHeight
 
-    def move(self, xOffset: int, yOffset: int, wait: bool = False) -> bool:
+    def move(self, xOffset: int, yOffset: int, wait: bool = False):
         """
         Moves the window relative to its current position
 
@@ -646,7 +644,7 @@ def move(self, xOffset: int, yOffset: int, wait: bool = False) -> bool:
 
     moveRel = move  # moveRel is an alias for the move() method.
 
-    def moveTo(self, newLeft: int, newTop: int, wait: bool = False) -> bool:
+    def moveTo(self, newLeft: int, newTop: int, wait: bool = False):
         """
         Moves the window to new coordinates on the screen
 
@@ -655,7 +653,7 @@ def moveTo(self, newLeft: int, newTop: int, wait: bool = False) -> bool:
         """
         newLeft = max(0, newLeft)  # Xlib/EWMH won't accept negative positions
         newTop = max(0, newTop)
-        EWMH.setMoveResizeWindow(self._hWnd, x=newLeft, y=newTop, w=self.width, h=self.height)
+        EWMH.setMoveResizeWindow(self._hWnd, x=newLeft, y=newTop, w=int(self.width), h=int(self.height))
         EWMH.display.flush()
         retries = 0
         while wait and retries < WAIT_ATTEMPTS and (self.left != newLeft or self.top != newTop):
@@ -663,7 +661,7 @@ def moveTo(self, newLeft: int, newTop: int, wait: bool = False) -> bool:
             time.sleep(WAIT_DELAY * retries)
         return self.left == newLeft and self.top == newTop
 
-    def _moveResizeTo(self, newLeft: int, newTop: int, newWidth: int, newHeight: int) -> bool:
+    def _moveResizeTo(self, newLeft: int, newTop: int, newWidth: int, newHeight: int):
         newLeft = max(0, newLeft)  # Xlib/EWMH won't accept negative positions
         newTop = max(0, newTop)
         EWMH.setMoveResizeWindow(self._hWnd, x=newLeft, y=newTop, w=newWidth, h=newHeight)
@@ -700,11 +698,11 @@ def lowerWindow(self) -> bool:
 
         :return: ''True'' if window lowered
         """
-        w = DISP.create_resource_object('window', self._hWnd)
+        w = DISP.create_resource_object('window', self._hWnd.id)
         w.configure(stack_mode=Xlib.X.Below)
         DISP.flush()
         windows = EWMH.getClientListStacking()
-        return windows and self._hWnd == windows[-1]
+        return bool(windows and self._hWnd == windows[-1])
 
     def raiseWindow(self) -> bool:
         """
@@ -712,11 +710,11 @@ def raiseWindow(self) -> bool:
 
         :return: ''True'' if window raised
         """
-        w = DISP.create_resource_object('window', self._hWnd)
+        w = DISP.create_resource_object('window', self._hWnd.id)
         w.configure(stack_mode=Xlib.X.Above)
         DISP.flush()
         windows = EWMH.getClientListStacking()
-        return windows and self._hWnd == windows[0]
+        return bool(windows and self._hWnd == windows[0])
 
     def sendBehind(self, sb: bool = True) -> bool:
         """
@@ -732,7 +730,7 @@ def sendBehind(self, sb: bool = True) -> bool:
         """
         if sb:
             # https://stackoverflow.com/questions/58885803/can-i-use-net-wm-window-type-dock-ewhm-extension-in-openbox
-            w = DISP.create_resource_object('window', self._hWnd)
+            w = DISP.create_resource_object('window', self._hWnd.id)
             w.unmap()
             # Place a Window behind desktop icons using PyQt on Ubuntu/GNOME
             # This adds the properties (notice the PropMode options), but with no effect on GNOME
@@ -749,7 +747,7 @@ def sendBehind(self, sb: bool = True) -> bool:
             # self._hWnd.delete_property(DISP.intern_atom('WM_TAKE_FOCUS', False))
             # DISP.flush()
             # This sends window below all others, but not behind the desktop icons
-            w.change_property(DISP.intern_atom(WM_WINDOW_TYPE, False), Xlib.Xatom.ATOM,  # pyright: ignore[reportGeneralTypeIssues]  # Missing Xlib typing
+            w.change_property(DISP.intern_atom(WM_WINDOW_TYPE, False), Xlib.Xatom.ATOM,
                               32, [DISP.intern_atom(WINDOW_DESKTOP, False), ],
                               Xlib.X.PropModeReplace)
             DISP.flush()
@@ -772,13 +770,13 @@ def sendBehind(self, sb: bool = True) -> bool:
                 m.click(mouse.Button.left, 1)
             return WINDOW_DESKTOP in EWMH.getWmWindowType(self._hWnd, str=True)
         else:
-            w = DISP.create_resource_object('window', self._hWnd)
+            w = DISP.create_resource_object('window', self._hWnd.id)
             w.unmap()
-            w.change_property(DISP.intern_atom(WM_WINDOW_TYPE, False), Xlib.Xatom.ATOM,  # pyright: ignore[reportGeneralTypeIssues]  # Missing Xlib typing
+            w.change_property(DISP.intern_atom(WM_WINDOW_TYPE, False), Xlib.Xatom.ATOM,
                               32, [DISP.intern_atom(WINDOW_NORMAL, False), ],
                               Xlib.X.PropModeReplace)
             DISP.flush()
-            w.change_property(DISP.intern_atom(WM_STATE, False), Xlib.Xatom.ATOM,  # pyright: ignore[reportGeneralTypeIssues]  # Missing Xlib typing
+            w.change_property(DISP.intern_atom(WM_STATE, False), Xlib.Xatom.ATOM,
                               32, [DISP.intern_atom(STATE_FOCUSED, False), ],
                               Xlib.X.PropModeReplace)
             DISP.flush()
@@ -799,13 +797,13 @@ def getAppName(self) -> str:
             stdout, stderr = p.communicate()
         return stdout.decode(encoding="utf8").replace("\n", "")
 
-    def getParent(self) -> Cursor | Drawable | Pixmap | Resource | Fontable | Window | GC | Colormap | Font:
+    def getParent(self) -> Window:
         """
         Get the handle of the current window parent. It can be another window or an application
 
         :return: handle of the window parent
         """
-        return self._hWnd.query_tree().parent
+        return self._hWnd.query_tree().parent  # type: ignore[no-any-return]
 
     def getChildren(self) -> list[int]:
         """
@@ -813,10 +811,10 @@ def getChildren(self) -> list[int]:
 
         :return: list of handles
         """
-        w = DISP.create_resource_object('window', self._hWnd)
-        return w.query_tree().children
+        w = DISP.create_resource_object('window', self._hWnd.id)
+        return w.query_tree().children  # type: ignore[no-any-return]
 
-    def getHandle(self) -> Cursor | Drawable | Pixmap | Resource | Fontable | Window | GC | Colormap | Font:
+    def getHandle(self):
         """
         Get the current window handle
 
@@ -831,10 +829,10 @@ def isParent(self, child: Window) -> bool:
         ----
             ''child'' handle of the window you want to check if the current window is parent of
         """
-        return child.query_tree().parent == self._hWnd
+        return child.query_tree().parent == self._hWnd  # type: ignore[no-any-return]
     isParentOf = isParent  # isParentOf is an alias of isParent method
 
-    def isChild(self, parent: Cursor | Drawable | Pixmap | Resource | Fontable | Window | GC | Colormap | Font) -> bool:
+    def isChild(self, parent: Window):
         """
         Check if current window is child of given window/app (handle)
 
@@ -852,7 +850,7 @@ def getDisplay(self):
         """
         screens = getAllScreens()
         name = ""
-        for key in screens.keys():
+        for key in screens:
             if pointInRect(self.centerx, self.centery, screens[key]["pos"].x, screens[key]["pos"].y, screens[key]["size"].width, screens[key]["size"].height):
                 name = key
                 break
@@ -879,7 +877,7 @@ def isMaximized(self) -> bool:
         return STATE_MAX_VERT in state and STATE_MAX_HORZ in state
 
     @property
-    def isActive(self) -> bool:
+    def isActive(self):
         """
         Check if current window is currently the active, foreground window
 
@@ -907,9 +905,9 @@ def visible(self) -> bool:
 
         :return: ``True`` if the window is currently visible
         """
-        win = DISP.create_resource_object('window', self._hWnd)
+        win = DISP.create_resource_object('window', self._hWnd.id)
         state = win.get_attributes().map_state
-        return state == Xlib.X.IsViewable
+        return state == Xlib.X.IsViewable  # type: ignore[no-any-return]
 
     isVisible = visible  # isVisible is an alias for the visible property.
 
@@ -922,18 +920,18 @@ def isAlive(self) -> bool:
         """
         ret = True
         try:
-            win = DISP.create_resource_object('window', self._hWnd)
+            win = DISP.create_resource_object('window', self._hWnd.id)
             state = win.get_attributes().map_state
-        except Xlib.error.BadWindow:  # pyright: ignore[reportGeneralTypeIssues]  # Missing Xlib typing
+        except Xlib.error.BadWindow:
             ret = False
         return ret
 
     @property
     def _isMapped(self) -> bool:
         # Returns ``True`` if the window is currently mapped
-        win = DISP.create_resource_object('window', self._hWnd)
+        win = DISP.create_resource_object('window', self._hWnd.id)
         state = win.get_attributes().map_state
-        return state != Xlib.X.IsUnmapped
+        return state != Xlib.X.IsUnmapped  # type: ignore[no-any-return]
 
     class _WatchDog:
         """
@@ -950,13 +948,23 @@ class _WatchDog:
         :meth kill: Stop the entire watchdog and all its hooks
         :meth isAlive: Check if watchdog is running
         """
-        def __init__(self, parent):
+        def __init__(self, parent: LinuxWindow):
             self._watchdog = None
             self._parent = parent
 
-        def start(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB=None,
-                  isMaximizedCB=None, resizedCB=None, movedCB=None, changedTitleCB=None, changedDisplayCB=None,
-                  interval=0.3):
+        def start(
+            self,
+            isAliveCB: Callable[[bool], None] | None = None,
+            isActiveCB: Callable[[bool], None] | None = None,
+            isVisibleCB: Callable[[bool], None] | None = None,
+            isMinimizedCB: Callable[[bool], None] | None = None,
+            isMaximizedCB: Callable[[bool], None] | None = None,
+            resizedCB: Callable[[tuple[float, float]], None] | None = None,
+            movedCB: Callable[[tuple[float, float]], None] | None = None,
+            changedTitleCB: Callable[[str], None] | None = None,
+            changedDisplayCB: Callable[[str], None] | None = None,
+            interval: float = 0.3
+        ):
             """
             Initialize and start watchdog and hooks (callbacks to be invoked when desired window states change)
 
@@ -999,9 +1007,18 @@ def start(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB
                                        isMaximizedCB, resizedCB, movedCB, changedTitleCB, changedDisplayCB,
                                        interval)
 
-        def updateCallbacks(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB=None,
-                                    isMaximizedCB=None, resizedCB=None, movedCB=None, changedTitleCB=None,
-                                    changedDisplayCB=None):
+        def updateCallbacks(
+            self,
+            isAliveCB: Callable[[bool], None] | None = None,
+            isActiveCB: Callable[[bool], None] | None = None,
+            isVisibleCB: Callable[[bool], None] | None = None,
+            isMinimizedCB: Callable[[bool], None] | None = None,
+            isMaximizedCB: Callable[[bool], None] | None = None,
+            resizedCB: Callable[[tuple[float, float]], None] | None = None,
+            movedCB: Callable[[tuple[float, float]], None] | None = None,
+            changedTitleCB: Callable[[str], None] | None = None,
+            changedDisplayCB: Callable[[str], None] | None = None
+        ):
             """
             Change the states this watchdog is hooked to
 
@@ -1034,7 +1051,7 @@ def updateCallbacks(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isM
                 self._watchdog.updateCallbacks(isAliveCB, isActiveCB, isVisibleCB, isMinimizedCB, isMaximizedCB,
                                               resizedCB, movedCB, changedTitleCB, changedDisplayCB)
 
-        def updateInterval(self, interval=0.3):
+        def updateInterval(self, interval: float = 0.3):
             """
             Change the interval to check changes
 
@@ -1076,6 +1093,17 @@ def isAlive(self):
                 alive = False
             return alive
 
+class _ScreenValue(TypedDict):
+    id: int
+    is_primary: bool
+    pos: Point
+    size: Size
+    workarea: Rect
+    scale: tuple[int, int]
+    dpi: tuple[int, int]
+    orientation: int
+    frequency: float
+    colordepth: int
 
 def getAllScreens():
     """
@@ -1111,7 +1139,7 @@ def getAllScreens():
     """
     # https://stackoverflow.com/questions/8705814/get-display-count-and-resolution-for-each-display-in-python-without-xrandr
     # https://www.x.org/releases/X11R7.7/doc/libX11/libX11/libX11.html#Obtaining_Information_about_the_Display_Image_Formats_or_Screens
-    result = {}
+    result: dict[str, _ScreenValue] = {}
     for i in range(DISP.screen_count()):
         try:
             screen = DISP.screen(i)
@@ -1129,7 +1157,7 @@ def getAllScreens():
 
                 if crtc and crtc.mode:  # displays with empty (0) mode seem not to be valid
                     name = params.name
-                    if name in result.keys():
+                    if name in result:
                         name = name + str(i)
                     id = crtc.sequence_number
                     x, y, w, h = crtc.x, crtc.y, crtc.width, crtc.height
@@ -1139,7 +1167,7 @@ def getAllScreens():
                     dpiX, dpiY = round(w * 25.4 / screen.width_in_mms), round(h * 25.4 / screen.height_in_mms)
                     scaleX, scaleY = round(dpiX / 96 * 100), round(dpiY / 96 * 100)
                     rot = int(math.log(crtc.rotation, 2))
-                    freq = 0
+                    freq = 0.0
                     for mode in modes:
                         if crtc.mode == mode.id:
                             freq = mode.dot_clock / (mode.h_total * mode.v_total)
@@ -1187,7 +1215,7 @@ def getScreenSize(name: str = "") -> Size:
     """
     screens = getAllScreens()
     res = Size(0, 0)
-    for key in screens.keys():
+    for key in screens:
         if (name and key == name) or (not name):
             res = screens[key]["size"]
             break
@@ -1204,7 +1232,7 @@ def getWorkArea(name: str = "") -> Rect:
     """
     screens = getAllScreens()
     res = Rect(0, 0, 0, 0)
-    for key in screens.keys():
+    for key in screens:
         if (name and key == name) or (not name):
             res = screens[key]["workarea"]
             break
diff --git a/src/pywinctl/_pywinctl_macos.py b/src/pywinctl/_pywinctl_macos.py
index b36e646..ded8281 100644
--- a/src/pywinctl/_pywinctl_macos.py
+++ b/src/pywinctl/_pywinctl_macos.py
@@ -1,5 +1,7 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
+# Incomplete type stubs for pyobjc
+# mypy: disable_error_code = no-any-return
 from __future__ import annotations
 import sys
 assert sys.platform == "darwin"
@@ -12,14 +14,16 @@
 import time
 import traceback
 import threading
+from collections.abc import Iterable, Sequence, Callable
+from typing import Any, cast, overload
+from typing_extensions import TypeAlias, TypedDict, Literal
 
-# https://github.com/ronaldoussoren/pyobjc/issues/198
-# https://github.com/ronaldoussoren/pyobjc/issues/417
-# https://github.com/ronaldoussoren/pyobjc/issues/419
-import AppKit  # type: ignore[import]
-import Quartz  # type: ignore[import]
+import AppKit
+import Quartz
 
-from pywinctl import pointInRect, BaseWindow, Rect, Point, Size, Re, _WinWatchDog
+from . import pointInRect, BaseWindow, Rect, Point, Size, Re, _WinWatchDog
+
+Incomplete: TypeAlias = Any
 
 WS = AppKit.NSWorkspace.sharedWorkspace()
 WAIT_ATTEMPTS = 10
@@ -60,6 +64,10 @@ def checkPermissions(activate: bool = False):
     ret = ret.replace("\n", "")
     return ret == "true"
 
+@overload
+def getActiveWindow(app: AppKit.NSApplication) -> MacOSNSWindow | None: ...
+@overload
+def getActiveWindow(app: None = ...) -> MacOSWindow | None: ...
 
 def getActiveWindow(app: AppKit.NSApplication | None = None):
     """
@@ -94,10 +102,10 @@ def getActiveWindow(app: AppKit.NSApplication | None = None):
         appID = entries[0]
         title = ", ".join(entries[1:])
         if appID:  # and title:
-            apps = _getAllApps()
-            for app in apps:
-                if str(app.processIdentifier()) == appID:  # pyright: ignore[reportOptionalMemberAccess]  # Is Unknown, but not None
-                    return MacOSWindow(app, title)
+            activeApps = _getAllApps()
+            for activeApp in activeApps:
+                if str(activeApp.processIdentifier()) == appID:
+                    return MacOSWindow(activeApp, title)
     else:
         for win in app.orderedWindows():  # .keyWindow() / .mainWindow() not working?!?!?!
             return MacOSNSWindow(app, win)
@@ -117,6 +125,10 @@ def getActiveWindowTitle(app: AppKit.NSApplication | None = None) -> str:
     else:
         return ""
 
+@overload
+def getAllWindows(app: AppKit.NSApplication) -> list[MacOSNSWindow]: ...
+@overload
+def getAllWindows(app: None = ...) -> list[MacOSWindow]: ...
 
 def getAllWindows(app: AppKit.NSApplication | None = None):
     """
@@ -125,8 +137,8 @@ def getAllWindows(app: AppKit.NSApplication | None = None):
     :param app: (optional) NSApp() object. If passed, returns the Window objects of all windows of given app
     :return: list of Window objects
     """
-    windows: list[MacOSNSWindow | MacOSWindow] = []
     if not app:
+        windows: list[MacOSWindow] = []
         activeApps = _getAllApps()
         titleList = _getWindowTitles()
         for item in titleList:
@@ -143,14 +155,16 @@ def getAllWindows(app: AppKit.NSApplication | None = None):
                     rect = None
             except:
                 continue
-            for app in activeApps:
-                if app.processIdentifier() == pID:  # pyright: ignore[reportOptionalMemberAccess]  # Is Unknown, but not None
-                    windows.append(MacOSWindow(app, title, rect))
+            for activeApp in activeApps:
+                if activeApp.processIdentifier() == pID:
+                    windows.append(MacOSWindow(activeApp, title, rect))
                     break
+        return windows
     else:
+        nsWindows: list[MacOSNSWindow] = []
         for win in app.orderedWindows():
-            windows.append(MacOSNSWindow(app, win))
-    return windows
+            nsWindows.append(MacOSNSWindow(app, win))
+        return nsWindows
 
 
 def getAllTitles(app: AppKit.NSApplication | None = None):
@@ -170,7 +184,7 @@ def getAllTitles(app: AppKit.NSApplication | None = None):
                                 return winNames'"""
         ret = subprocess.check_output(cmd, shell=True).decode(encoding="utf-8").replace("\n", "").replace("{", "[").replace("}", "]")
         res = ast.literal_eval(ret)
-        matches = []
+        matches: list[str] = []
         if len(res) > 0:
             for item in res[0]:
                 for title in item:
@@ -179,8 +193,12 @@ def getAllTitles(app: AppKit.NSApplication | None = None):
         matches = [win.title for win in getAllWindows(app)]
     return matches
 
+@overload
+def getWindowsWithTitle(title: str | re.Pattern[str], app: tuple[str, ...] | None = ..., condition: int = ..., flags: int = ...) -> list[MacOSWindow]: ...
+@overload
+def getWindowsWithTitle(title: str | re.Pattern[str], app: AppKit.NSApp, condition: int = ..., flags: int = ...) -> list[MacOSNSWindow]: ...
 
-def getWindowsWithTitle(title, app=(), condition=Re.IS, flags=0):
+def getWindowsWithTitle(title: str | re.Pattern[str], app: AppKit.NSApp | tuple[str, ...] | None = None, condition: int = Re.IS, flags: int = 0):
     """
     Get the list of window objects whose title match the given string with condition and flags.
     Use ''condition'' to delimit the search. Allowed values are stored in pywinctl.Re sub-class (e.g. pywinctl.Re.CONTAINS)
@@ -205,36 +223,43 @@ def getWindowsWithTitle(title, app=(), condition=Re.IS, flags=0):
     :param flags: (optional) specific flags to apply to condition. Defaults to 0 (no flags)
     :return: list of Window objects
     """
-    matches = []
-    if title and condition in Re._cond_dic.keys():
-        lower = False
-        if condition in (Re.MATCH, Re.NOTMATCH):
-            title = re.compile(title, flags)
-        elif condition in (Re.EDITDISTANCE, Re.DIFFRATIO):
-            if not isinstance(flags, int) or not (0 < flags <= 100):
-                flags = 90
-        elif flags == Re.IGNORECASE:
-            lower = True
-            title = title.lower()
-        if not app or (app and isinstance(app, tuple)):
-            activeApps = _getAllApps()
-            titleList = _getWindowTitles()
-            for item in titleList:
-                pID = item[0]
-                winTitle = item[1].lower() if lower else item[1]
-                if winTitle and Re._cond_dic[condition](title, winTitle, flags):
-                    x, y, w, h = int(item[2][0]), int(item[2][1]), int(item[3][0]), int(item[3][1])
-                    rect = Rect(x, y, x + w, y + h)
-                    for a in activeApps:
-                        if (not app and a.processIdentifier() == pID) or a.localizedName() in app:
-                            matches.append(MacOSWindow(a, item[1], rect))
-                            break
-        else:
-            windows = getAllWindows(app)
-            for win in windows:
-                if win.title and Re._cond_dic[condition](title, win.title.lower() if lower else win.title, flags):
-                    matches.append(win)
-    return matches
+
+    if not (title and condition in Re._cond_dic):
+        return []  # pyright: ignore[reportUnknownVariableType]  # Type doesn't matter here
+
+    lower = False
+    if condition in (Re.MATCH, Re.NOTMATCH):
+        title = re.compile(title, flags)
+    elif condition in (Re.EDITDISTANCE, Re.DIFFRATIO):
+        if not isinstance(flags, int) or not (0 < flags <= 100):
+            flags = 90
+    elif flags == Re.IGNORECASE:
+        lower = True
+        if isinstance(title, re.Pattern):
+            title = title.pattern
+        title = title.lower()
+
+    if app is None or isinstance(app, tuple):
+        matches: list[MacOSWindow] = []
+        activeApps = _getAllApps()
+        titleList = _getWindowTitles()
+        for item in titleList:
+            pID = item[0]
+            winTitle = item[1].lower() if lower else item[1]
+            if winTitle and Re._cond_dic[condition](title, winTitle, flags):
+                x, y, w, h = int(item[2][0]), int(item[2][1]), int(item[3][0]), int(item[3][1])
+                rect = Rect(x, y, x + w, y + h)
+                for a in activeApps:
+                    if (app and a.localizedName() in app) or (a.processIdentifier() == pID):
+                        matches.append(MacOSWindow(a, item[1], rect))
+                        break
+        return matches
+    else:
+        return [
+            win for win
+            in getAllWindows(app)
+            if win.title and Re._cond_dic[condition](title, win.title.lower() if lower else win.title, flags)
+        ]
 
 
 def getAllAppsNames() -> list[str]:
@@ -255,7 +280,7 @@ def getAllAppsNames() -> list[str]:
     return res or []
 
 
-def getAppsWithName(name, condition=Re.IS, flags=0):
+def getAppsWithName(name: str | re.Pattern[str], condition: int = Re.IS, flags: int = 0):
     """
     Get the list of app names which match the given string using the given condition and flags.
     Use ''condition'' to delimit the search. Allowed values are stored in pywinctl.Re sub-class (e.g. pywinctl.Re.CONTAINS)
@@ -279,8 +304,8 @@ def getAppsWithName(name, condition=Re.IS, flags=0):
     :param flags: (optional) specific flags to apply to condition. Defaults to 0 (no flags)
     :return: list of app names
     """
-    matches = []
-    if name and condition in Re._cond_dic.keys():
+    matches: list[str] = []
+    if name and condition in Re._cond_dic:
         lower = False
         if condition in (Re.MATCH, Re.NOTMATCH):
             name = re.compile(name, flags)
@@ -289,6 +314,8 @@ def getAppsWithName(name, condition=Re.IS, flags=0):
                 flags = 90
         elif flags == Re.IGNORECASE:
             lower = True
+            if isinstance(name, re.Pattern):
+                name = name.pattern
             name = name.lower()
         for title in getAllAppsNames():
             if title and Re._cond_dic[condition](name, title.lower() if lower else title, flags):
@@ -296,7 +323,7 @@ def getAppsWithName(name, condition=Re.IS, flags=0):
     return matches
 
 
-def getAllAppsWindowsTitles() -> dict:
+def getAllAppsWindowsTitles():
     """
     Get all visible apps names and their open windows titles
 
@@ -315,15 +342,22 @@ def getAllAppsWindowsTitles() -> dict:
                                 end tell
                                 return winNames'"""
     ret = subprocess.check_output(cmd, shell=True).decode(encoding="utf-8").replace("\n", "").replace("{", "[").replace("}", "]")
-    res = ast.literal_eval(ret)
-    result = {}
+    res: tuple[list[str], list[list[str]]] = ast.literal_eval(ret)
+    result: dict[str, list[str]] = {}
     if res and len(res) > 0:
         for i, item in enumerate(res[0]):
             result[item] = res[1][i]
     return result
 
 
-def getWindowsAt(x: int, y: int, app: AppKit.NSApplication | None = None, allWindows: list[MacOSNSWindow | MacOSWindow] | None = None):
+@overload
+def getWindowsAt(x: int, y: int, app: AppKit.NSApplication, allWindows: list[MacOSNSWindow] | None = ...) -> list[MacOSNSWindow]: ...
+@overload
+def getWindowsAt(x: int, y: int, app: None = ..., allWindows: list[MacOSWindow] | None = ...) -> list[MacOSWindow]: ...
+@overload
+def getWindowsAt(x: int, y: int, app: AppKit.NSApplication | None = ..., allWindows: list[MacOSWindow | MacOSNSWindow] | list[MacOSNSWindow] | list[MacOSWindow] | None = ...) -> list[MacOSWindow | MacOSNSWindow] | list[MacOSNSWindow] | list[MacOSWindow]: ...
+
+def getWindowsAt(x: int, y: int, app: AppKit.NSApplication | None = None, allWindows: list[MacOSNSWindow | MacOSWindow] | list[MacOSNSWindow] | list[MacOSWindow] | None = None) -> list[MacOSWindow | MacOSNSWindow] | list[MacOSNSWindow] | list[MacOSWindow]:
     """
     Get the list of Window objects whose windows contain the point ``(x, y)`` on screen
 
@@ -333,16 +367,21 @@ def getWindowsAt(x: int, y: int, app: AppKit.NSApplication | None = None, allWin
     :param allWindows: (optional) list of window objects (required to improve performance in Apple Script version)
     :return: list of Window objects
     """
-    if not allWindows:
-        allWindows = getAllWindows(app)
-    windowBoxGenerator = ((window, window.box) for window in allWindows)
+    windows = allWindows if allWindows else getAllWindows(app)
+    windowBoxGenerator = ((window, window.box) for window in windows)
     return [
         window for (window, box)
         in windowBoxGenerator
         if pointInRect(x, y, box.left, box.top, box.width, box.height)]
 
+@overload
+def getTopWindowAt(x: int, y: int, app: AppKit.NSApplication, allWindows: list[MacOSNSWindow] | None = ...) -> MacOSNSWindow | None: ...
+@overload
+def getTopWindowAt(x: int, y: int, app: None = ..., allWindows: list[MacOSWindow] | None = ...) -> MacOSWindow | None: ...
+@overload
+def getTopWindowAt(x: int, y: int, app: AppKit.NSApplication | None = ..., allWindows: list[MacOSWindow | MacOSNSWindow] | list[MacOSNSWindow] | list[MacOSWindow] | None = ...) -> MacOSWindow | MacOSNSWindow | None: ...
 
-def getTopWindowAt(x: int, y: int, app: AppKit.NSApplication | None = None, allWindows: list[MacOSNSWindow | MacOSWindow] | None = None):
+def getTopWindowAt(x: int, y: int, app: AppKit.NSApplication | None = None, allWindows: list[MacOSNSWindow | MacOSWindow] | list[MacOSNSWindow] | list[MacOSWindow] | None = None):
     """
     Get *a* Window object at the point ``(x, y)`` on screen.
     Which window is not garranteed. See https://github.com/Kalmat/PyWinCtl/issues/20#issuecomment-1193348238
@@ -360,7 +399,7 @@ def getTopWindowAt(x: int, y: int, app: AppKit.NSApplication | None = None, allW
 
 
 def _getAllApps(userOnly: bool = True):
-    matches = []
+    matches: list[AppKit.NSRunningApplication] = []
     for app in WS.runningApplications():
         if not userOnly or (userOnly and app.activationPolicy() == Quartz.NSApplicationActivationPolicyRegular):
             matches.append(app)
@@ -373,9 +412,9 @@ def _getAllWindows(excludeDesktop: bool = True, screenOnly: bool = True, userLay
     # Besides, since it may not have kCGWindowName value and the kCGWindowNumber can't be accessed from Apple Script, it's useless
     flags = Quartz.kCGWindowListExcludeDesktopElements if excludeDesktop else 0 | \
             Quartz.kCGWindowListOptionOnScreenOnly if screenOnly else 0
-    ret = Quartz.CGWindowListCopyWindowInfo(flags, Quartz.kCGNullWindowID)
+    ret: list[dict[Incomplete, int]] = Quartz.CGWindowListCopyWindowInfo(flags, Quartz.kCGNullWindowID)
     if userLayer:
-        matches = []
+        matches: list[dict[Incomplete, int]] = []
         for win in ret:
             if win[Quartz.kCGWindowLayer] != 0:
                 matches.append(win)
@@ -385,14 +424,14 @@ def _getAllWindows(excludeDesktop: bool = True, screenOnly: bool = True, userLay
 
 def _getAllAppWindows(app: AppKit.NSApplication, userLayer: bool = True):
     windows = _getAllWindows()
-    windowsInApp = []
+    windowsInApp: list[dict[Incomplete, int]] = []
     for win in windows:
         if (not userLayer or (userLayer and win[Quartz.kCGWindowLayer] == 0)) and win[Quartz.kCGWindowOwnerPID] == app.processIdentifier():
             windowsInApp.append(win)
     return windowsInApp
 
 
-def _getAppWindowsTitles(appName):
+def _getAppWindowsTitles(appName: str):
     cmd = """on run arg1
                 set appName to arg1 as string
                 set winNames to {}
@@ -422,7 +461,7 @@ def _getWindowTitles() -> list[list[str]]:
                                 return winNames'"""
     ret = subprocess.check_output(cmd, shell=True).decode(encoding="utf-8").replace("\n", "").replace("{", "[").replace("}", "]")
     res = ast.literal_eval(ret)
-    result = []
+    result: list[list[str]] = []
     if len(res) > 0:
         for i, pID in enumerate(res[0]):
             try:
@@ -446,13 +485,24 @@ def _getBorderSizes():
     frame = AppKit.NSMakeRect(400, 800, 250, 100)
     mask = AppKit.NSWindowStyleMaskTitled | AppKit.NSWindowStyleMaskClosable | AppKit.NSWindowStyleMaskMiniaturizable | AppKit.NSWindowStyleMaskResizable
     w = AppKit.NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(frame, mask, AppKit.NSBackingStoreBuffered, False)
-    titlebarHeight = w.titlebarHeight()
+    titlebarHeight = int(w.titlebarHeight())
     borderWidth = int(w.frame().size.width - w.contentRectForFrameRect_(frame).size.width)
     # w.display()
     # a.run()
     # w.releasedWhenClosed = True  # Method not found (?)
     w.close()
-    return int(titlebarHeight), int(borderWidth)
+    return titlebarHeight, borderWidth
+
+
+_ItemInfoValue = TypedDict("_ItemInfoValue", {"value": str, "class": str, "settable": bool})
+class _SubMenuStructure(TypedDict, total=False):
+    hSubMenu: int
+    wID: int
+    entries: dict[str, _SubMenuStructure]
+    parent: int
+    rect: Rect
+    item_info: dict[str, _ItemInfoValue]
+    shortcut: str
 
 
 class MacOSWindow(BaseWindow):
@@ -460,14 +510,10 @@ class MacOSWindow(BaseWindow):
     def _rect(self):
         return self.__rect
 
-    @_rect.setter
-    def _rect(self, value):
-        self.__rect = value
-
     def __init__(self, app: AppKit.NSRunningApplication, title: str, bounds: Rect | None = None):
         super().__init__()
         self._app = app
-        self._appName = app.localizedName()
+        self._appName: str = app.localizedName()
         self._appPID = app.processIdentifier()
         self._winTitle = title
         # self._parent = self.getParent()  # It is slow and not required by now
@@ -529,7 +575,7 @@ def getClientFrame(self):
     def __repr__(self):
         return '%s(hWnd=%s)' % (self.__class__.__name__, self._app)
 
-    def __eq__(self, other):
+    def __eq__(self, other: object):
         return isinstance(other, MacOSWindow) and self._app == other._app
 
     def close(self, force: bool = False) -> bool:
@@ -1063,8 +1109,9 @@ def getChildren(self):
 
         :return: list of handles (role:name) as string. Role can only be "AXWindow" in this case
         """
+        result: list[str] = []
         if not self.title:
-            return []
+            return result
 
         cmd = """on run {arg1, arg2}
                     set appName to arg1 as string
@@ -1082,7 +1129,6 @@ def getChildren(self):
         ret, err = proc.communicate(cmd)
         ret = ret.replace("\n", "").replace("{", "['").replace("}", "']").replace('"', '').replace(", ", "', '")
         ret = ast.literal_eval(ret)
-        result = []
         for item in ret:
             if item.startswith("window"):
                 res = item[item.find("window ")+len("window "):item.rfind(" of window "+self.title)]
@@ -1134,7 +1180,7 @@ def getDisplay(self):
         """
         screens = getAllScreens()
         name = ""
-        for key in screens.keys():
+        for key in screens:
             if pointInRect(self.centerx, self.centery, screens[key]["pos"].x, screens[key]["pos"].y, screens[key]["size"].width, screens[key]["size"].height):
                 name = key
                 break
@@ -1311,13 +1357,23 @@ class _WatchDog:
         :meth kill: Stop the entire watchdog and all its hooks
         :meth isAlive: Check if watchdog is running
         """
-        def __init__(self, parent):
+        def __init__(self, parent: MacOSWindow):
             self._watchdog = None
             self._parent = parent
 
-        def start(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB=None,
-                  isMaximizedCB=None, resizedCB=None, movedCB=None, changedTitleCB=None, changedDisplayCB=None,
-                  interval=0.3):
+        def start(
+            self,
+            isAliveCB: Callable[[bool], None] | None = None,
+            isActiveCB: Callable[[bool], None] | None = None,
+            isVisibleCB: Callable[[bool], None] | None = None,
+            isMinimizedCB: Callable[[bool], None] | None = None,
+            isMaximizedCB: Callable[[bool], None] | None = None,
+            resizedCB: Callable[[tuple[float, float]], None] | None = None,
+            movedCB: Callable[[tuple[float, float]], None] | None = None,
+            changedTitleCB: Callable[[str], None] | None = None,
+            changedDisplayCB: Callable[[str], None] | None = None,
+            interval: float = 0.3
+        ):
             """
             Initialize and start watchdog and hooks (callbacks to be invoked when desired window states change)
 
@@ -1360,9 +1416,18 @@ def start(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB
                                        isMaximizedCB, resizedCB, movedCB, changedTitleCB, changedDisplayCB,
                                        interval)
 
-        def updateCallbacks(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB=None,
-                                    isMaximizedCB=None, resizedCB=None, movedCB=None, changedTitleCB=None,
-                                    changedDisplayCB=None):
+        def updateCallbacks(
+            self,
+            isAliveCB: Callable[[bool], None] | None = None,
+            isActiveCB: Callable[[bool], None] | None = None,
+            isVisibleCB: Callable[[bool], None] | None = None,
+            isMinimizedCB: Callable[[bool], None] | None = None,
+            isMaximizedCB: Callable[[bool], None] | None = None,
+            resizedCB: Callable[[tuple[float, float]], None] | None = None,
+            movedCB: Callable[[tuple[float, float]], None] | None = None,
+            changedTitleCB: Callable[[str], None] | None = None,
+            changedDisplayCB: Callable[[str], None] | None = None
+        ):
             """
             Change the states this watchdog is hooked to
 
@@ -1395,7 +1460,7 @@ def updateCallbacks(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isM
                 self._watchdog.updateCallbacks(isAliveCB, isActiveCB, isVisibleCB, isMinimizedCB, isMaximizedCB,
                                               resizedCB, movedCB, changedTitleCB, changedDisplayCB)
 
-        def updateInterval(self, interval=0.3):
+        def updateInterval(self, interval: float = 0.3):
             """
             Change the interval to check changes
 
@@ -1442,11 +1507,11 @@ class _Menu:
 
         def __init__(self, parent: MacOSWindow):
             self._parent = parent
-            self._menuStructure = {}
-            self.menuList = []
-            self.itemList = []
+            self._menuStructure: dict[str, _SubMenuStructure] = {}
+            self.menuList: list[str] = []
+            self.itemList: list[str] = []
 
-        def getMenu(self, addItemInfo: bool = False) -> dict:
+        def getMenu(self, addItemInfo: bool = False):
             """
             Loads and returns Menu options, sub-menus and related information, as dictionary.
 
@@ -1487,10 +1552,10 @@ def getMenu(self, addItemInfo: bool = False) -> dict:
             self.menuList = []
             self.itemList = []
 
-            nameList = []
-            sizeList = []
-            posList = []
-            attrList = []
+            nameList: list[list[list[list[str]]]] = []
+            sizeList: list[list[list[list[tuple[int, int] | Literal['missing value']]]]] = []
+            posList: list[list[list[list[tuple[int, int] | Literal['missing value']]]]] = []
+            attrList: list[list[list[list[list[tuple[str, str, bool, str]]]]]] = []
 
             def findit():
 
@@ -1561,18 +1626,28 @@ def findit():
 
             def fillit():
 
-                def subfillit(subNameList, subSizeList, subPosList, subAttrList, section="", level=0, mainlevel=0, path: list[int] | None=None, parent=0):
+                def subfillit(
+                    subNameList: Iterable[str],
+                    subSizeList: Sequence[tuple[int, int] | Literal['missing value']],
+                    subPosList: Sequence[tuple[int, int] | Literal['missing value']],
+                    subAttrList: Sequence[Iterable[tuple[str, str, bool, str]]],
+                    section: str = "",
+                    level: int = 0,
+                    mainlevel: int = 0,
+                    path: list[int] | None = None,
+                    parent: int = 0
+                ):
                     path = path or []
-                    option = self._menuStructure
+                    option: dict[str, _SubMenuStructure] = self._menuStructure
                     if section:
                         for sec in section.split(SEP):
                             if sec:
-                                option = option[sec]
+                                option = cast("dict[str, _SubMenuStructure]", option[sec])
 
                     for i, name in enumerate(subNameList):
-                        pos = subPosList[i] if len(subPosList) > i else []
-                        size = subSizeList[i] if len(subSizeList) > i else []
-                        attr = subAttrList[i] if (addItemInfo and len(subAttrList) > 0) else []
+                        pos = subPosList[i] if len(subPosList) > i else "missing value"
+                        size = subSizeList[i] if len(subSizeList) > i else "missing value"
+                        attr: Iterable[tuple[str, str, bool, str]] = subAttrList[i] if (addItemInfo and len(subAttrList) > 0) else []
                         if not name:
                             continue
                         elif name == "missing value":
@@ -1590,17 +1665,27 @@ def subfillit(subNameList, subSizeList, subPosList, subAttrList, section="", lev
                                 option[name]["item_info"] = item_info
                                 option[name]["shortcut"] = self._getaccesskey(item_info)
                             if level + 1 < len(nameList):
-                                submenu = nameList[level + 1][mainlevel][0]
-                                subPos = posList[level + 1][mainlevel][0]
-                                subSize = sizeList[level + 1][mainlevel][0]
-                                subAttr = attrList[level + 1][mainlevel][0] if addItemInfo else []
+                                submenu: list[str] = nameList[level + 1][mainlevel][0]
+                                subPos: list[tuple[int, int] | Literal['missing value']] = posList[level + 1][mainlevel][0]
+                                subSize: list[tuple[int, int] | Literal['missing value']] = sizeList[level + 1][mainlevel][0]
+                                subAttr: list[list[tuple[str, str, bool, str]]] = attrList[level + 1][mainlevel][0] if addItemInfo else []
                                 subPath = path[3:] + ([0] * (level - 3)) + [i, 0] + ([0] * (level - 2))
                                 for j in subPath:
-                                    if len(submenu) > j and isinstance(submenu[j], list):
-                                        submenu = submenu[j]
-                                        subPos = subPos[j]
-                                        subSize = subSize[j]
-                                        subAttr = subAttr[j] if addItemInfo else []
+                                    submenuj = submenu[j]
+                                    subPosj = subPos[j]
+                                    subSizej = subSize[j]
+                                    subAttrj = subAttr[j]
+                                    if (
+                                        len(submenu) > j
+                                        and isinstance(submenuj, list)
+                                        and isinstance(subPosj, list)
+                                        and isinstance(subSizej, list)
+                                        and isinstance(subAttrj, list)
+                                    ):
+                                        submenu = submenuj
+                                        subPos = subPosj
+                                        subSize = subSizej
+                                        subAttr = cast("list[list[tuple[str, str, bool, str]]]", subAttrj) if addItemInfo else []
                                     else:
                                         break
                                 if submenu:
@@ -1613,7 +1698,8 @@ def subfillit(subNameList, subSizeList, subPosList, subAttrList, section="", lev
                                 else:
                                     option[name]["hSubMenu"] = 0
 
-                for i, item in enumerate(nameList[0]):
+                # TODO: How is this even possible if we had to access so many levels of nameList in teh code above???
+                for i, item in enumerate(cast("list[str]", nameList[0])):
                     hSubMenu = self._getNewHSubMenu(item)
                     self._menuStructure[item] = {"hSubMenu": hSubMenu, "wID": self._getNewWid(item), "entries": {}}
                     subfillit(nameList[1][i][0], sizeList[1][i][0], posList[1][i][0], attrList[1][i][0] if addItemInfo else [],
@@ -1623,7 +1709,7 @@ def subfillit(subNameList, subSizeList, subPosList, subAttrList, section="", lev
 
             return self._menuStructure
 
-        def clickMenuItem(self, itemPath: list | None = None, wID: int = 0) -> bool:
+        def clickMenuItem(self, itemPath: Sequence[str] | None = None, wID: int = 0) -> bool:
             """
             Simulates a click on a menu item
 
@@ -1672,7 +1758,7 @@ def clickMenuItem(self, itemPath: list | None = None, wID: int = 0) -> bool:
 
             return found
 
-        def getMenuInfo(self, hSubMenu: int) -> dict:
+        def getMenuInfo(self, hSubMenu: int):
             """
             Returns the MENUINFO struct of the given sub-menu or main menu if none given
 
@@ -1738,7 +1824,7 @@ def getMenuItemCount(self, hSubMenu: int) -> int:
 
             return count
 
-        def getMenuItemInfo(self, hSubMenu: int, wID: int) -> dict:
+        def getMenuItemInfo(self, hSubMenu: int, wID: int):
             """
             Returns the MENUITEMINFO struct for the given menu item
 
@@ -1793,7 +1879,7 @@ class of attribute
                     proc = subprocess.Popen(['osascript', '-s', 's', '-', str(self._parent._app.localizedName())],
                                             stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf8')
                     ret, err = proc.communicate(cmd)
-                    itemInfo = self._parseAttr(ret, convert=True)
+                    itemInfo = self._parseAttr(ret)
 
             return itemInfo
 
@@ -1845,21 +1931,23 @@ def getMenuItemRect(self, hSubMenu: int, wID: int) -> Rect:
 
             return Rect(x, y, x + w, y + h)
 
-        def _isListEmpty(self, inList):
+        def _isListEmpty(self, inList: list[Any]):
             # https://stackoverflow.com/questions/1593564/python-how-to-check-if-a-nested-list-is-essentially-empty/51582274
             if isinstance(inList, list):
                 return all(map(self._isListEmpty, inList))
             return False
 
-        def _parseAttr(self, attr, convert=False):
+        def _parseAttr(self, attr: str | Iterable[tuple[str, str, bool, str]]):
 
-            itemInfo = {}
-            if convert:
+            itemInfo: dict[str, _ItemInfoValue] = {}
+            if isinstance(attr, str):
                 attr = attr.replace("\n", "").replace('missing value', '"missing value"') \
                     .replace("{", "[").replace("}", "]").replace("value:", "'") \
                     .replace(", class:", "', '").replace(", settable:", "', '").replace(", name:", "', ")
-                attr = ast.literal_eval(attr)
-            for item in attr:
+                items: Iterable[tuple[str, str, bool, str]] = ast.literal_eval(attr)
+            else:
+                items = attr
+            for item in items:
                 if len(item) >= 4:
                     itemInfo[item[3]] = {"value": item[0], "class": item[1], "settable": item[2]}
 
@@ -1870,45 +1958,43 @@ def _checkMenuStruct(self):
                 self.getMenu()
             return self._menuStructure
 
-        def _getNewWid(self, ref):
+        def _getNewWid(self, ref: str):
             self.itemList.append(ref)
             return len(self.itemList)
 
-        def _getPathFromWid(self, wID):
+        def _getPathFromWid(self, wID: int):
             itemPath = []
             if self._checkMenuStruct():
                 if 0 < wID <= len(self.itemList):
                     itemPath = self.itemList[wID - 1].split(SEP)
             return itemPath
 
-        def _getNewHSubMenu(self, ref):
+        def _getNewHSubMenu(self, ref: str):
             self.menuList.append(ref)
             return len(self.menuList)
 
-        def _getPathFromHSubMenu(self, hSubMenu):
+        def _getPathFromHSubMenu(self, hSubMenu: int):
             menuPath = []
             if self._checkMenuStruct():
                 if 0 < hSubMenu <= len(self.menuList):
                     menuPath = self.menuList[hSubMenu - 1].split(SEP)
             return menuPath
 
-        def _getMenuItemWid(self, itemPath: str) -> str:
-            wID = ""
+        def _getMenuItemWid(self, itemPath: str):
+            wID = 0
             if itemPath:
                 option = self._menuStructure
                 for item in itemPath[:-1]:
-                    if item in option.keys() and "entries" in option[item].keys():
-                        option = option[item]["entries"]
+                    entries = option.get(item, {}).get("entries", None)
+                    if entries:
+                        option = entries
                     else:
                         option = {}
                         break
-
-                if option and itemPath and itemPath[-1] in option.keys() and "wID" in option[itemPath[-1]]:
-                    wID = option[itemPath[-1]]["wID"]
-
+                wID = option.get(itemPath[-1], {}).get("wID", 0)
             return wID
 
-        def _getaccesskey(self, item_info):
+        def _getaccesskey(self, item_info: dict[str, dict[str, str]] | dict[str, _ItemInfoValue]):
             # https://github.com/babarrett/hammerspoon/blob/master/cheatsheets.lua
             # https://github.com/pyatom/pyatom/blob/master/atomac/ldtpd/core.py
 
@@ -1921,21 +2007,15 @@ def _getaccesskey(self, item_info):
             except:
                 key = ""
             try:
-                modifiers = item_info["AXMenuItemCmdModifiers"]["value"]
-                if modifiers.isnumeric():
-                    modifiers = int(modifiers)
+                modifiers = int(item_info["AXMenuItemCmdModifiers"]["value"])
             except:
                 modifiers = -1
             try:
-                glyph = item_info["AXMenuItemCmdGlyph"]["value"]
-                if glyph.isnumeric():
-                    glyph = int(glyph)
+                glyph = int(item_info["AXMenuItemCmdGlyph"]["value"])
             except:
                 glyph = -1
             try:
-                virtual_key = item_info["AXMenuItemCmdVirtualKey"]["value"]
-                if virtual_key.isnumeric():
-                    virtual_key = int(virtual_key)
+                virtual_key = int(item_info["AXMenuItemCmdVirtualKey"]["value"])
             except:
                 virtual_key = -1
 
@@ -1984,7 +2064,7 @@ def _getaccesskey(self, item_info):
 
 class _SendTop(threading.Thread):
 
-    def __init__(self, hWnd, interval=0.5):
+    def __init__(self, hWnd: MacOSWindow | MacOSNSWindow, interval: float = 0.5):
         threading.Thread.__init__(self)
         self._hWnd = hWnd
         self._interval = interval
@@ -2007,7 +2087,7 @@ def restart(self):
 
 class _SendBottom(threading.Thread):
 
-    def __init__(self, hWnd, interval=0.5):
+    def __init__(self, hWnd: MacOSWindow, interval: float = 0.5):
         threading.Thread.__init__(self)
         self._hWnd = hWnd
         self._app = hWnd._app
@@ -2015,7 +2095,7 @@ def __init__(self, hWnd, interval=0.5):
         self._interval = interval
         self._kill = threading.Event()
         _apps = _getAllApps()
-        self._apps = []
+        self._apps: list[AppKit.NSRunningApplication] = []
         for app in _apps:
             if app.processIdentifier() != self._appPID:
                 self._apps.append(app)
@@ -2045,10 +2125,6 @@ class MacOSNSWindow(BaseWindow):
     def _rect(self):
         return self.__rect
 
-    @_rect.setter
-    def _rect(self, value):
-        self.__rect = value
-
     def __init__(self, app: AppKit.NSApplication, hWnd: AppKit.NSWindow):
         super().__init__()
         self._app = app
@@ -2098,7 +2174,7 @@ def getClientFrame(self):
     def __repr__(self):
         return '%s(hWnd=%s)' % (self.__class__.__name__, self._hWnd)
 
-    def __eq__(self, other):
+    def __eq__(self, other: object):
         return isinstance(other, MacOSNSWindow) and self._hWnd == other._hWnd
 
     def close(self) -> bool:
@@ -2221,7 +2297,11 @@ def resizeTo(self, newWidth: int, newHeight: int, wait: bool = False) -> bool:
         :param wait: set to ''True'' to wait until action is confirmed (in a reasonable time lap)
         :return: ''True'' if window resized to the given size
         """
-        self._hWnd.setFrame_display_animate_(AppKit.NSMakeRect(self.bottomleft.x, self.bottomleft.y, newWidth, newHeight), True, True)
+        self._hWnd.setFrame_display_animate_(
+            AppKit.NSMakeRect(self.bottomleft.x, self.bottomleft.y, newWidth, newHeight),
+            True,
+            True
+        )
         retries = 0
         while wait and retries < WAIT_ATTEMPTS and self.width != newWidth and self.height != newHeight:
             retries += 1
@@ -2353,7 +2433,7 @@ def getChildren(self) -> list[int]:
         """
         return self._hWnd.childWindows()
 
-    def getHandle(self) -> int:
+    def getHandle(self):
         """
         Get the current window handle
 
@@ -2361,7 +2441,7 @@ def getHandle(self) -> int:
         """
         return self._hWnd
 
-    def isParent(self, child) -> bool:
+    def isParent(self, child: AppKit.NSWindow) -> bool:
         """
         Check if current window is parent of given window (handle)
 
@@ -2371,7 +2451,7 @@ def isParent(self, child) -> bool:
         return child.parentWindow() == self._hWnd
     isParentOf = isParent  # isParentOf is an alias of isParent method
 
-    def isChild(self, parent) -> bool:
+    def isChild(self, parent: int) -> bool:
         """
         Check if current window is child of given window/app (handle)
 
@@ -2389,7 +2469,7 @@ def getDisplay(self):
         """
         screens = getAllScreens()
         name = ""
-        for key in screens.keys():
+        for key in screens:
             if pointInRect(self.centerx, self.centery, screens[key]["pos"].x, screens[key]["pos"].y, screens[key]["size"].width, screens[key]["size"].height):
                 name = key
                 break
@@ -2422,7 +2502,7 @@ def isActive(self) -> bool:
         """
         windows = getAllWindows(self._app)
         for win in windows:
-            return self._hWnd == win
+            return self._hWnd == win._hWnd
         return False
 
     @property
@@ -2476,13 +2556,23 @@ class _WatchDog:
         :meth kill: Stop the entire watchdog and all its hooks
         :meth isAlive: Check if watchdog is running
         """
-        def __init__(self, parent):
+        def __init__(self, parent: MacOSNSWindow):
             self._watchdog = None
             self._parent = parent
 
-        def start(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB=None,
-                          isMaximizedCB=None, resizedCB=None, movedCB=None, changedTitleCB=None, changedDisplayCB=None,
-                          interval=0.3):
+        def start(
+            self,
+            isAliveCB: Callable[[bool], None] | None = None,
+            isActiveCB: Callable[[bool], None] | None = None,
+            isVisibleCB: Callable[[bool], None] | None = None,
+            isMinimizedCB: Callable[[bool], None] | None = None,
+            isMaximizedCB: Callable[[bool], None] | None = None,
+            resizedCB: Callable[[tuple[float, float]], None] | None = None,
+            movedCB: Callable[[tuple[float, float]], None] | None = None,
+            changedTitleCB: Callable[[str], None] | None = None,
+            changedDisplayCB: Callable[[str], None] | None = None,
+            interval: float = 0.3
+        ):
             """
             Initialize and start watchdog and hooks (callbacks to be invoked when desired window states change)
 
@@ -2525,9 +2615,18 @@ def start(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB
             else:
                 self._watchdog = None
 
-        def updateCallbacks(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB=None,
-                                    isMaximizedCB=None, resizedCB=None, movedCB=None, changedTitleCB=None,
-                                    changedDisplayCB=None):
+        def updateCallbacks(
+            self,
+            isAliveCB: Callable[[bool], None] | None = None,
+            isActiveCB: Callable[[bool], None] | None = None,
+            isVisibleCB: Callable[[bool], None] | None = None,
+            isMinimizedCB: Callable[[bool], None] | None = None,
+            isMaximizedCB: Callable[[bool], None] | None = None,
+            resizedCB: Callable[[tuple[float, float]], None] | None = None,
+            movedCB: Callable[[tuple[float, float]], None] | None = None,
+            changedTitleCB: Callable[[str], None] | None = None,
+            changedDisplayCB: Callable[[str], None] | None = None
+        ):
             """
             Change the states this watchdog is hooked to
 
@@ -2562,7 +2661,7 @@ def updateCallbacks(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isM
             else:
                 self._watchdog = None
 
-        def updateInterval(self, interval=0.3):
+        def updateInterval(self, interval: float = 0.3):
             """
             Change the interval to check changes
 
@@ -2622,7 +2721,7 @@ def getMousePos() -> Point:
     mp = Quartz.NSEvent.mouseLocation()
     screens = getAllScreens()
     x = y = 0
-    for key in screens.keys():
+    for key in screens:
         if pointInRect(mp.x, mp.y, screens[key]["pos"].x, screens[key]["pos"].y, screens[key]["size"].width, screens[key]["size"].height):
             x = int(mp.x)
             y = int(screens[key]["size"].height) - abs(int(mp.y))
@@ -2630,6 +2729,17 @@ def getMousePos() -> Point:
     return Point(x, y)
 cursor = getMousePos  # cursor is an alias for getMousePos
 
+class _ScreenValue(TypedDict):
+    id: int
+    is_primary: bool
+    pos: Point
+    size: Size
+    workarea: Rect
+    scale: tuple[int, int]
+    dpi: tuple[int, int]
+    orientation: int
+    frequency: float
+    colordepth: int
 
 def getAllScreens():
     """
@@ -2663,7 +2773,7 @@ def getAllScreens():
             "colordepth":
                 Bits per pixel referred to the display color depth
     """
-    result = {}
+    result: dict[str, _ScreenValue] = {}
     screens = AppKit.NSScreen.screens()
     for i, screen in enumerate(screens):
         try:
@@ -2710,7 +2820,7 @@ def getScreenSize(name: str = "") -> Size:
     """
     screens = getAllScreens()
     res = Size(0, 0)
-    for key in screens.keys():
+    for key in screens:
         if (name and key == name) or (not name):
             res = screens[key]["size"]
             break
@@ -2726,7 +2836,7 @@ def getWorkArea(name: str = "") -> Rect:
     """
     screens = getAllScreens()
     res = Rect(0, 0, 0, 0)
-    for key in screens.keys():
+    for key in screens:
         if (name and key == name) or (not name):
             res = screens[key]["workarea"]
             break
diff --git a/src/pywinctl/_pywinctl_win.py b/src/pywinctl/_pywinctl_win.py
index 85d0277..997e860 100644
--- a/src/pywinctl/_pywinctl_win.py
+++ b/src/pywinctl/_pywinctl_win.py
@@ -2,7 +2,6 @@
 # -*- coding: utf-8 -*-
 from __future__ import annotations
 import sys
-from typing import cast
 assert sys.platform == "win32"
 
 import ctypes
@@ -11,6 +10,9 @@
 import time
 import traceback
 from ctypes import wintypes
+from typing import TYPE_CHECKING, cast, overload, type_check_only
+from typing_extensions import NotRequired, TypedDict, Literal
+from collections import Callable, Sequence
 
 import win32api
 import win32con
@@ -19,7 +21,10 @@
 import win32process
 from win32com.client import GetObject
 
-from pywinctl import BaseWindow, Point, Re, Rect, Size, _WinWatchDog, pointInRect
+from . import BaseWindow, Point, Re, Rect, Size, _WinWatchDog, pointInRect
+
+if TYPE_CHECKING:
+    from win32.lib.win32gui_struct import _MENUITEMINFO
 
 # WARNING: Changes are not immediately applied, specially for hide/show (unmap/map)
 #          You may set wait to True in case you need to effectively know if/when change has been applied.
@@ -52,7 +57,7 @@ def getActiveWindow():
         return None
 
 
-def getActiveWindowTitle() -> str:
+def getActiveWindowTitle():
     """
     Get the title of the currently active (focused) Window
 
@@ -86,7 +91,7 @@ def getAllTitles() -> list[str]:
     return [window.title for window in getAllWindows()]
 
 
-def getWindowsWithTitle(title, app=(), condition=Re.IS, flags=0):
+def getWindowsWithTitle(title: str | re.Pattern[str], app: tuple[str, ...] | None = (), condition: int = Re.IS, flags: int = 0):
     """
     Get the list of window objects whose title match the given string with condition and flags.
     Use ''condition'' to delimit the search. Allowed values are stored in pywinctl.Re sub-class (e.g. pywinctl.Re.CONTAINS)
@@ -111,8 +116,8 @@ def getWindowsWithTitle(title, app=(), condition=Re.IS, flags=0):
     :param flags: (optional) specific flags to apply to condition. Defaults to 0 (no flags)
     :return: list of Window objects
     """
-    matches = []
-    if title and condition in Re._cond_dic.keys():
+    matches: list[Win32Window] = []
+    if title and condition in Re._cond_dic:
         lower = False
         if condition in (Re.MATCH, Re.NOTMATCH):
             title = re.compile(title, flags)
@@ -122,6 +127,8 @@ def getWindowsWithTitle(title, app=(), condition=Re.IS, flags=0):
                 flags = 90
         elif flags == Re.IGNORECASE:
             lower = True
+            if isinstance(title, re.Pattern):
+                title = title.pattern
             title = title.lower()
         for win in getAllWindows():
             if win.title and Re._cond_dic[condition](title, win.title.lower() if lower else win.title, flags) \
@@ -136,10 +143,10 @@ def getAllAppsNames() -> list[str]:
 
     :return: list of names as strings
     """
-    return list(getAllAppsWindowsTitles().keys())
+    return list(getAllAppsWindowsTitles())
 
 
-def getAppsWithName(name, condition=Re.IS, flags=0):
+def getAppsWithName(name: str | re.Pattern[str], condition: int = Re.IS, flags: int = 0):
     """
     Get the list of app names which match the given string using the given condition and flags.
     Use ''condition'' to delimit the search. Allowed values are stored in pywinctl.Re sub-class (e.g. pywinctl.Re.CONTAINS)
@@ -163,8 +170,8 @@ def getAppsWithName(name, condition=Re.IS, flags=0):
     :param flags: (optional) specific flags to apply to condition. Defaults to 0 (no flags)
     :return: list of app names
     """
-    matches = []
-    if name and condition in Re._cond_dic.keys():
+    matches: list[str] = []
+    if name and condition in Re._cond_dic:
         lower = False
         if condition in (Re.MATCH, Re.NOTMATCH):
             name = re.compile(name, flags)
@@ -173,6 +180,8 @@ def getAppsWithName(name, condition=Re.IS, flags=0):
                 flags = 90
         elif flags == Re.IGNORECASE:
             lower = True
+            if isinstance(name, re.Pattern):
+                name = name.pattern
             name = name.lower()
         for title in getAllAppsNames():
             if title and Re._cond_dic[condition](name, title.lower() if lower else title, flags):
@@ -180,7 +189,7 @@ def getAppsWithName(name, condition=Re.IS, flags=0):
     return matches
 
 
-def getAllAppsWindowsTitles() -> dict:
+def getAllAppsWindowsTitles():
     """
     Get all visible apps names and their open windows titles
 
@@ -192,14 +201,14 @@ def getAllAppsWindowsTitles() -> dict:
     :return: python dictionary
     """
     process_list = _getAllApps(tryToFilter=True)
-    result = {}
+    result: dict[str, list[str]] = {}
     for win in getAllWindows():
         pID = win32process.GetWindowThreadProcessId(win.getHandle())
         for item in process_list:
             appPID = item[0]
             appName = item[1]
             if appPID == pID[1]:
-                if appName in result.keys():
+                if appName in result:
                     result[appName].append(win.title)
                 else:
                     result[appName] = [win.title]
@@ -229,7 +238,7 @@ def getTopWindowAt(x: int, y: int):
     :param y: Y screen coordinate of the window
     :return: Window object or None
     """
-    hwnd: int = win32gui.WindowFromPoint((x, y))
+    hwnd = win32gui.WindowFromPoint((x, y))
 
     # Want to pull the parent window from the window handle
     # By using GetAncestor we are able to get the parent window instead of the owner window.
@@ -238,13 +247,13 @@ def getTopWindowAt(x: int, y: int):
     return Win32Window(hwnd) if hwnd else None
 
 
-def _findWindowHandles(parent: int | None = None, window_class: str | None = None, title: str | None = None, onlyVisible: bool = False) -> list[int]:
+def _findWindowHandles(parent: int | None = None, window_class: str | None = None, title: str | None = None, onlyVisible: bool = False):
     # https://stackoverflow.com/questions/56973912/how-can-i-set-windows-10-desktop-background-with-smooth-transition
     # Fixed: original post returned duplicated handles when trying to retrieve all windows (no class nor title)
 
-    def _make_filter(class_name: str | None, title: str | None, onlyVisible=False):
+    def _make_filter(class_name: str | None, title: str | None, onlyVisible: bool = False):
 
-        def enum_windows(handle: int, h_list: list):
+        def enum_windows(handle: int, h_list: list[int]):
             if class_name and class_name not in win32gui.GetClassName(handle):
                 return True  # continue enumeration
             if title and title not in win32gui.GetWindowText(handle):
@@ -255,35 +264,60 @@ def enum_windows(handle: int, h_list: list):
         return enum_windows
 
     cb = _make_filter(window_class, title, onlyVisible)
+    handle_list: list[int] = []
     try:
-        handle_list = []
         if parent:
             win32gui.EnumChildWindows(parent, cb, handle_list)
         else:
             win32gui.EnumWindows(cb, handle_list)
+    finally:
         return handle_list
-    except:
-        return []
 
+@overload
+def _getAllApps(tryToFilter: Literal[True]) -> list[tuple[int, str, str]]:...
+
+@overload
+def _getAllApps(tryToFilter: Literal[False] = ...) -> list[tuple[int, str, str | None]]:...
 
-def _getAllApps(tryToFilter=False):
+def _getAllApps(tryToFilter: bool = False) -> list[tuple[int, str, str | None]] | list[tuple[int, str, str]]:
     # https://stackoverflow.com/questions/550653/cross-platform-way-to-get-pids-by-process-name-in-python
     WMI = GetObject('winmgmts:')
     processes = WMI.InstancesOf('Win32_Process')
-    process_list = [(p.Properties_("ProcessID").Value, p.Properties_("Name").Value, p.Properties_("CommandLine").Value) for p in processes]
+    process_list: list[tuple[int, str, str | None]] = [(p.Properties_("ProcessID").Value, p.Properties_("Name").Value, p.Properties_("CommandLine").Value) for p in processes]
     if tryToFilter:
         # Trying to figure out how to identify user-apps (non-system apps). Commandline property seems to partially work
-        matches = []
-        for item in process_list:
-            if item[2]:
-                matches.append(item)
-        process_list = matches
+        return [item for item in process_list if item[2]]
     return process_list
 
 
-def _getWindowInfo(hWnd):
-
+def _getWindowInfo(hWnd: int | str | bytes | bool | None):
     class tagWINDOWINFO(ctypes.Structure):
+        # Help type-checkers with ctypes.Structure
+        cbSize: int
+        rcWindow: wintypes.RECT
+        rcClient: wintypes.RECT
+        dwStyle: int
+        dwExStyle: int
+        dwWindowStatus: int
+        cxWindowBorders: int
+        cyWindowBorders: int
+        atomWindowType: int
+        wCreatorVersion: int
+        @type_check_only
+        def __init__(
+            self,
+            cbSize: int = ...,
+            rcWindow: wintypes.RECT = ...,
+            rcClient: wintypes.RECT = ...,
+            dwStyle: int = ...,
+            dwExStyle: int = ...,
+            dwWindowStatus: int = ...,
+            cxWindowBorders: int = ...,
+            cyWindowBorders: int = ...,
+            atomWindowType: int = ...,
+            wCreatorVersion: int = ...
+        ): ...
+
         _fields_ = [
             ('cbSize', wintypes.DWORD),
             ('rcWindow', wintypes.RECT),
@@ -297,9 +331,9 @@ class tagWINDOWINFO(ctypes.Structure):
             ('wCreatorVersion', wintypes.WORD)
         ]
 
-    PWINDOWINFO = ctypes.POINTER(tagWINDOWINFO)
-    LPWINDOWINFO = ctypes.POINTER(tagWINDOWINFO)
-    WINDOWINFO = tagWINDOWINFO
+    # PWINDOWINFO = ctypes.POINTER(tagWINDOWINFO)
+    # LPWINDOWINFO = ctypes.POINTER(tagWINDOWINFO)
+    # WINDOWINFO = tagWINDOWINFO
     wi = tagWINDOWINFO()
     wi.cbSize = ctypes.sizeof(wi)
     try:
@@ -320,16 +354,20 @@ class tagWINDOWINFO(ctypes.Structure):
     # titleSize = ctypes.windll.user32.GetSystemMetrics(win32con.SM_CYCAPTION)
     return wi
 
+class _SubMenuStructure(TypedDict):
+    hSubMenu: int
+    wID: int | None
+    entries: dict[str, _SubMenuStructure]
+    parent: int
+    rect: Rect | None
+    item_info: NotRequired[_MENUITEMINFO]
+    shortcut: str
 
 class Win32Window(BaseWindow):
     @property
     def _rect(self):
         return self.__rect
 
-    @_rect.setter
-    def _rect(self, value):
-        self.__rect = value
-
     def __init__(self, hWnd: int):
         super().__init__()
         self._hWnd = hWnd
@@ -379,7 +417,7 @@ def getClientFrame(self):
         :return: Rect struct
         """
         wi = _getWindowInfo(self._hWnd)
-        rcClient = self._rect
+        rcClient = cast(Rect, self._rect)
         if wi:
             rcClient = wi.rcClient
         return Rect(int(rcClient.left), int(rcClient.top), int(rcClient.right), int(rcClient.bottom))
@@ -387,7 +425,7 @@ def getClientFrame(self):
     def __repr__(self):
         return '%s(hWnd=%s)' % (self.__class__.__name__, self._hWnd)
 
-    def __eq__(self, other):
+    def __eq__(self, other: object):
         return isinstance(other, Win32Window) and self._hWnd == other._hWnd
 
     def close(self) -> bool:
@@ -474,7 +512,7 @@ def hide(self, wait: bool = False) -> bool:
             time.sleep(WAIT_DELAY * retries)
         return not self.isVisible
 
-    def activate(self, wait: bool = False) -> bool:
+    def activate(self, wait: bool = False):
         """
         Activate this window and make it the foreground (focused) window
 
@@ -506,12 +544,12 @@ def resizeTo(self, newWidth: int, newHeight: int, wait: bool = False) -> bool:
         :param wait: set to ''True'' to wait until action is confirmed (in a reasonable time lap)
         :return: ''True'' if window resized to the given size
         """
-        win32gui.MoveWindow(self._hWnd, self.left, self.top, newWidth, newHeight, True)
+        win32gui.MoveWindow(self._hWnd, int(self.left), int(self.top), newWidth, newHeight, True)
         retries = 0
         while wait and retries < WAIT_ATTEMPTS and (self.width != newWidth or self.height != newHeight):
             retries += 1
             time.sleep(WAIT_DELAY * retries)
-        return self.width == newWidth and self.height == newHeight
+        return int(self.width) == newWidth and int(self.height) == newHeight
 
     def move(self, xOffset: int, yOffset: int, wait: bool = False) -> bool:
         """
@@ -537,29 +575,30 @@ def moveTo(self, newLeft: int, newTop: int, wait: bool = False) -> bool:
         :param wait: set to ''True'' to wait until action is confirmed (in a reasonable time lap)
         :return: ''True'' if window moved to the given position
         """
-        win32gui.MoveWindow(self._hWnd, newLeft, newTop, self.width, self.height, True)
+        win32gui.MoveWindow(self._hWnd, newLeft, newTop, int(self.width), int(self.height), True)
         retries = 0
         while wait and retries < WAIT_ATTEMPTS and (self.left != newLeft or self.top != newTop):
             retries += 1
             time.sleep(WAIT_DELAY * retries)
-        return self.left == newLeft and self.top == newTop
+        return int(self.left) == newLeft and int(self.top) == newTop
 
     def _moveResizeTo(self, newLeft: int, newTop: int, newWidth: int, newHeight: int) -> bool:
         win32gui.MoveWindow(self._hWnd, newLeft, newTop, newWidth, newHeight, True)
-        return newLeft == self.left and newTop == self.top and newWidth == self.width and newHeight == self.height
+        return newLeft == int(self.left) and newTop == int(self.top) and newWidth == int(self.width) and newHeight == int(self.height)
 
     def alwaysOnTop(self, aot: bool = True):
         """
         Keeps window on top of all others.
 
         :param aot: set to ''False'' to deactivate always-on-top behavior
-        :return: ''True'' if command succeeded
+        :return: Always returns ''True''
         """
         if self._t and self._t.is_alive():
             self._t.kill()
         # https://stackoverflow.com/questions/25381589/pygame-set-window-on-top-without-changing-its-position/49482325 (kmaork)
         zorder = win32con.HWND_TOPMOST if aot else win32con.HWND_NOTOPMOST
         win32gui.SetWindowPos(self._hWnd, zorder, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
+        return True
 
     def alwaysOnBottom(self, aob: bool = True) -> bool:
         """
@@ -594,19 +633,21 @@ def lowerWindow(self):
         """
         Lowers the window to the bottom so that it does not obscure any sibling windows
 
-        :return: ''True'' if window lowered
+        :return: Always returns ''True''
         """
         win32gui.SetWindowPos(self._hWnd, win32con.HWND_BOTTOM, 0, 0, 0, 0,
                                        win32con.SWP_NOSIZE | win32con.SWP_NOMOVE | win32con.SWP_NOACTIVATE)
+        return True
 
     def raiseWindow(self):
         """
         Raises the window to top so that it is not obscured by any sibling windows.
 
-        :return: ''True'' if window raised
+        :return: Always returns ''True''
         """
         win32gui.SetWindowPos(self._hWnd, win32con.HWND_TOP, 0, 0, 0, 0,
                                        win32con.SWP_NOSIZE | win32con.SWP_NOMOVE | win32con.SWP_NOACTIVATE)
+        return True
 
     def sendBehind(self, sb: bool = True) -> bool:
         """
@@ -620,9 +661,9 @@ def sendBehind(self, sb: bool = True) -> bool:
         if sb:
             def getWorkerW():
 
-                thelist = []
+                thelist: list[int] = []
 
-                def findit(hwnd, ctx):
+                def findit(hwnd: int, ctx: None):
                     p = win32gui.FindWindowEx(hwnd, None, "SHELLDLL_DefView", "")
                     if p != 0:
                         thelist.append(win32gui.FindWindowEx(None, hwnd, "WorkerW", ""))
@@ -663,9 +704,8 @@ def getAppName(self) -> str:
         # https://stackoverflow.com/questions/550653/cross-platform-way-to-get-pids-by-process-name-in-python
         WMI = GetObject('winmgmts:')
         processes = WMI.InstancesOf('Win32_Process')
-        process_list = [(p.Properties_("ProcessID").Value, p.Properties_("Name").Value) for p in processes]
-        pID = win32process.GetWindowThreadProcessId(self._hWnd)
-        pID = pID[1]
+        process_list: list[tuple[int, str]] = [(p.Properties_("ProcessID").Value, p.Properties_("Name").Value) for p in processes]
+        pID = win32process.GetWindowThreadProcessId(self._hWnd)[1]
         name = ""
         for item in process_list:
             if item[0] == pID:
@@ -680,7 +720,7 @@ def getParent(self) -> int:
         """
         return win32gui.GetParent(self._hWnd)
 
-    def getChildren(self) -> list[int]:
+    def getChildren(self):
         """
         Get the children handles of current window
 
@@ -735,21 +775,15 @@ def _getContent(self):
         # https://stackoverflow.com/questions/14500026/python-how-to-get-the-text-label-from-another-program-window
         # Does not work with Terminal or Chrome. Besides, I don't think it can be done in Linux nor macOS
 
-        content = []
-
-        def findContent(childWindow):
+        for childWindow in self.getChildren():
             bufferSize = win32gui.SendMessage(childWindow, win32con.WM_GETTEXTLENGTH, 0, 0) * 2
-            buf = win32gui.PyMakeBuffer(bufferSize)  # pyright: ignore[reportGeneralTypeIssues]  # deprecated
-            win32gui.SendMessage(childWindow, win32con.WM_GETTEXT, bufferSize, buf)
-            a = buf.tobytes().decode('UTF-16', 'replace')
+            buffer = b"\x00" * bufferSize
+            win32gui.SendMessage(childWindow, win32con.WM_GETTEXT, bufferSize, buffer)
+            a = buffer.decode('UTF-16', 'replace')
             if a:
                 b = a.split("\r\n")
-                content.append(b)
+                yield b
 
-        children = self.getChildren()
-        for child in children:
-            findContent(child)
-        return content
 
 
     @property
@@ -772,7 +806,7 @@ def isMaximized(self) -> bool:
         return state[1] == win32con.SW_SHOWMAXIMIZED
 
     @property
-    def isActive(self) -> bool:
+    def isActive(self):
         """
         Check if current window is currently the active, foreground window
 
@@ -831,13 +865,23 @@ class _WatchDog:
         :meth kill: Stop the entire watchdog and all its hooks
         :meth isAlive: Check if watchdog is running
         """
-        def __init__(self, parent):
+        def __init__(self, parent: Win32Window):
             self._watchdog = None
             self._parent = parent
 
-        def start(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB=None,
-                  isMaximizedCB=None, resizedCB=None, movedCB=None, changedTitleCB=None, changedDisplayCB=None,
-                  interval=0.3):
+        def start(
+            self,
+            isAliveCB: Callable[[bool], None] | None = None,
+            isActiveCB: Callable[[bool], None] | None = None,
+            isVisibleCB: Callable[[bool], None] | None = None,
+            isMinimizedCB: Callable[[bool], None] | None = None,
+            isMaximizedCB: Callable[[bool], None] | None = None,
+            resizedCB: Callable[[tuple[float, float]], None] | None = None,
+            movedCB: Callable[[tuple[float, float]], None] | None = None,
+            changedTitleCB: Callable[[str], None] | None = None,
+            changedDisplayCB: Callable[[str], None] | None = None,
+            interval: float = 0.3
+        ):
             """
             Initialize and start watchdog and hooks (callbacks to be invoked when desired window states change)
 
@@ -880,9 +924,18 @@ def start(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB
                                        isMaximizedCB, resizedCB, movedCB, changedTitleCB, changedDisplayCB,
                                        interval)
 
-        def updateCallbacks(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isMinimizedCB=None,
-                                    isMaximizedCB=None, resizedCB=None, movedCB=None, changedTitleCB=None,
-                                    changedDisplayCB=None):
+        def updateCallbacks(
+            self,
+            isAliveCB: Callable[[bool], None] | None = None,
+            isActiveCB: Callable[[bool], None] | None = None,
+            isVisibleCB: Callable[[bool], None] | None = None,
+            isMinimizedCB: Callable[[bool], None] | None = None,
+            isMaximizedCB: Callable[[bool], None] | None = None,
+            resizedCB: Callable[[tuple[float, float]], None] | None = None,
+            movedCB: Callable[[tuple[float, float]], None] | None = None,
+            changedTitleCB: Callable[[str], None] | None = None,
+            changedDisplayCB: Callable[[str], None] | None = None
+        ):
             """
             Change the states this watchdog is hooked to
 
@@ -915,7 +968,7 @@ def updateCallbacks(self, isAliveCB=None, isActiveCB=None, isVisibleCB=None, isM
                 self._watchdog.updateCallbacks(isAliveCB, isActiveCB, isVisibleCB, isMinimizedCB, isMaximizedCB,
                                               resizedCB, movedCB, changedTitleCB, changedDisplayCB)
 
-        def updateInterval(self, interval=0.3):
+        def updateInterval(self, interval: float = 0.3):
             """
             Change the interval to check changes
 
@@ -963,10 +1016,10 @@ def __init__(self, parent: Win32Window):
             self._parent = parent
             self._hWnd = parent._hWnd
             self._hMenu = win32gui.GetMenu(self._hWnd)
-            self._menuStructure = {}
+            self._menuStructure: dict[str, _SubMenuStructure]  = {}
             self._sep = "|&|"
 
-        def getMenu(self, addItemInfo: bool = False) -> dict:
+        def getMenu(self, addItemInfo: bool = False):
             """
             Loads and returns Menu options, sub-menus and related information, as dictionary.
 
@@ -1002,7 +1055,7 @@ def findit(parent: int, level: str = "", parentRect: Rect | None = None) -> None
                 option = self._menuStructure
                 if level:
                     for section in level.split(self._sep)[1:]:
-                        option = option[section]
+                        option = cast("dict[str, _SubMenuStructure]", option[section])
 
                 for i in range(win32gui.GetMenuItemCount(parent)):
                     item_info = self._getMenuItemInfo(hSubMenu=parent, itemPos=i)
@@ -1022,7 +1075,7 @@ def findit(parent: int, level: str = "", parentRect: Rect | None = None) -> None
                 findit(self._hMenu)
             return self._menuStructure
 
-        def clickMenuItem(self, itemPath: list | None = None, wID: int = 0) -> bool:
+        def clickMenuItem(self, itemPath: Sequence[str] | None = None, wID: int | None = 0) -> bool:
             """
             Simulates a click on a menu item
 
@@ -1046,13 +1099,13 @@ def clickMenuItem(self, itemPath: list | None = None, wID: int = 0) -> bool:
                         self.getMenu()
                     option = self._menuStructure
                     for item in itemPath[:-1]:
-                        if item in option.keys() and "entries" in option[item].keys():
+                        if item in option:
                             option = option[item]["entries"]
                         else:
                             option = {}
                             break
 
-                    if option and itemPath[-1] in option.keys() and "wID" in option[itemPath[-1]].keys():
+                    if option and itemPath[-1] in option:
                         itemID = option[itemPath[-1]]["wID"]
 
                 if itemID:
@@ -1078,7 +1131,7 @@ def getMenuInfo(self, hSubMenu: int = 0):
                 menu_info = win32gui_struct.UnpackMENUINFO(buf)
             return menu_info
 
-        def getMenuItemCount(self, hSubMenu: int = 0) -> int:
+        def getMenuItemCount(self, hSubMenu: int = 0):
             """
             Returns the number of items within a menu (main menu if no sub-menu given)
 
@@ -1099,7 +1152,7 @@ def getMenuItemInfo(self, hSubMenu: int, wID: int):
             """
             item_info = None
             if self._hMenu:
-                buf, extras = win32gui_struct.EmptyMENUITEMINFO()
+                buf, _extras = win32gui_struct.EmptyMENUITEMINFO()
                 win32gui.GetMenuItemInfo(hSubMenu, wID, False, buf)
                 item_info = win32gui_struct.UnpackMENUITEMINFO(buf)
             return item_info
@@ -1107,7 +1160,7 @@ def getMenuItemInfo(self, hSubMenu: int, wID: int):
         def _getMenuItemInfo(self, hSubMenu: int, itemPos: int):
             item_info = None
             if self._hMenu:
-                buf, extras = win32gui_struct.EmptyMENUITEMINFO()
+                buf, _extras = win32gui_struct.EmptyMENUITEMINFO()
                 win32gui.GetMenuItemInfo(hSubMenu, itemPos, True, buf)
                 item_info = win32gui_struct.UnpackMENUITEMINFO(buf)
             return item_info
@@ -1121,23 +1174,23 @@ def getMenuItemRect(self, hSubMenu: int, wID: int) -> Rect:
             :return: Rect struct
             """
 
-            def findit(menu, hSubMenu, wID):
+            def findit(menu: dict[str, _SubMenuStructure], hSubMenu: int, wID: int | None):
 
-                menuFound = [{}]
+                menuFound: list[dict[str, _SubMenuStructure]] = [{}]
 
-                def findMenu(inMenu, hSubMenu):
+                def findMenu(inMenu: dict[str, _SubMenuStructure], hSubMenu: int):
 
-                    for key in inMenu.keys():
+                    for key in inMenu:
                         if inMenu[key]["hSubMenu"] == hSubMenu:
                             menuFound[0] = inMenu[key]["entries"]
                             break
-                        elif "entries" in inMenu[key].keys():
+                        elif "entries" in inMenu[key]:
                             findMenu(inMenu[key]["entries"], hSubMenu)
 
                 findMenu(menu, hSubMenu)
                 subMenu = menuFound[0]
                 itemPos = -1
-                for key in subMenu.keys():
+                for key in subMenu:
                     itemPos += 1
                     if subMenu[key]["wID"] == wID:
                         return itemPos
@@ -1172,7 +1225,7 @@ def _getMenuItemRect(self, hSubMenu: int, itemPos: int, parentRect: Rect | None
 
 class _SendBottom(threading.Thread):
 
-    def __init__(self, hWnd, interval=0.5):
+    def __init__(self, hWnd: int, interval: float = 0.5):
         threading.Thread.__init__(self)
         self._hWnd = hWnd
         self._interval = interval
@@ -1206,8 +1259,19 @@ def restart(self):
         self._kill = threading.Event()
         self.run()
 
-
-def getAllScreens() -> dict:
+class _ScreenValue(TypedDict):
+    id: int
+    is_primary: bool
+    pos: Point
+    size: Size
+    workarea: Rect
+    scale: tuple[int, int]
+    dpi: tuple[int, int]
+    orientation: int
+    frequency: float
+    colordepth: int
+
+def getAllScreens():
     """
     load all monitors plugged to the pc, as a dict
 
@@ -1240,7 +1304,7 @@ def getAllScreens() -> dict:
                 Bits per pixel referred to the display color depth
     """
     # https://stackoverflow.com/questions/35814309/winapi-changedisplaysettingsex-does-not-work
-    result = {}
+    result: dict[str, _ScreenValue] = {}
     ctypes.windll.user32.SetProcessDPIAware()
     monitors = win32api.EnumDisplayMonitors()
     i = 0
@@ -1259,13 +1323,12 @@ def getAllScreens() -> dict:
                 for mon in monitors:
                     monitor = mon[0].handle
                     monitor_info = win32api.GetMonitorInfo(monitor)
-                    name = monitor_info.get("Device", None)
-                    if name == dev.DeviceName:
+                    if monitor_info["Device"] == dev.DeviceName:
                         break
 
                 if monitor_info:
-                    x, y, r, b = monitor_info.get("Monitor", (0, 0, 0, 0))
-                    wx, wy, wr, wb = monitor_info.get("Work", (0, 0, 0, 0))
+                    x, y, r, b = monitor_info["Monitor"]
+                    wx, wy, wr, wb = monitor_info["Work"]
                     settings = win32api.EnumDisplaySettings(dev.DeviceName, win32con.ENUM_CURRENT_SETTINGS)
                     # values seem to be affected by the scale factor of the first display
                     wr, wb = wx + settings.PelsWidth + (wr - r), wy + settings.PelsHeight + (wb - b)
@@ -1321,7 +1384,7 @@ def getScreenSize(name: str = "") -> Size:
     """
     size = Size(0, 0)
     screens = getAllScreens()
-    for key in screens.keys():
+    for key in screens:
         if (name and key == name) or (not name and screens[key]["is_primary"]):
             size = screens[key]["size"]
             break
@@ -1339,7 +1402,7 @@ def getWorkArea(name: str = "") -> Rect:
     """
     screens = getAllScreens()
     workarea = Rect(0, 0, 0, 0)
-    for key in screens.keys():
+    for key in screens:
         if (name and key == name) or (not name and screens[key]["is_primary"]):
             workarea = screens[key]["workarea"]
             break
@@ -1377,11 +1440,11 @@ def displayWindowsUnderMouse(xOffset: int = 0, yOffset: int = 0):
         sys.stdout.flush()
 
 
-def activeCB(active):
+def activeCB(active: object):
     print("NEW ACTIVE STATUS", active)
 
 
-def movedCB(pos):
+def movedCB(pos: object):
     print("NEW POS", pos)
 
 
diff --git a/typings/AppKit.pyi b/typings/AppKit.pyi
new file mode 100644
index 0000000..8148de0
--- /dev/null
+++ b/typings/AppKit.pyi
@@ -0,0 +1,56 @@
+# https://github.com/ronaldoussoren/pyobjc/issues/198
+# https://github.com/ronaldoussoren/pyobjc/issues/417
+# https://github.com/ronaldoussoren/pyobjc/issues/419
+
+from typing import Any
+
+NSWindowStyleMaskTitled: int
+NSWindowStyleMaskClosable: int
+NSWindowStyleMaskMiniaturizable: int
+NSWindowStyleMaskResizable: int
+
+class NSObject:
+    @staticmethod
+    def alloc()-> Any: ...
+    def __getattr__(self, name: str) -> Any: ...
+
+
+class NSWorkspace(NSObject):
+    @staticmethod
+    def sharedWorkspace() -> Any: ...
+    def __getattr__(self, name: str) -> Any: ...
+
+
+class NSScreen(NSObject):
+    @staticmethod
+    def screens() -> list[Any]: ...
+    def __getattr__(self, name: str) -> Any: ...
+
+
+class NSMakeRect(NSObject):
+    def __init__(self, left: float, top: float, width: float, height: float) -> None: ...
+    def __getattr__(self, name: str) -> Any: ...
+
+
+class NSApplication(NSObject):
+    @staticmethod
+    def sharedApplication() -> Any: ...
+    def __getattr__(self, name: str) -> Any: ...
+
+
+class NSApp(NSApplication):
+    def __getattr__(self, name: str) -> Any: ...
+
+
+class NSRunningApplication(NSObject):
+    def __getattr__(self, name: str) -> Any: ...
+
+
+class NSWindow(NSObject):
+    @staticmethod
+    def alloc() -> Any: ...
+    def __getattr__(self, name: str) -> Any: ...
+
+
+class NSBackingStoreBuffered(NSObject):
+    def __getattr__(self, name: str) -> Any: ...
diff --git a/typings/Quartz/CoreGraphics/__init__.pyi b/typings/Quartz/CoreGraphics/__init__.pyi
new file mode 100644
index 0000000..25749ad
--- /dev/null
+++ b/typings/Quartz/CoreGraphics/__init__.pyi
@@ -0,0 +1,83 @@
+# pyright: reportUnknownParameterType=false, reportMissingParameterType=false, reportMissingTypeArgument=false
+from typing import Any
+
+CFArrayCreate: Any
+CFArrayCreateMutable: Any
+CFDictionaryCreate: Any
+CFDictionaryCreateMutable: Any
+CFSetCreate: Any
+CFSetCreateMutable: Any
+kCFTypeArrayCallBacks: Any
+kCFTypeDictionaryKeyCallBacks: Any
+kCFTypeDictionaryValueCallBacks: Any
+kCFTypeSetCallBacks: Any
+
+def CFCopyLocalizedString(key, comment): ...
+def CFCopyLocalizedStringFromTable(key, tbl, comment): ...
+def CFCopyLocalizedStringFromTableInBundle(key, tbl, bundle, comment): ...
+def CFCopyLocalizedStringWithDefaultValue(key, tbl, bundle, value, comment): ...
+def CFSTR(strval): ...
+def CGDataConsumerCreate(*args, **kwargs) -> Any: ...
+def CGDataProviderCreateSequential(*args, **kwargs) -> Any: ...
+def CGDataProviderCreateWithData(info, data, size, release) -> object: ...
+def CGDisplayRegisterReconfigurationCallback(*args, **kwargs) -> Any: ...
+def CGDisplayRemoveReconfigurationCallback(*args, **kwargs) -> Any: ...
+def CGEventTapCreate(*args, **kwargs) -> Any: ...
+def CGEventTapCreateForPSN(*args, **kwargs) -> Any: ...
+def CGFunctionCreate(
+    info, domainDimension, domain, rangeDimension, range, evaluate
+) -> Any: ...
+def CGPSConverterCreate(*args, **kwargs) -> Any: ...
+def CGPatternCreate(*args, **kwargs) -> Any: ...
+def CGRegisterScreenRefreshCallback(*args, **kwargs) -> Any: ...
+def CGScreenRegisterMoveCallback(*args, **kwargs) -> Any: ...
+def CGScreenUnregisterMoveCallback(*args, **kwargs) -> Any: ...
+def CGUnregisterScreenRefreshCallback(*args, **kwargs) -> Any: ...
+def CGBitmapContextCreate(*args, **kwargs) -> Any: ...
+def CGBitmapContextCreateWithData(*args, **kwargs) -> Any: ...
+def CGFontCopyTableTags(*args, **kwargs) -> Any: ...
+def CGPDFObjectGetValue(*args, **kwargs) -> Any: ...
+def CGWindowListCreate(*args, **kwargs) -> Any: ...
+def CGWindowListCreateDescriptionFromArray(*args, **kwargs) -> Any: ...
+def CGWindowListCreateImageFromArray(*args, **kwargs) -> Any: ...
+def CGReleaseScreenRefreshRects(*args, **kwargs) -> Any: ...
+def CGWaitForScreenRefreshRects(*args, **kwargs) -> Any: ...
+def CGWaitForScreenUpdateRects(*args, **kwargs) -> Any: ...
+
+_inline_list_: Any
+
+def sel32or64(a, b): ...
+def selAorI(a, b): ...
+
+misc: Any
+constants: str
+enums: str
+functions: Any
+aliases: Any
+cftypes: Any
+expressions: Any
+
+def CGPDFDictionaryApplyFunction(*args, **kwargs) -> Any: ...
+def CGPathApply(*args, **kwargs) -> Any: ...
+def setCGPathElement(*args, **kwargs) -> Any: ...
+
+class CGSavedGState:
+    context: Any
+    def __init__(self, context) -> None: ...
+    def __enter__(self): ...
+    def __exit__(self, exc_type, exc_value, exc_tp): ...
+
+class CGTransparencyLayer:
+    context: Any
+    info: Any
+    rect: Any
+    def __init__(self, context, info, rect: Any | None = ...) -> None: ...
+    def __enter__(self): ...
+    def __exit__(self, exc_type, exc_value, exc_tp): ...
+
+class CGContextPage:
+    context: Any
+    mediaBox: Any
+    def __init__(self, context, mediaBox: Any | None = ...) -> None: ...
+    def __enter__(self): ...
+    def __exit__(self, exc_type, exc_value, exc_tp): ...
diff --git a/typings/Quartz/CoreVideo/__init__.pyi b/typings/Quartz/CoreVideo/__init__.pyi
new file mode 100644
index 0000000..50bb9ae
--- /dev/null
+++ b/typings/Quartz/CoreVideo/__init__.pyi
@@ -0,0 +1,30 @@
+# pyright: reportUnknownParameterType=false, reportMissingParameterType=false, reportMissingTypeArgument=false
+from typing import Any
+
+CFArrayCreate: Any
+CFArrayCreateMutable: Any
+CFDictionaryCreate: Any
+CFDictionaryCreateMutable: Any
+CFSetCreate: Any
+CFSetCreateMutable: Any
+kCFTypeArrayCallBacks: Any
+kCFTypeDictionaryKeyCallBacks: Any
+kCFTypeDictionaryValueCallBacks: Any
+kCFTypeSetCallBacks: Any
+def CFCopyLocalizedString(key, comment): ...
+def CFCopyLocalizedStringFromTable(key, tbl, comment): ...
+def CFCopyLocalizedStringFromTableInBundle(key, tbl, bundle, comment): ...
+def CFCopyLocalizedStringWithDefaultValue(key, tbl, bundle, value, comment): ...
+def CFSTR(strval): ...
+def CVPixelBufferCreateWithBytes(*args, **kwargs) -> Any: ...
+def sel32or64(a, b): ...
+def selAorI(a, b): ...
+
+
+misc: Any
+constants: str
+enums: str
+functions: Any
+aliases: Any
+cftypes: Any
+expressions: Any
diff --git a/typings/Quartz/ImageIO/__init__.pyi b/typings/Quartz/ImageIO/__init__.pyi
new file mode 100644
index 0000000..3da0a0e
--- /dev/null
+++ b/typings/Quartz/ImageIO/__init__.pyi
@@ -0,0 +1,29 @@
+# pyright: reportUnknownParameterType=false, reportMissingParameterType=false, reportMissingTypeArgument=false
+from typing import Any
+
+CFArrayCreate: Any
+CFArrayCreateMutable: Any
+CFDictionaryCreate: Any
+CFDictionaryCreateMutable: Any
+CFSetCreate: Any
+CFSetCreateMutable: Any
+kCFTypeArrayCallBacks: Any
+kCFTypeDictionaryKeyCallBacks: Any
+kCFTypeDictionaryValueCallBacks: Any
+kCFTypeSetCallBacks: Any
+def CFCopyLocalizedString(key, comment): ...
+def CFCopyLocalizedStringFromTable(key, tbl, comment): ...
+def CFCopyLocalizedStringFromTableInBundle(key, tbl, bundle, comment): ...
+def CFCopyLocalizedStringWithDefaultValue(key, tbl, bundle, value, comment): ...
+def CFSTR(strval): ...
+def sel32or64(a, b): ...
+def selAorI(a, b): ...
+
+
+misc: Any
+constants: str
+enums: str
+functions: Any
+aliases: Any
+cftypes: Any
+expressions: Any
diff --git a/typings/Quartz/ImageKit/__init__.pyi b/typings/Quartz/ImageKit/__init__.pyi
new file mode 100644
index 0000000..401560f
--- /dev/null
+++ b/typings/Quartz/ImageKit/__init__.pyi
@@ -0,0 +1,28 @@
+# pyright: reportUnknownParameterType=false, reportMissingParameterType=false, reportMissingTypeArgument=false, reportUnknownMemberType=false
+# mypy: disable-error-code=type-arg
+from typing import Any
+
+import objc._lazyimport  # type: ignore
+
+IKCameraDeviceViewDisplayMode: Any
+IKCameraDeviceViewTransferMode: Any
+IKDeviceBrowserViewDisplayMode: Any
+IKScannerDeviceViewDisplayMode: Any
+IKScannerDeviceViewTransferMode: Any
+_ObjCLazyModule__aliases_deprecated: dict
+_ObjCLazyModule__enum_deprecated: dict
+_ObjCLazyModule__expressions: dict
+_ObjCLazyModule__expressions_mapping: objc._lazyimport.GetAttrMap
+_ObjCLazyModule__varmap_dct: dict
+_ObjCLazyModule__varmap_deprecated: dict
+
+def sel32or64(a, b): ...
+def selAorI(a, b): ...
+
+misc: Any
+constants: str
+enums: str
+aliases: Any
+r: Any
+protocols: Any
+expressions: Any
diff --git a/typings/Quartz/PDFKit/__init__.pyi b/typings/Quartz/PDFKit/__init__.pyi
new file mode 100644
index 0000000..7e05100
--- /dev/null
+++ b/typings/Quartz/PDFKit/__init__.pyi
@@ -0,0 +1,48 @@
+# pyright: reportUnknownParameterType=false, reportMissingParameterType=false, reportMissingTypeArgument=false, reportUnknownMemberType=false
+# mypy: disable-error-code=type-arg
+from typing import Any
+
+import objc._lazyimport  # type: ignore
+
+PDFAccessPermissions: Any
+PDFActionNamedName: Any
+PDFAnnotationHighlightingMode: Any
+PDFAnnotationKey: Any
+PDFAnnotationLineEndingStyle: Any
+PDFAnnotationSubtype: Any
+PDFAnnotationTextIconType: Any
+PDFAnnotationWidgetSubtype: Any
+PDFAppearanceCharacteristicsKey: Any
+PDFAreaOfInterest: Any
+PDFBorderKey: Any
+PDFBorderStyle: Any
+PDFDisplayBox: Any
+PDFDisplayDirection: Any
+PDFDisplayMode: Any
+PDFDocumentAttribute: Any
+PDFDocumentPermissions: Any
+PDFDocumentWriteOption: Any
+PDFInterpolationQuality: Any
+PDFLineStyle: Any
+PDFMarkupType: Any
+PDFPrintScalingMode: Any
+PDFTextAnnotationIconType: Any
+PDFWidgetCellState: Any
+PDFWidgetControlType: Any
+_ObjCLazyModule__aliases_deprecated: dict
+_ObjCLazyModule__enum_deprecated: dict
+_ObjCLazyModule__expressions: dict
+_ObjCLazyModule__expressions_mapping: objc._lazyimport.GetAttrMap
+_ObjCLazyModule__varmap_dct: dict
+_ObjCLazyModule__varmap_deprecated: dict
+
+def sel32or64(a, b): ...
+def selAorI(a, b): ...
+
+misc: Any
+constants: str
+enums: str
+aliases: Any
+r: Any
+protocols: Any
+expressions: Any
diff --git a/typings/Quartz/QuartzComposer/__init__.pyi b/typings/Quartz/QuartzComposer/__init__.pyi
new file mode 100644
index 0000000..0d28259
--- /dev/null
+++ b/typings/Quartz/QuartzComposer/__init__.pyi
@@ -0,0 +1,28 @@
+# pyright: reportUnknownParameterType=false, reportMissingParameterType=false, reportMissingTypeArgument=false
+from typing import Any
+
+CFArrayCreate: Any
+CFArrayCreateMutable: Any
+CFDictionaryCreate: Any
+CFDictionaryCreateMutable: Any
+CFSetCreate: Any
+CFSetCreateMutable: Any
+kCFTypeArrayCallBacks: Any
+kCFTypeDictionaryKeyCallBacks: Any
+kCFTypeDictionaryValueCallBacks: Any
+kCFTypeSetCallBacks: Any
+def CFCopyLocalizedString(key, comment): ...
+def CFCopyLocalizedStringFromTable(key, tbl, comment): ...
+def CFCopyLocalizedStringFromTableInBundle(key, tbl, bundle, comment): ...
+def CFCopyLocalizedStringWithDefaultValue(key, tbl, bundle, value, comment): ...
+def CFSTR(strval): ...
+def sel32or64(a, b): ...
+def selAorI(a, b): ...
+
+
+misc: Any
+constants: str
+enums: str
+r: Any
+protocols: Any
+expressions: Any
diff --git a/typings/Quartz/QuartzCore/__init__.pyi b/typings/Quartz/QuartzCore/__init__.pyi
new file mode 100644
index 0000000..6f0b775
--- /dev/null
+++ b/typings/Quartz/QuartzCore/__init__.pyi
@@ -0,0 +1,100 @@
+# pyright: reportUnknownParameterType=false, reportMissingParameterType=false, reportMissingTypeArgument=false, reportUnknownMemberType=false, reportUnknownVariableType=false
+# mypy: disable-error-code=type-arg
+from typing import Any, ClassVar
+
+import objc  # type: ignore
+import objc._lazyimport  # type: ignore
+
+CAAnimationCalculationMode: Any
+CAAnimationRotationMode: Any
+CAAutoresizingMask: Any
+CAConstraintAttribute: Any
+CACornerMask: Any
+CAEdgeAntialiasingMask: Any
+CAEmitterLayerEmitterMode: Any
+CAEmitterLayerEmitterShape: Any
+CAEmitterLayerRenderMode: Any
+CAGradientLayerType: Any
+CALayerContentsFilter: Any
+CALayerContentsFormat: Any
+CALayerContentsGravity: Any
+CALayerCornerCurve: Any
+CAMediaTimingFillMode: Any
+CAMediaTimingFunctionName: Any
+CAScrollLayerScrollMode: Any
+CAShapeLayerFillRule: Any
+CAShapeLayerLineCap: Any
+CAShapeLayerLineJoin: Any
+CATextLayerAlignmentMode: Any
+CATextLayerTruncationMode: Any
+CATransitionSubtype: Any
+CATransitionType: Any
+CAValueFunctionName: Any
+_ObjCLazyModule__aliases_deprecated: dict
+_ObjCLazyModule__enum_deprecated: dict
+_ObjCLazyModule__expressions: dict
+_ObjCLazyModule__expressions_mapping: objc._lazyimport.GetAttrMap
+_ObjCLazyModule__varmap_dct: dict
+_ObjCLazyModule__varmap_deprecated: dict
+
+def sel32or64(a, b): ...
+def selAorI(a, b): ...
+
+misc: Any
+constants: str
+enums: str
+functions: Any
+aliases: Any
+r: Any
+protocols: Any
+expressions: Any
+
+class CAFrameRateRange(objc._structwrapper):  # type: ignore
+    _fields: ClassVar[tuple] = ...
+    __match_args__: ClassVar[tuple] = ...
+    __typestr__: ClassVar[bytes] = ...
+    maximum: Any
+    minimum: Any
+    preferred: Any
+    def __init__(self, *args, **kwargs) -> None: ...
+    def _asdict(self, *args, **kwargs) -> Any: ...
+    def _replace(self, *args, **kwargs) -> Any: ...
+    def copy(self, *args, **kwargs) -> Any: ...
+    def __delattr__(self, name) -> Any: ...
+    def __delitem__(self, other) -> Any: ...
+    def __getitem__(self, index) -> Any: ...
+    def __pyobjc_copy__(self, *args, **kwargs) -> Any: ...
+    def __reduce__(self) -> Any: ...
+    def __setattr__(self, name, value) -> Any: ...
+    def __setitem__(self, index, object) -> Any: ...
+
+class CATransform3D(objc._structwrapper):  # type: ignore
+    _fields: ClassVar[tuple] = ...
+    __match_args__: ClassVar[tuple] = ...
+    __typestr__: ClassVar[bytes] = ...
+    m11: Any
+    m12: Any
+    m13: Any
+    m14: Any
+    m21: Any
+    m22: Any
+    m23: Any
+    m24: Any
+    m31: Any
+    m32: Any
+    m33: Any
+    m34: Any
+    m41: Any
+    m42: Any
+    m43: Any
+    m44: Any
+    def __init__(self, *args, **kwargs) -> None: ...
+    def _asdict(self, *args, **kwargs) -> Any: ...
+    def _replace(self, *args, **kwargs) -> Any: ...
+    def copy(self, *args, **kwargs) -> Any: ...
+    def __delitem__(self, other) -> Any: ...
+    def __getitem__(self, index) -> Any: ...
+    def __pyobjc_copy__(self, *args, **kwargs) -> Any: ...
+    def __reduce__(self) -> Any: ...
+    def __setattr__(self, name, value) -> Any: ...
+    def __setitem__(self, index, object) -> Any: ...
diff --git a/typings/Quartz/QuartzFilters/__init__.pyi b/typings/Quartz/QuartzFilters/__init__.pyi
new file mode 100644
index 0000000..a9c60e5
--- /dev/null
+++ b/typings/Quartz/QuartzFilters/__init__.pyi
@@ -0,0 +1,27 @@
+# pyright: reportUnknownParameterType=false, reportMissingParameterType=false, reportMissingTypeArgument=false
+from typing import Any
+
+import Foundation as Foundation  # type: ignore
+
+
+def sel32or64(a, b): ...
+def selAorI(a, b): ...
+
+
+misc: Any
+constants: str
+enums: str
+r: Any
+protocols: Any
+expressions: Any
+
+
+class NSDisabledAutomaticTermination:
+    def __init__(self, reason) -> None: ...
+    def __enter__(self) -> None: ...
+    def __exit__(self, exc_type, exc_val, exc_tb): ...
+
+
+class NSDisabledSuddenTermination:
+    def __enter__(self) -> None: ...
+    def __exit__(self, exc_type, exc_val, exc_tb): ...
diff --git a/typings/Quartz/QuickLookUI/__init__.pyi b/typings/Quartz/QuickLookUI/__init__.pyi
new file mode 100644
index 0000000..9e1d038
--- /dev/null
+++ b/typings/Quartz/QuickLookUI/__init__.pyi
@@ -0,0 +1,23 @@
+# pyright: reportUnknownParameterType=false, reportMissingParameterType=false, reportMissingTypeArgument=false, reportUnknownMemberType=false
+# mypy: disable-error-code=type-arg
+from typing import Any
+
+import objc._lazyimport  # type: ignore
+
+QLPreviewViewStyle: Any
+_ObjCLazyModule__aliases_deprecated: dict
+_ObjCLazyModule__enum_deprecated: dict
+_ObjCLazyModule__expressions: dict
+_ObjCLazyModule__expressions_mapping: objc._lazyimport.GetAttrMap
+_ObjCLazyModule__varmap_dct: dict
+_ObjCLazyModule__varmap_deprecated: dict
+
+def sel32or64(a, b): ...
+def selAorI(a, b): ...
+
+misc: Any
+constants: str
+enums: str
+r: Any
+protocols: Any
+expressions: Any
diff --git a/typings/Quartz/__init__.pyi b/typings/Quartz/__init__.pyi
new file mode 100644
index 0000000..b8bc34c
--- /dev/null
+++ b/typings/Quartz/__init__.pyi
@@ -0,0 +1,18 @@
+# https://github.com/ronaldoussoren/pyobjc/issues/198
+# https://github.com/ronaldoussoren/pyobjc/issues/417
+# https://github.com/ronaldoussoren/pyobjc/issues/419
+from typing import Any
+
+import objc as objc  # type: ignore
+from AppKit import *
+from Quartz.CoreGraphics import *
+from Quartz.CoreVideo import *
+from Quartz.ImageIO import *
+from Quartz.ImageKit import *
+from Quartz.PDFKit import *
+from Quartz.QuartzComposer import *
+from Quartz.QuartzCore import *
+from Quartz.QuartzFilters import *
+from Quartz.QuickLookUI import *
+
+def __getattr__(__name: str) -> Any: ...  # pyright: ignore[reportIncompleteStub]
diff --git a/typings/ewmh/__init__.pyi b/typings/ewmh/__init__.pyi
new file mode 100644
index 0000000..d9c1322
--- /dev/null
+++ b/typings/ewmh/__init__.pyi
@@ -0,0 +1,5 @@
+import sys
+
+assert sys.platform == "linux"  # pyright: ignore[reportInvalidStubStatement]
+
+from .ewmh import EWMH as EWMH
diff --git a/typings/ewmh/ewmh.pyi b/typings/ewmh/ewmh.pyi
new file mode 100644
index 0000000..4750ac0
--- /dev/null
+++ b/typings/ewmh/ewmh.pyi
@@ -0,0 +1,148 @@
+import sys
+
+assert sys.platform == "linux"  # pyright: ignore[reportInvalidStubStatement]
+# TODO: Push this upstream or upload to Typeshed
+# https://github.com/parkouss/pyewmh/pull/19
+from collections.abc import Iterable
+from typing import Any
+
+from typing_extensions import Literal, TypeAlias
+from Xlib.display import Display  # type: ignore[import,reportMissingTypeStubs]
+from Xlib.xobject.drawable import Window  # type: ignore[import,reportMissingTypeStubs]
+
+_GetAttrsProperty: TypeAlias = Literal[
+    "_NET_CLIENT_LIST",
+    "_NET_CLIENT_LIST_STACKING",
+    "_NET_NUMBER_OF_DESKTOPS",
+    "_NET_DESKTOP_GEOMETRY",
+    "_NET_DESKTOP_VIEWPORT",
+    "_NET_CURRENT_DESKTOP",
+    "_NET_ACTIVE_WINDOW",
+    "_NET_WORKAREA",
+    "_NET_SHOWING_DESKTOP",
+    "_NET_WM_NAME",
+    "_NET_WM_VISIBLE_NAME",
+    "_NET_WM_DESKTOP",
+    "_NET_WM_WINDOW_TYPE",
+    "_NET_WM_STATE",
+    "_NET_WM_ALLOWED_ACTIONS",
+    "_NET_WM_PID",
+]
+_SetAttrsProperty: TypeAlias = Literal[
+    "_NET_NUMBER_OF_DESKTOPS",
+    "_NET_DESKTOP_GEOMETRY",
+    "_NET_DESKTOP_VIEWPORT",
+    "_NET_CURRENT_DESKTOP",
+    "_NET_ACTIVE_WINDOW",
+    "_NET_SHOWING_DESKTOP",
+    "_NET_CLOSE_WINDOW",
+    "_NET_MOVERESIZE_WINDOW",
+    "_NET_WM_NAME",
+    "_NET_WM_VISIBLE_NAME",
+    "_NET_WM_DESKTOP",
+    "_NET_WM_STATE",
+]
+
+class EWMH:
+    NET_WM_WINDOW_TYPES: tuple[
+        Literal["_NET_WM_WINDOW_TYPE_DESKTOP"],
+        Literal["_NET_WM_WINDOW_TYPE_DOCK"],
+        Literal["_NET_WM_WINDOW_TYPE_TOOLBAR"],
+        Literal["_NET_WM_WINDOW_TYPE_MENU"],
+        Literal["_NET_WM_WINDOW_TYPE_UTILITY"],
+        Literal["_NET_WM_WINDOW_TYPE_SPLASH"],
+        Literal["_NET_WM_WINDOW_TYPE_DIALOG"],
+        Literal["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"],
+        Literal["_NET_WM_WINDOW_TYPE_POPUP_MENU"],
+        Literal["_NET_WM_WINDOW_TYPE_NOTIFICATION"],
+        Literal["_NET_WM_WINDOW_TYPE_COMBO"],
+        Literal["_NET_WM_WINDOW_TYPE_DND"],
+        Literal["_NET_WM_WINDOW_TYPE_NORMAL"],
+    ]
+    NET_WM_ACTIONS: tuple[
+        Literal["_NET_WM_ACTION_MOVE"],
+        Literal["_NET_WM_ACTION_RESIZE"],
+        Literal["_NET_WM_ACTION_MINIMIZE"],
+        Literal["_NET_WM_ACTION_SHADE"],
+        Literal["_NET_WM_ACTION_STICK"],
+        Literal["_NET_WM_ACTION_MAXIMIZE_HORZ"],
+        Literal["_NET_WM_ACTION_MAXIMIZE_VERT"],
+        Literal["_NET_WM_ACTION_FULLSCREEN"],
+        Literal["_NET_WM_ACTION_CHANGE_DESKTOP"],
+        Literal["_NET_WM_ACTION_CLOSE"],
+        Literal["_NET_WM_ACTION_ABOVE"],
+        Literal["_NET_WM_ACTION_BELOW"],
+    ]
+    NET_WM_STATES: tuple[
+        Literal["_NET_WM_STATE_MODAL"],
+        Literal["_NET_WM_STATE_STICKY"],
+        Literal["_NET_WM_STATE_MAXIMIZED_VERT"],
+        Literal["_NET_WM_STATE_MAXIMIZED_HORZ"],
+        Literal["_NET_WM_STATE_SHADED"],
+        Literal["_NET_WM_STATE_SKIP_TASKBAR"],
+        Literal["_NET_WM_STATE_SKIP_PAGER"],
+        Literal["_NET_WM_STATE_HIDDEN"],
+        Literal["_NET_WM_STATE_FULLSCREEN"],
+        Literal["_NET_WM_STATE_ABOVE"],
+        Literal["_NET_WM_STATE_BELOW"],
+        Literal["_NET_WM_STATE_DEMANDS_ATTENTION"],
+    ]
+    display: Display
+    root: Window
+    def __init__(
+        self, _display: Display | None = ..., root: Window | None = ...
+    ) -> None: ...
+    def setNumberOfDesktops(self, nb: int) -> None: ...
+    def setDesktopGeometry(self, w: int, h: int) -> None: ...
+    def setDesktopViewport(self, w: int, h: int) -> None: ...
+    def setCurrentDesktop(self, i: int) -> None: ...
+    def setActiveWindow(self, win: Window) -> None: ...
+    def setShowingDesktop(self, show: bool | int) -> None: ...
+    def setCloseWindow(self, win: Window) -> None: ...
+    def setWmName(self, win: Window, name: str) -> None: ...
+    def setWmVisibleName(self, win: Window, name: str) -> None: ...
+    def setWmDesktop(self, win: Window, i: int) -> None: ...
+    def setMoveResizeWindow(
+        self,
+        win: Window,
+        gravity: int = ...,
+        x: int | None = ...,
+        y: int | None = ...,
+        w: int | None = ...,
+        h: int | None = ...,
+    ) -> None: ...
+    def setWmState(
+        self, win: Window, action: int, state: int | str, state2: int | str = ...
+    ) -> None: ...
+    def getClientList(self) -> list[Window | None]: ...
+    def getClientListStacking(self) -> list[Window | None]: ...
+    def getNumberOfDesktops(self) -> int: ...
+    def getDesktopGeometry(self) -> tuple[int, int]: ...
+    def getDesktopViewPort(self) -> tuple[int, int]: ...
+    def getCurrentDesktop(self) -> int: ...
+    def getActiveWindow(self) -> Window | None: ...
+    def getWorkArea(self) -> tuple[int, int, int, int]: ...
+    def getShowingDesktop(self) -> int: ...
+    def getWmName(self, win: Window) -> str: ...
+    def getWmVisibleName(self, win: Window) -> str: ...
+    def getWmDesktop(self, win: Window) -> int | None: ...
+    def getWmWindowType(
+        self, win: Window, str: bool = ...
+    ) -> list[str] | list[int]: ...
+    def getWmState(self, win: Window, str: bool = ...) -> list[str] | list[int]: ...
+    def getWmAllowedActions(self, win: Window, str: bool) -> list[int] | list[str]: ...
+    def getWmPid(self, win: Window) -> int | None: ...
+    def getReadableProperties(
+        self,
+    ) -> Iterable[_GetAttrsProperty]: ...
+    # Another good candidate for AnyOf: https://github.com/python/typing/issues/566
+    def getProperty(
+        self, prop: str, *args: Window | bool, **kwargs: Window | bool
+    ) -> Any: ...
+    def getWritableProperties(self) -> Iterable[_SetAttrsProperty]: ...
+    def setProperty(
+        self,
+        prop: str,
+        *args: Window | str | int | bool | None,
+        **kwargs: Window | str | int | bool | None
+    ) -> None: ...


Test patch:
diff --git a/tests/test_MacNSWindow.py b/tests/test_MacNSWindow.py
index 69460f1..64f91ac 100644
--- a/tests/test_MacNSWindow.py
+++ b/tests/test_MacNSWindow.py
@@ -10,11 +10,11 @@
 
 import time
 
-from AppKit import (  # type: ignore[import]
+from AppKit import (
     NSApp, NSObject, NSApplication, NSMakeRect, NSWindow, NSWindowStyleMaskTitled, NSWindowStyleMaskClosable,
     NSWindowStyleMaskMiniaturizable, NSWindowStyleMaskResizable, NSBackingStoreBuffered)
 
-import pywinctl  # type: ignore[import]
+import pywinctl
 
 
 # Cocoa prefers composition to inheritance. The members of an object's
@@ -34,7 +34,7 @@ def setDemoMode(self):
     def unsetDemoMode(self):
         self.demoMode = False
 
-    def applicationDidFinishLaunching_(self, aNotification):
+    def applicationDidFinishLaunching_(self, aNotification: None):
         '''Called automatically when the application has launched'''
         # Set it as the frontmost application
         NSApp().activateIgnoringOtherApps_(True)
@@ -262,13 +262,13 @@ def applicationDidFinishLaunching_(self, aNotification):
             assert not self.npw.isVisible
             assert not self.npw.isAlive
 
-    def windowWillClose_(self, aNotification):
+    def windowWillClose_(self, aNotification: None):
         '''Called automatically when the window is closed'''
         print("Window has been closed")
         # Terminate the application
         NSApp().terminate_(self)
 
-    def windowDidBecomeKey_(self, aNotification):
+    def windowDidBecomeKey_(self, aNotification: None):
         print("Now I'm ACTIVE")
 
 
diff --git a/tests/test_pywinctl.py b/tests/test_pywinctl.py
index 030f74d..23ea7e7 100644
--- a/tests/test_pywinctl.py
+++ b/tests/test_pywinctl.py
@@ -1,11 +1,14 @@
 #!/usr/bin/python
 # -*- coding: utf-8 -*-
 
+from __future__ import annotations
+
 import subprocess
 import sys
 import time
+from typing import Any, cast
 
-import pywinctl  # type: ignore[import]  # pyright: ignore[reportMissingImports]
+import pywinctl
 
 
 def test_basic():
@@ -45,6 +48,7 @@ def test_basic():
         assert len(testWindows) == 1
 
         npw = testWindows[0]
+        assert isinstance(npw, pywinctl.Window)
 
         basic_macOS(npw)
 
@@ -53,505 +57,506 @@ def test_basic():
     else:
         raise NotImplementedError('PyWinCtl currently does not support this platform. If you have useful knowledge, please contribute! https://github.com/Kalmat/pywinctl')
 
+if sys.platform == "win32":
+    def basic_win32(npw: pywinctl.Window | None):
 
-def basic_win32(npw):
-
-    assert npw is not None
-
-    wait = True
-    timelap = 0.5
-
-    # Test maximize/minimize/restore.
-    if npw.isMaximized:  # Make sure it starts un-maximized
-        npw.restore(wait=wait)
-
-    assert not npw.isMaximized
-
-    npw.maximize(wait=wait)
-    assert npw.isMaximized
-    npw.restore(wait=wait)
-    assert not npw.isMaximized
-
-    npw.minimize(wait=wait)
-    assert npw.isMinimized
-    npw.restore(wait=wait)
-    assert not npw.isMinimized
-
-    # Test resizing
-    npw.resizeTo(800, 600, wait=wait)
-    assert npw.size == (800, 600)
-    assert npw.width == 800
-    assert npw.height == 600
-
-    npw.resizeRel(10, 20, wait=wait)
-    assert npw.size == (810, 620)
-    assert npw.width == 810
-    assert npw.height == 620
-
-    # Test moving
-    npw.moveTo(100, 200, wait=wait)
-    assert npw.topleft == (100, 200)
-    assert npw.left == 100
-    assert npw.top == 200
-    assert npw.right == 910
-    assert npw.bottom == 820
-    assert npw.bottomright == (910, 820)
-    assert npw.bottomleft == (100, 820)
-    assert npw.topright == (910, 200)
-
-    npw.moveRel(1, 2, wait=wait)
-    assert npw.topleft == (101, 202)
-    assert npw.left == 101
-    assert npw.top == 202
-    assert npw.right == 911
-    assert npw.bottom == 822
-    assert npw.bottomright == (911, 822)
-    assert npw.bottomleft == (101, 822)
-    assert npw.topright == (911, 202)
-
-    # Move via the properties
-    npw.resizeTo(800, 600)
-    npw.moveTo(200, 200)
-
-    npw.left = 250
-    time.sleep(timelap)
-    assert npw.left == 250
-
-    npw.right = 950
-    time.sleep(timelap)
-    assert npw.right == 950
-
-    npw.top = 150
-    time.sleep(timelap)
-    assert npw.top == 150
-
-    npw.bottom = 775
-    time.sleep(timelap)
-    assert npw.bottom == 775
-
-    npw.topleft = (155, 350)
-    time.sleep(timelap)
-    assert npw.topleft == (155, 350)
-
-    npw.topright = (1000, 300)
-    time.sleep(timelap)
-    assert npw.topright == (1000, 300)
-
-    npw.bottomleft = (300, 975)
-    time.sleep(timelap)
-    assert npw.bottomleft == (300, 975)
-
-    npw.bottomright = (1000, 900)
-    time.sleep(timelap)
-    assert npw.bottomright == (1000, 900)
-
-    npw.midleft = (300, 400)
-    time.sleep(timelap)
-    assert npw.midleft == (300, 400)
-
-    npw.midright = (1050, 600)
-    time.sleep(timelap)
-    assert npw.midright == (1050, 600)
-
-    npw.midtop = (500, 350)
-    time.sleep(timelap)
-    assert npw.midtop == (500, 350)
-
-    npw.midbottom = (500, 800)
-    time.sleep(timelap)
-    assert npw.midbottom == (500, 800)
-
-    npw.center = (500, 350)
-    time.sleep(timelap)
-    assert npw.center == (500, 350)
-
-    npw.centerx = 1000
-    time.sleep(timelap)
-    assert npw.centerx == 1000
-
-    npw.centery = 600
-    time.sleep(timelap)
-    assert npw.centery == 600
-
-    npw.width = 700
-    time.sleep(timelap)
-    assert npw.width == 700
-
-    npw.height = 500
-    time.sleep(timelap)
-    assert npw.height == 500
-
-    npw.size = (801, 601)
-    time.sleep(timelap)
-    assert npw.size == (801, 601)
-
-    # Test window stacking
-    npw.lowerWindow()
-    time.sleep(timelap)
-    npw.raiseWindow()
-    time.sleep(timelap)
-    npw.alwaysOnTop()
-    time.sleep(timelap)
-    npw.alwaysOnTop(aot=False)
-    time.sleep(timelap)
-    npw.alwaysOnBottom()
-    time.sleep(timelap)
-    npw.alwaysOnBottom(aob=False)
-    time.sleep(timelap)
-    npw.sendBehind()
-    time.sleep(timelap)
-    npw.sendBehind(sb=False)
-    time.sleep(timelap)
-
-    # Test parent methods
-    parent = npw.getParent()
-    assert npw.isChild(parent)
-
-    # Test menu options
-    menu = npw.menu.getMenu()
-    submenu = {}
-    for i, key in enumerate(menu.keys()):
-        if i == 4:
-            submenu = menu[key]["entries"]
-    option = {}
-    for i, key in enumerate(submenu.keys()):
-        if i == 3:
-            option = submenu[key]
-    if option:
-        npw.menu.clickMenuItem(wID=option["wID"])
-        time.sleep(5)
-
-    # Test closing
-    npw.close()
-
+        assert npw is not None
 
-def basic_linux(npw):
-    # WARNING: Xlib/EWMH does not support negative positions, so be careful with positions calculations
-    # and/or set proper screen resolution to avoid negative values (tested OK on 1920x1200)
+        wait = True
+        timelap = 0.5
 
-    assert npw is not None
+        # Test maximize/minimize/restore.
+        if npw.isMaximized:  # Make sure it starts un-maximized
+            npw.restore(wait=wait)
 
-    wait = True
-    timelap = 0.5
+        assert not npw.isMaximized
 
-    # Test maximize/minimize/restore.
-    if npw.isMaximized:  # Make sure it starts un-maximized
+        npw.maximize(wait=wait)
+        assert npw.isMaximized
         npw.restore(wait=wait)
+        assert not npw.isMaximized
 
-    assert not npw.isMaximized
-
-    npw.maximize(wait=wait)
-    assert npw.isMaximized
-    npw.restore(wait=wait)
-    assert not npw.isMaximized
-
-    npw.minimize(wait=wait)
-    assert npw.isMinimized
-    npw.restore(wait=wait)
-    assert not npw.isMinimized
-
-    # Test resizing
-    npw.resizeTo(800, 600, wait=wait)
-    assert npw.size == (800, 600)
-    assert npw.width == 800
-    assert npw.height == 600
-
-    npw.resizeRel(10, 20, wait=wait)
-    assert npw.size == (810, 620)
-    assert npw.width == 810
-    assert npw.height == 620
-
-    # Test moving
-    npw.moveTo(100, 200, wait=wait)
-    assert npw.topleft == (100, 200)
-    assert npw.left == 100
-    assert npw.top == 200
-    assert npw.right == 910
-    assert npw.bottom == 820
-    assert npw.bottomright == (910, 820)
-    assert npw.bottomleft == (100, 820)
-    assert npw.topright == (910, 200)
-
-    npw.moveRel(1, 2, wait=wait)
-    assert npw.topleft == (101, 202)
-    assert npw.left == 101
-    assert npw.top == 202
-    assert npw.right == 911
-    assert npw.bottom == 822
-    assert npw.bottomright == (911, 822)
-    assert npw.bottomleft == (101, 822)
-    assert npw.topright == (911, 202)
-
-    # Move via the properties
-    npw.resizeTo(800, 600)
-    npw.moveTo(200, 200)
-
-    npw.left = 250
-    time.sleep(timelap)
-    assert npw.left == 250
-
-    npw.right = 950
-    time.sleep(timelap)
-    assert npw.right == 950
-
-    npw.top = 150
-    time.sleep(timelap)
-    assert npw.top == 150
-
-    npw.bottom = 775
-    time.sleep(timelap)
-    assert npw.bottom == 775
-
-    npw.topleft = (155, 350)
-    time.sleep(timelap)
-    assert npw.topleft == (155, 350)
-
-    npw.topright = (1000, 300)
-    time.sleep(timelap)
-    assert npw.topright == (1000, 300)
-
-    npw.bottomleft = (300, 975)
-    time.sleep(timelap)
-    assert npw.bottomleft == (300, 975)
-
-    npw.bottomright = (1000, 900)
-    time.sleep(timelap)
-    assert npw.bottomright == (1000, 900)
-
-    npw.midleft = (300, 400)
-    time.sleep(timelap)
-    assert npw.midleft == (300, 400)
-
-    npw.midright = (1050, 600)
-    time.sleep(timelap)
-    assert npw.midright == (1050, 600)
-
-    npw.midtop = (500, 350)
-    time.sleep(timelap)
-    assert npw.midtop == (500, 350)
-
-    npw.midbottom = (500, 800)
-    time.sleep(timelap)
-    assert npw.midbottom == (500, 800)
-
-    npw.center = (500, 350)
-    time.sleep(timelap)
-    assert npw.center == (500, 350)
-
-    npw.centerx = 1000
-    time.sleep(timelap)
-    assert npw.centerx == 1000
-
-    npw.centery = 600
-    time.sleep(timelap)
-    assert npw.centery == 600
-
-    npw.width = 700
-    time.sleep(timelap)
-    assert npw.width == 700
-
-    npw.height = 500
-    time.sleep(timelap)
-    assert npw.height == 500
-
-    npw.size = (801, 601)
-    time.sleep(timelap)
-    assert npw.size == (801, 601)
-
-    # Test window stacking
-    npw.lowerWindow()
-    time.sleep(timelap)
-    npw.raiseWindow()
-    time.sleep(timelap)
-    npw.alwaysOnTop()
-    time.sleep(timelap)
-    npw.alwaysOnTop(aot=False)
-    time.sleep(timelap)
-    npw.alwaysOnBottom()
-    time.sleep(timelap)
-    npw.alwaysOnBottom(aob=False)
-    time.sleep(timelap)
-    npw.sendBehind()
-    time.sleep(timelap)
-    npw.sendBehind(sb=False)
-    time.sleep(timelap)
-
-    # Test parent methods
-    parent = npw.getParent()
-    assert npw.isChild(parent)
-
-    # Test closing
-    npw.close()
-
-
-def basic_macOS(npw):
-
-    assert npw is not None
-
-    wait = True
-    timelap = 0.00
-
-    # Test maximize/minimize/restore.
-    if npw.isMaximized:  # Make sure it starts un-maximized
+        npw.minimize(wait=wait)
+        assert npw.isMinimized
         npw.restore(wait=wait)
+        assert not npw.isMinimized
+
+        # Test resizing
+        npw.resizeTo(800, 600, wait=wait)
+        assert npw.size == (800, 600)
+        assert npw.width == 800
+        assert npw.height == 600
+
+        npw.resizeRel(10, 20, wait=wait)
+        assert npw.size == (810, 620)
+        assert npw.width == 810
+        assert npw.height == 620
+
+        # Test moving
+        npw.moveTo(100, 200, wait=wait)
+        assert npw.topleft == (100, 200)
+        assert npw.left == 100
+        assert npw.top == 200
+        assert npw.right == 910
+        assert npw.bottom == 820
+        assert npw.bottomright == (910, 820)
+        assert npw.bottomleft == (100, 820)
+        assert npw.topright == (910, 200)
+
+        npw.moveRel(1, 2, wait=wait)
+        assert npw.topleft == (101, 202)
+        assert npw.left == 101
+        assert npw.top == 202
+        assert npw.right == 911
+        assert npw.bottom == 822
+        assert npw.bottomright == (911, 822)
+        assert npw.bottomleft == (101, 822)
+        assert npw.topright == (911, 202)
+
+        # Move via the properties
+        npw.resizeTo(800, 600)
+        npw.moveTo(200, 200)
+
+        npw.left = 250
+        time.sleep(timelap)
+        assert npw.left == 250
+
+        npw.right = 950
+        time.sleep(timelap)
+        assert npw.right == 950
+
+        npw.top = 150
+        time.sleep(timelap)
+        assert npw.top == 150
+
+        npw.bottom = 775
+        time.sleep(timelap)
+        assert npw.bottom == 775
+
+        npw.topleft = (155, 350)
+        time.sleep(timelap)
+        assert npw.topleft == (155, 350)
+
+        npw.topright = (1000, 300)
+        time.sleep(timelap)
+        assert npw.topright == (1000, 300)
+
+        npw.bottomleft = (300, 975)
+        time.sleep(timelap)
+        assert npw.bottomleft == (300, 975)
+
+        npw.bottomright = (1000, 900)
+        time.sleep(timelap)
+        assert npw.bottomright == (1000, 900)
+
+        npw.midleft = (300, 400)
+        time.sleep(timelap)
+        assert npw.midleft == (300, 400)
+
+        npw.midright = (1050, 600)
+        time.sleep(timelap)
+        assert npw.midright == (1050, 600)
+
+        npw.midtop = (500, 350)
+        time.sleep(timelap)
+        assert npw.midtop == (500, 350)
+
+        npw.midbottom = (500, 800)
+        time.sleep(timelap)
+        assert npw.midbottom == (500, 800)
+
+        npw.center = (500, 350)
+        time.sleep(timelap)
+        assert npw.center == (500, 350)
+
+        npw.centerx = 1000
+        time.sleep(timelap)
+        assert npw.centerx == 1000
+
+        npw.centery = 600
+        time.sleep(timelap)
+        assert npw.centery == 600
+
+        npw.width = 700
+        time.sleep(timelap)
+        assert npw.width == 700
+
+        npw.height = 500
+        time.sleep(timelap)
+        assert npw.height == 500
+
+        npw.size = (801, 601)
+        time.sleep(timelap)
+        assert npw.size == (801, 601)
+
+        # Test window stacking
+        npw.lowerWindow()
+        time.sleep(timelap)
+        npw.raiseWindow()
+        time.sleep(timelap)
+        npw.alwaysOnTop()
+        time.sleep(timelap)
+        npw.alwaysOnTop(aot=False)
+        time.sleep(timelap)
+        npw.alwaysOnBottom()
+        time.sleep(timelap)
+        npw.alwaysOnBottom(aob=False)
+        time.sleep(timelap)
+        npw.sendBehind()
+        time.sleep(timelap)
+        npw.sendBehind(sb=False)
+        time.sleep(timelap)
+
+        # Test parent methods
+        parent = npw.getParent()
+        assert npw.isChild(parent)
+
+        # Test menu options
+        menu = npw.menu.getMenu()
+        submenu: dict[str, Any] = {}
+        for i, key in enumerate(menu):
+            if i == 4:
+                submenu = menu[key]["entries"]
+
+        option: dict[str, Any] | None = None
+        for i, key in enumerate(submenu):
+            if i == 3:
+                option = submenu[key]
+        if option:
+            npw.menu.clickMenuItem(wID=option["wID"])
+            time.sleep(5)
+
+        # Test closing
+        npw.close()
+
+if sys.platform == "linux":
+    def basic_linux(npw: pywinctl.Window | None):
+        # WARNING: Xlib/EWMH does not support negative positions, so be careful with positions calculations
+        # and/or set proper screen resolution to avoid negative values (tested OK on 1920x1200)
+
+        assert npw is not None
+
+        wait = True
+        timelap = 0.5
+
+        # Test maximize/minimize/restore.
+        if npw.isMaximized:  # Make sure it starts un-maximized
+            npw.restore(wait=wait)
+
+        assert not npw.isMaximized
+
+        npw.maximize(wait=wait)
+        assert npw.isMaximized
+        npw.restore(wait=wait)
+        assert not npw.isMaximized
 
-    assert not npw.isMaximized
-
-    npw.maximize(wait=wait)
-    time.sleep(timelap)
-    assert npw.isMaximized
-    npw.restore(wait=wait)
-    time.sleep(timelap)
-    assert not npw.isMaximized
-
-    npw.minimize(wait=wait)
-    time.sleep(timelap)
-    assert npw.isMinimized
-    npw.restore(wait=wait)
-    time.sleep(timelap)
-    assert not npw.isMinimized
-
-    # Test resizing
-    npw.resizeTo(600, 400, wait=wait)
-    time.sleep(timelap)
-    assert npw.size == (600, 400)
-    assert npw.width == 600
-    assert npw.height == 400
-
-    npw.resizeRel(10, 20, wait=wait)
-    assert npw.size == (610, 420)
-    assert npw.width == 610
-    assert npw.height == 420
-
-    # Test moving
-    npw.moveTo(50, 24, wait=wait)
-    assert npw.topleft == (50, 24)
-    assert npw.left == 50
-    assert npw.top == 24
-    assert npw.right == 660
-    assert npw.bottom == 444
-    assert npw.bottomright == (660, 444)
-    assert npw.bottomleft == (50, 444)
-    assert npw.topright == (660, 24)
-
-    npw.moveRel(1, 2, wait=wait)
-    assert npw.topleft == (51, 26)
-    assert npw.left == 51
-    assert npw.top == 26
-    assert npw.right == 661
-    assert npw.bottom == 446
-    assert npw.bottomright == (661, 446)
-    assert npw.bottomleft == (51, 446)
-    assert npw.topright == (661, 26)
-
-    # Move via the properties
-    npw.resizeTo(601, 401, wait=wait)
-    npw.moveTo(100, 250, wait=wait)
-
-    npw.left = 200
-    time.sleep(timelap)
-    assert npw.left == 200
-
-    npw.right = 200
-    time.sleep(timelap)
-    assert npw.right == 200
-
-    npw.top = 200
-    time.sleep(timelap)
-    assert npw.top == 200
-
-    npw.bottom = 800
-    time.sleep(timelap)
-    assert npw.bottom == 800
-
-    npw.topleft = (300, 400)
-    time.sleep(timelap)
-    assert npw.topleft == (300, 400)
-
-    npw.topright = (300, 400)
-    time.sleep(timelap)
-    assert npw.topright == (300, 400)
-
-    npw.bottomleft = (300, 700)
-    time.sleep(timelap)
-    assert npw.bottomleft == (300, 700)
-
-    npw.bottomright = (300, 900)
-    time.sleep(timelap)
-    assert npw.bottomright == (300, 900)
-
-    npw.midleft = (300, 400)
-    time.sleep(timelap)
-    assert npw.midleft == (300, 400)
-
-    npw.midright = (300, 400)
-    time.sleep(timelap)
-    assert npw.midright == (300, 400)
-
-    npw.midtop = (300, 400)
-    time.sleep(timelap)
-    assert npw.midtop == (300, 400)
-
-    npw.midbottom = (300, 700)
-    time.sleep(timelap)
-    assert npw.midbottom == (300, 700)
-
-    npw.center = (300, 400)
-    time.sleep(timelap)
-    assert npw.center == (300, 400)
-
-    npw.centerx = 1000
-    time.sleep(timelap)
-    assert npw.centerx == 1000
-
-    npw.centery = 300
-    time.sleep(timelap)
-    assert npw.centery == 300
-
-    npw.width = 600
-    time.sleep(timelap)
-    assert npw.width == 600
-
-    npw.height = 400
-    time.sleep(timelap)
-    assert npw.height == 400
-
-    npw.size = (810, 610)
-    time.sleep(timelap)
-    assert npw.size == (810, 610)
-
-    npw.lowerWindow()
-    time.sleep(timelap)
-
-    npw.raiseWindow()
-    time.sleep(timelap)
-
-    # Test window stacking
-    npw.lowerWindow()
-    time.sleep(timelap)
-    npw.raiseWindow()
-    time.sleep(timelap)
-
-    # Test parent methods
-    parent = npw.getParent()
-    assert parent and npw.isChild(parent)
-
-    # Test menu options
-    menu = npw.menu.getMenu()
-    submenu = {}
-    for i, key in enumerate(menu.keys()):
-        if i == 1:
-            submenu = menu[key]["entries"]
-    option = {}
-    for i, key in enumerate(submenu.keys()):
-        if i == 0:
-            option = submenu[key]
-    if option:
-        npw.menu.clickMenuItem(wID=option["wID"])
-        time.sleep(5)
+        npw.minimize(wait=wait)
+        assert npw.isMinimized
+        npw.restore(wait=wait)
+        assert not npw.isMinimized
+
+        # Test resizing
+        npw.resizeTo(800, 600, wait=wait)
+        assert npw.size == (800, 600)
+        assert npw.width == 800
+        assert npw.height == 600
+
+        npw.resizeRel(10, 20, wait=wait)
+        assert npw.size == (810, 620)
+        assert npw.width == 810
+        assert npw.height == 620
+
+        # Test moving
+        npw.moveTo(100, 200, wait=wait)
+        assert npw.topleft == (100, 200)
+        assert npw.left == 100
+        assert npw.top == 200
+        assert npw.right == 910
+        assert npw.bottom == 820
+        assert npw.bottomright == (910, 820)
+        assert npw.bottomleft == (100, 820)
+        assert npw.topright == (910, 200)
+
+        npw.moveRel(1, 2, wait=wait)
+        assert npw.topleft == (101, 202)
+        assert npw.left == 101
+        assert npw.top == 202
+        assert npw.right == 911
+        assert npw.bottom == 822
+        assert npw.bottomright == (911, 822)
+        assert npw.bottomleft == (101, 822)
+        assert npw.topright == (911, 202)
+
+        # Move via the properties
+        npw.resizeTo(800, 600)
+        npw.moveTo(200, 200)
+
+        npw.left = 250
+        time.sleep(timelap)
+        assert npw.left == 250
+
+        npw.right = 950
+        time.sleep(timelap)
+        assert npw.right == 950
+
+        npw.top = 150
+        time.sleep(timelap)
+        assert npw.top == 150
+
+        npw.bottom = 775
+        time.sleep(timelap)
+        assert npw.bottom == 775
+
+        npw.topleft = (155, 350)
+        time.sleep(timelap)
+        assert npw.topleft == (155, 350)
+
+        npw.topright = (1000, 300)
+        time.sleep(timelap)
+        assert npw.topright == (1000, 300)
+
+        npw.bottomleft = (300, 975)
+        time.sleep(timelap)
+        assert npw.bottomleft == (300, 975)
+
+        npw.bottomright = (1000, 900)
+        time.sleep(timelap)
+        assert npw.bottomright == (1000, 900)
+
+        npw.midleft = (300, 400)
+        time.sleep(timelap)
+        assert npw.midleft == (300, 400)
+
+        npw.midright = (1050, 600)
+        time.sleep(timelap)
+        assert npw.midright == (1050, 600)
+
+        npw.midtop = (500, 350)
+        time.sleep(timelap)
+        assert npw.midtop == (500, 350)
+
+        npw.midbottom = (500, 800)
+        time.sleep(timelap)
+        assert npw.midbottom == (500, 800)
+
+        npw.center = (500, 350)
+        time.sleep(timelap)
+        assert npw.center == (500, 350)
+
+        npw.centerx = 1000
+        time.sleep(timelap)
+        assert npw.centerx == 1000
+
+        npw.centery = 600
+        time.sleep(timelap)
+        assert npw.centery == 600
+
+        npw.width = 700
+        time.sleep(timelap)
+        assert npw.width == 700
+
+        npw.height = 500
+        time.sleep(timelap)
+        assert npw.height == 500
+
+        npw.size = (801, 601)
+        time.sleep(timelap)
+        assert npw.size == (801, 601)
+
+        # Test window stacking
+        npw.lowerWindow()
+        time.sleep(timelap)
+        npw.raiseWindow()
+        time.sleep(timelap)
+        npw.alwaysOnTop()
+        time.sleep(timelap)
+        npw.alwaysOnTop(aot=False)
+        time.sleep(timelap)
+        npw.alwaysOnBottom()
+        time.sleep(timelap)
+        npw.alwaysOnBottom(aob=False)
+        time.sleep(timelap)
+        npw.sendBehind()
+        time.sleep(timelap)
+        npw.sendBehind(sb=False)
+        time.sleep(timelap)
+
+        # Test parent methods
+        parent = npw.getParent()
+        assert npw.isChild(parent)
+
+        # Test closing
+        npw.close()
+
+if sys.platform == "darwin":
+    def basic_macOS(npw: pywinctl.Window):
+
+        assert npw is not None
+
+        wait = True
+        timelap = 0.00
+
+        # Test maximize/minimize/restore.
+        if npw.isMaximized:  # Make sure it starts un-maximized
+            npw.restore(wait=wait)
+
+        assert not npw.isMaximized
+
+        npw.maximize(wait=wait)
+        time.sleep(timelap)
+        assert npw.isMaximized
+        npw.restore(wait=wait)
+        time.sleep(timelap)
+        assert not npw.isMaximized
 
-    # Test closing
-    npw.close()
+        npw.minimize(wait=wait)
+        time.sleep(timelap)
+        assert npw.isMinimized
+        npw.restore(wait=wait)
+        time.sleep(timelap)
+        assert not npw.isMinimized
+
+        # Test resizing
+        npw.resizeTo(600, 400, wait=wait)
+        time.sleep(timelap)
+        assert npw.size == (600, 400)
+        assert npw.width == 600
+        assert npw.height == 400
+
+        npw.resizeRel(10, 20, wait=wait)
+        assert npw.size == (610, 420)
+        assert npw.width == 610
+        assert npw.height == 420
+
+        # Test moving
+        npw.moveTo(50, 24, wait=wait)
+        assert npw.topleft == (50, 24)
+        assert npw.left == 50
+        assert npw.top == 24
+        assert npw.right == 660
+        assert npw.bottom == 444
+        assert npw.bottomright == (660, 444)
+        assert npw.bottomleft == (50, 444)
+        assert npw.topright == (660, 24)
+
+        npw.moveRel(1, 2, wait=wait)
+        assert npw.topleft == (51, 26)
+        assert npw.left == 51
+        assert npw.top == 26
+        assert npw.right == 661
+        assert npw.bottom == 446
+        assert npw.bottomright == (661, 446)
+        assert npw.bottomleft == (51, 446)
+        assert npw.topright == (661, 26)
+
+        # Move via the properties
+        npw.resizeTo(601, 401, wait=wait)
+        npw.moveTo(100, 250, wait=wait)
+
+        npw.left = 200
+        time.sleep(timelap)
+        assert npw.left == 200
+
+        npw.right = 200
+        time.sleep(timelap)
+        assert npw.right == 200
+
+        npw.top = 200
+        time.sleep(timelap)
+        assert npw.top == 200
+
+        npw.bottom = 800
+        time.sleep(timelap)
+        assert npw.bottom == 800
+
+        npw.topleft = (300, 400)
+        time.sleep(timelap)
+        assert npw.topleft == (300, 400)
+
+        npw.topright = (300, 400)
+        time.sleep(timelap)
+        assert npw.topright == (300, 400)
+
+        npw.bottomleft = (300, 700)
+        time.sleep(timelap)
+        assert npw.bottomleft == (300, 700)
+
+        npw.bottomright = (300, 900)
+        time.sleep(timelap)
+        assert npw.bottomright == (300, 900)
+
+        npw.midleft = (300, 400)
+        time.sleep(timelap)
+        assert npw.midleft == (300, 400)
+
+        npw.midright = (300, 400)
+        time.sleep(timelap)
+        assert npw.midright == (300, 400)
+
+        npw.midtop = (300, 400)
+        time.sleep(timelap)
+        assert npw.midtop == (300, 400)
+
+        npw.midbottom = (300, 700)
+        time.sleep(timelap)
+        assert npw.midbottom == (300, 700)
+
+        npw.center = (300, 400)
+        time.sleep(timelap)
+        assert npw.center == (300, 400)
+
+        npw.centerx = 1000
+        time.sleep(timelap)
+        assert npw.centerx == 1000
+
+        npw.centery = 300
+        time.sleep(timelap)
+        assert npw.centery == 300
+
+        npw.width = 600
+        time.sleep(timelap)
+        assert npw.width == 600
+
+        npw.height = 400
+        time.sleep(timelap)
+        assert npw.height == 400
+
+        npw.size = (810, 610)
+        time.sleep(timelap)
+        assert npw.size == (810, 610)
+
+        npw.lowerWindow()
+        time.sleep(timelap)
+
+        npw.raiseWindow()
+        time.sleep(timelap)
+
+        # Test window stacking
+        npw.lowerWindow()
+        time.sleep(timelap)
+        npw.raiseWindow()
+        time.sleep(timelap)
+
+        # Test parent methods
+        parent = npw.getParent()
+        assert parent and npw.isChild(parent)
+
+        # Test menu options
+        menu = npw.menu.getMenu()
+        submenu = {}
+        for i, key in enumerate(menu):
+            if i == 1:
+                submenu = menu[key].get("entries", {})
+        option: dict[str, Any] | None = None
+        for i, key in enumerate(submenu):
+            if i == 0:
+                option = cast("dict[str, Any]", submenu[key])
+        if option:
+            npw.menu.clickMenuItem(wID=option.get("wID", ""))
+            time.sleep(5)
+
+        # Test closing
+        npw.close()
 
 
 def main():