summaryrefslogtreecommitdiff
path: root/kvmd/apps/edidconf/__init__.py
blob: baf2ed43f4ad894b6dbf87e8fdddeb1d818dcb88 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# ========================================================================== #
#                                                                            #
#    KVMD - The main PiKVM daemon.                                           #
#                                                                            #
#    Copyright (C) 2018-2022  Maxim Devaev <[email protected]>               #
#                                                                            #
#    This program is free software: you can redistribute it and/or modify    #
#    it under the terms of the GNU General Public License as published by    #
#    the Free Software Foundation, either version 3 of the License, or       #
#    (at your option) any later version.                                     #
#                                                                            #
#    This program is distributed in the hope that it will be useful,         #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of          #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           #
#    GNU General Public License for more details.                            #
#                                                                            #
#    You should have received a copy of the GNU General Public License       #
#    along with this program.  If not, see <https://www.gnu.org/licenses/>.  #
#                                                                            #
# ========================================================================== #


import sys
import re
import argparse

from typing import List
from typing import Optional

from ...validators.basic import valid_bool

from .. import init


# =====
class _Edid:
    def __init__(self, path: str) -> None:
        with open(path) as file:
            self.__load_from_hex(file.read())

    def write(self, path: str) -> None:
        self.__update_checksums()
        text = "\n".join(
            "".join(
                f"{item:0{2}X}"
                for item in self.__data[index:index + 16]
            )
            for index in range(0, len(self.__data), 16)
        )
        if path:
            with open(path, "w") as file:
                file.write(text + "\n")
        else:
            print(text)

    def is_audio_enabled(self) -> bool:
        return bool(self.__data[131] & 0b01000000)

    def set_audio_enabled(self, enabled: bool) -> None:
        if enabled:
            self.__data[131] |= 0b01000000
        else:
            self.__data[131] &= (0xFF - 0b01000000)  # ~X

    def __load_from_hex(self, text: str) -> None:
        text = re.sub(r"\s", "", text)
        self.__data = [
            int(text[index:index + 2], 16)
            for index in range(0, len(text), 2)
        ]
        assert len(self.__data) == 256, f"Invalid EDID length: {len(self.__data)}, should be 256 bytes"
        assert self.__data[126] == 1, "Zero extensions number"
        assert (self.__data[128], self.__data[129]) == (0x02, 0x03), "Can't find CEA-861"

    def __update_checksums(self) -> None:
        self.__data[127] = 256 - (sum(self.__data[:127]) % 256)
        self.__data[255] = 256 - (sum(self.__data[128:255]) % 256)


# =====
def main(argv: Optional[List[str]]=None) -> None:
    (parent_parser, argv, _) = init(
        add_help=False,
        argv=argv,
    )
    parser = argparse.ArgumentParser(
        prog="kvmd-edidconf",
        description="A simple and primitive KVMD EDID editor",
        parents=[parent_parser],
    )
    parser.add_argument("-f", "--edid-file", dest="path", default="/etc/kvmd/tc358743-edid.hex",
                        help="EDID hex text file path", metavar="<file>")
    parser.add_argument("--stdout", action="store_true",
                        help="Write to stdout instead of the rewriting the source file")
    parser.add_argument("--set-audio", type=valid_bool, dest="set_audio", default=None,
                        help="Enable or disable basic audio", metavar="<yes|no>")
    parser.add_argument("--show-info", action="store_true",
                        help="Write summary info to stderr")
    options = parser.parse_args(argv[1:])

    edid = _Edid(options.path)
    changed = False

    if options.set_audio is not None:
        edid.set_audio_enabled(options.set_audio)
        changed = True

    if changed:
        edid.write("" if options.stdout else options.path)

    if options.show_info:
        print(f"Audio: {'yes' if edid.is_audio_enabled() else 'no'}", file=sys.stderr)