From 7d7edb1c03647965fa04fabe1e141f1c2230569d Mon Sep 17 00:00:00 2001 From: Maxim Devaev Date: Sat, 3 May 2025 03:54:05 +0300 Subject: [PATCH] pikvm/pikvm#1501: Switch: Option to disable HDMI dummy plug --- kvmd/apps/kvmd/api/switch.py | 1 + kvmd/apps/kvmd/switch/__init__.py | 26 ++++++++++++++++-------- kvmd/apps/kvmd/switch/chain.py | 30 ++++++++++++++++++++++++++-- kvmd/apps/kvmd/switch/device.py | 4 ++++ kvmd/apps/kvmd/switch/proto.py | 22 ++++++++++++++++++-- kvmd/apps/kvmd/switch/state.py | 18 +++++++++++++++-- kvmd/apps/kvmd/switch/storage.py | 10 ++++++++++ kvmd/apps/kvmd/switch/types.py | 13 ++++++++++++ switch/switch.uf2 | Bin 195584 -> 200192 bytes web/share/js/kvm/switch.js | 32 +++++++++++++++++++++++++++--- 10 files changed, 139 insertions(+), 17 deletions(-) diff --git a/kvmd/apps/kvmd/api/switch.py b/kvmd/apps/kvmd/api/switch.py index bf91b83e..d89f1b84 100644 --- a/kvmd/apps/kvmd/api/switch.py +++ b/kvmd/apps/kvmd/api/switch.py @@ -79,6 +79,7 @@ class SwitchApi: param: validator(req.query.get(param)) for (param, validator) in [ ("edid_id", (lambda arg: valid_switch_edid_id(arg, allow_default=True))), + ("dummy", valid_bool), ("name", valid_switch_port_name), ("atx_click_power_delay", valid_switch_atx_click_delay), ("atx_click_power_long_delay", valid_switch_atx_click_delay), diff --git a/kvmd/apps/kvmd/switch/__init__.py b/kvmd/apps/kvmd/switch/__init__.py index f0cc98f2..6f2337a4 100644 --- a/kvmd/apps/kvmd/switch/__init__.py +++ b/kvmd/apps/kvmd/switch/__init__.py @@ -32,6 +32,7 @@ from .lib import Inotify from .types import Edid from .types import Edids +from .types import Dummies from .types import Color from .types import Colors from .types import PortNames @@ -68,6 +69,7 @@ class SwitchUnknownEdidError(SwitchOperationError): # ===== class Switch: # pylint: disable=too-many-public-methods __X_EDIDS = "edids" + __X_DUMMIES = "dummies" __X_COLORS = "colors" __X_PORT_NAMES = "port_names" __X_ATX_CP_DELAYS = "atx_cp_delays" @@ -75,7 +77,7 @@ class Switch: # pylint: disable=too-many-public-methods __X_ATX_CR_DELAYS = "atx_cr_delays" __X_ALL = frozenset([ - __X_EDIDS, __X_COLORS, __X_PORT_NAMES, + __X_EDIDS, __X_DUMMIES, __X_COLORS, __X_PORT_NAMES, __X_ATX_CP_DELAYS, __X_ATX_CPL_DELAYS, __X_ATX_CR_DELAYS, ]) @@ -105,6 +107,12 @@ class Switch: # pylint: disable=too-many-public-methods if save: self.__save_notifier.notify() + def __x_set_dummies(self, dummies: Dummies, save: bool=True) -> None: + self.__chain.set_dummies(dummies) + self.__cache.set_dummies(dummies) + if save: + self.__save_notifier.notify() + def __x_set_colors(self, colors: Colors, save: bool=True) -> None: self.__chain.set_colors(colors) self.__cache.set_colors(colors) @@ -236,6 +244,7 @@ class Switch: # pylint: disable=too-many-public-methods self, port: int, edid_id: (str | None)=None, + dummy: (bool | None)=None, name: (str | None)=None, atx_click_power_delay: (float | None)=None, atx_click_power_long_delay: (float | None)=None, @@ -250,15 +259,16 @@ class Switch: # pylint: disable=too-many-public-methods edids.assign(port, edid_id) self.__x_set_edids(edids) - for (key, value) in [ - (self.__X_PORT_NAMES, name), - (self.__X_ATX_CP_DELAYS, atx_click_power_delay), - (self.__X_ATX_CPL_DELAYS, atx_click_power_long_delay), - (self.__X_ATX_CR_DELAYS, atx_click_reset_delay), + for (reset, key, value) in [ + (None, self.__X_DUMMIES, dummy), # None can't be used now + ("", self.__X_PORT_NAMES, name), + (0, self.__X_ATX_CP_DELAYS, atx_click_power_delay), + (0, self.__X_ATX_CPL_DELAYS, atx_click_power_long_delay), + (0, self.__X_ATX_CR_DELAYS, atx_click_reset_delay), ]: if value is not None: new = getattr(self.__cache, f"get_{key}")() - new[port] = (value or None) # None == reset to default + new[port] = (None if value == reset else value) # Value or reset default getattr(self, f"_Switch__x_set_{key}")(new) # ===== @@ -375,7 +385,7 @@ class Switch: # pylint: disable=too-many-public-methods prevs = dict.fromkeys(self.__X_ALL) while True: await self.__save_notifier.wait() - while (await self.__save_notifier.wait(5)): + while not (await self.__save_notifier.wait(5)): pass while True: try: diff --git a/kvmd/apps/kvmd/switch/chain.py b/kvmd/apps/kvmd/switch/chain.py index 7137f5b9..4bd9b4eb 100644 --- a/kvmd/apps/kvmd/switch/chain.py +++ b/kvmd/apps/kvmd/switch/chain.py @@ -34,6 +34,7 @@ from .lib import aiotools from .lib import aioproc from .types import Edids +from .types import Dummies from .types import Colors from .proto import Response @@ -80,6 +81,11 @@ class _CmdSetEdids(_BaseCmd): edids: Edids +@dataclasses.dataclass(frozen=True) +class _CmdSetDummies(_BaseCmd): + dummies: Dummies + + @dataclasses.dataclass(frozen=True) class _CmdSetColors(_BaseCmd): colors: Colors @@ -189,7 +195,7 @@ class Chain: # pylint: disable=too-many-instance-attributes self.__actual = False self.__edids = Edids() - + self.__dummies = Dummies({}) self.__colors = Colors() self.__units: list[_UnitContext] = [] @@ -225,6 +231,9 @@ class Chain: # pylint: disable=too-many-instance-attributes def set_edids(self, edids: Edids) -> None: self.__queue_cmd(_CmdSetEdids(edids)) # Will be copied because of multiprocessing.Queue() + def set_dummies(self, dummies: Dummies) -> None: + self.__queue_cmd(_CmdSetDummies(dummies)) + def set_colors(self, colors: Colors) -> None: self.__queue_cmd(_CmdSetColors(colors)) @@ -348,6 +357,9 @@ class Chain: # pylint: disable=too-many-instance-attributes case _CmdSetEdids(): self.__edids = cmd.edids + case _CmdSetDummies(): + self.__dummies = cmd.dummies + case _CmdSetColors(): self.__colors = cmd.colors @@ -373,7 +385,7 @@ class Chain: # pylint: disable=too-many-instance-attributes def __adjust_quirks(self) -> None: for (unit, ctx) in enumerate(self.__units): - if ctx.state is not None and (ctx.state.version.sw_dev or ctx.state.version.sw >= 7): + if ctx.state is not None and ctx.state.version.is_fresh(7): ignore_hpd = (unit == 0 and self.__ignore_hpd_on_top) if ctx.state.quirks.ignore_hpd != ignore_hpd: get_logger().info("Applying quirk ignore_hpd=%s to [%d] ...", @@ -403,6 +415,7 @@ class Chain: # pylint: disable=too-many-instance-attributes self.__ensure_config_port(unit, ctx) if self.__actual: self.__ensure_config_edids(unit, ctx) + self.__ensure_config_dummies(unit, ctx) self.__ensure_config_colors(unit, ctx) def __ensure_config_port(self, unit: int, ctx: _UnitContext) -> None: @@ -429,6 +442,19 @@ class Chain: # pylint: disable=too-many-instance-attributes ctx.changing_rid = self.__device.request_set_edid(unit, ch, edid) break # Busy globally + def __ensure_config_dummies(self, unit: int, ctx: _UnitContext) -> None: + assert ctx.state is not None + if ctx.state.version.is_fresh(8) and ctx.can_be_changed(): + for ch in range(4): + port = self.get_virtual_port(unit, ch) + dummy = self.__dummies[port] + if ctx.state.video_dummies[ch] != dummy: + get_logger().info("Changing dummy flag on port %d on [%d:%d]: %d -> %d ...", + port, unit, ch, + ctx.state.video_dummies[ch], dummy) + ctx.changing_rid = self.__device.request_set_dummy(unit, ch, dummy) + break # Busy globally (actually not but it can be changed in the firmware) + def __ensure_config_colors(self, unit: int, ctx: _UnitContext) -> None: assert self.__actual assert ctx.state is not None diff --git a/kvmd/apps/kvmd/switch/device.py b/kvmd/apps/kvmd/switch/device.py index a057a50d..df532804 100644 --- a/kvmd/apps/kvmd/switch/device.py +++ b/kvmd/apps/kvmd/switch/device.py @@ -41,6 +41,7 @@ from .proto import BodySetBeacon from .proto import BodyAtxClick from .proto import BodySetEdid from .proto import BodyClearEdid +from .proto import BodySetDummy from .proto import BodySetColors from .proto import BodySetQuirks @@ -164,6 +165,9 @@ class Device: return self.__send_request(Header.SET_EDID, unit, BodySetEdid(ch, edid)) return self.__send_request(Header.CLEAR_EDID, unit, BodyClearEdid(ch)) + def request_set_dummy(self, unit: int, ch: int, on: bool) -> int: + return self.__send_request(Header.SET_DUMMY, unit, BodySetDummy(ch, on)) + def request_set_colors(self, unit: int, ch: int, colors: Colors) -> int: return self.__send_request(Header.SET_COLORS, unit, BodySetColors(ch, colors)) diff --git a/kvmd/apps/kvmd/switch/proto.py b/kvmd/apps/kvmd/switch/proto.py index 2081a4b6..3c39d238 100644 --- a/kvmd/apps/kvmd/switch/proto.py +++ b/kvmd/apps/kvmd/switch/proto.py @@ -61,6 +61,7 @@ class Header(Packable, Unpackable): CLEAR_EDID = 10 SET_COLORS = 12 SET_QUIRKS = 13 + SET_DUMMY = 14 __struct = struct.Struct(" bool: + return (self.sw_dev or (self.sw >= version)) + @dataclasses.dataclass(frozen=True) class UnitFlags: @@ -121,11 +125,12 @@ class UnitState(Unpackable): # pylint: disable=too-many-instance-attributes video_hpd: tuple[bool, bool, bool, bool, bool] video_edid: tuple[bool, bool, bool, bool] video_crc: tuple[int, int, int, int] + video_dummies: tuple[bool, bool, bool, bool] usb_5v_sens: tuple[bool, bool, bool, bool] atx_busy: tuple[bool, bool, bool, bool] quirks: UnitQuirks - __struct = struct.Struct(" bool: if edid is None: @@ -142,7 +147,7 @@ class UnitState(Unpackable): # pylint: disable=too-many-instance-attributes sw_version, hw_version, flags, ch, beacons, nc0, nc1, nc2, nc3, nc4, nc5, video_5v_sens, video_hpd, video_edid, vc0, vc1, vc2, vc3, - usb_5v_sens, atx_busy, quirks, + usb_5v_sens, atx_busy, quirks, video_dummies, ) = cls.__struct.unpack_from(data, offset=offset) return UnitState( version=UnitVersion( @@ -163,6 +168,7 @@ class UnitState(Unpackable): # pylint: disable=too-many-instance-attributes video_hpd=cls.__make_flags5(video_hpd), video_edid=cls.__make_flags4(video_edid), video_crc=(vc0, vc1, vc2, vc3), + video_dummies=cls.__make_flags4(video_dummies), usb_5v_sens=cls.__make_flags4(usb_5v_sens), atx_busy=cls.__make_flags4(atx_busy), quirks=UnitQuirks(ignore_hpd=bool(quirks & 0x01)), @@ -270,6 +276,18 @@ class BodyClearEdid(Packable): return self.ch.to_bytes() +@dataclasses.dataclass(frozen=True) +class BodySetDummy(Packable): + ch: int + on: bool + + def __post_init__(self) -> None: + assert 0 <= self.ch <= 3 + + def pack(self) -> bytes: + return self.ch.to_bytes() + self.on.to_bytes() + + @dataclasses.dataclass(frozen=True) class BodySetColors(Packable): ch: int diff --git a/kvmd/apps/kvmd/switch/state.py b/kvmd/apps/kvmd/switch/state.py index 84f01cfd..92231791 100644 --- a/kvmd/apps/kvmd/switch/state.py +++ b/kvmd/apps/kvmd/switch/state.py @@ -27,6 +27,7 @@ import time from typing import AsyncGenerator from .types import Edids +from .types import Dummies from .types import Color from .types import Colors from .types import PortNames @@ -48,8 +49,8 @@ class _UnitInfo: # ===== -class StateCache: # pylint: disable=too-many-instance-attributes - __FW_VERSION = 7 +class StateCache: # pylint: disable=too-many-instance-attributes,too-many-public-methods + __FW_VERSION = 8 __FULL = 0xFFFF __SUMMARY = 0x01 @@ -62,6 +63,7 @@ class StateCache: # pylint: disable=too-many-instance-attributes def __init__(self) -> None: self.__edids = Edids() + self.__dummies = Dummies({}) self.__colors = Colors() self.__port_names = PortNames({}) self.__atx_cp_delays = AtxClickPowerDelays({}) @@ -77,6 +79,9 @@ class StateCache: # pylint: disable=too-many-instance-attributes def get_edids(self) -> Edids: return self.__edids.copy() + def get_dummies(self) -> Dummies: + return self.__dummies.copy() + def get_colors(self) -> Colors: return self.__colors @@ -226,6 +231,9 @@ class StateCache: # pylint: disable=too-many-instance-attributes "reset": self.__atx_cr_delays[port], }, }, + "video": { + "dummy": self.__dummies[port], + }, }) if x_edids: state["edids"]["used"].append(self.__edids.get_id_for_port(port)) @@ -327,6 +335,12 @@ class StateCache: # pylint: disable=too-many-instance-attributes if changed: self.__bump_state(self.__EDIDS) + def set_dummies(self, dummies: Dummies) -> None: + changed = (not self.__dummies.compare_on_ports(dummies, self.__get_ports())) + self.__dummies = dummies.copy() + if changed: + self.__bump_state(self.__FULL) + def set_colors(self, colors: Colors) -> None: changed = (self.__colors != colors) self.__colors = colors diff --git a/kvmd/apps/kvmd/switch/storage.py b/kvmd/apps/kvmd/switch/storage.py index 6e3a0a76..f8348009 100644 --- a/kvmd/apps/kvmd/switch/storage.py +++ b/kvmd/apps/kvmd/switch/storage.py @@ -39,6 +39,7 @@ from .lib import get_logger from .types import Edid from .types import Edids +from .types import Dummies from .types import Color from .types import Colors from .types import PortNames @@ -52,6 +53,8 @@ class StorageContext: __F_EDIDS_ALL = "edids_all.json" __F_EDIDS_PORT = "edids_port.json" + __F_DUMMIES = "dummies.json" + __F_COLORS = "colors.json" __F_PORT_NAMES = "port_names.json" @@ -74,6 +77,9 @@ class StorageContext: }) await self.__write_json_keyvals(self.__F_EDIDS_PORT, edids.port) + async def write_dummies(self, dummies: Dummies) -> None: + await self.__write_json_keyvals(self.__F_DUMMIES, dummies.kvs) + async def write_colors(self, colors: Colors) -> None: await self.__write_json_keyvals(self.__F_COLORS, { role: { @@ -116,6 +122,10 @@ class StorageContext: port_edids = await self.__read_json_keyvals_int(self.__F_EDIDS_PORT) return Edids(all_edids, port_edids) + async def read_dummies(self) -> Dummies: + kvs = await self.__read_json_keyvals_int(self.__F_DUMMIES) + return Dummies({key: bool(value) for (key, value) in kvs.items()}) + async def read_colors(self) -> Colors: raw = await self.__read_json_keyvals(self.__F_COLORS) return Colors(**{ # type: ignore diff --git a/kvmd/apps/kvmd/switch/types.py b/kvmd/apps/kvmd/switch/types.py index 33a7f3ad..475ec30f 100644 --- a/kvmd/apps/kvmd/switch/types.py +++ b/kvmd/apps/kvmd/switch/types.py @@ -281,6 +281,19 @@ class _PortsDict(Generic[_T]): else: self.kvs[port] = value + def __eq__(self, other: "_PortsDict[_T]") -> bool: + if not isinstance(other, self.__class__): + return False + return (self.kvs == other.kvs) + + +class Dummies(_PortsDict[bool]): + def __init__(self, kvs: dict[int, bool]) -> None: + super().__init__(True, kvs) + + def copy(self) -> "Dummies": + return Dummies(self.kvs) + class PortNames(_PortsDict[str]): def __init__(self, kvs: dict[int, str]) -> None: diff --git a/switch/switch.uf2 b/switch/switch.uf2 index e5a1725e97622f8bca75a37282bef511a737b468..6e1346ce94c29b8fef780e0640666884ca0bbeed 100644 GIT binary patch delta 33055 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 delta 30690 zcmcJ230M?Yw(z~x4Kz!e)<$hMt?ouea6wUnqNb&>1R50;Hz1~2G>FDv;?m#(5+`v> z0+yx#jd7cdYeIbmEi?Vfw?x#!+$N_Gd7wE36m@cNH}92l=>gQ&lGA4|oW3#lJ>Q^C$B%DCbS@T49g zx_T(0>(*fnLQ0wx=e-8*NrAtDDZYsT{FQbz1tHZ>s0ki5kTxYeqRWpW%@;8drjHTd&q4>Ul{Xo zhq50wug3_@acuqJ7W!xKyNm%1MtO{yFKA!FW_S-M?%5ty=!p8Z;m3@B?q!>awB`?cB=@u_4N38SCaw8+GX7ZY zH8k&3@hAYC@{AG<#`38Mp#%842k(TZ?!gq&bL|S@j(g7wdy{c%w3-b-ze#XxnB;YP4 zxUbas?V!VN7FD_+f(rKm#C#`2kdykA8vh;XMM2JbVk^!+HVQYg9azh%{rrnXsex-7 z@VstUG2uM~Yt7ed@+|>np|*UBdPk6Lv86|OD11J%LubpgXyAFCr6)Xp(b5Z^_cdsD zEJ{Io8X2S2yrs>@Xe`0IP}PAF)voN4?^lNS8CUu4qN;LMh4>v+_&=k)&9EzDh{NnclD?| zFngTwnW{T!O|x-~rs8f|&ng_#l-8>f$J|ZR!u_2z9Mk86yR1GStQTsVtdB-YKG(6h znm_XiQ^T-&e2cZ?Ud7%>L+s8QbRsI@FtZoTb)(H}3#UTOeU9#CEHq_6dTT-e|GmS^ zL>ynTyw4GA+`&W@B*x?p)U@cqUkKWH zCM-;}4itqOFrU!TA1a0Wi5+2VQbI^Ec|NqG54hnI4qyJXJut-|1tw7lmD5Pg8`^tW zLh>r|dhuVhV}r&rGd|d=zYU-rZ8cU=8VRVg{u9Nq1iu zgEhOK#Tq4)%pr)T2O+xBQYAV)%3Q>b2#>*W>~T$q*r}|#m;Y#ybIYogvca;R{V>{yC$L|Khp4XAEKgdV+?~Vz5^l)9P*b9>%Sl4B!9}gh zag3M)*NHjwK*xv~>06ncq#59>wlE%dC3jYhHSO6_S0y^CN~fc`T5~pAbX0z3QHEka z!7%+KhWW27|8v zk)=gTWApM$$1OcD&LuUitivSY~-a98;&53M}9`oZc?@@@G?XHR1e&1RUm{Px91 z!Bw4J{29wuy0L#+(WgOg*?@zilu|FUT+` zji-CV(8+X-hv#EVFuVeITbElq{(^S=CEA&+uM1N0w>zga*E6CM5iRLyN8+bIh*+05 z7-}9|dngg=<%LiHT>mH6;x!aC7VDdroTz?Qb0!U{&|^c7p$cT7E6Xr3I< zU}7kKahHiv@@aMxqu^WYX6EQ}3)9c`rsYl1fX&-1MGJ#rww@WUHEnUt(wpHLY~BR3 zxY9X`m*|7duflBJ6KzZmGLK!TWk1yPcIzTQ&_#em7oJJ{R*gM(#-?vJ2~&5Q)AW15 zG~UmA&N^qyio|od`hnkUS(E$fZ%*Wbg*MiV%}rkuv`(1jD`A?y;hbm<`mN0TOy_)j zxuzp0Cn-01My4h+36;PMzkxO(O(r&5T{Hd*?iv47PKiDVks;(5QNsw=kKj5tsTw|| zv%Y*7U9Kt0`Hf)|Xr}e^Gf7YYeCD`Dl2bX(i9Yy0P4u9hY?CF}5Muiz`#JD!YqBST z(kEKRW{249SraTe+XPEpRs;&M(a~$N;-GG97IE__oo#tO#zWS+rBNjjrM9J?tIYBTA9iy{O+$zEM}rs<*icqnV62HM^`;uP8$?3vh>DftH=0# zuzCSA_NDu)4wu-Lj9qen_2IOR{IU5D(>}>Bg3&b2_%N*lM$Ln?I_D@Vg3tSDW1aWp zsW5)lHxn}_vz}=7#CZ}&O3eOcUL{PPehQ~j+C%!gBcK1x5bz#~u7C1_wa_bqYEVX? z8?`^|E41{hob+W41@Y(`mN(p%U9_;o@H6LQ6IpnXOd2hT{x0edMtxcFMlf}|I~lmDx2rn_u&zRNa6H$Nwx zripcm?tXUq%%ll9#L0ufyJOPQr()87ll|WAMNtxzjpW-e_lDpPt-%PM$iMSn%vRd zuvH5f;_sKOCjP$YCBe(b5zp^*^D%yNz&H!@h_jD%I{U*`;_Tb=?^`3mnmd*pTAA;3 z^L6=a@(+QVC&tMz9gFK0KTI2&k7J4!-v^g|c=p4z;z9TRe>-5@=xBY}jtx15f1Mg^ zin48I*!Yl!uOSF|-mdD+=Q#Y)42CKnZ2M{&F8|6IjfmP|)dv5CVr)Qypg(tBaE2&7 z`TQ^lfPPl0`}0{hm%XDr;Wv~*d6Y)@)VSyxPjrLHqpVcVaMRXUam(F(Cv zV#$%EM^_aswda!n<;UKu#*u1>M+);q<6W}atsSkP9j!z=A%2Igf_a;n*!;r$`)L`> z&uJEB4)b93&)|PgE!IJ-LVWLov{;w#?Z~^Ib_(Jc64^XRn>UbnVn^#2cH>Zd-O)O{ zV_~6VC~idjsV*;gg7{~;NQh1uCX>}wh7svWJTtmojQHmna14p~cj+9hUG3gZBH^zgW6K>MX+y<8XX*w>~FqR0}Bj8oo?xOn?(cQujN^1@dg(OHLHXJD2UIXh6P-sRM zhP4O`|8`_l@GIN5TDHoD>1Nk3CBc)Qm;-s;BKAM4j`TN*!=KDTzjF=VjgrAzDh%HK zp26ELSu)f?+;lGY+Lra~=vY0DW#`6*<+kT!Te6dLlBzQ2X9`i$1_*KHXZo36NSdEX z2J!;9k{G3-ro=$QT4E;JlkPRrF|vjct{=g5PI5JT9xcjWn*n1-Ykp|k%4p4z>o1f0 zVo`+RK7tANk(h8VNLt*gS(Ec)(GgeM$U?0--`O%hQ>cy0SyB|~syzadKU_!f9w{cZ z-_|^5>4JG@qUEgfa~*t6fK>(gBqMdabEJ-U&P4smR5Z>tJpEzWR|lcceQPWUgxX5l^Gl1CjPZN8dfbx5D?eBr39-&8m_AO!^a0~5 zpNz6`&U+H`h$D{NjdOk%6)?W9pn$#-1@woYpF}hNljSzp07y9}({V_-`2+k%A! z|2qqty+stZSSJXplL%V^Q^(aU>tNAZ{F_nx{O9p#Vr!Rc#{Fj8E=(#AuH+OX7bI=T zRDsQwAj%oCt1>C`(nJ*`=_vCOxE3Q!0ix46C5D;kO{d8oWrXVyxaKAwW;R1yv`G>d z$)k3dEq*I%cL(iPYbJ<6I}DSj!=hWgAwo}aMd%g_S>LXM*j&^Os~+hI@8hzQ0U#L+ z7Q*?gB+5AB_35(}am33rpXim7lmJUKE!EioR)Pm*T;m$j}h z`0DP~+U|u##3S!3v=*YB3cjEH7t23o#?QOF?%xITH#jrTKRNCHCzt&{^4P!mp%IucDm7a|Ol5XnICGlO-JY!0jezDoM)HzTL>t~J0HzY!fd zMjT?qHNc$ovq>c7Gdukwrt3dON2mTZI?5uZ?MjNT{}0j8`(Wuh+wU#0;CX-V^t=b1 zp7)?LMmpe(k)G6aS@&D^PYtp~pi@>l8tMJ{UphrK6o(3G2$iUz=p`Q`amG<4#2LrE zM4a)_5)$7-tPg3N!o2*I@r>5*A!MMVR;^ib-zu&T>Xr!W1AG3Vl_c476b$|ptN>#3 zk2-^-Sr8)KPjj?BZ-+3avAgv^*K8Vfw{Ea^w{GteQ=9($N1e|b6wcHp71!OG>57Fs zp^!hn7a*_`qPul**8;E{6!l}{^UiBaV-`s@hF@>Yn-+~=40oych3$(V(YVN!Xxzm3 z^B22wrbgw7YGBplJdU9vPs6|Lz{aeUjCf2}GDgTj?(9GjsNvZ=1mPq=IHlbmO=8lg zk<2A|_F;DvaM zq8~pEGMWmcLKW&MgAt7sJk{CV`u9%uP)aBjV`n3oP%7NcCNiPnz3o}Wbg?gm%{l80 z>#6cP>mv{@-?tu>EA^Q7wj1=;Oi>_hKcQdyN&1!D#L&h6>M+O`b))&X8KUJ4x%c&n8j1WsPqn&o;I+U>F^$UyfObCGE#hp-O>7o z4$@=3ohtsBHqwiv54{roAzvu;VQ9An`uqfh2m!!2UrRRHror|=Q|%|!D!#BQmxhEm z|ECVb|HGl+ueM{p#X<33cHOqzc6&Jtn`<{M`hg)fgU=w6(YWr4DW3})C&rUCjAz^J zs?+&hbveH)66aURG+HhvS1`K@DVF^}V_>6Iy5h7gpCu`GT;1|%iq}r#n!BzrEgOQk z{mvk6|MMhF`>w|Oot!m>bwYT!A8PM9YwtR1Z`Js)HOU`a{xt~w&=rJ!;0i*oLJ+Fj zITO7L^SSR%$H+as;l}SB$Y+myH~sx&|D6@-2a2-Sik-u%<2{2(pncc&8ib|>fx0t< zyj~Mf_I=TQ7v~1xEnC9*jT&`%PLaV?yC1^OyUy@)zpL%rmL6qcwzn+L>!Iv!Ecv%A7b!# z--j787~y$v`K$tUrQb8V4$uB^_8exMQB+H;L9kha#AX93G?n*P_o(Pu^&ssKY|{0r zc<9`iyPp;an{*G;dc!8&1GX@2elY{vG=U9+k;z~J@AXl8qGd82!Rlb+E#mkj#$*__ zoCx>(_Q|jeR`9;L{ z?dMwlotr9l?(aeV^8XU90RKv|yo9UIw-nOcflW@n_7XFNDXHCBJuT7qsOpH%VQ>Cx zJBm;=S`w3zRQyYwzE!G9pTG)l{&hRjPf1kqOYI1_o;)?esT{~4F9;Wu6E0IuL%R=u zt;?K3A($4z_+?!JH?;rX4~e` zDZ@LBFiHNik>V2^j@F{C&K4yNTa|4}n&MA&llv5h(s*F`cP)gd6^JhC>J9UoKkWSx zX8+nDl6u81S^}$1_MauXGc)kc!LDrm6^zm#JFp3oCS*3y!M$`gAiK2};2G&&6Kvs; zOf<=>) z#0!W-z~Q#P!-iuVq93h=v;|U{?!#468<7%`M1x8r*?(pl{ob+r8A|lDCD_W+3lc;{ zY6l5cGe}}JZ0-WX@U{-k*?I_b!*=Cl`_0W$L=ts)v=bmBdMIM+a0VYZYvuF$T z4?D{*(&@^;pd1c_@Lq>wLkW70xCt_gvvi&d$J{UvOM8~)u?0(&Y(riuyMJi{OE39} zRi{gMPF+f~8}oOwru^~j-aM7N%Z3SD43lxO6gXYx;dHnFGh7C0FQVE-FLAB5*=5POiwCk3F?*J?h_gTHa@2vhsK8b z@vUv_nO8v)WIv;aF=23vGyXa;k%z-w13^J9VcuBhd{ zd@sjRXuQw@Uy9X7DEw#@IygV!8W&L=pWB08uVf7`h0|PHh20(5}8s5g9Dj7TMo_z5Dd_Ij42NG!aN!M@oBn3oV=H5PVI?KjW2k58Qn$WR#tL-DO6cxFyP4dCUz%%- z!rf0`(nJEj_B=!vsCGnXX|-3VFF&^nmR5~5O&$7((9K@Hd=mR^X3SCK zgwfp*VZQ0yp#KtJA9QYijb`bMx*_&X0^j9=e+=JH8r1<#dyX^%WPEP3)%|UtsCigRzB;dwsB|d|jNNggA*3h}uT6i(em( zL)psL2VoWa>FfQeHamOw^@%LK$*n-`c%kihNn18|Q(EtBcChI(as-F;iLFZu^GzB1 z^`^cPR@s42*+@89NI^|@*JmxHufDUrYwKU=%fINZC4Duki<8yPWA$6yXGgRX1lkj1 zw70xMb#<`Uw}d;LRVs_O_GjsBCPiIG91)o7SVIM}J=XepJZbJWI!Nf?YYf9oXXbc-rClmYO=GP+rnA;Etgi- zNsaWaF}RxD^_E`!p2Na~J^ASGYPe)-n31~EZy6zPiXjcPC6?qCgN5%>se zDGL(&N(=KSiD6ALnajp2z4+4(_F!2w-p5`odoJLcj)hrXRaB++G#bp(+ui+Tm?X5H zBx$c7VNQc=zUA^W!3AFYO!n37Nq8uGcDo+>{%_kSvGfjib5_04KS1=4Ui|fLHgAUs z{}+3BhXt3jzwQ`Jt?g#%ciiwMvp~))As0hpE%0&tXhajyNj{sLW+f_k40CKagg1%s z4Am=4FSF(E6l-R;FU(R@N1xrO_oe*J4_a8d!byR%zd>nT`+x}&3T$pg_<*2xmE|t# z6|zgWIxB3sqMAReFgF9~CKm~Ueh7QKVlwqZ8~eCo2uoK*guc-3#V_pgM?ubvOHd-U z6YuclFLv~Wy;GqJHixN2`782z@GrBAtA;+iy)D2^>)quSTjI8?$5B@I>cHo|I z%2{n*ya*mWMd-XK64lP{)B&eD38(e#BBwd*hCL%fH#$5_)iWYnX->A-OMAxmJJ#jp zQWiFlNVKY;076A#0mKs6rah6pHc3IU7H3rzh&+WR3OprBcyc#qU)no*L5;(Uue19j z?@9{tBR&=Q9bZ_ZWjL1`J@`Mt`Mg=o%=m(uV5_6`B1D|ZBifaz`a}lpz%jl@e0i(g z3t`Ba?P}UE2pQqzGMlQ8Ly3$()LpP|-v@4czuUgGNdh}b5_Z_!{p0X4_PzZhibH@G zJ@C>G^o7g4E5i~|xnd{9`SPzrBtn)?J@~=x*f`U<0BURrsPx-a8GoVC%*^ZOf7lVL z;^}U(JQ|GHf@cza`6X>s_&SxU?H*dg_T2gl)3_Pv9>d;Zj+M2F%>HBxZ8zy=)7X1_Q%I{#Q#I+GKMwvRxO zpbcugFK_9{>9>1Q4`Xh;(WHp+LKX3mjhMd5G`7q%Rxn1g%Ix;yU+ExgvD9I~4gHf$ zO#i94ynhl^hRR{NzeB};;|O!v`KmT-2yxoE(APV|+#8sfHpPF~GS{d?70j)czDeJ= zXaG3_$Wz$j_k)T*bojfF%mgI9P9#^E8!bLb4_k;z$hZguO%N?e#|F`i4xURL@1>N@ z)ka%!6uNP)$&2sR@t5gqU}1%B{8tld?05IeX)(5EqR@5KU1Zol=ls+%T%s${p^eP3 z1PvTxKY7Q>V7)oi0lOac$)B>r4~-TLSDPYON{ZA{OoxWBWrtKRmQ4P#(ff$D1nDW` z4BF9ptJ@rk8V}srXBlaO)1t;njKZ`v?L7-wa#K-M!@D5IN7cg|JR+07Go-duJhJAOQeY!rD7J zV_C5{SfX ze!5OI#=(00!SxvEu?wAEkhg#oivHQD6k0szxkOw(JPDPn_?YftP6Jl+*SfJ`kkfz% z+eTO-3xWzjg@zdT9A{l)&2G^Ho`Znrh!fA^Im4$@<%6LAH!;4pSd>(Qi_3$ZDE+#F zTqutN%3e;C5C!WaY{3PN)@SVCC|YwN_dIGT94zYnEp7#!qX28{*-kf@g8>4MQrmlzFk_4(k% zA^>hBzzawCgoMg{2n-_Pu%?X+O_0s|XazHgnQHO+i`S<)Oi-RT_{bRz*q9zaYf@t3 zt(po(#SaFZ3>UKE%T-YL7QV z{-Zg@X-xNj9zNKf0yls2LRdT5TLwXq|{b`*jTmi|;6`0;9iDCJg(7>E7Z1kCsA3u9iMtqDl+7eda zU(M1dMq(2?>%>dZzFh}fNUlmGBt{?Gpc~0anR#%PzQZ7DCS|6enVAyJi0?PlLmO0! z=+~+pt#NROUdgZR!eF(do%gG{ijBpFVm)GsB@c(x-~)E>Nh4m(PCa=*M6aDCpr0i{ z&+;dueM1Wlw2<6g9y_=$j?yKvi|Q6YPT`BXD0kB|p=p||X`N1Y58|=D&fzqwAPU@_ zPXij_-_SROrBBBWAZAQ-5HKEV#zPVHKu)P9!1onUmaywizsef$P2mllBa)DeKON2f zaAp*nb|w(7Vi%uz!_Cbcfg5!7@ z9vQMk&XT9(LeLcc(<3My;A4*FQ=un6d&!0=Y)Om8Cx)gYUC*KhKKYdlk{60DM1O zhmdAakb$ybI=G-`3f4(=q z#6EjI1Mg+o^Aqp}w()#_cdHDcRfeq9`QSdeZDuCU`a_F4(TAVdt%R3R`oIn_OJ4|N zrbBR}B14@Hwtfx$o#pC;#A0|!2)HK3B^uo4@I7E6_)n6`g{u`{O>j+|z!qId9^wP2 zV8!J$W74CTwzq77j3DXL6#ynhvOo#=dErA5#jbE09V zeKksV$X!W#msIjXWz|>HR4ZMT0+~}`*`mz`TYa6jK;Y@;J+ZXfhkve1$xm+g z0}c5(eIt#0_~YFwr*914Q{8llj08`v6Aznw@qM>c3k0ba$fUXmQeB#ePqQyvny%X3 zoyc^x4(?)4UK$p#8Uk_>t}83FRuPBs1p&tk5*!r&q!WeDu*^sdE$C`hc5VMFEF2mJ zVuqd4Fd9d(>)=zve%vr3pcOp08dRsOfZVz(H&<*GFj{38S9Pv;b^Gm3hl6x!_-#bb zW=8tWo$>r0^44bb%OCmiV8}32$BXmyA(KaU4RHSm3OPBCP{|+5Kpz*O=-}CPxTe81 zx>kwU%*&DZefEvZL&v`j>><4nRAbuL;+u@h{rQvaO~8s%rIS492}v5h|1=+IBETv| zaai?dQZILT7?s||(pN%*2!caTAs}X(#m>HB2~;@97Dq=TqMx*oVfZbeWecv*?3F8; zfIHouN|8=bwxG6biQ0sQ?2{{zEPZV^-pZc679+ra5#}%2S^!_b{`1;^NW#w#z)wku z!|@2NbUe(SP{d(%8=gIea(xmTfBgfItTtC5n=2v9^4GzJZZ?Q8tokdd3T{H>ZFi-1 zk$_;448d)H`OX9Lq5$)v4CXt4Y4X722{3swm?nU^I|s+JtM87c65`np?z((@qjZwL z3m)r-zk1lcB4zgnK+)_WvqT`XL`J3=VD4Abnb1*fqY?e9!|}|o5HkJ>FDf`NLi6Cc zYK+Gwv`YmfOJzvz1CoagtP$T}+i{@wB?0Co8O%d~dE{n>Ro|9!`Uon2c2#~WmG5V$ z0Jn3AeSt3*rPLM(_zPtCd4T-IjbFQ5fLShs`31D~_ytOp#Fjo@CZc22KS)`540xV| z;@8*-Pfj@Jd)QXezA9jQRfg>eK(=`(t`J~W$Y9z4rei72VBhS>Ap|-514!0^H@OLw{`|0q>g zCy?v*u(Vnrv|2)l<-4PI(%1uI6JTr-n4LV1=4c9l-0{vY9u;vsMXHq&97O_-A_)$T zR}v_10gAK#>dyJCkwJMAD3yq-)guhYt4NifSVa=(9a1j*NOgdxTDxB0V!aH10DKF9YUP#O=^PBoU2(va$2LN#Z{3Nd}TL58FkfeI9%6j&-tAgSx)A;77< zWfc05dL6v~O*9S`$Ej7adL16Y>4WhaZ(*Qv`-AZ`(SWtT6GZx*jGJK6A{4uQ1E&s< zVGJb{`vFI;KGwb=V0lA^r5}NEnG0usMkW(Y>I@#4w3`JCn`Iab1S;GE#i;{je1zk` zVUH|WQyihZank6Bsf@=Buv0$85n;csuC%6NnG5`T@goa2av(ku*p>uQ z9~2#wz27?=JnUM!HL_LUWUGvmfrOI?DJMD^ClRDR(o?T}Q^4}33`-<|8X`b(_7Isy zhLAdV>C(-|+X9BSWf+DLsAvgPnE+KLgNnw{YR(}9?zv<#%6_58~758a89`50CqzqX+j_zFo(N8$6sB3p3&1ep3ObCrqaibIPi)#6!!wGl_ zLB!c(WEx2zv=|RZoO+Cm2t(=`^$948W}WRHG0^*HR`M~rmP00`%dQ7vRj zc(9Y}PR0g~PQmpWFHj?q0GYf@L<&(-?H+;19vP7oLSz~~MtQx%U7CgsZs_*}==WsM z(+G4b&Ky__9k~`xo20#l=!WOKOqyDS1tT4zlDk_^IW%V%LVZa*w?9?zNu2#TnSxRY zo|$;J`VKG^YxOb_JjL9XGvUyUD{|KE6ByVhV_+s}n1&0yoD6U~)9`a4O3Ud0sW{7P2moH{|)0cixy9OwYi(;X0q9gq>5L!jn)x`|UK%CO8M z{xscVnA+b9Sbi_Vl1{K>3beQm3-0IMu;lnmQn$cELHh>*!yjZA7QhhL#ObXbN}M`X z#)*~S$(G0y3Z`u0e$B;E9K8rvV46F% z2tVuYm7@aUqcX&c2;w|U1Ms9g{GkZUsprYK%p+j=IAXvK*dsa!1#A~19-B78J$VL5 zX#~HEL+Yxzm-6v&U)^=`SV@;~zlT?CbO*uh9h^+gY2idi>kp0-@SQB~2}#aAUuIdbtO$SR>7#rZqeX;;wGu{83OG;7aIPg#>pf7MdYKH% zdQxBFsn^yCSn6b0N(fY`0L9st%Q~-=)NPbtI4xi}EyJ*pK)vpP;?%2Te7sKTH%U70 zjDY2g49g}0wZ(&F>{$WotOV*kyAqYc0g|()kR}us>uo6zYfOa*9Lgk^rWrWyajS{@ zY6~97jo*fab%N9Ltd()JjnH~alzE*@=C?>)nTHQfT_S5+M(VeF>b0K=e10n7ljFDJ z2vyfHqzN!qnK^m~fV;=}Mj5&t1l>Clbbk`i{Yi%I9RgJ$#j;6;rGnH~30OG$W|=@$ zq;5CfMs5C(`(QVY)Dg`UehBk_BD^tR(M_p_d1~l+NCvo^KBwL$L%y3J*dt-_&jO2o zma({pK<)KFaq2P|mc68Yzo%aNrGVv28J7J7>YxXTQ}2*rIY_+y`+^VV>^o(GzE9d7 z!hwO`e2p}^6XcXZCxcnlS@gGWzvdDT;ZSO41Gnf99_BUnuSnCtRUX1~MWMA91$HjV z*f~VV9lysqrv80jb8j8P z5WjM#j=`Is#16>P^^!pEl8oLlLhl2QGvU;GWa587>Z?6dkhVd<(jdc9O%zchD3P=8 zlPRf&)SZwF#>)bR%Q6fn2-HbjMyNPZhNnH**gXUA>_z+Anm>& z+G~O&*JP5MA!47yHY#8`S9K20A<&%tI1Y0Mh9oS!1uLT_NFP4B%5|S3fnhz)>%R>c z6dsa9I62xm+4&1@aI_P7@%7vv>T!~r_;rE!bs6z`Li{s4-p_-ITl5)zUCDzt6q|DD z8tmUgRN5opqUkF4W((!u6&=_oj^Q=Y;NgoYsTTJq~(t z$yRzE55zj|@Ohl*?*Cf?f?F~K=Lv!f(y?79Go}lq{woNxbli-u@EWgh_;I~-uJJ4E z%=2^hQ!+yaK#smB#db!9?INka>V_X(08N@gY2(`|>i5 za0_@xKz2ul>@pdhSMYQk$}PWw-S1v=>d$33uMnKq@KSXsgjE}nCRGU~f~&lSpM{o< z*93W;PU3rk%=a=f*KlYcX3_0KX@UdMr<-!{RKa`^3pHrLw^5zb!}bVMl|!1vKN@$OLsLp`LS zGvV3^L&geM;0rwuk$)(h;$HU)d3VC|n$*XZ@@ml>wf6);?#TqXNfdG0* zllt#Gouh3Lu(Zgqd`F;~@ZM<6bl8~zrU;|$z>LV;Pmhsi6x4k^4{26H(L*5&i_CHA z1{v8Vg7B_{>;r-90~y)71nLI?inD(sQ|J$*uGxcuQ(u!&XeRadF|8-M+YPMw0*i#L zOt>d(i+`b9)_wf8$f)*_z{n%07Kb*+-zUf(0x@@laZ^U@A&xK*It1r{e+LS#JxC!ff8|UqjITEO7C&jEhHLdMa+j z&p6D#175eNXNZ|N9~ixa+xb7^DFCwXXFMFA;2MASm^r8ZPR8-iL?Rxi4LGqDHb%kZ zG_`@SYeFSN|MyPV)iODC4ass&eHZ(?1O3i8?iXmo=^x|i)T21=mB;Wi zq^`7jZ(?c~pj|bcq#a*tDfC9KRZ~$hjU3&EFQ~}VPAFo_ATTn* z=wswDA428lIPOUsnHf6pp+Te()I0wkyYAcImqzEheiw1_)Dq^QO*r=MjI1BZxalCu z?i7YJXa5m<-m^hX=#S+sc0jIVqxII9@}bdQl}k!b`#4U{tJyYtvfpgI?6IK=Mop!$OI`5a4tiysE~# zgxBf>Y*>QrMKI*XAnr*o3JfTa%I&=g+Lu5Pa0>XtxynGw8ESKO1trVB6IS|AD}qHf z2>HbzTR6-`1QlVc59QioBl&l4O1cBkhqTv8)S?tvR7zOncpZTXmZ-%`fbx<-1yiBJ z2zf$waVGc)w`LH18=U51aH2k5u+i&{aQFs9XL)hsLaCE(%kUPEddrZ864L!Vq&amD znE?Gry~~s#kuOsYO>DwIy*iRmJ^>sw0Y`EU;D9%9i$IV!;S2$vi8YL*kM z0PI8!Cw$$c`~*^dGE#uln>2*Z95|5bC*CIyq}(S5ICUS&a{{Ge%CGP{rysPa2LGe+ zzKbdmUJwqlO|`nIujfVCD4PTXenb|NZFw`j(_yQY| z4*rPb&ew2i(+XkZ!(*dbjUb9fA_~VxQ_(%~aMW1Ra@nbbjnMoUH)14ZCW;?PtyZSM zR4pK9wNCnJfk7)2HIA`QsH*|Xx!@v~;9t?b3^A4PdK(0U0tdP#6_ z{3z0cW2jL*o@$vrhI&h+4m&3@@yC!hvDArBX&b)*2oVl8gSfT8uAuEfGHu6#-bfo9 z`x?^eOvt38l5uoBDuDzoc@BFnR3I+kMvtRD5s7Pi3vBn6u|19o4U|$RhsVj{4|ZQ9 zn(ret?IUYCo^U^riUm6JCsLe9hf@!e2|1B~nW(fdDH%;|sxx%5w}!)xKs{5ym7AzI zqf@3V60lEUE-@91x!(?^3SH5K<8t_!@{-!9>0VAUFvW{rUnk?qM2N*xYk{YC6qFw$Q+@(z#!wNsj!R;w5V$O4 zsI8*-+7N+Mh>R3N+9Y~3%Bf>zN=hX4$)0*`n1Cfrh9#MJ-V`dztL_0DHRNWbP+B#- z*hQ}gN38l5c3ZgRDU`)k`)LX_Tx3euPe9jCf= zIUXoZ&B(Mrht$ub60w?FJdg4m$P5>dh0Bo5qXzoT>kcrY0SfDPEgYQ=2K3)tRyq|E z^hF1aRMB-!KE`*==;}vJhvA)q51J7F5qCVDGP@-iAfO!}Lz_-`$fRoe^|?DTz7^gM z!BrV~tT~>UWyMu95#C-E536hEv-Fd;gBs}H=pU}AzyRJjh;{DF5A z6*y)ilM#$7D-tFp;PRY=o~Df8VED=3Xv8vzKi^6GJp8c{_=<3RVH)K*RBwjYBGQ3C zeXs&$Bw%(HFoWs&2|-q(luxXbIo*0DUfW<{$Xgmi@Aw!3;#hNb9%b^PgU#92ySh|s znHB18!~>S}Je@_Cw<#W74zl)j-z6|7n%3l1Fx|}ZFI|SDTC|9_{R%*EJcTFm^pUGl2GE~#{yz+QuLkg3cx7X9b5kdz? z$U4|c1@~EWBW*Ccv8`ztygM`4e^bKCBmRQsHpBKDHB#T)mddE2Z#17~Fuj18nJ^;{ zn`c;0=S8D$tX06%HSX1HYDCJ}HezGd%~iheuGz@g=u+k~dadP5{G^66dD(diNbJuF zITBBOLu_DjOEe-s0a4MM0R%TCWCFRz_hvB_8f<*cWOAnyiw6q~50)^jGnX(n>xLG* zOHpY~(3<`t>A?T8LmxHp&B(Zm`K386a>Vsue+_zh`pFnryFqVX1gpI*w=s4@W z<_vfzAjrBGfaY2M&~k(}q0;z)*KYzb+sM^UVu3B8mP&ZG76d#Tuqk03kSf22tEgQ& zn0Q7Nmy@t77z`g6-P5cG`%{fT{5ze*qzJwAypNmVkZJGufa_HO8nkZYS|`$n&E3ZJ z264QmMhe=AlxU||U&ic0OYU_W*3;WjDf2gAF(dD%d&E-GAQ;!wQi+IPN)#d=|F#!N9yva@P6x}Ag2VsAP-yX$%-T; zkovBTOrxprtbG)hollMNy$xg&#*tuXUp_S=czq|0Vk|M!M@@qtjmnO(%!{sI{*c|A zos%tcq8%#8IaDGi_b8tl*keJb0c|wqjl7$h%qU}E!2NJFDR(Aok&622;bMbzqdIff=%!P zC>tO)StjU5@Er?e`ddbHR6u?0ochU*MzlaQT0)e2R6q?ZzR*dmxZtcY4@KD$aqbSvUarAx!~Z{On@`~TR8NaaL5Sf4^q4Ur_tKl`nbzYbHsln z8vYx|xX)hkte!$g2y%^($W;K!Bkx#NbHBbq!Jf|w(Ts9rX0(EeHk1IEDi&VOg+ekw zWKB)Mws|?&5pm7;qPdBysF65~TfWM(c;VEGD1TpZ$$p6YX%z+LVDt2bc9g)?C<#~G zBOB?HBI=BWI8-t`$dQbIe$RO_C2&rfSjK2svm(-LEwzKITT8`q^g8M%Egan?aATx! z{92g*;6urUulH~Q+s;zE;MbE$vP6ghIQz>|h43X%H?WinKEjmAp06Zh1@`!xi7oD6&;A^o~E{8%Mp?RBV6*-rU$ zQJbhQ#KEG4gZ>27cp0iq06K3ArQxEtQ1xz5@-_p2O^|_ZAyl`C0+r=5EkD7_9g1&)UV4peTUNNn1$K&h%xlY=#isQ!qiE0 zPF~isWeb+)$(`gYPL&E^4@wd#H(`WYYEDjaDfP)QP~w)PHtXIHlaE4+MsduVzE_R)p* zgJFGT3(}m2at_K#C?7z1AIcsm??Cx(3peZ_b)KLmuMbOdVvi3JvhRyzcWSvF?@*ps zi@C?|llntYFD5o&-;c1BB4LYre273Dq3Yo{791HD{)%^;Mw$dDqC~}TaDp_QDrrjU zx4GcR{cn|xuIFnd^()~1#2KVH0@s7C>sziXdG=eSc=x}lAkSyQ36O3$>GA)u^!`oc zZtai*K%kjv677i0TmZ>vV31tJXaC!4_8)&6$H1o%62M#_O0FITxY7@(1)BRXxXHgL zyHmQUn1_3}5Rf+{LQIzkLEt`dj6bP@1*aO`F%5<7xiig3vlI%sp9a@4P|m^qQYcm^ zu!_~}fwB!Y;V!zaufz2f*Zl&xM!N3dx0W=cphQ3^CJjjOb3GKl|JJn`5RiRDvOh)k zs>r?=*}fw-Tm!kvGD%TFzBLs}J(R~#qJ6ODG?Y7#h#UM2)=Y-9&W0ZF+WyoYSW^yr zEFVGodL`s1?!mpN9ZtQM^6ZoU9%=$oxhK`sK;@`Z*p;DcsBkJXm1AnCNNQ6m_X=El z+y}FG3}q7qj7{f`)KJd&9!^?FhcRD6atJ3RI|nm`j+iOw2#!BNJljbsrFSkY-*aDc zCiTg|I!nbAIKatmI7z)M@~WLBAebdXaFURz^N``xhor8KuOkFbdmJo1Tfj0~f`!C_ z5K5K0b$mt{lsTX$IKsPr26^+6)0YLk4}GiuP-Qx4;N7AQX8)@XSKkTPNfB0^#{9DvruDa|^zr zwv*n0vk5Y}zoNne*235lva4nr98{3-yFlPzfsBKTgx^b4YEN-HotQk?jSP4 zguM_?h#&ZZfaL`VmJ8fJE>XkbJOgFL+qn%5U^?#Iq0`&1&AcA47k zHnJ=MZMJ+!ivsYAGT`qB*(MKjaAH8ld=sg^D+=By6Z|fz`vL02**agKkT0X~ z1A%I$=6R8;i; z#Q&LKo$A7ZKxi0?!lN7xDCHZ{NQqd zq2)4$ej$V&d-_QGiU9SB4C*m~dP3FdNG6wzfLO>n5yCK4(M0!d-MlI^e^u7}32ENu zA;PKe%fx6S&Y?rl9eJfp64vn@q-`g)WBgSEtlUpfi5nSw#ySQ45q|?`F(u!B2MJi| z_dDI5dV#$kwZ#H=t0deJRJHGUK%J=f$iH_TmVbZwDbjb~)fy@3e?C?J4@gNn>x*5y z{{s&1NgGUgs=?^D>4%D=}w1^=c?~JI@jV1y z{uq?pop%%=usFRaE>Jl&Px%Ll3wWW4@JV*;w{aLNj9fUSaHgNgJfkfXm@bqs4aXA{ zU^GgFXZ>CzKo!ZLlmyCKfFiH-NzKli)Tu-at+1MClddLs6{+`AMC(X`iKMy+m+q7n zq_vz{jmDdu%kFgo`E@e#eu_=eyP-8{DgC<&`u)cj5XHfH+>fB+CXB?Ivk){Lzv)_m MMB^kSdCB_!0X(Wb*Z=?k diff --git a/web/share/js/kvm/switch.js b/web/share/js/kvm/switch.js index a33e2fd0..9d261cc4 100644 --- a/web/share/js/kvm/switch.js +++ b/web/share/js/kvm/switch.js @@ -459,7 +459,7 @@ export function Switch() { let create_content = function(el_parent) { let html = ` - +
Port name:
-
- `; + + let fw = model.units[model.ports[port].unit].firmware; + if (fw.devbuild || fw.version >= 8) { + html += ` +
+
+ + + + +
Simulate display on inactive port: +
+ + +
+
+ `; + } + + html += "
"; for (let kv of Object.entries(atx_actions)) { html += ` @@ -491,6 +515,7 @@ export function Switch() { `; } html += "
"; + el_parent.innerHTML = html; let el_selector = $("__switch-port-edid-selector"); @@ -521,6 +546,7 @@ export function Switch() { let params = { "port": port, "edid_id": $("__switch-port-edid-selector").value, + "dummy": $("__switch-port-dummy-switch").checked, "name": $("__switch-port-name-input").value, }; for (let action of Object.keys(atx_actions)) {