Add DSLogic capture, signal conversion and DSView decoders
This commit is contained in:
87
python/logic_analyzer/dsview_host.py
Normal file
87
python/logic_analyzer/dsview_host.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""Process boundary for the patched DSView GUI (host protocol 1, Windows).
|
||||
|
||||
The host owns only its child process/window. Qt 5, Python and libusb remain
|
||||
inside DSView; they must never be loaded into the hosting application's Qt.
|
||||
"""
|
||||
from contextlib import contextmanager
|
||||
import ctypes
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
|
||||
def resolve_runtime(bundled, executable=None, frozen=None):
|
||||
"""A dsview folder beside the GUI EXE overrides its bundled runtime."""
|
||||
frozen = getattr(sys, 'frozen', False) if frozen is None else frozen
|
||||
if frozen:
|
||||
external = Path(executable or sys.executable).resolve().parent / 'dsview'
|
||||
if external.exists():
|
||||
# An incomplete external install should be reported, not silently
|
||||
# replaced with an older bundled component.
|
||||
return external
|
||||
return Path(bundled)
|
||||
|
||||
|
||||
def child_environment(runtime, stylesheet, capture_directory, environment=None):
|
||||
"""Remove Python/Qt/PyInstaller overrides before starting the private runtime."""
|
||||
env = dict(os.environ if environment is None else environment)
|
||||
for key in list(env):
|
||||
if key.upper().startswith(('PYTHON', 'QT_', 'QML', 'PYSIDE', '_PYI', '_MEIPASS')):
|
||||
env.pop(key)
|
||||
windows = Path(env.get('SystemRoot', r'C:\Windows'))
|
||||
env['PATH'] = os.pathsep.join(map(str, (Path(runtime), windows / 'System32', windows)))
|
||||
env['DSVIEW_HOST_STYLE'] = str(stylesheet)
|
||||
env['DSVIEW_CAPTURE_DIR'] = str(capture_directory)
|
||||
env['PYTHONDONTWRITEBYTECODE'] = '1'
|
||||
return env
|
||||
|
||||
|
||||
def ready_window(line):
|
||||
"""Only accept the versioned child's explicit ready message."""
|
||||
fields = line.strip().split()
|
||||
if len(fields) == 2 and fields[0] == 'DSVIEW_READY':
|
||||
try:
|
||||
value = int(fields[1])
|
||||
return value if value > 0 else None
|
||||
except ValueError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def window_api():
|
||||
from ctypes import wintypes
|
||||
api = ctypes.WinDLL('user32', use_last_error=True)
|
||||
api.GetWindowThreadProcessId.argtypes = [wintypes.HWND, ctypes.POINTER(wintypes.DWORD)]
|
||||
api.GetWindowThreadProcessId.restype = wintypes.DWORD
|
||||
api.GetParent.argtypes = [wintypes.HWND]
|
||||
api.GetParent.restype = wintypes.HWND
|
||||
return api
|
||||
|
||||
|
||||
def owns_window(window, pid, parent=None):
|
||||
if sys.platform != 'win32' or not window or not pid:
|
||||
return False
|
||||
from ctypes import wintypes
|
||||
api = window_api()
|
||||
owner = wintypes.DWORD()
|
||||
api.GetWindowThreadProcessId(window, ctypes.byref(owner))
|
||||
return owner.value == pid and (parent is None or api.GetParent(window) == parent)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def independent_dll_directory():
|
||||
"""Do not inherit PyInstaller's Qt DLL search directory into DSView."""
|
||||
if sys.platform != 'win32' or not getattr(sys, 'frozen', False):
|
||||
yield
|
||||
return
|
||||
api = ctypes.WinDLL('kernel32', use_last_error=True)
|
||||
api.GetDllDirectoryW.argtypes = [ctypes.c_uint32, ctypes.c_wchar_p]
|
||||
api.SetDllDirectoryW.argtypes = [ctypes.c_wchar_p]
|
||||
buffer = ctypes.create_unicode_buffer(32768)
|
||||
api.GetDllDirectoryW(len(buffer), buffer)
|
||||
if not api.SetDllDirectoryW(None):
|
||||
raise ctypes.WinError(ctypes.get_last_error())
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
api.SetDllDirectoryW(buffer.value or None)
|
||||
Reference in New Issue
Block a user