From 14caf91be23764b2b973982ebeb95cc96f9fd96a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 11 May 2026 15:50:19 +1000 Subject: [PATCH] pyxtest: fix the vidmode SwitchToModeRequest test This test was missing SetClientVersion(2) so the reply was a the old 0.x protocol (and the 36 byte GetModeLine reply). Update so it runs for both versions now. Fixes: acbc46e708c5 ("pyxtest: add tests for the byteswapping patches") Part-of: --- test/pyxtest/proto/vidmode.py | 73 ++++++++++++++++++++ test/pyxtest/test_vidmode.py | 126 ++++++++++++++++++++++++---------- 2 files changed, 161 insertions(+), 38 deletions(-) diff --git a/test/pyxtest/proto/vidmode.py b/test/pyxtest/proto/vidmode.py index 6a9eebb95..db02fdb90 100644 --- a/test/pyxtest/proto/vidmode.py +++ b/test/pyxtest/proto/vidmode.py @@ -19,6 +19,7 @@ VidModeValidateModeLine = 9 VidModeSwitchToMode = 10 VidModeGetViewPort = 11 VidModeSetViewPort = 12 +VidModeSetClientVersion = 14 @dataclass @@ -36,6 +37,25 @@ class QueryVersionRequest: ) +@dataclass +class SetClientVersionRequest: + """VidModeSetClientVersion request (8 bytes).""" + + opcode: int + major: int = 2 + minor: int = 0 + + def to_bytes(self, byte_order: str = "<") -> bytes: + return struct.pack( + f"{byte_order}BBH HH", + self.opcode, + VidModeSetClientVersion, + 2, # 8 bytes = 2 words + self.major, + self.minor, + ) + + @dataclass class GetModeLineRequest: """VidModeGetModeLine request.""" @@ -134,6 +154,59 @@ class SwitchToModeRequest: ) +@dataclass +class OldSwitchToModeRequest: + """VidModeSwitchToMode request (v0 format, 36 bytes). + + xXF86OldVidModeSwitchToModeReq: + reqType(1) + xf86vidmodeReqType(1) + length(2) + + screen(4) + dotclock(4) + + hdisplay(2) + hsyncstart(2) + hsyncend(2) + htotal(2) + + vdisplay(2) + vsyncstart(2) + vsyncend(2) + vtotal(2) + + flags(4) + privsize(4) + """ + + opcode: int + screen: int = 0 + dotclock: int = 0 + hdisplay: int = 0 + hsyncstart: int = 0 + hsyncend: int = 0 + htotal: int = 0 + vdisplay: int = 0 + vsyncstart: int = 0 + vsyncend: int = 0 + vtotal: int = 0 + flags: int = 0 + privsize: int = 0 + + def to_bytes(self, byte_order: str = "<") -> bytes: + return struct.pack( + f"{byte_order}BBH" # header (4) + f"I" # screen (4) + f"I" # dotclock (4) + f"HHHH" # hdisplay, hsyncstart, hsyncend, htotal (8) + f"HHHH" # vdisplay, vsyncstart, vsyncend, vtotal (8) + f"I" # flags (4) + f"I", # privsize (4) + self.opcode, + VidModeSwitchToMode, + 9, # 36 bytes = 9 words + self.screen, + self.dotclock, + self.hdisplay, + self.hsyncstart, + self.hsyncend, + self.htotal, + self.vdisplay, + self.vsyncstart, + self.vsyncend, + self.vtotal, + self.flags, + self.privsize, + ) + + @dataclass class GetAllModeLinesRequest: """VidModeGetAllModeLines request.""" diff --git a/test/pyxtest/test_vidmode.py b/test/pyxtest/test_vidmode.py index fa47163fb..f8babd37f 100644 --- a/test/pyxtest/test_vidmode.py +++ b/test/pyxtest/test_vidmode.py @@ -13,7 +13,10 @@ from xclient import Extension, X11Error, X11Reply class TestVidModeSwitchToMode: @pytest.mark.swapped_client @pytest.mark.xorg_only - def test_switch_to_mode_fields_swapped(self, xserver, xclient_swapped): + @pytest.mark.parametrize("vidmode_version", [0, 2]) + def test_switch_to_mode_fields_swapped( + self, xserver, xclient_swapped, vidmode_version + ): """ SProcVidModeSwitchToMode previously only swapped stuff->screen. All mode-line fields (dotclock, hdisplay, …) were left in @@ -40,16 +43,14 @@ class TestVidModeSwitchToMode: if not isinstance(resp, X11Reply): pytest.skip("VidMode QueryVersion failed") + if vidmode_version >= 2: + req = vidmode.SetClientVersionRequest( + opcode=ext.opcode, + major=vidmode_version, + ) + conn.send_request(req.to_bytes(">")) + # Get the current mode line so we can echo it back. - # xXF86VidModeGetModeLineReply (v2, 52 bytes): - # [8] dotclock(4) - # [12] hdisplay(2) hsyncstart(2) - # [16] hsyncend(2) htotal(2) - # [20] hskew(4) - # [24] vdisplay(2) vsyncstart(2) - # [28] vsyncend(2) vtotal(2) - # [32] flags(4) - # [36] privsize(4) req = vidmode.GetModeLineRequest( opcode=ext.opcode, screen=0, @@ -60,37 +61,86 @@ class TestVidModeSwitchToMode: if isinstance(resp, X11Error): pytest.skip("GetModeLine not supported (VidMode not initialised?)") assert isinstance(resp, X11Reply), f"Expected reply, got {resp}" - assert len(resp.data) >= 40, f"Reply too short: {len(resp.data)}" - dotclock = struct.unpack_from(">I", resp.data, 8)[0] - hdisplay = struct.unpack_from(">H", resp.data, 12)[0] - hsyncstart = struct.unpack_from(">H", resp.data, 14)[0] - hsyncend = struct.unpack_from(">H", resp.data, 16)[0] - htotal = struct.unpack_from(">H", resp.data, 18)[0] - hskew = struct.unpack_from(">I", resp.data, 20)[0] - vdisplay = struct.unpack_from(">H", resp.data, 24)[0] - vsyncstart = struct.unpack_from(">H", resp.data, 26)[0] - vsyncend = struct.unpack_from(">H", resp.data, 28)[0] - vtotal = struct.unpack_from(">H", resp.data, 30)[0] - flags = struct.unpack_from(">I", resp.data, 32)[0] + if vidmode_version >= 2: + # v2 xXF86VidModeGetModeLineReply (52 bytes): + # [8] dotclock(4) + # [12] hdisplay(2) hsyncstart(2) + # [16] hsyncend(2) htotal(2) + # [20] hskew(2) vdisplay(2) + # [24] vsyncstart(2) vsyncend(2) + # [28] vtotal(2) pad2(2) + # [32] flags(4) + # [36..48] reserved(12) + # [48] privsize(4) + assert len(resp.data) >= 52, f"Reply too short: {len(resp.data)}" + dotclock = struct.unpack_from(">I", resp.data, 8)[0] + hdisplay = struct.unpack_from(">H", resp.data, 12)[0] + hsyncstart = struct.unpack_from(">H", resp.data, 14)[0] + hsyncend = struct.unpack_from(">H", resp.data, 16)[0] + htotal = struct.unpack_from(">H", resp.data, 18)[0] + hskew = struct.unpack_from(">H", resp.data, 20)[0] + vdisplay = struct.unpack_from(">H", resp.data, 22)[0] + vsyncstart = struct.unpack_from(">H", resp.data, 24)[0] + vsyncend = struct.unpack_from(">H", resp.data, 26)[0] + vtotal = struct.unpack_from(">H", resp.data, 28)[0] + flags = struct.unpack_from(">I", resp.data, 32)[0] + else: + # v0 xXF86OldVidModeGetModeLineReply (36 bytes, no hskew): + # [8] dotclock(4) + # [12] hdisplay(2) hsyncstart(2) + # [16] hsyncend(2) htotal(2) + # [20] vdisplay(2) vsyncstart(2) + # [24] vsyncend(2) vtotal(2) + # [28] flags(4) + # [32] privsize(4) + assert len(resp.data) >= 36, f"Reply too short: {len(resp.data)}" + dotclock = struct.unpack_from(">I", resp.data, 8)[0] + hdisplay = struct.unpack_from(">H", resp.data, 12)[0] + hsyncstart = struct.unpack_from(">H", resp.data, 14)[0] + hsyncend = struct.unpack_from(">H", resp.data, 16)[0] + htotal = struct.unpack_from(">H", resp.data, 18)[0] + vdisplay = struct.unpack_from(">H", resp.data, 20)[0] + vsyncstart = struct.unpack_from(">H", resp.data, 22)[0] + vsyncend = struct.unpack_from(">H", resp.data, 24)[0] + vtotal = struct.unpack_from(">H", resp.data, 26)[0] + flags = struct.unpack_from(">I", resp.data, 28)[0] # Switch to the same mode — should succeed. - req = vidmode.SwitchToModeRequest( - opcode=ext.opcode, - screen=0, - dotclock=dotclock, - hdisplay=hdisplay, - hsyncstart=hsyncstart, - hsyncend=hsyncend, - htotal=htotal, - hskew=hskew, - vdisplay=vdisplay, - vsyncstart=vsyncstart, - vsyncend=vsyncend, - vtotal=vtotal, - flags=flags, - privsize=0, - ) + # Use the matching request format for the protocol version. + if vidmode_version >= 2: + req = vidmode.SwitchToModeRequest( + opcode=ext.opcode, + screen=0, + dotclock=dotclock, + hdisplay=hdisplay, + hsyncstart=hsyncstart, + hsyncend=hsyncend, + htotal=htotal, + hskew=hskew, + vdisplay=vdisplay, + vsyncstart=vsyncstart, + vsyncend=vsyncend, + vtotal=vtotal, + flags=flags, + privsize=0, + ) + else: + req = vidmode.OldSwitchToModeRequest( + opcode=ext.opcode, + screen=0, + dotclock=dotclock, + hdisplay=hdisplay, + hsyncstart=hsyncstart, + hsyncend=hsyncend, + htotal=htotal, + vdisplay=vdisplay, + vsyncstart=vsyncstart, + vsyncend=vsyncend, + vtotal=vtotal, + flags=flags, + privsize=0, + ) conn.send_request(req.to_bytes(">")) resp = conn.recv_response(timeout=2.0)