Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions configs/qemu.ini
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ size=32
[keyboard]
variant=qemu

# ---------------------------------------------------------------------------
# Mouse settings
# ---------------------------------------------------------------------------
[mouse]
variant=qemu

# ---------------------------------------------------------------------------
# USB devices
# ---------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions mtda/console/qemu.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ def open(self):
result = self.opened
if self.opened is False:
try:
self.tx = open("/tmp/qemu-serial.in", mode="wb", buffering=0)
self.rx = open("/tmp/qemu-serial.out", mode="rb", buffering=0)
self.tx = open(self.qemu.serial_in, mode="wb", buffering=0)
self.rx = open(self.qemu.serial_out, mode="rb", buffering=0)

result = True
finally:
Expand Down
15 changes: 8 additions & 7 deletions mtda/keyboard/qemu.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,22 @@ def press(self, key, repeat=1, ctrl=False, shift=False, alt=False,
'\n': 'ret'
}

mod = ""
mods = []
if ctrl:
mod = "ctrl-"
mods.append("ctrl")
if shift:
mod = f"{mod}shift-"
mods.append("shift")
if alt:
mod = f"{mod}alt-"
mods.append("alt")
if meta:
mod = f"{mod}meta_l-"
mods.append("meta_l")

result = True
key = symbols[key] if key in symbols else key
keys = [{"type": "qcode", "data": k} for k in mods + [key]]
while repeat > 0:
repeat = repeat - 1
key = symbols[key] if key in symbols else key
self.qemu.cmd(f"sendkey {mod}{key}")
self.qemu.qmp("send-key", {"keys": keys})
time.sleep(0.1)
return result

Expand Down
98 changes: 98 additions & 0 deletions mtda/mouse/qemu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# ---------------------------------------------------------------------------
# QEMU mouse driver for MTDA
# ---------------------------------------------------------------------------
#
# This software is a part of MTDA.
# Copyright (C) 2026 Siemens AG
#
# ---------------------------------------------------------------------------
# SPDX-License-Identifier: MIT
# ---------------------------------------------------------------------------

# Local imports
from mtda.mouse.controller import MouseController
import mtda.constants as CONSTS

# bit0=left, bit1=right, bit2=middle
BUTTONS = (
(1, "left"),
(2, "right"),
(4, "middle"),
)


class QemuController(MouseController):

def __init__(self, mtda):
self.mtda = mtda
self.qemu = mtda.power
self._index = None

def configure(self, conf):
self.mtda.debug(3, "mouse.qemu.configure()")
return True

def probe(self):
self.mtda.debug(3, "mouse.qemu.probe()")

result = (self.qemu is not None and self.qemu.variant == "qemu")
if result is False:
self.mtda.debug(1, "mouse.qemu.probe(): "
"a qemu power controller is required")

self.mtda.debug(3, f"mouse.qemu.probe(): {str(result)}")
return result

def idle(self):
return True

def _tablet_state(self):
"""Return (index, active) for the "-device usb-tablet" device.
The index is cached after the first successful lookup since
the device set is fixed at boot, but "active" is read fresh
every time: it reflects which pointer QEMU currently uses.
"""
mice = self.qemu.qmp("query-mice")
for mouse in (mice if mice is not None else []):
if self._index is None:
if "tablet" in mouse.get("name", "").lower():
self._index = mouse.get("index")
else:
continue
if mouse.get("index") == self._index:
return self._index, mouse.get("current", False)

return self._index, False

def move(self, x, y, buttons=0):
self.mtda.debug(3, f"mouse.qemu.move({x}, {y}, {buttons})")

index, active = self._tablet_state()
if index is not None:
if active is False:
self.qemu.qmp(
"human-monitor-command",
{"command-line": f"mouse_set {index}"})
else:
self.mtda.debug(1, "mouse.qemu.move(): "
"usb-tablet not found in 'query-mice'")

ax = int(x * CONSTS.MOUSE.MAX_X)
ay = int(y * CONSTS.MOUSE.MAX_Y)
self.mtda.debug(3, "mouse.qemu.move(): "
f"ax={ax} ay={ay} buttons={buttons}")
events = [
{"type": "abs", "data": {"axis": "x", "value": ax}},
{"type": "abs", "data": {"axis": "y", "value": ay}},
]
for bit, button in BUTTONS:
data = {"down": bool(buttons & bit), "button": button}
events.append({"type": "btn", "data": data})
result = self.qemu.qmp("input-send-event", {"events": events})

self.mtda.debug(3, f"mouse.qemu.move(): {result}")
return result


def instantiate(mtda):
return QemuController(mtda)
Loading
Loading