#!/usr/bin/env python3 """Reproduce output-removal crashes only in a private headless compositor.""" import argparse import json import os from pathlib import Path import resource import socket import struct import subprocess import tempfile import time parser = argparse.ArgumentParser() parser.add_argument('--mode', choices=['window', 'native', 'forced'], required=True) parser.add_argument('--plugins', type=Path, required=True) parser.add_argument('--gdb', action='store_true') parser.add_argument('--cycle', action='store_true') parser.add_argument('--order', default='213') parser.add_argument('--return-order', default='123') parser.add_argument('--clients', type=int, default=1) parser.add_argument('--cycles', type=int, default=1) parser.add_argument('--pause', type=float, default=.1) parser.add_argument('--inhibit', action='store_true') parser.add_argument('--unload', action='store_true') args = parser.parse_args() assert sorted(args.order) == sorted(args.return_order) == list('123') assert os.geteuid() != 0, 'Run as an ordinary user' resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) runtime = Path(tempfile.mkdtemp(prefix='wayfire-crash-repro-')) env = os.environ.copy() for key in ['WAYLAND_DISPLAY', 'DISPLAY', 'WAYFIRE_SOCKET', '_WAYFIRE_SOCKET', 'WAYFIRE_CONFIG_FILE', 'WAYFIRE_DEFAULT_CONFIG_BACKEND', 'LD_LIBRARY_PATH']: env.pop(key, None) assert env.get('DBUS_SESSION_BUS_ADDRESS'), 'Use dbus-run-session' env.update(XDG_RUNTIME_DIR=str(runtime), XDG_CONFIG_HOME=str(runtime / 'config'), DBUS_SYSTEM_BUS_ADDRESS=env['DBUS_SESSION_BUS_ADDRESS'], WLR_BACKENDS='headless', WLR_HEADLESS_OUTPUTS='3', WLR_RENDERER='gles2', WLR_RENDER_DRM_DEVICE='/dev/dri/renderD128', WAYFIRE_PLUGIN_PATH=str(args.plugins), GDK_BACKEND='wayland') config = runtime / 'wayfire.ini' config.write_text('''[core] plugins = ipc ipc-rules window-rules stipc wm-actions force-fullscreen hotplug-test xwayland = false vwidth = 1 vheight = 1 stitch_enabled = true stitch_outputs = HEADLESS-1 HEADLESS-2 HEADLESS-3 default_output = VIRTUAL-1 [force-fullscreen] key_toggle_fullscreen = KEY_F constrain_pointer = false preserve_aspect = true transparent_behind_views = true [output:HEADLESS-1] mode = 640x480@60 position = 0,0 [output:HEADLESS-2] mode = 640x480@60 position = 640,0 [output:HEADLESS-3] mode = 640x480@60 position = 1280,0 ''') def ipc(method, data=None): path = env['WAYFIRE_SOCKET'] assert Path(path).parent == runtime with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s: s.settimeout(3) s.connect(path) payload = json.dumps({'method': method, 'data': data or {}}).encode() s.sendall(struct.pack('