115 lines
4.7 KiB
Python
115 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright (c) 2026 Storm Dragon
|
|
# Authors: Storm Dragon, Angus Kola
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
"""Check Qt menu and button states on a private X11/AT-SPI desktop."""
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
sys.dont_write_bytecode = True
|
|
from native_x11 import wait_for
|
|
from native_test_session import run_private_session
|
|
|
|
|
|
def fixture(version):
|
|
if version == '5':
|
|
from PyQt5 import QtCore, QtWidgets
|
|
else:
|
|
from PySide6 import QtCore, QtWidgets
|
|
|
|
class ControlWindow(QtWidgets.QMainWindow):
|
|
def keyPressEvent(self, event):
|
|
nonlocal option
|
|
targets = {QtCore.Qt.Key_F6: option, QtCore.Qt.Key_F7: ordinary,
|
|
QtCore.Qt.Key_F8: contextOption}
|
|
if event.key() in targets:
|
|
target = targets[event.key()]
|
|
popup = contextMenu if target is contextOption else menu
|
|
popup.popup(self.mapToGlobal(QtCore.QPoint(20, 50)))
|
|
popup.setActiveAction(target)
|
|
elif event.key() == QtCore.Qt.Key_F9:
|
|
checkbox.setFocus()
|
|
elif event.key() == QtCore.Qt.Key_F10:
|
|
toggle.setFocus()
|
|
elif event.key() == QtCore.Qt.Key_F11:
|
|
menu.removeAction(option)
|
|
option.deleteLater()
|
|
option = menu.addAction('Optional feature')
|
|
elif event.key() == QtCore.Qt.Key_F12:
|
|
option.setCheckable(False)
|
|
else:
|
|
super().keyPressEvent(event)
|
|
|
|
application = QtWidgets.QApplication([])
|
|
window = ControlWindow()
|
|
window.setWindowTitle('Qt control states probe')
|
|
content = QtWidgets.QWidget()
|
|
layout = QtWidgets.QVBoxLayout(content)
|
|
checkbox = QtWidgets.QCheckBox('Updates')
|
|
layout.addWidget(checkbox)
|
|
toggle = QtWidgets.QPushButton('Power')
|
|
toggle.setCheckable(True)
|
|
layout.addWidget(toggle)
|
|
window.setCentralWidget(content)
|
|
menu = window.menuBar().addMenu('&Actions')
|
|
option = menu.addAction('Optional feature')
|
|
option.setCheckable(True)
|
|
ordinary = menu.addAction('Ordinary action')
|
|
nestedMenu = menu.addMenu('Nested actions')
|
|
nestedMenu.addAction('Nested command')
|
|
contextMenu = QtWidgets.QMenu(window)
|
|
contextOption = contextMenu.addAction('Context option')
|
|
contextOption.setCheckable(True)
|
|
# Shared actions can retain their menu-bar accessibility parent even
|
|
# while the context menu emits their focus events.
|
|
contextMenu.addAction(ordinary)
|
|
contextMenu.addAction('Context command')
|
|
contextMenu.addAction(option)
|
|
window.resize(400, 250)
|
|
window.show()
|
|
checkbox.setFocus()
|
|
application.exec()
|
|
|
|
|
|
def run_inside():
|
|
root = Path(__file__).resolve().parents[1]
|
|
for version in ('5', '6'):
|
|
process = subprocess.Popen([sys.executable, __file__, '--fixture', version])
|
|
try:
|
|
def find_window():
|
|
result = subprocess.run(['xdotool', 'search', '--onlyvisible', '--name',
|
|
'^Qt control states probe$'], capture_output=True, text=True)
|
|
return result.stdout.strip().splitlines()[0] if result.stdout.strip() else None
|
|
|
|
windowId = wait_for(find_window, 'Qt fixture window missing')
|
|
subprocess.run(['xdotool', 'windowfocus', windowId], check=True)
|
|
from native_x11 import set_active_window
|
|
set_active_window(int(windowId))
|
|
subprocess.run(['go', 'test', './internal/app', '-run', '^TestNativeQtControlStates$',
|
|
'-count=1', '-v', '-timeout=45s'], cwd=root, check=True, timeout=60,
|
|
env=dict(os.environ, CTHULHU_NATIVE_QT_CONTROLS=version,
|
|
GOCACHE='/tmp/cthulhu-cache'))
|
|
finally:
|
|
process.terminate()
|
|
process.wait(timeout=5)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
if '--fixture' in sys.argv:
|
|
fixture(sys.argv[-1])
|
|
elif '--inside' in sys.argv:
|
|
run_inside()
|
|
else:
|
|
with tempfile.TemporaryDirectory(prefix='cthulhu-qt-controls-') as temporary:
|
|
environment = dict(os.environ, HOME=temporary, XDG_CONFIG_HOME=temporary,
|
|
XDG_CACHE_HOME=temporary, XDG_DATA_HOME=temporary,
|
|
GOMODCACHE=subprocess.check_output(['go', 'env', 'GOMODCACHE'], text=True).strip(),
|
|
QT_QPA_PLATFORM='xcb', QT_LINUX_ACCESSIBILITY_ALWAYS_ON='1',
|
|
NO_AT_BRIDGE='0')
|
|
environment.pop('AT_SPI_BUS_ADDRESS', None)
|
|
run_private_session(['xvfb-run', '-a', 'dbus-run-session', '--', sys.executable,
|
|
__file__, '--inside'], env=environment, check=True)
|