a73x

test/native_scale.py

Ref:   Size: 3.8 KiB   History

#!/usr/bin/env python3
"""Scale transitions on an explicitly selected, isolated Sway output.

Use the compositor's XDG_RUNTIME_DIR, WAYLAND_DISPLAY and SWAYSOCK. This changes
the named output's scale and restores it on exit; never point it at a desktop
output. Both binaries must come from the same release build.
"""
import argparse
import json
import os
import subprocess
import sys

sys.dont_write_bytecode = True
from native_resize import (clamps, drag_and_keys, modal_leakage, pty_sizes,
                           pixels_and_batch, edge, one_divider)
from native_tiling import require


def sway(*args):
    result = subprocess.run(['swaymsg', '-r', *args], check=True,
                            capture_output=True, text=True, timeout=5)
    return json.loads(result.stdout)


def main():
    from native_lifecycle import LifecycleRig, start_workspace
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('mux')
    parser.add_argument('muxg')
    parser.add_argument('--output', required=True, help='owned headless Sway output')
    args = parser.parse_args()
    require(args.output.startswith('HEADLESS-'), 'use an isolated headless output')
    for name in ('SWAYSOCK', 'XDG_RUNTIME_DIR', 'WAYLAND_DISPLAY'):
        require(os.environ.get(name), 'set the isolated compositor ' + name)
    outputs = sway('-t', 'get_outputs')
    output = next((o for o in outputs if o['name'] == args.output and o['active']), None)
    require(output is not None, 'selected output is not active')
    original_scale = output['scale']

    def scale(value):
        require(all(r['success'] for r in sway('output', args.output, 'scale', str(value))),
                'compositor refused scale change')

    os.environ['MUXG_VIDEODRIVER'] = 'wayland'
    rig = LifecycleRig(args.mux, args.muxg)
    rig.float_size, rig.float_scale = (1000, 650), 2
    try:
        scale(2)
        sessions = start_workspace(rig)
        identities = [(p['id'], p['generation']) for p in rig.state()['panes']]
        command = (f'[pid={rig.gui.pid}] move container to output {args.output}, '
                   'floating enable, border none, resize set width 1000 px height 650 px')
        require(all(r['success'] for r in sway(command)), 'could not place owned window')
        rig.wait_state(lambda s: s['logical_width'] == 1000 and s['logical_height'] == 650
                       and s['width'] == 2 * s['logical_width'])
        drag_and_keys(rig, identities, sessions)
        modal_leakage(rig, identities)
        clamps(rig, identities, sessions)
        pixels_and_batch(rig, identities, sessions)
        for value in (1, 1.5, 2):
            state = rig.state()
            rail = one_divider(state, 'beside')['rect']
            x, y = rail['x'] + rail['w'] / 2, rail['y'] + rail['h'] / 4
            rig.send('mousedown:' + rig.point(state, x, y))
            rig.wait_state(lambda s: s['drag'] is not None)
            scale(value)
            state = rig.wait_state(lambda s: s['width'] == value * s['logical_width'])
            require(state['drag'] is None, 'scale change retained stale drag capture')
            before = edge(state, 'beside')
            rig.send('mousemove:20,20', 'mouseup:20,20')
            require(edge(rig.state(), 'beside') == before,
                    'stale motion changed divider after scale transition')
            pty_sizes(rig, sessions, identities)
            drag_and_keys(rig, identities, sessions)
            pixels_and_batch(rig, identities, sessions)
        (rig.root / 'scale-final.json').write_text(json.dumps(rig.state(), indent=2))
        rig.quit()
        print('PASS: Wayland 200% -> 100% -> 150% -> 200%; drag cancellation, '
              'nested resize, PTYs and pixels', flush=True)
    finally:
        try:
            scale(original_scale)
        finally:
            rig.close()


if __name__ == '__main__':
    main()