diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 926b4e31..00000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,19 +0,0 @@ -[bumpversion] -commit = True -tag = True -current_version = 4.20 -parse = (?P\d+)\.(?P\d+)(\.(?P\d+)(\-(?P[a-z]+))?)? -serialize = - {major}.{minor} - -[bumpversion:file:kvmd/__init__.py] -search = __version__ = "{current_version}" -replace = __version__ = "{new_version}" - -[bumpversion:file:setup.py] -search = version="{current_version}" -replace = version="{new_version}" - -[bumpversion:file:PKGBUILD] -search = pkgver={current_version} -replace = pkgver={new_version} diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 447d3a8d..00000000 --- a/.editorconfig +++ /dev/null @@ -1,10 +0,0 @@ -root = true - -[*] -end_of_file = lf -indent_style = tab -indent_size = 4 - -[*.{py,yaml}] -indent_style = space -indent_size = 4 diff --git a/.gitignore b/.gitignore index e3fcca5e..96074115 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ /venv/ .vscode/settings.j/son kvmd_config/ +__pycache__/ +kvmd_data/run/kvmd/* +.vscode/ diff --git a/Makefile b/Makefile index ab47094f..b4c6cc73 100644 --- a/Makefile +++ b/Makefile @@ -294,6 +294,13 @@ run-build-dev: -f build/Dockerfile . \ --push +run-build-new: + $(DOCKER) buildx build -t registry.cn-hangzhou.aliyuncs.com/silentwind/kvmd:dev \ + --platform linux/amd64 \ + --build-arg CACHEBUST=$(date +%s) \ + -f build/Dockerfile . \ + --load + run-build-release: $(DOCKER) buildx build -t registry.cn-hangzhou.aliyuncs.com/silentwind/kvmd \ --progress plain \ @@ -342,4 +349,18 @@ run-nogpio: testenv && python -m kvmd.apps.ngxmkconf /etc/kvmd/nginx/nginx.conf.mako /etc/kvmd/nginx/nginx.conf \ && nginx -c /etc/kvmd/nginx/nginx.conf -g 'user http; error_log stderr;' \ && $(if $(CMD),$(CMD),python -m kvmd.apps.kvmd --run) \ - " \ No newline at end of file + " +nuitka: + python -m nuitka kvmd-test.py --standalone --onefile --no-deployment-flag=self-execution --include-module=\ + kvmd.plugins.auth.htpasswd,kvmd.plugins.auth.http,kvmd.plugins.auth.ldap,\ + kvmd.plugins.auth.pam,kvmd.plugins.auth.radius,\ + kvmd.plugins.hid.ch9329,kvmd.plugins.hid.bt,kvmd.plugins.hid.otg,\ + kvmd.plugins.atx.disabled,kvmd.plugins.atx.gpio,\ + kvmd.plugins.msd.disabled,kvmd.plugins.msd.otg,\ + kvmd.plugins.ugpio.gpio,kvmd.plugins.ugpio.wol,kvmd.plugins.ugpio.cmd,\ + kvmd.plugins.ugpio.ipmi,kvmd.plugins.ugpio.anelpwr,kvmd.plugins.ugpio.cmdret,\ + kvmd.plugins.ugpio.extron,kvmd.plugins.ugpio.ezcoo,kvmd.plugins.ugpio.hidrelay,\ + kvmd.plugins.ugpio.hue,kvmd.plugins.ugpio.locator,kvmd.plugins.ugpio.noyito,\ + kvmd.plugins.ugpio.otgconf,kvmd.plugins.ugpio.pway,kvmd.plugins.ugpio.pwm,\ + kvmd.plugins.ugpio.servo,kvmd.plugins.ugpio.tesmart,kvmd.plugins.ugpio.xh_hk4401,\ + passlib.handlers.sha1_crypt,pygments.formatters.terminal \ No newline at end of file diff --git a/build/Dockerfile b/build/Dockerfile index 77b279a6..d87119a0 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -30,23 +30,9 @@ RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/' /etc/apt/sources.lis RUN if [ ${TARGETARCH} = arm ]; then ARCH=armhf; elif [ ${TARGETARCH} = arm64 ]; then ARCH=aarch64; elif [ ${TARGETARCH} = amd64 ]; then ARCH=x86_64; fi \ && curl https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.$ARCH -L -o /usr/local/bin/ttyd \ && chmod +x /usr/local/bin/ttyd \ - && adduser kvmd --gecos "" --disabled-password \ - && ln -sf /usr/share/tesseract-ocr/*/tessdata /usr/share/tessdata \ - && mkdir -p /etc/kvmd_backup/override.d /var/lib/kvmd/msd/images /var/lib/kvmd/msd/meta /var/lib/kvmd/pst/data /var/lib/kvmd/msd/NormalFiles /opt/vc/bin /run/kvmd /tmp/kvmd-nginx \ - && touch /run/kvmd/ustreamer.sock + && ln -sf /usr/share/tesseract-ocr/*/tessdata /usr/share/tessdata -COPY testenv/fakes/vcgencmd scripts/kvmd* /usr/bin/ -COPY extras/ /usr/share/kvmd/extras/ -COPY web/ /usr/share/kvmd/web/ -COPY scripts/kvmd-gencert /usr/share/kvmd/ -COPY build/platform/docker /usr/share/kvmd/platform -COPY contrib/keymaps /usr/share/kvmd/keymaps -COPY kvmd/ build/init.sh /kvmd/ -COPY configs/kvmd/ /etc/kvmd_backup/ -COPY configs/nginx/ /etc/kvmd_backup/nginx/ -COPY configs/janus/ /etc/kvmd_backup/janus/ -COPY configs/hw_info/ /etc/kvmd_backup/hw_info/ -COPY testenv/js/ /usr/share/janus/javascript/ +COPY . /One-KVM -ENTRYPOINT ["/kvmd/init.sh"] \ No newline at end of file +ENTRYPOINT ["bash"] \ No newline at end of file diff --git a/configs/nginx/kvmd.ctx-http.conf b/configs/nginx/kvmd.ctx-http.conf deleted file mode 100644 index ceb68ef8..00000000 --- a/configs/nginx/kvmd.ctx-http.conf +++ /dev/null @@ -1,7 +0,0 @@ -upstream kvmd { - server unix:/run/kvmd/kvmd.sock fail_timeout=0s max_fails=0; -} - -upstream ustreamer { - server unix:/run/kvmd/ustreamer.sock fail_timeout=0s max_fails=0; -} diff --git a/configs/os/boot-config/v0-hdmi-rpi2.txt b/configs/os/boot-config/v0-hdmi-rpi2.txt deleted file mode 100644 index 853673e9..00000000 --- a/configs/os/boot-config/v0-hdmi-rpi2.txt +++ /dev/null @@ -1,7 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=128 -enable_uart=1 -dtoverlay=tc358743,i2c_pins_28_29=1 diff --git a/configs/os/boot-config/v0-hdmi-rpi3.txt b/configs/os/boot-config/v0-hdmi-rpi3.txt deleted file mode 100644 index f9c43c3b..00000000 --- a/configs/os/boot-config/v0-hdmi-rpi3.txt +++ /dev/null @@ -1,8 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=128 -enable_uart=1 -dtoverlay=tc358743 -dtoverlay=disable-bt diff --git a/configs/os/boot-config/v0-hdmi-zero2w.txt b/configs/os/boot-config/v0-hdmi-zero2w.txt deleted file mode 100644 index 54459566..00000000 --- a/configs/os/boot-config/v0-hdmi-zero2w.txt +++ /dev/null @@ -1,8 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=96 -enable_uart=1 -dtoverlay=tc358743,i2c_pins_28_29=1 -dtoverlay=disable-bt diff --git a/configs/os/boot-config/v0-hdmiusb-rpi2.txt b/configs/os/boot-config/v0-hdmiusb-rpi2.txt deleted file mode 100644 index d78de4ae..00000000 --- a/configs/os/boot-config/v0-hdmiusb-rpi2.txt +++ /dev/null @@ -1,6 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=16 -enable_uart=1 diff --git a/configs/os/boot-config/v0-hdmiusb-rpi3.txt b/configs/os/boot-config/v0-hdmiusb-rpi3.txt deleted file mode 100644 index 5d41ab3c..00000000 --- a/configs/os/boot-config/v0-hdmiusb-rpi3.txt +++ /dev/null @@ -1,7 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=16 -enable_uart=1 -dtoverlay=disable-bt diff --git a/configs/os/boot-config/v0-hdmiusb-zero2w.txt b/configs/os/boot-config/v0-hdmiusb-zero2w.txt deleted file mode 100644 index 5d41ab3c..00000000 --- a/configs/os/boot-config/v0-hdmiusb-zero2w.txt +++ /dev/null @@ -1,7 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=16 -enable_uart=1 -dtoverlay=disable-bt diff --git a/configs/os/boot-config/v1-hdmi-rpi2.txt b/configs/os/boot-config/v1-hdmi-rpi2.txt deleted file mode 100644 index e94b342d..00000000 --- a/configs/os/boot-config/v1-hdmi-rpi2.txt +++ /dev/null @@ -1,8 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=128 -enable_uart=1 -dtoverlay=tc358743,i2c_pins_28_29=1 -dtoverlay=spi0-1cs diff --git a/configs/os/boot-config/v1-hdmi-rpi3.txt b/configs/os/boot-config/v1-hdmi-rpi3.txt deleted file mode 100644 index ad44d95c..00000000 --- a/configs/os/boot-config/v1-hdmi-rpi3.txt +++ /dev/null @@ -1,9 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=128 -enable_uart=1 -dtoverlay=tc358743 -dtoverlay=disable-bt -dtoverlay=spi0-1cs diff --git a/configs/os/boot-config/v1-hdmi-zero2w.txt b/configs/os/boot-config/v1-hdmi-zero2w.txt deleted file mode 100644 index 0a1e9009..00000000 --- a/configs/os/boot-config/v1-hdmi-zero2w.txt +++ /dev/null @@ -1,9 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=96 -enable_uart=1 -dtoverlay=tc358743,i2c_pins_28_29=1 -dtoverlay=disable-bt -dtoverlay=spi0-1cs diff --git a/configs/os/boot-config/v1-hdmiusb-rpi2.txt b/configs/os/boot-config/v1-hdmiusb-rpi2.txt deleted file mode 100644 index 9957f1a4..00000000 --- a/configs/os/boot-config/v1-hdmiusb-rpi2.txt +++ /dev/null @@ -1,7 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=16 -enable_uart=1 -dtoverlay=spi0-1cs diff --git a/configs/os/boot-config/v1-hdmiusb-rpi3.txt b/configs/os/boot-config/v1-hdmiusb-rpi3.txt deleted file mode 100644 index fe4ce032..00000000 --- a/configs/os/boot-config/v1-hdmiusb-rpi3.txt +++ /dev/null @@ -1,8 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=16 -enable_uart=1 -dtoverlay=disable-bt -dtoverlay=spi0-1cs diff --git a/configs/os/boot-config/v1-hdmiusb-zero2w.txt b/configs/os/boot-config/v1-hdmiusb-zero2w.txt deleted file mode 100644 index fe4ce032..00000000 --- a/configs/os/boot-config/v1-hdmiusb-zero2w.txt +++ /dev/null @@ -1,8 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=16 -enable_uart=1 -dtoverlay=disable-bt -dtoverlay=spi0-1cs diff --git a/configs/os/boot-config/v2-hdmi-rpi3.txt b/configs/os/boot-config/v2-hdmi-rpi3.txt deleted file mode 100644 index 7a2f75b4..00000000 --- a/configs/os/boot-config/v2-hdmi-rpi3.txt +++ /dev/null @@ -1,9 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=128 -enable_uart=1 -dtoverlay=tc358743 -dtoverlay=disable-bt -dtoverlay=dwc2,dr_mode=peripheral diff --git a/configs/os/boot-config/v2-hdmi-rpi4.txt b/configs/os/boot-config/v2-hdmi-rpi4.txt deleted file mode 100644 index 7a2f75b4..00000000 --- a/configs/os/boot-config/v2-hdmi-rpi4.txt +++ /dev/null @@ -1,9 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=128 -enable_uart=1 -dtoverlay=tc358743 -dtoverlay=disable-bt -dtoverlay=dwc2,dr_mode=peripheral diff --git a/configs/os/boot-config/v2-hdmi-zero2w.txt b/configs/os/boot-config/v2-hdmi-zero2w.txt deleted file mode 100644 index 6ec3dc54..00000000 --- a/configs/os/boot-config/v2-hdmi-zero2w.txt +++ /dev/null @@ -1,9 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=96 -enable_uart=1 -dtoverlay=tc358743,i2c_pins_28_29=1 -dtoverlay=disable-bt -dtoverlay=dwc2,dr_mode=peripheral diff --git a/configs/os/boot-config/v2-hdmiusb-rpi4.txt b/configs/os/boot-config/v2-hdmiusb-rpi4.txt deleted file mode 100644 index a53ba19c..00000000 --- a/configs/os/boot-config/v2-hdmiusb-rpi4.txt +++ /dev/null @@ -1,8 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=16 -enable_uart=1 -dtoverlay=disable-bt -dtoverlay=dwc2,dr_mode=peripheral diff --git a/configs/os/boot-config/v3-hdmi-rpi4.txt b/configs/os/boot-config/v3-hdmi-rpi4.txt deleted file mode 100644 index e9ec5038..00000000 --- a/configs/os/boot-config/v3-hdmi-rpi4.txt +++ /dev/null @@ -1,22 +0,0 @@ -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=128 -enable_uart=1 -dtoverlay=tc358743 -dtoverlay=disable-bt -dtoverlay=dwc2,dr_mode=peripheral -dtparam=act_led_gpio=13 - -# HDMI audio capture -dtoverlay=tc358743-audio - -# SPI (AUM) -dtoverlay=spi0-1cs - -# I2C (display) -dtparam=i2c_arm=on - -# Clock -dtoverlay=i2c-rtc,pcf8563,wakeup-source diff --git a/configs/os/boot-config/v4mini-hdmi-rpi4.txt b/configs/os/boot-config/v4mini-hdmi-rpi4.txt deleted file mode 100644 index 7664187d..00000000 --- a/configs/os/boot-config/v4mini-hdmi-rpi4.txt +++ /dev/null @@ -1,21 +0,0 @@ -# PiKVM -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=128 -enable_uart=1 -dtoverlay=disable-bt - -# USB emulation -dtoverlay=dwc2,dr_mode=peripheral - -# Video and audio -dtoverlay=tc358743,4lane=1 -dtoverlay=tc358743-audio - -# I2C (display) -dtparam=i2c_arm=on - -# Clock -dtoverlay=i2c-rtc,pcf8563,wakeup-source diff --git a/configs/os/boot-config/v4plus-hdmi-rpi4.txt b/configs/os/boot-config/v4plus-hdmi-rpi4.txt deleted file mode 100644 index 05821ea4..00000000 --- a/configs/os/boot-config/v4plus-hdmi-rpi4.txt +++ /dev/null @@ -1,29 +0,0 @@ -# PiKVM -# See /boot/overlays/README for all available options -initramfs initramfs-linux.img followkernel - -hdmi_force_hotplug=1 -gpu_mem=128 -enable_uart=1 -dtoverlay=disable-bt - -# USB emulation -dtoverlay=dwc2,dr_mode=peripheral - -# Video and audio -dtoverlay=tc358743,4lane=1 -dtoverlay=tc358743-audio - -# Passthrough -dtoverlay=vc4-kms-v3d -disable_overscan=1 - -# I2C (display) -dtparam=i2c_arm=on - -# Clock -dtoverlay=i2c-rtc,pcf8563,wakeup-source - -# Passthrough -dtoverlay=vc4-kms-v3d -disable_overscan=1 diff --git a/configs/os/cmdline/v0-hdmi-rpi2.sed b/configs/os/cmdline/v0-hdmi-rpi2.sed deleted file mode 100644 index 3089ad26..00000000 --- a/configs/os/cmdline/v0-hdmi-rpi2.sed +++ /dev/null @@ -1,5 +0,0 @@ -s/console=ttyAMA0\,115200//g -s/kgdboc=ttyAMA0\,115200//g -s/console=serial0\,115200//g -s/kgdboc=serial0\,115200//g -s/rootwait/rootwait cma=128M/g diff --git a/configs/os/cmdline/v0-hdmi-rpi3.sed b/configs/os/cmdline/v0-hdmi-rpi3.sed deleted file mode 100644 index 3089ad26..00000000 --- a/configs/os/cmdline/v0-hdmi-rpi3.sed +++ /dev/null @@ -1,5 +0,0 @@ -s/console=ttyAMA0\,115200//g -s/kgdboc=ttyAMA0\,115200//g -s/console=serial0\,115200//g -s/kgdboc=serial0\,115200//g -s/rootwait/rootwait cma=128M/g diff --git a/configs/os/cmdline/v0-hdmi-zero2w.sed b/configs/os/cmdline/v0-hdmi-zero2w.sed deleted file mode 100644 index ef88f019..00000000 --- a/configs/os/cmdline/v0-hdmi-zero2w.sed +++ /dev/null @@ -1,5 +0,0 @@ -s/console=ttyAMA0\,115200//g -s/kgdboc=ttyAMA0\,115200//g -s/console=serial0\,115200//g -s/kgdboc=serial0\,115200//g -s/rootwait/rootwait cma=96M/g diff --git a/configs/os/cmdline/v0-hdmiusb-rpi2.sed b/configs/os/cmdline/v0-hdmiusb-rpi2.sed deleted file mode 100644 index d2ac8b1e..00000000 --- a/configs/os/cmdline/v0-hdmiusb-rpi2.sed +++ /dev/null @@ -1,4 +0,0 @@ -s/console=ttyAMA0\,115200//g -s/kgdboc=ttyAMA0\,115200//g -s/console=serial0\,115200//g -s/kgdboc=serial0\,115200//g diff --git a/configs/os/cmdline/v0-hdmiusb-rpi3.sed b/configs/os/cmdline/v0-hdmiusb-rpi3.sed deleted file mode 100644 index d2ac8b1e..00000000 --- a/configs/os/cmdline/v0-hdmiusb-rpi3.sed +++ /dev/null @@ -1,4 +0,0 @@ -s/console=ttyAMA0\,115200//g -s/kgdboc=ttyAMA0\,115200//g -s/console=serial0\,115200//g -s/kgdboc=serial0\,115200//g diff --git a/configs/os/cmdline/v0-hdmiusb-zero2w.sed b/configs/os/cmdline/v0-hdmiusb-zero2w.sed deleted file mode 100644 index d2ac8b1e..00000000 --- a/configs/os/cmdline/v0-hdmiusb-zero2w.sed +++ /dev/null @@ -1,4 +0,0 @@ -s/console=ttyAMA0\,115200//g -s/kgdboc=ttyAMA0\,115200//g -s/console=serial0\,115200//g -s/kgdboc=serial0\,115200//g diff --git a/configs/os/cmdline/v1-hdmi-rpi2.sed b/configs/os/cmdline/v1-hdmi-rpi2.sed deleted file mode 100644 index ee1a5540..00000000 --- a/configs/os/cmdline/v1-hdmi-rpi2.sed +++ /dev/null @@ -1 +0,0 @@ -s/rootwait/rootwait cma=128M/g diff --git a/configs/os/cmdline/v1-hdmi-rpi3.sed b/configs/os/cmdline/v1-hdmi-rpi3.sed deleted file mode 100644 index ee1a5540..00000000 --- a/configs/os/cmdline/v1-hdmi-rpi3.sed +++ /dev/null @@ -1 +0,0 @@ -s/rootwait/rootwait cma=128M/g diff --git a/configs/os/cmdline/v1-hdmi-zero2w.sed b/configs/os/cmdline/v1-hdmi-zero2w.sed deleted file mode 100644 index 9c551c25..00000000 --- a/configs/os/cmdline/v1-hdmi-zero2w.sed +++ /dev/null @@ -1 +0,0 @@ -s/rootwait/rootwait cma=96M/g diff --git a/configs/os/cmdline/v2-hdmi-rpi3.sed b/configs/os/cmdline/v2-hdmi-rpi3.sed deleted file mode 100644 index ee1a5540..00000000 --- a/configs/os/cmdline/v2-hdmi-rpi3.sed +++ /dev/null @@ -1 +0,0 @@ -s/rootwait/rootwait cma=128M/g diff --git a/configs/os/cmdline/v2-hdmi-rpi4.sed b/configs/os/cmdline/v2-hdmi-rpi4.sed deleted file mode 100644 index ee1a5540..00000000 --- a/configs/os/cmdline/v2-hdmi-rpi4.sed +++ /dev/null @@ -1 +0,0 @@ -s/rootwait/rootwait cma=128M/g diff --git a/configs/os/cmdline/v2-hdmi-zero2w.sed b/configs/os/cmdline/v2-hdmi-zero2w.sed deleted file mode 100644 index 9c551c25..00000000 --- a/configs/os/cmdline/v2-hdmi-zero2w.sed +++ /dev/null @@ -1 +0,0 @@ -s/rootwait/rootwait cma=96M/g diff --git a/configs/os/cmdline/v2-hdmiusb-rpi4.sed b/configs/os/cmdline/v2-hdmiusb-rpi4.sed deleted file mode 100644 index e69de29b..00000000 diff --git a/configs/os/cmdline/v3-hdmi-rpi4.sed b/configs/os/cmdline/v3-hdmi-rpi4.sed deleted file mode 100644 index ee1a5540..00000000 --- a/configs/os/cmdline/v3-hdmi-rpi4.sed +++ /dev/null @@ -1 +0,0 @@ -s/rootwait/rootwait cma=128M/g diff --git a/configs/os/cmdline/v4mini-hdmi-rpi4.sed b/configs/os/cmdline/v4mini-hdmi-rpi4.sed deleted file mode 100644 index ee1a5540..00000000 --- a/configs/os/cmdline/v4mini-hdmi-rpi4.sed +++ /dev/null @@ -1 +0,0 @@ -s/rootwait/rootwait cma=128M/g diff --git a/configs/os/cmdline/v4plus-hdmi-rpi4.sed b/configs/os/cmdline/v4plus-hdmi-rpi4.sed deleted file mode 100644 index ee1a5540..00000000 --- a/configs/os/cmdline/v4plus-hdmi-rpi4.sed +++ /dev/null @@ -1 +0,0 @@ -s/rootwait/rootwait cma=128M/g diff --git a/configs/os/kvmd-webterm.conf b/configs/os/kvmd-webterm.conf deleted file mode 100644 index 3a015fb3..00000000 --- a/configs/os/kvmd-webterm.conf +++ /dev/null @@ -1,6 +0,0 @@ -g kvmd-webterm - - - -u kvmd-webterm - "Pi-KVM - Web terminal" /home/kvmd-webterm - -m kvmd-webterm kvmd -m kvmd-nginx kvmd-webterm diff --git a/configs/os/modules-load/v0-hdmi.conf b/configs/os/modules-load/v0-hdmi.conf deleted file mode 100644 index 5be25408..00000000 --- a/configs/os/modules-load/v0-hdmi.conf +++ /dev/null @@ -1 +0,0 @@ -tc358743 diff --git a/configs/os/modules-load/v1-hdmi.conf b/configs/os/modules-load/v1-hdmi.conf deleted file mode 100644 index 5be25408..00000000 --- a/configs/os/modules-load/v1-hdmi.conf +++ /dev/null @@ -1 +0,0 @@ -tc358743 diff --git a/configs/os/modules-load/v2-hdmi.conf b/configs/os/modules-load/v2-hdmi.conf deleted file mode 100644 index af538dba..00000000 --- a/configs/os/modules-load/v2-hdmi.conf +++ /dev/null @@ -1,3 +0,0 @@ -dwc2 -libcomposite -tc358743 diff --git a/configs/os/modules-load/v2-hdmiusb.conf b/configs/os/modules-load/v2-hdmiusb.conf deleted file mode 100644 index 9c9626be..00000000 --- a/configs/os/modules-load/v2-hdmiusb.conf +++ /dev/null @@ -1,2 +0,0 @@ -dwc2 -libcomposite diff --git a/configs/os/modules-load/v3-hdmi.conf b/configs/os/modules-load/v3-hdmi.conf deleted file mode 100644 index f1ede9d1..00000000 --- a/configs/os/modules-load/v3-hdmi.conf +++ /dev/null @@ -1,4 +0,0 @@ -dwc2 -libcomposite -tc358743 -i2c-dev diff --git a/configs/os/modules-load/v4mini-hdmi.conf b/configs/os/modules-load/v4mini-hdmi.conf deleted file mode 100644 index f1ede9d1..00000000 --- a/configs/os/modules-load/v4mini-hdmi.conf +++ /dev/null @@ -1,4 +0,0 @@ -dwc2 -libcomposite -tc358743 -i2c-dev diff --git a/configs/os/modules-load/v4plus-hdmi.conf b/configs/os/modules-load/v4plus-hdmi.conf deleted file mode 100644 index f1ede9d1..00000000 --- a/configs/os/modules-load/v4plus-hdmi.conf +++ /dev/null @@ -1,4 +0,0 @@ -dwc2 -libcomposite -tc358743 -i2c-dev diff --git a/configs/os/services/kvmd-bootconfig.service b/configs/os/services/kvmd-bootconfig.service deleted file mode 100644 index ba5469b9..00000000 --- a/configs/os/services/kvmd-bootconfig.service +++ /dev/null @@ -1,29 +0,0 @@ -[Unit] -Description=PiKVM - Boot configuration -After=systemd-modules-load.service kvmd-oled.service -Before=\ - kvmd-webterm.service \ - kvmd-certbot.service \ - kvmd-ipmi.service \ - kvmd-janus-static.service \ - kvmd-janus.service \ - kvmd-nginx.service \ - kvmd-otg.service \ - kvmd-otgnet.service \ - kvmd-pst.service \ - kvmd-tc358743.service \ - kvmd-vnc.service \ - kvmd-watchdog.service \ - kvmd.service \ - pikvm-bootconfig.service \ - sshd.service \ - network-pre.target - -[Service] -Type=oneshot -ExecStart=/usr/bin/kvmd-bootconfig --do-the-thing -ExecStop=/bin/true -RemainAfterExit=true - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd-certbot.service b/configs/os/services/kvmd-certbot.service deleted file mode 100644 index 03515b5c..00000000 --- a/configs/os/services/kvmd-certbot.service +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=PiKVM - Certbot-Renew for KVMD-Nginx - -[Service] -Type=oneshot -EnvironmentFile=-/etc/conf.d/kvmd-certbot -ExecStart=/usr/bin/kvmd-certbot renew --quiet -# Should we?.. -# PrivateTmp=true diff --git a/configs/os/services/kvmd-certbot.timer b/configs/os/services/kvmd-certbot.timer deleted file mode 100644 index 84fc9fcd..00000000 --- a/configs/os/services/kvmd-certbot.timer +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Run KVMD-Certbot twice daily - -[Timer] -OnCalendar=*-*-* 00/12:00:00 -RandomizedDelaySec=12h -Persistent=true - -[Install] -WantedBy=timers.target diff --git a/configs/os/services/kvmd-ipmi.service b/configs/os/services/kvmd-ipmi.service deleted file mode 100644 index 17bcc2b9..00000000 --- a/configs/os/services/kvmd-ipmi.service +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=One-KVM - IPMI to KVMD proxy -After=kvmd.service - -[Service] -User=kvmd-ipmi -Group=kvmd-ipmi -Type=simple -Restart=always -RestartSec=3 -AmbientCapabilities=CAP_NET_BIND_SERVICE - -ExecStart=/usr/bin/kvmd-ipmi --run -TimeoutStopSec=3 - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd-janus-static.service b/configs/os/services/kvmd-janus-static.service deleted file mode 100644 index 15ed723b..00000000 --- a/configs/os/services/kvmd-janus-static.service +++ /dev/null @@ -1,22 +0,0 @@ -[Unit] -Description=PiKVM - Janus WebRTC Gateway (Static Config) -After=network.target network-online.target nss-lookup.target kvmd.service - -[Service] -User=kvmd-janus -Group=kvmd-janus -Type=simple -Restart=always -RestartSec=3 -AmbientCapabilities=CAP_NET_RAW -LimitNOFILE=65536 - -# Crutch for UNIX socket perms -UMask=0117 - -ExecStart=/usr/bin/janus --disable-colors --plugins-folder=/usr/lib/ustreamer/janus --configs-folder=/etc/kvmd/janus -TimeoutStopSec=10 -KillMode=mixed - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd-janus.service b/configs/os/services/kvmd-janus.service deleted file mode 100644 index ee36835b..00000000 --- a/configs/os/services/kvmd-janus.service +++ /dev/null @@ -1,22 +0,0 @@ -[Unit] -Description=One-KVM - Janus WebRTC Gateway -After=network.target network-online.target nss-lookup.target kvmd.service - -[Service] -User=kvmd-janus -Group=kvmd-janus -Type=simple -Restart=always -RestartSec=3 -AmbientCapabilities=CAP_NET_RAW -LimitNOFILE=65536 - -# Crutch for UNIX socket perms -UMask=0117 - -ExecStart=/usr/bin/kvmd-janus --run -TimeoutStopSec=10 -KillMode=mixed - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd-nginx.service b/configs/os/services/kvmd-nginx.service deleted file mode 100644 index 0189fd5f..00000000 --- a/configs/os/services/kvmd-nginx.service +++ /dev/null @@ -1,21 +0,0 @@ -[Unit] -Description=One-KVM - HTTP entrypoint -After=network.target network-online.target nss-lookup.target kvmd.service - -[Service] -Type=forking -PIDFile=/run/kvmd/nginx.pid -PrivateDevices=yes -SyslogLevel=err -Restart=always -RestartSec=3 - -ExecStartPre=/usr/bin/kvmd-nginx-mkconf /etc/kvmd/nginx/nginx.conf.mako /run/kvmd/nginx.conf -ExecStart=/usr/sbin/nginx -p /etc/kvmd/nginx -c /run/kvmd/nginx.conf -g 'pid /run/kvmd/nginx.pid; user kvmd-nginx; error_log stderr;' -ExecReload=/usr/sbin/nginx -s reload -p /etc/kvmd/nginx -c /run/kvmd/nginx.conf -g 'pid /run/kvmd/nginx.pid; user kvmd-nginx; error_log stderr;' -KillSignal=SIGQUIT -KillMode=mixed -TimeoutStopSec=3 - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd-oled-reboot.service b/configs/os/services/kvmd-oled-reboot.service deleted file mode 100644 index 23bbca15..00000000 --- a/configs/os/services/kvmd-oled-reboot.service +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=PiKVM - Display reboot message on the OLED -DefaultDependencies=no - -[Service] -Type=oneshot -ExecStart=/bin/bash -c "kill -USR1 `systemctl show -P MainPID kvmd-oled`" -ExecStop=/bin/true -RemainAfterExit=yes - -[Install] -WantedBy=reboot.target diff --git a/configs/os/services/kvmd-oled-shutdown.service b/configs/os/services/kvmd-oled-shutdown.service deleted file mode 100644 index 61d94c51..00000000 --- a/configs/os/services/kvmd-oled-shutdown.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=PiKVM - Display shutdown message on the OLED -Conflicts=reboot.target -Before=shutdown.target poweroff.target halt.target -DefaultDependencies=no - -[Service] -Type=oneshot -ExecStart=/bin/bash -c "kill -USR2 `systemctl show -P MainPID kvmd-oled`" -ExecStop=/bin/true -RemainAfterExit=yes - -[Install] -WantedBy=shutdown.target diff --git a/configs/os/services/kvmd-oled.service b/configs/os/services/kvmd-oled.service deleted file mode 100644 index ea0d4850..00000000 --- a/configs/os/services/kvmd-oled.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=PiKVM - A small OLED daemon -After=systemd-modules-load.service -ConditionPathExists=/dev/i2c-1 - -[Service] -Type=simple -Restart=always -RestartSec=3 -ExecStartPre=/usr/bin/kvmd-oled --interval=3 --clear-on-exit --image=@hello.ppm -ExecStart=/usr/bin/kvmd-oled -TimeoutStopSec=3 - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd-otg.service b/configs/os/services/kvmd-otg.service deleted file mode 100644 index c4209779..00000000 --- a/configs/os/services/kvmd-otg.service +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=One-KVM- OTG setup -After=systemd-modules-load.service -Before=kvmd.service - -[Service] -Type=oneshot -ExecStart=/usr/bin/kvmd-otg start -ExecStop=/usr/bin/kvmd-otg stop -RemainAfterExit=true - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd-otgnet.service b/configs/os/services/kvmd-otgnet.service deleted file mode 100644 index 693c9bba..00000000 --- a/configs/os/services/kvmd-otgnet.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=PiKVM - OTG network service -After=kvmd-otg.service -Wants=network-pre.target -After=network-pre.target - -[Service] -Type=oneshot -ExecStart=/usr/bin/kvmd-otgnet start -ExecStop=/usr/bin/kvmd-otgnet stop -RemainAfterExit=true - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd-pst.service b/configs/os/services/kvmd-pst.service deleted file mode 100644 index 9d77c3a0..00000000 --- a/configs/os/services/kvmd-pst.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=PiKVM - The KVMD persistent storage manager -Before=kvmd.service - -[Service] -User=kvmd-pst -Group=kvmd-pst -Type=simple -Restart=always -RestartSec=3 - -ExecStart=/usr/bin/kvmd-pst --run -TimeoutStopSec=5 - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd-tc358743.service b/configs/os/services/kvmd-tc358743.service deleted file mode 100644 index 494da726..00000000 --- a/configs/os/services/kvmd-tc358743.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=PiKVM - EDID loader for TC358743 -Wants=dev-kvmd\x2dvideo.device -After=dev-kvmd\x2dvideo.device systemd-modules-load.service -Before=kvmd.service - -[Service] -Type=oneshot -ExecStart=/usr/bin/v4l2-ctl --device=/dev/kvmd-video --set-edid=file=/etc/kvmd/tc358743-edid.hex --info-edid -ExecStop=/usr/bin/v4l2-ctl --device=/dev/kvmd-video --clear-edid -RemainAfterExit=true - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd-vnc.service b/configs/os/services/kvmd-vnc.service deleted file mode 100644 index f04b295b..00000000 --- a/configs/os/services/kvmd-vnc.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=One-KVM - VNC to KVMD/Streamer proxy -After=kvmd.service - -[Service] -User=kvmd-vnc -Group=kvmd-vnc -Type=simple -Restart=always -RestartSec=3 - -ExecStart=/usr/bin/kvmd-vnc --run -TimeoutStopSec=3 - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd-watchdog.service b/configs/os/services/kvmd-watchdog.service deleted file mode 100644 index b6f4c45e..00000000 --- a/configs/os/services/kvmd-watchdog.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=PiKVM - RTC-based hardware watchdog -After=systemd-modules-load.service - -[Service] -Type=simple -Restart=always -RestartSec=3 - -ExecStart=/usr/bin/kvmd-watchdog run -TimeoutStopSec=3 - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd-webterm.service b/configs/os/services/kvmd-webterm.service deleted file mode 100644 index ca2089c5..00000000 --- a/configs/os/services/kvmd-webterm.service +++ /dev/null @@ -1,24 +0,0 @@ -[Unit] -Description=One-KVM - Web terminal (ttyd) -After=network.target - -[Service] -User=kvmd-webterm -Group=kvmd-webterm -WorkingDirectory=/home/kvmd-webterm -Restart=always -RestartSec=1 - -# Crutch for UNIX socket perms -UMask=0117 - -EnvironmentFile=-/etc/conf.d/kvmd-webterm -ExecStart=/usr/bin/ttyd \ - -W \ - --interface=/run/kvmd/ttyd.sock \ - --port=0 \ - $KVMD_WEBTERM_ARGS \ - /bin/bash -c 'echo -ne "\033]0;One-KVM Terminal: `hostname -f` (ttyd)\007"; bash /etc/kvmd/armbain-motd; export TERM=linux; umask 0022; bash' - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/services/kvmd.service b/configs/os/services/kvmd.service deleted file mode 100644 index 65b04bb7..00000000 --- a/configs/os/services/kvmd.service +++ /dev/null @@ -1,18 +0,0 @@ -[Unit] -Description=One-KVM - The main daemon -After=network.target network-online.target nss-lookup.target - -[Service] -User=kvmd -Group=kvmd -Type=simple -Restart=always -RestartSec=3 -AmbientCapabilities=CAP_NET_RAW - -ExecStart=/usr/bin/kvmd --run -TimeoutStopSec=10 -KillMode=mixed - -[Install] -WantedBy=multi-user.target diff --git a/configs/os/sudoers/v0-hdmi b/configs/os/sudoers/v0-hdmi deleted file mode 100644 index bb549dc8..00000000 --- a/configs/os/sudoers/v0-hdmi +++ /dev/null @@ -1 +0,0 @@ -kvmd-pst ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-pst-remount diff --git a/configs/os/sudoers/v0-hdmiusb b/configs/os/sudoers/v0-hdmiusb deleted file mode 100644 index bb549dc8..00000000 --- a/configs/os/sudoers/v0-hdmiusb +++ /dev/null @@ -1 +0,0 @@ -kvmd-pst ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-pst-remount diff --git a/configs/os/sudoers/v1-hdmi b/configs/os/sudoers/v1-hdmi deleted file mode 100644 index bb549dc8..00000000 --- a/configs/os/sudoers/v1-hdmi +++ /dev/null @@ -1 +0,0 @@ -kvmd-pst ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-pst-remount diff --git a/configs/os/sudoers/v1-hdmiusb b/configs/os/sudoers/v1-hdmiusb deleted file mode 100644 index bb549dc8..00000000 --- a/configs/os/sudoers/v1-hdmiusb +++ /dev/null @@ -1 +0,0 @@ -kvmd-pst ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-pst-remount diff --git a/configs/os/sudoers/v2-hdmi b/configs/os/sudoers/v2-hdmi deleted file mode 100644 index 673d4033..00000000 --- a/configs/os/sudoers/v2-hdmi +++ /dev/null @@ -1,2 +0,0 @@ -kvmd ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-otgmsd-remount -kvmd-pst ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-pst-remount diff --git a/configs/os/sudoers/v2-hdmiusb b/configs/os/sudoers/v2-hdmiusb deleted file mode 100644 index 673d4033..00000000 --- a/configs/os/sudoers/v2-hdmiusb +++ /dev/null @@ -1,2 +0,0 @@ -kvmd ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-otgmsd-remount -kvmd-pst ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-pst-remount diff --git a/configs/os/sudoers/v3-hdmi b/configs/os/sudoers/v3-hdmi deleted file mode 100644 index 673d4033..00000000 --- a/configs/os/sudoers/v3-hdmi +++ /dev/null @@ -1,2 +0,0 @@ -kvmd ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-otgmsd-remount -kvmd-pst ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-pst-remount diff --git a/configs/os/sudoers/v4mini-hdmi b/configs/os/sudoers/v4mini-hdmi deleted file mode 100644 index 673d4033..00000000 --- a/configs/os/sudoers/v4mini-hdmi +++ /dev/null @@ -1,2 +0,0 @@ -kvmd ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-otgmsd-remount -kvmd-pst ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-pst-remount diff --git a/configs/os/sudoers/v4plus-hdmi b/configs/os/sudoers/v4plus-hdmi deleted file mode 100644 index 673d4033..00000000 --- a/configs/os/sudoers/v4plus-hdmi +++ /dev/null @@ -1,2 +0,0 @@ -kvmd ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-otgmsd-remount -kvmd-pst ALL=(ALL) NOPASSWD: /usr/bin/kvmd-helper-pst-remount diff --git a/configs/os/sysctl.conf b/configs/os/sysctl.conf deleted file mode 100644 index abe53aa6..00000000 --- a/configs/os/sysctl.conf +++ /dev/null @@ -1,10 +0,0 @@ -# Overly-aggressive data writeback policies to minimize the chance of data corruption -# Start dirty data writeback when it exceeds 256 KiB or 1s has passed -vm.dirty_background_bytes = 262144 -vm.dirty_writeback_centisecs = 100 - -# https://groups.google.com/g/meetecho-janus/c/xoWIQfaoJm8 -net.core.rmem_default = 500000 -net.core.wmem_default = 500000 -net.core.rmem_max = 1000000 -net.core.wmem_max = 1000000 diff --git a/configs/os/sysusers.conf b/configs/os/sysusers.conf deleted file mode 100644 index 0359974d..00000000 --- a/configs/os/sysusers.conf +++ /dev/null @@ -1,38 +0,0 @@ -g kvmd - - -g kvmd-pst - - -g kvmd-ipmi - - -g kvmd-vnc - - -g kvmd-nginx - - -g kvmd-janus - - -g kvmd-certbot - - - -u kvmd - "PiKVM - The main daemon" - -u kvmd-pst - "PiKVM - Persistent storage" - -u kvmd-ipmi - "PiKVM - IPMI to KVMD proxy" - -u kvmd-vnc - "PiKVM - VNC to KVMD/Streamer proxy" - -u kvmd-nginx - "PiKVM - HTTP entrypoint" - -u kvmd-janus - "PiKVM - Janus WebRTC Gateway" - -u kvmd-certbot - "PiKVM - Certbot-Renew for KVMD-Nginx" - -m kvmd video -m kvmd gpio -m kvmd uucp -m kvmd spi -m kvmd systemd-journal -m kvmd kvmd-pst - -m kvmd-pst kvmd - -m kvmd-ipmi kvmd - -m kvmd-vnc kvmd -m kvmd-vnc kvmd-certbot - -m kvmd-janus kvmd -m kvmd-janus audio - -m kvmd-nginx kvmd -m kvmd-nginx kvmd-janus -m kvmd-nginx kvmd-certbot - -m kvmd-certbot kvmd-pst diff --git a/configs/os/tmpfiles.conf b/configs/os/tmpfiles.conf deleted file mode 100644 index 973c5d43..00000000 --- a/configs/os/tmpfiles.conf +++ /dev/null @@ -1,7 +0,0 @@ -D /run/kvmd 0775 kvmd kvmd - -D /run/kvmd-certbot 0755 root root - -D /run/kvmd-certbot/webroot 0755 kvmd-certbot kvmd-certbot - - -D /tmp/kvmd 0775 kvmd kvmd - -D /tmp/kvmd-nginx 0700 kvmd-nginx root - -D /tmp/kvmd-certbot 0755 kvmd-certbot kvmd-certbot - diff --git a/configs/os/udev/common.rules b/configs/os/udev/common.rules deleted file mode 100644 index 1a0ccded..00000000 --- a/configs/os/udev/common.rules +++ /dev/null @@ -1,3 +0,0 @@ -# Here are described some bindings for PiKVM devices. -# Do not edit this file. -KERNEL=="ttyACM[0-9]*", SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="eda3", SYMLINK+="kvmd-hid-bridge" diff --git a/configs/os/udev/v0-hdmi-rpi2.rules b/configs/os/udev/v0-hdmi-rpi2.rules deleted file mode 100644 index e60aa798..00000000 --- a/configs/os/udev/v0-hdmi-rpi2.rules +++ /dev/null @@ -1,4 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="3f801000.csi|3f801000.csi1", ATTR{name}=="unicam-image", GROUP="kvmd", SYMLINK+="kvmd-video", TAG+="systemd" -KERNEL=="ttyAMA0", SYMLINK+="kvmd-hid" diff --git a/configs/os/udev/v0-hdmi-rpi3.rules b/configs/os/udev/v0-hdmi-rpi3.rules deleted file mode 100644 index e60aa798..00000000 --- a/configs/os/udev/v0-hdmi-rpi3.rules +++ /dev/null @@ -1,4 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="3f801000.csi|3f801000.csi1", ATTR{name}=="unicam-image", GROUP="kvmd", SYMLINK+="kvmd-video", TAG+="systemd" -KERNEL=="ttyAMA0", SYMLINK+="kvmd-hid" diff --git a/configs/os/udev/v0-hdmi-zero2w.rules b/configs/os/udev/v0-hdmi-zero2w.rules deleted file mode 100644 index e60aa798..00000000 --- a/configs/os/udev/v0-hdmi-zero2w.rules +++ /dev/null @@ -1,4 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="3f801000.csi|3f801000.csi1", ATTR{name}=="unicam-image", GROUP="kvmd", SYMLINK+="kvmd-video", TAG+="systemd" -KERNEL=="ttyAMA0", SYMLINK+="kvmd-hid" diff --git a/configs/os/udev/v0-hdmiusb-rpi2.rules b/configs/os/udev/v0-hdmiusb-rpi2.rules deleted file mode 100644 index 244f7ce4..00000000 --- a/configs/os/udev/v0-hdmiusb-rpi2.rules +++ /dev/null @@ -1,4 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", PROGRAM="/usr/bin/kvmd-udev-hdmiusb-check rpi2 %b", ATTR{index}=="0", GROUP="kvmd", SYMLINK+="kvmd-video" -KERNEL=="ttyAMA0", SYMLINK+="kvmd-hid" diff --git a/configs/os/udev/v0-hdmiusb-rpi3.rules b/configs/os/udev/v0-hdmiusb-rpi3.rules deleted file mode 100644 index bd96d232..00000000 --- a/configs/os/udev/v0-hdmiusb-rpi3.rules +++ /dev/null @@ -1,4 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", PROGRAM="/usr/bin/kvmd-udev-hdmiusb-check rpi3 %b", ATTR{index}=="0", GROUP="kvmd", SYMLINK+="kvmd-video" -KERNEL=="ttyAMA0", SYMLINK+="kvmd-hid" diff --git a/configs/os/udev/v0-hdmiusb-zero2w.rules b/configs/os/udev/v0-hdmiusb-zero2w.rules deleted file mode 100644 index 933f7337..00000000 --- a/configs/os/udev/v0-hdmiusb-zero2w.rules +++ /dev/null @@ -1,4 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video0", SUBSYSTEM=="video4linux", GROUP="kvmd", SYMLINK+="kvmd-video" -KERNEL=="ttyAMA0", SYMLINK+="kvmd-hid" diff --git a/configs/os/udev/v1-hdmi-rpi2.rules b/configs/os/udev/v1-hdmi-rpi2.rules deleted file mode 100644 index 49ca9ce0..00000000 --- a/configs/os/udev/v1-hdmi-rpi2.rules +++ /dev/null @@ -1,3 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="3f801000.csi|3f801000.csi1", ATTR{name}=="unicam-image", GROUP="kvmd", SYMLINK+="kvmd-video", TAG+="systemd" diff --git a/configs/os/udev/v1-hdmi-rpi3.rules b/configs/os/udev/v1-hdmi-rpi3.rules deleted file mode 100644 index 49ca9ce0..00000000 --- a/configs/os/udev/v1-hdmi-rpi3.rules +++ /dev/null @@ -1,3 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="3f801000.csi|3f801000.csi1", ATTR{name}=="unicam-image", GROUP="kvmd", SYMLINK+="kvmd-video", TAG+="systemd" diff --git a/configs/os/udev/v1-hdmi-zero2w.rules b/configs/os/udev/v1-hdmi-zero2w.rules deleted file mode 100644 index 49ca9ce0..00000000 --- a/configs/os/udev/v1-hdmi-zero2w.rules +++ /dev/null @@ -1,3 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="3f801000.csi|3f801000.csi1", ATTR{name}=="unicam-image", GROUP="kvmd", SYMLINK+="kvmd-video", TAG+="systemd" diff --git a/configs/os/udev/v1-hdmiusb-rpi2.rules b/configs/os/udev/v1-hdmiusb-rpi2.rules deleted file mode 100644 index eafd8ef8..00000000 --- a/configs/os/udev/v1-hdmiusb-rpi2.rules +++ /dev/null @@ -1,3 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", PROGRAM="/usr/bin/kvmd-udev-hdmiusb-check rpi2 %b", ATTR{index}=="0", GROUP="kvmd", SYMLINK+="kvmd-video" diff --git a/configs/os/udev/v1-hdmiusb-rpi3.rules b/configs/os/udev/v1-hdmiusb-rpi3.rules deleted file mode 100644 index f28bdfae..00000000 --- a/configs/os/udev/v1-hdmiusb-rpi3.rules +++ /dev/null @@ -1,3 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", PROGRAM="/usr/bin/kvmd-udev-hdmiusb-check rpi3 %b", ATTR{index}=="0", GROUP="kvmd", SYMLINK+="kvmd-video" diff --git a/configs/os/udev/v1-hdmiusb-zero2w.rules b/configs/os/udev/v1-hdmiusb-zero2w.rules deleted file mode 100644 index 1a2a6f7d..00000000 --- a/configs/os/udev/v1-hdmiusb-zero2w.rules +++ /dev/null @@ -1,3 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video0", SUBSYSTEM=="video4linux", GROUP="kvmd", SYMLINK+="kvmd-video" diff --git a/configs/os/udev/v2-hdmi-rpi3.rules b/configs/os/udev/v2-hdmi-rpi3.rules deleted file mode 100644 index 6c6a6d3e..00000000 --- a/configs/os/udev/v2-hdmi-rpi3.rules +++ /dev/null @@ -1,6 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="3f801000.csi|3f801000.csi1", ATTR{name}=="unicam-image", GROUP="kvmd", SYMLINK+="kvmd-video", TAG+="systemd" -KERNEL=="hidg0", GROUP="kvmd", SYMLINK+="kvmd-hid-keyboard" -KERNEL=="hidg1", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse" -KERNEL=="hidg2", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse-alt" diff --git a/configs/os/udev/v2-hdmi-rpi4.rules b/configs/os/udev/v2-hdmi-rpi4.rules deleted file mode 100644 index 88ba66b1..00000000 --- a/configs/os/udev/v2-hdmi-rpi4.rules +++ /dev/null @@ -1,6 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="fe801000.csi|fe801000.csi1", ATTR{name}=="unicam-image", GROUP="kvmd", SYMLINK+="kvmd-video", TAG+="systemd" -KERNEL=="hidg0", GROUP="kvmd", SYMLINK+="kvmd-hid-keyboard" -KERNEL=="hidg1", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse" -KERNEL=="hidg2", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse-alt" diff --git a/configs/os/udev/v2-hdmi-zero2w.rules b/configs/os/udev/v2-hdmi-zero2w.rules deleted file mode 100644 index 6c6a6d3e..00000000 --- a/configs/os/udev/v2-hdmi-zero2w.rules +++ /dev/null @@ -1,6 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="3f801000.csi|3f801000.csi1", ATTR{name}=="unicam-image", GROUP="kvmd", SYMLINK+="kvmd-video", TAG+="systemd" -KERNEL=="hidg0", GROUP="kvmd", SYMLINK+="kvmd-hid-keyboard" -KERNEL=="hidg1", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse" -KERNEL=="hidg2", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse-alt" diff --git a/configs/os/udev/v2-hdmiusb-generic.rules b/configs/os/udev/v2-hdmiusb-generic.rules deleted file mode 100644 index 0865f227..00000000 --- a/configs/os/udev/v2-hdmiusb-generic.rules +++ /dev/null @@ -1,7 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ATTR{index}=="0", GROUP="kvmd", SYMLINK+="kvmd-video" -KERNEL=="hidg0", GROUP="kvmd", SYMLINK+="kvmd-hid-keyboard" -KERNEL=="hidg1", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse" -KERNEL=="hidg2", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse-alt" -KERNEL=="ttyUSB0", GROUP="kvmd", SYMLINK+="kvmd-hid" diff --git a/configs/os/udev/v2-hdmiusb-rpi4.rules b/configs/os/udev/v2-hdmiusb-rpi4.rules deleted file mode 100644 index 2bab50be..00000000 --- a/configs/os/udev/v2-hdmiusb-rpi4.rules +++ /dev/null @@ -1,6 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", PROGRAM="/usr/bin/kvmd-udev-hdmiusb-check rpi4 %b", ATTR{index}=="0", GROUP="kvmd", SYMLINK+="kvmd-video" -KERNEL=="hidg0", GROUP="kvmd", SYMLINK+="kvmd-hid-keyboard" -KERNEL=="hidg1", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse" -KERNEL=="hidg2", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse-alt" diff --git a/configs/os/udev/v3-hdmi-rpi4.rules b/configs/os/udev/v3-hdmi-rpi4.rules deleted file mode 100644 index 88ba66b1..00000000 --- a/configs/os/udev/v3-hdmi-rpi4.rules +++ /dev/null @@ -1,6 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="fe801000.csi|fe801000.csi1", ATTR{name}=="unicam-image", GROUP="kvmd", SYMLINK+="kvmd-video", TAG+="systemd" -KERNEL=="hidg0", GROUP="kvmd", SYMLINK+="kvmd-hid-keyboard" -KERNEL=="hidg1", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse" -KERNEL=="hidg2", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse-alt" diff --git a/configs/os/udev/v4mini-hdmi-rpi4.rules b/configs/os/udev/v4mini-hdmi-rpi4.rules deleted file mode 100644 index 88ba66b1..00000000 --- a/configs/os/udev/v4mini-hdmi-rpi4.rules +++ /dev/null @@ -1,6 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="fe801000.csi|fe801000.csi1", ATTR{name}=="unicam-image", GROUP="kvmd", SYMLINK+="kvmd-video", TAG+="systemd" -KERNEL=="hidg0", GROUP="kvmd", SYMLINK+="kvmd-hid-keyboard" -KERNEL=="hidg1", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse" -KERNEL=="hidg2", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse-alt" diff --git a/configs/os/udev/v4plus-hdmi-rpi4.rules b/configs/os/udev/v4plus-hdmi-rpi4.rules deleted file mode 100644 index 87cfc700..00000000 --- a/configs/os/udev/v4plus-hdmi-rpi4.rules +++ /dev/null @@ -1,7 +0,0 @@ -# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name -# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names -KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="fe801000.csi|fe801000.csi1", ATTR{name}=="unicam-image", GROUP="kvmd", SYMLINK+="kvmd-video", TAG+="systemd" -KERNEL=="hidg0", GROUP="kvmd", SYMLINK+="kvmd-hid-keyboard" -KERNEL=="hidg1", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse" -KERNEL=="hidg2", GROUP="kvmd", SYMLINK+="kvmd-hid-mouse-alt" -SUBSYSTEM=="drm", ACTION=="change", ENV{DEVLINKS}=="/dev/dri/by-path/platform-gpu-card", RUN+="/usr/bin/kvmd-udev-restart-pass %k %E{CONNECTOR}" diff --git a/contrib/README b/contrib/README deleted file mode 100644 index 4a479412..00000000 --- a/contrib/README +++ /dev/null @@ -1,3 +0,0 @@ -* keymaps: - - https://github.com/qemu/qemu/blob/master/pc-bios/keymaps - - https://github.com/j0xaf/keymap-us-altgr-intl/blob/main/keymap-us-altgr-intl diff --git a/hid/arduino/.gitignore b/hid/arduino/.gitignore deleted file mode 100644 index 05b843bc..00000000 --- a/hid/arduino/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/.platformio/ -/.pio/ -/.current -/.vscode/ -/.config -/platformio.ini diff --git a/hid/arduino/Makefile b/hid/arduino/Makefile deleted file mode 100644 index 652ddd6e..00000000 --- a/hid/arduino/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -serial: - make _build E=serial C=avr -spi: - make _build E=spi C=avr -aum: - make _build E=aum C=avr -stm32: - platformio run --environment patch --project-conf platformio-stm32.ini - make _build E=serial C=stm32 -_build: - rm -f .current .config - platformio run --environment $(E) --project-conf platformio-$(C).ini - echo -n $(E) > .current - echo -n $(C) > .config - -# Added to easy test all builds -_build_all: aum spi serial stm32 - rm -f .current .config - -install: upload -upload: - $(eval $@_CURRENT := $(shell cat .current)) - $(eval $@_CONFIG := $(shell cat .config)) - bash -ex -c " \ - current=`cat .current`; \ - if [ '$($@_CURRENT)' == 'spi' ] || [ '$($@_CURRENT)' == 'aum' ]; then \ - gpioset 0 25=1; \ - gpioset 0 25=0; \ - fi \ - " - platformio run --environment '$($@_CURRENT)' --project-conf 'platformio-$($@_CONFIG).ini' --target upload - - -bootloader-spi: install-bootloader-spi -install-bootloader-spi: upload-bootloader-spi -upload-bootloader-spi: - platformio run --environment bootloader_spi --project-conf platformio-avr.ini --target bootloader - - -update: - platformio platform update - - -clean-all: clean - rm -rf .platformio -clean: - rm -rf .pio .current .config platformio.ini - - -help: - @ cat Makefile diff --git a/hid/arduino/avrdude-rpi.conf b/hid/arduino/avrdude-rpi.conf deleted file mode 100644 index 8a6f5460..00000000 --- a/hid/arduino/avrdude-rpi.conf +++ /dev/null @@ -1,7 +0,0 @@ -programmer - id = "rpi"; - desc = "RPi SPI programmer"; - type = "linuxspi"; - reset = 25; - baudrate = 400000; -; diff --git a/hid/arduino/avrdude.py b/hid/arduino/avrdude.py deleted file mode 100644 index 3ae227f9..00000000 --- a/hid/arduino/avrdude.py +++ /dev/null @@ -1,54 +0,0 @@ -# https://docs.platformio.org/en/latest/projectconf/advanced_scripting.html - - -from os import rename -from os import symlink -from os.path import exists -from os.path import join - -import platform - -Import("env") - - -# ===== -def _get_tool_path() -> str: - path = env.PioPlatform().get_package_dir("tool-avrdude") - assert exists(path) - return path - - -def _fix_ld_arm() -> None: - tool_path = _get_tool_path() - flag_path = join(tool_path, ".fix-ld-arm.done") - - if not exists(flag_path): - def patch(*_, **__) -> None: - symlink("/usr/lib/libtinfo.so.6", join(tool_path, "libtinfo.so.5")) - open(flag_path, "w").close() - - env.Execute(patch) - - -def _replace_to_system(new_path: str) -> None: - tool_path = _get_tool_path() - flag_path = join(tool_path, ".replace-to-system.done") - - if not exists(flag_path): - def patch(*_, **__) -> None: - old_path = join(tool_path, "avrdude") - bak_path = join(tool_path, "_avrdude_bak") - rename(old_path, bak_path) - symlink(new_path, old_path) - open(flag_path, "w").close() - - env.Execute(patch) - - -# ===== -if "arm" in platform.machine(): - _fix_ld_arm() - -_path = "/usr/bin/avrdude" -if exists(_path): - _replace_to_system(_path) diff --git a/hid/arduino/lib/drivers-avr/eeprom.h b/hid/arduino/lib/drivers-avr/eeprom.h deleted file mode 100644 index 9e727704..00000000 --- a/hid/arduino/lib/drivers-avr/eeprom.h +++ /dev/null @@ -1,40 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include - -#include "storage.h" - - -namespace DRIVERS { - struct Eeprom : public Storage { - using Storage::Storage; - - void readBlock(void *dest, const void *src, size_t size) override { - eeprom_read_block(dest, src, size); - } - - void updateBlock(const void *src, void *dest, size_t size) override { - eeprom_update_block(src, dest, size); - } - }; -} diff --git a/hid/arduino/lib/drivers-avr/factory.cpp b/hid/arduino/lib/drivers-avr/factory.cpp deleted file mode 100644 index 2fb54187..00000000 --- a/hid/arduino/lib/drivers-avr/factory.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include "usb/hid.h" -#include "ps2/hid.h" -#include "factory.h" -#include "eeprom.h" -#include "serial.h" -#include "spi.h" - -#ifndef ARDUINO_ARCH_AVR -# error "Only AVR is supported" -#endif - - -namespace DRIVERS { - Keyboard *Factory::makeKeyboard(type _type) { - switch (_type) { -# ifdef HID_WITH_USB - case USB_KEYBOARD: - return new UsbKeyboard(); -# endif - -# ifdef HID_WITH_PS2 - case PS2_KEYBOARD: - return new Ps2Keyboard(); -# endif - - default: - return new Keyboard(DUMMY); - } - } - - Mouse *Factory::makeMouse(type _type) { - switch (_type) { -# ifdef HID_WITH_USB - case USB_MOUSE_ABSOLUTE: - case USB_MOUSE_ABSOLUTE_WIN98: - return new UsbMouseAbsolute(_type); - case USB_MOUSE_RELATIVE: - return new UsbMouseRelative(); -# endif - default: - return new Mouse(DRIVERS::DUMMY); - } - } - - Storage *Factory::makeStorage(type _type) { - switch (_type) { -# ifdef HID_DYNAMIC - case NON_VOLATILE_STORAGE: - return new Eeprom(DRIVERS::NON_VOLATILE_STORAGE); -# endif - default: - return new Storage(DRIVERS::DUMMY); - } - } - - Board *Factory::makeBoard(type _type) { - switch (_type) { - default: - return new Board(DRIVERS::DUMMY); - } - } - - Connection *Factory::makeConnection(type _type) { -# ifdef CMD_SERIAL - return new Serial(); -# elif defined(CMD_SPI) - return new Spi(); -# else -# error CMD phy is not defined -# endif - } -} diff --git a/hid/arduino/lib/drivers-avr/ps2/hid.h b/hid/arduino/lib/drivers-avr/ps2/hid.h deleted file mode 100644 index 56cc321d..00000000 --- a/hid/arduino/lib/drivers-avr/ps2/hid.h +++ /dev/null @@ -1,95 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include -#include - -#include "keyboard.h" -#include "keymap.h" - -// #define HID_PS2_KBD_CLOCK_PIN 7 -// #define HID_PS2_KBD_DATA_PIN 5 - - -class Ps2Keyboard : public DRIVERS::Keyboard { - // https://wiki.osdev.org/PS/2_Keyboard - - public: - Ps2Keyboard() : DRIVERS::Keyboard(DRIVERS::PS2_KEYBOARD), _dev(HID_PS2_KBD_CLOCK_PIN, HID_PS2_KBD_DATA_PIN) {} - - void begin() override { - _dev.keyboard_init(); - } - - void periodic() override { - _dev.keyboard_handle(&_leds); - } - - void sendKey(uint8_t code, bool state) override { - Ps2KeyType ps2_type; - uint8_t ps2_code; - - keymapPs2(code, &ps2_type, &ps2_code); - if (ps2_type != PS2_KEY_TYPE_UNKNOWN) { - // Не отправлялась часть нажатий. Когда clock на нуле, комп не принимает ничего от клавы. - // Этот костыль понижает процент пропущенных нажатий. - while (digitalRead(HID_PS2_KBD_CLOCK_PIN) == 0) {}; - if (state) { - switch (ps2_type) { - case PS2_KEY_TYPE_REG: _dev.keyboard_press(ps2_code); break; - case PS2_KEY_TYPE_SPEC: _dev.keyboard_press_special(ps2_code); break; - case PS2_KEY_TYPE_PRINT: _dev.keyboard_press_printscreen(); break; - case PS2_KEY_TYPE_PAUSE: _dev.keyboard_pausebreak(); break; - case PS2_KEY_TYPE_UNKNOWN: break; - } - } else { - switch (ps2_type) { - case PS2_KEY_TYPE_REG: _dev.keyboard_release(ps2_code); break; - case PS2_KEY_TYPE_SPEC: _dev.keyboard_release_special(ps2_code); break; - case PS2_KEY_TYPE_PRINT: _dev.keyboard_release_printscreen(); break; - case PS2_KEY_TYPE_PAUSE: break; - case PS2_KEY_TYPE_UNKNOWN: break; - } - } - } - } - - bool isOffline() override { - return false; - } - - KeyboardLedsState getLeds() override { - periodic(); - KeyboardLedsState result = { - .caps = _leds & 0b00000100, - .scroll = _leds & 0b00000001, - .num = _leds & 0b00000010, - }; - return result; - } - - private: - PS2dev _dev; - uint8_t _leds = 0; -}; diff --git a/hid/arduino/lib/drivers-avr/ps2/keymap.h b/hid/arduino/lib/drivers-avr/ps2/keymap.h deleted file mode 100644 index b864c8c1..00000000 --- a/hid/arduino/lib/drivers-avr/ps2/keymap.h +++ /dev/null @@ -1,152 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - - -enum Ps2KeyType : uint8_t { - PS2_KEY_TYPE_UNKNOWN = 0, - PS2_KEY_TYPE_REG = 1, - PS2_KEY_TYPE_SPEC = 2, - PS2_KEY_TYPE_PRINT = 3, - PS2_KEY_TYPE_PAUSE = 4, -}; - - -void keymapPs2(uint8_t code, Ps2KeyType *ps2_type, uint8_t *ps2_code) { - *ps2_type = PS2_KEY_TYPE_UNKNOWN; - *ps2_code = 0; - - switch (code) { - case 1: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 28; return; // KeyA - case 2: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 50; return; // KeyB - case 3: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 33; return; // KeyC - case 4: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 35; return; // KeyD - case 5: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 36; return; // KeyE - case 6: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 43; return; // KeyF - case 7: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 52; return; // KeyG - case 8: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 51; return; // KeyH - case 9: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 67; return; // KeyI - case 10: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 59; return; // KeyJ - case 11: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 66; return; // KeyK - case 12: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 75; return; // KeyL - case 13: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 58; return; // KeyM - case 14: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 49; return; // KeyN - case 15: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 68; return; // KeyO - case 16: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 77; return; // KeyP - case 17: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 21; return; // KeyQ - case 18: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 45; return; // KeyR - case 19: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 27; return; // KeyS - case 20: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 44; return; // KeyT - case 21: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 60; return; // KeyU - case 22: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 42; return; // KeyV - case 23: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 29; return; // KeyW - case 24: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 34; return; // KeyX - case 25: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 53; return; // KeyY - case 26: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 26; return; // KeyZ - case 27: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 22; return; // Digit1 - case 28: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 30; return; // Digit2 - case 29: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 38; return; // Digit3 - case 30: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 37; return; // Digit4 - case 31: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 46; return; // Digit5 - case 32: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 54; return; // Digit6 - case 33: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 61; return; // Digit7 - case 34: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 62; return; // Digit8 - case 35: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 70; return; // Digit9 - case 36: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 69; return; // Digit0 - case 37: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 90; return; // Enter - case 38: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 118; return; // Escape - case 39: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 102; return; // Backspace - case 40: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 13; return; // Tab - case 41: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 41; return; // Space - case 42: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 78; return; // Minus - case 43: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 85; return; // Equal - case 44: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 84; return; // BracketLeft - case 45: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 91; return; // BracketRight - case 46: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 93; return; // Backslash - case 47: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 76; return; // Semicolon - case 48: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 82; return; // Quote - case 49: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 14; return; // Backquote - case 50: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 65; return; // Comma - case 51: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 73; return; // Period - case 52: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 74; return; // Slash - case 53: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 88; return; // CapsLock - case 54: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 5; return; // F1 - case 55: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 6; return; // F2 - case 56: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 4; return; // F3 - case 57: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 12; return; // F4 - case 58: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 3; return; // F5 - case 59: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 11; return; // F6 - case 60: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 131; return; // F7 - case 61: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 10; return; // F8 - case 62: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 1; return; // F9 - case 63: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 9; return; // F10 - case 64: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 120; return; // F11 - case 65: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 7; return; // F12 - case 66: *ps2_type = PS2_KEY_TYPE_PRINT; *ps2_code = 255; return; // PrintScreen - case 67: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 112; return; // Insert - case 68: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 108; return; // Home - case 69: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 125; return; // PageUp - case 70: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 113; return; // Delete - case 71: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 105; return; // End - case 72: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 122; return; // PageDown - case 73: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 116; return; // ArrowRight - case 74: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 107; return; // ArrowLeft - case 75: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 114; return; // ArrowDown - case 76: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 117; return; // ArrowUp - case 77: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 20; return; // ControlLeft - case 78: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 18; return; // ShiftLeft - case 79: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 17; return; // AltLeft - case 80: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 31; return; // MetaLeft - case 81: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 20; return; // ControlRight - case 82: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 89; return; // ShiftRight - case 83: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 17; return; // AltRight - case 84: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 39; return; // MetaRight - case 85: *ps2_type = PS2_KEY_TYPE_PAUSE; *ps2_code = 255; return; // Pause - case 86: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 126; return; // ScrollLock - case 87: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 119; return; // NumLock - case 88: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 47; return; // ContextMenu - case 89: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 74; return; // NumpadDivide - case 90: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 124; return; // NumpadMultiply - case 91: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 123; return; // NumpadSubtract - case 92: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 121; return; // NumpadAdd - case 93: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 90; return; // NumpadEnter - case 94: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 105; return; // Numpad1 - case 95: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 114; return; // Numpad2 - case 96: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 122; return; // Numpad3 - case 97: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 107; return; // Numpad4 - case 98: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 115; return; // Numpad5 - case 99: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 116; return; // Numpad6 - case 100: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 108; return; // Numpad7 - case 101: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 117; return; // Numpad8 - case 102: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 125; return; // Numpad9 - case 103: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 112; return; // Numpad0 - case 104: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 113; return; // NumpadDecimal - case 105: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 94; return; // Power - case 106: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 97; return; // IntlBackslash - case 107: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 106; return; // IntlYen - case 108: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 81; return; // IntlRo - case 109: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 19; return; // KanaMode - case 110: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 100; return; // Convert - case 111: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 103; return; // NonConvert - } -} diff --git a/hid/arduino/lib/drivers-avr/ps2/keymap.h.mako b/hid/arduino/lib/drivers-avr/ps2/keymap.h.mako deleted file mode 100644 index 8e31e44d..00000000 --- a/hid/arduino/lib/drivers-avr/ps2/keymap.h.mako +++ /dev/null @@ -1,44 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - - -enum Ps2KeyType : uint8_t { - PS2_KEY_TYPE_UNKNOWN = 0, - PS2_KEY_TYPE_REG = 1, - PS2_KEY_TYPE_SPEC = 2, - PS2_KEY_TYPE_PRINT = 3, - PS2_KEY_TYPE_PAUSE = 4, -}; - -<%! import operator %> -void keymapPs2(uint8_t code, Ps2KeyType *ps2_type, uint8_t *ps2_code) { - *ps2_type = PS2_KEY_TYPE_UNKNOWN; - *ps2_code = 0; - - switch (code) { -% for km in sorted(keymap, key=operator.attrgetter("mcu_code")): - case ${km.mcu_code}: *ps2_type = PS2_KEY_TYPE_${km.ps2_key.type.upper()}; *ps2_code = ${km.ps2_key.code}; return; // ${km.web_name} -% endfor - } -} diff --git a/hid/arduino/lib/drivers-avr/spi.cpp b/hid/arduino/lib/drivers-avr/spi.cpp deleted file mode 100644 index 9572c513..00000000 --- a/hid/arduino/lib/drivers-avr/spi.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include "spi.h" - -#ifdef CMD_SPI - - -static volatile uint8_t _spi_in[8] = {0}; -static volatile uint8_t _spi_in_index = 0; - -static volatile uint8_t _spi_out[8] = {0}; -static volatile uint8_t _spi_out_index = 0; - - -namespace DRIVERS { - void Spi::begin() { - pinMode(MISO, OUTPUT); - SPCR = (1 << SPE) | (1 << SPIE); // Slave, SPI En, IRQ En - } - - void Spi::periodic() { - if (!_spi_out[0] && _spi_in_index == 8) { - _data_cb((const uint8_t *)_spi_in, 8); - } - } - - void Spi::write(const uint8_t *data, size_t size) { - // Меджик в нулевом байте разрешает начать ответ - for (int index = 7; index >= 0; --index) { - _spi_out[index] = data[index]; - } - } -} - -ISR(SPI_STC_vect) { - uint8_t in = SPDR; - if (_spi_out[0] && _spi_out_index < 8) { - SPDR = _spi_out[_spi_out_index]; - if (!(SPSR & (1 << WCOL))) { - ++_spi_out_index; - if (_spi_out_index == 8) { - _spi_out_index = 0; - _spi_in_index = 0; - _spi_out[0] = 0; - } - } - } else { - static bool receiving = false; - if (!receiving && in != 0) { - receiving = true; - } - if (receiving && _spi_in_index < 8) { - _spi_in[_spi_in_index] = in; - ++_spi_in_index; - } - if (_spi_in_index == 8) { - receiving = false; - } - SPDR = 0; - } -} - -#endif diff --git a/hid/arduino/lib/drivers-avr/spi.h b/hid/arduino/lib/drivers-avr/spi.h deleted file mode 100644 index 742a808d..00000000 --- a/hid/arduino/lib/drivers-avr/spi.h +++ /dev/null @@ -1,40 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include - -#include "connection.h" - - -namespace DRIVERS { - struct Spi : public Connection { - Spi() : Connection(CONNECTION) {} - - void begin() override; - - void periodic() override; - - void write(const uint8_t *data, size_t size) override; - }; -} diff --git a/hid/arduino/lib/drivers-avr/usb/hid.h b/hid/arduino/lib/drivers-avr/usb/hid.h deleted file mode 100644 index ce15f427..00000000 --- a/hid/arduino/lib/drivers-avr/usb/hid.h +++ /dev/null @@ -1,230 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include -#include - -#include "keyboard.h" -#include "mouse.h" -#include "tools.h" -#include "usb-keymap.h" -#ifdef AUM -# include "aum.h" -#endif - -using namespace DRIVERS; - -// ----------------------------------------------------------------------------- -#ifdef HID_USB_CHECK_ENDPOINT -// https://github.com/arduino/ArduinoCore-avr/blob/2f67c916f6ab6193c404eebe22efe901e0f9542d/cores/arduino/USBCore.cpp#L249 -// https://sourceforge.net/p/arduinomidilib/svn/41/tree/branch/3.1/Teensy/teensy_core/usb_midi/usb_api.cpp#l103 -# ifdef AUM -# define CHECK_AUM_USB { if (!aumIsUsbConnected()) { return true; } } -# else -# define CHECK_AUM_USB -# endif -# define CLS_IS_OFFLINE(_hid) \ - bool isOffline() override { \ - CHECK_AUM_USB; \ - uint8_t ep = _hid.getPluggedEndpoint(); \ - uint8_t intr_state = SREG; \ - cli(); \ - UENUM = ep & 7; \ - bool rw_allowed = UEINTX & (1 << RWAL); \ - SREG = intr_state; \ - if (rw_allowed) { \ - return false; \ - } \ - return true; \ - } -# define CHECK_HID_EP { if (isOffline()) return; } - -#else -# define CLS_IS_OFFLINE(_hid) \ - bool isOffline() override { \ - return false; \ - } -# define CHECK_HID_EP - -#endif - - -class UsbKeyboard : public DRIVERS::Keyboard { - public: - UsbKeyboard() : DRIVERS::Keyboard(DRIVERS::USB_KEYBOARD) {} - - void begin() override { - _kbd.begin(); - } - - void periodic() override { -# ifdef HID_USB_CHECK_ENDPOINT - static unsigned long prev_ts = 0; - if (is_micros_timed_out(prev_ts, 50000)) { - static bool prev_online = true; - bool online = !isOffline(); - if (!_sent || (online && !prev_online)) { - _sendCurrent(); - } - prev_online = online; - prev_ts = micros(); - } -# endif - } - - void clear() override { - _kbd.releaseAll(); - } - - void sendKey(uint8_t code, bool state) override { - enum KeyboardKeycode usb_code = keymapUsb(code); - if (usb_code > 0) { - if (state ? _kbd.add(usb_code) : _kbd.remove(usb_code)) { - _sendCurrent(); - } - } - } - - CLS_IS_OFFLINE(_kbd) - - KeyboardLedsState getLeds() override { - uint8_t leds = _kbd.getLeds(); - KeyboardLedsState result = { - .caps = leds & LED_CAPS_LOCK, - .scroll = leds & LED_SCROLL_LOCK, - .num = leds & LED_NUM_LOCK, - }; - return result; - } - - private: - BootKeyboard_ _kbd; - bool _sent = true; - - void _sendCurrent() { -# ifdef HID_USB_CHECK_ENDPOINT - if (isOffline()) { - _sent = false; - } else { -# endif - _sent = (_kbd.send() >= 0); -# ifdef HID_USB_CHECK_ENDPOINT - } -# endif - } -}; - -#define CLS_SEND_BUTTONS \ - void sendButtons( \ - bool left_select, bool left_state, \ - bool right_select, bool right_state, \ - bool middle_select, bool middle_state, \ - bool up_select, bool up_state, \ - bool down_select, bool down_state \ - ) override { \ - if (left_select) _sendButton(MOUSE_LEFT, left_state); \ - if (right_select) _sendButton(MOUSE_RIGHT, right_state); \ - if (middle_select) _sendButton(MOUSE_MIDDLE, middle_state); \ - if (up_select) _sendButton(MOUSE_PREV, up_state); \ - if (down_select) _sendButton(MOUSE_NEXT, down_state); \ - } - -class UsbMouseAbsolute : public DRIVERS::Mouse { - public: - UsbMouseAbsolute(DRIVERS::type _type) : Mouse(_type) {} - - void begin() override { - _mouse.begin(); - _mouse.setWin98FixEnabled(getType() == DRIVERS::USB_MOUSE_ABSOLUTE_WIN98); - } - - void clear() override { - _mouse.releaseAll(); - } - - CLS_SEND_BUTTONS - - void sendMove(int x, int y) override { - CHECK_HID_EP; - _mouse.moveTo(x, y); - } - - void sendWheel(int delta_y) override { - // delta_x is not supported by hid-project now - CHECK_HID_EP; - _mouse.move(0, 0, delta_y); - } - - CLS_IS_OFFLINE(_mouse) - - private: - SingleAbsoluteMouse_ _mouse; - - void _sendButton(uint8_t button, bool state) { - CHECK_HID_EP; - if (state) _mouse.press(button); - else _mouse.release(button); - } -}; - -class UsbMouseRelative : public DRIVERS::Mouse { - public: - UsbMouseRelative() : DRIVERS::Mouse(DRIVERS::USB_MOUSE_RELATIVE) {} - - void begin() override { - _mouse.begin(); - } - - void clear() override { - _mouse.releaseAll(); - } - - CLS_SEND_BUTTONS - - void sendRelative(int x, int y) override { - CHECK_HID_EP; - _mouse.move(x, y, 0); - } - - void sendWheel(int delta_y) override { - // delta_x is not supported by hid-project now - CHECK_HID_EP; - _mouse.move(0, 0, delta_y); - } - - CLS_IS_OFFLINE(_mouse) - - private: - BootMouse_ _mouse; - - void _sendButton(uint8_t button, bool state) { - CHECK_HID_EP; - if (state) _mouse.press(button); - else _mouse.release(button); - } -}; - -#undef CLS_SEND_BUTTONS -#undef CLS_IS_OFFLINE -#undef CHECK_HID_EP diff --git a/hid/arduino/lib/drivers-stm32/README.md b/hid/arduino/lib/drivers-stm32/README.md deleted file mode 100644 index 93de3312..00000000 --- a/hid/arduino/lib/drivers-stm32/README.md +++ /dev/null @@ -1,22 +0,0 @@ -This is WIP. Use AVR version as reference. - -It was tested with bluepill. Most boards are clones. -If you have problem with USB please check https://stm32duinoforum.com/forum/wiki_subdomain/index_title_Blue_Pill.html for pull up resistor. If it still does not work check another board or cable. - -TODO: -- [x] Serial communication -- [x] USB keyboard -- [x] USB keyboard - add scroll status -- [x] USB keyboard - key sending -- [x] USB keyboard - test key mapping -- [x] Persistent storage -- [ ] SPI communication -- [ ] PS2 keyboard -- [x] USB absolute mouse -- [x] USB absolute mouse - add whele -- [x] USB relative mouse -- [x] USB relative mouse - add whele -- [ ] USB mouses - up down button -- [ ] WIN98 USB mouse -- [x] undefine SERIAL_USB -- [ ] boot keyboard diff --git a/hid/arduino/lib/drivers-stm32/backup-register.h b/hid/arduino/lib/drivers-stm32/backup-register.h deleted file mode 100644 index 6265dd04..00000000 --- a/hid/arduino/lib/drivers-stm32/backup-register.h +++ /dev/null @@ -1,53 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - -# pragma once - - -#include - -#include "storage.h" - - -namespace DRIVERS { - struct BackupRegister : public Storage { - BackupRegister() : Storage(NON_VOLATILE_STORAGE) { - _rtc.enableClockInterface(); - } - - void readBlock(void *dest, const void *src, size_t size) override { - uint8_t *dest_ = reinterpret_cast(dest); - for(size_t index = 0; index < size; ++index) { - dest_[index] = _rtc.getBackupRegister(reinterpret_cast(src) + index + 1); - } - } - - void updateBlock(const void *src, void *dest, size_t size) override { - const uint8_t *src_ = reinterpret_cast(src); - for(size_t index = 0; index < size; ++index) { - _rtc.setBackupRegister(reinterpret_cast(dest) + index + 1, src_[index]); - } - } - - private: - STM32F1_RTC _rtc; - }; -} diff --git a/hid/arduino/lib/drivers-stm32/bluepill_sch.png b/hid/arduino/lib/drivers-stm32/bluepill_sch.png deleted file mode 100644 index 956448dd..00000000 Binary files a/hid/arduino/lib/drivers-stm32/bluepill_sch.png and /dev/null differ diff --git a/hid/arduino/lib/drivers-stm32/board-stm32.h b/hid/arduino/lib/drivers-stm32/board-stm32.h deleted file mode 100644 index 97411cb2..00000000 --- a/hid/arduino/lib/drivers-stm32/board-stm32.h +++ /dev/null @@ -1,103 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "board.h" -#include - - -namespace DRIVERS { - class BoardStm32 : public Board { - public: - BoardStm32() : Board(BOARD){ - //2 sec timeout - iwdg_init(IWDG_PRE_16, 0xFFF); - pinMode(LED_BUILTIN, OUTPUT); - } - - void reset() override { - nvic_sys_reset(); - } - - void periodic() override { - iwdg_feed(); - if (is_micros_timed_out(_prev_ts, 100000)) { - switch(_state) { - case 0: - digitalWrite(LED_BUILTIN, LOW); - break; - case 2: - if(_rx_data) { - _rx_data = false; - digitalWrite(LED_BUILTIN, LOW); - } - break; - case 4: - if(_keyboard_online) { - _keyboard_online = false; - digitalWrite(LED_BUILTIN, LOW); - } - break; - case 8: - if(_mouse_online) { - _mouse_online = false; - digitalWrite(LED_BUILTIN, LOW); - } - break; - case 1: // heartbeat off - case 3: // _rx_data off - case 7: // _keyboard_online off - case 11: // _mouse_online off - digitalWrite(LED_BUILTIN, HIGH); - break; - case 19: - _state = -1; - break; - } - ++_state; - _prev_ts = micros(); - } - } - - void updateStatus(status status) override { - switch (status) { - case RX_DATA: - _rx_data = true; - break; - case KEYBOARD_ONLINE: - _keyboard_online = true; - break; - case MOUSE_ONLINE: - _mouse_online = true; - break; - } - } - - private: - unsigned long _prev_ts = 0; - uint8_t _state = 0; - bool _rx_data = false; - bool _keyboard_online = false; - bool _mouse_online = false; - }; -} diff --git a/hid/arduino/lib/drivers-stm32/factory.cpp b/hid/arduino/lib/drivers-stm32/factory.cpp deleted file mode 100644 index 9540bb7d..00000000 --- a/hid/arduino/lib/drivers-stm32/factory.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include "factory.h" -#include "usb/keyboard-stm32.h" -#include "usb/hid-wrapper-stm32.h" -#include "usb/mouse-absolute-stm32.h" -#include "usb/mouse-relative-stm32.h" -#include "backup-register.h" -#include "board-stm32.h" -#include "serial.h" - -#ifndef __STM32F1__ -# error "Only STM32F1 is supported" -#endif -#ifdef SERIAL_USB -# error "Disable random USB enumeration" -#endif - - -namespace DRIVERS { - HidWrapper _hidWrapper; - - Keyboard *Factory::makeKeyboard(type _type) { - switch (_type) { -# ifdef HID_WITH_USB - case USB_KEYBOARD: - return new UsbKeyboard(_hidWrapper); -# endif - default: - return new Keyboard(DUMMY); - } - } - - Mouse *Factory::makeMouse(type _type) { - switch(_type) { -# ifdef HID_WITH_USB - case USB_MOUSE_ABSOLUTE: - return new UsbMouseAbsolute(_hidWrapper); - case USB_MOUSE_RELATIVE: - return new UsbMouseRelative(_hidWrapper); -# endif - default: - return new Mouse(DRIVERS::DUMMY); - } - } - - Storage *Factory::makeStorage(type _type) { - switch (_type) { -# ifdef HID_DYNAMIC - case NON_VOLATILE_STORAGE: - return new BackupRegister(); -# endif - default: - return new Storage(DRIVERS::DUMMY); - } - } - - Board *Factory::makeBoard(type _type) { - switch (_type) { - case BOARD: - return new BoardStm32(); - default: - return new Board(DRIVERS::DUMMY); - } - } - - Connection *Factory::makeConnection(type _type) { -# ifdef CMD_SERIAL - return new Serial(); -# else -# error CMD phy is not defined -# endif - } -} diff --git a/hid/arduino/lib/drivers-stm32/usb/hid-wrapper-stm32.h b/hid/arduino/lib/drivers-stm32/usb/hid-wrapper-stm32.h deleted file mode 100644 index 3df0f4ea..00000000 --- a/hid/arduino/lib/drivers-stm32/usb/hid-wrapper-stm32.h +++ /dev/null @@ -1,72 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include - - -namespace DRIVERS { - class HidWrapper { - public: - void begin() { - if (_init) { - return; - } - _init = true; - - _report_descriptor_length = 0; - for (unsigned index = 0; index < _count; ++index) { - _report_descriptor_length += _descriptors_size[index]; - } - - _report_descriptor = new uint8[_report_descriptor_length]; - - size_t offset = 0; - for (unsigned index = 0; index < _count; ++index) { - memcpy(_report_descriptor + offset, _report_descriptors[index], _descriptors_size[index]); - offset += _descriptors_size[index]; - } - - usbHid.begin(_report_descriptor, _report_descriptor_length); - } - - void addReportDescriptor(const uint8_t *report_descriptor, uint16_t report_descriptor_length) { - _report_descriptors[_count] = report_descriptor; - _descriptors_size[_count] = report_descriptor_length; - ++_count; - } - - USBHID usbHid; - - private: - bool _init = false; - - static constexpr uint8_t MAX_USB_DESCRIPTORS = 2; - const uint8_t *_report_descriptors[MAX_USB_DESCRIPTORS]; - uint8_t _descriptors_size[MAX_USB_DESCRIPTORS]; - - uint8_t _count = 0; - uint8_t *_report_descriptor; - uint16_t _report_descriptor_length; - }; -} diff --git a/hid/arduino/lib/drivers-stm32/usb/keyboard-stm32.h b/hid/arduino/lib/drivers-stm32/usb/keyboard-stm32.h deleted file mode 100644 index 578e0751..00000000 --- a/hid/arduino/lib/drivers-stm32/usb/keyboard-stm32.h +++ /dev/null @@ -1,92 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include - -#include "tools.h" -#include "keyboard.h" -#include "usb-keymap.h" -#include "hid-wrapper-stm32.h" - - -namespace DRIVERS { - const uint8_t reportDescriptionKeyboard[] = { - HID_KEYBOARD_REPORT_DESCRIPTOR(), - }; - - class UsbKeyboard : public Keyboard { - public: - UsbKeyboard(HidWrapper& _hidWrapper) : Keyboard(USB_KEYBOARD), - _hidWrapper(_hidWrapper), _keyboard(_hidWrapper.usbHid) { - _hidWrapper.addReportDescriptor(reportDescriptionKeyboard, sizeof(reportDescriptionKeyboard)); - } - - void begin() override { - _hidWrapper.begin(); - _keyboard.begin(); - } - - void clear() override { - _keyboard.releaseAll(); - } - - void sendKey(uint8_t code, bool state) override { - uint16_t usb_code = keymapUsb(code); - if (usb_code == 0) { - return; - } - - // 0xE0 is a prefix from HID-Project keytable - if (usb_code >= 0xE0 && usb_code <= 0xE7) { - usb_code = usb_code - 0xE0 + 0x80; - } else { - usb_code += KEY_HID_OFFSET; - } - - if (state) { - _keyboard.press(usb_code); - } else { - _keyboard.release(usb_code); - } - } - - bool isOffline() override { - return (USBComposite == false); - } - - KeyboardLedsState getLeds() override { - uint8_t leds = _keyboard.getLEDs(); - KeyboardLedsState result = { - .caps = leds & 0b00000010, - .scroll = leds & 0b00000100, - .num = leds & 0b00000001, - }; - return result; - } - - private: - HidWrapper& _hidWrapper; - HIDKeyboard _keyboard; - }; -} diff --git a/hid/arduino/lib/drivers-stm32/usb/mouse-absolute-stm32.h b/hid/arduino/lib/drivers-stm32/usb/mouse-absolute-stm32.h deleted file mode 100644 index 4090cd4f..00000000 --- a/hid/arduino/lib/drivers-stm32/usb/mouse-absolute-stm32.h +++ /dev/null @@ -1,86 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include - -#include "mouse.h" -#include "hid-wrapper-stm32.h" - - -namespace DRIVERS { - const uint8_t reportDescriptionMouseAbsolute[] = { - HID_ABS_MOUSE_REPORT_DESCRIPTOR() - }; - - class UsbMouseAbsolute : public Mouse { - public: - UsbMouseAbsolute(HidWrapper& _hidWrapper) : Mouse(USB_MOUSE_ABSOLUTE), - _hidWrapper(_hidWrapper), _mouse(_hidWrapper.usbHid) { - _hidWrapper.addReportDescriptor(reportDescriptionMouseAbsolute, sizeof(reportDescriptionMouseAbsolute)); - } - - void begin() override { - _hidWrapper.begin(); - } - - void clear() override { - _mouse.release(0xFF); - } - - void sendButtons ( - bool left_select, bool left_state, - bool right_select, bool right_state, - bool middle_select, bool middle_state, - bool up_select, bool up_state, - bool down_select, bool down_state) override { - -# define SEND_BUTTON(x_low, x_up) { \ - if (x_low##_select) { \ - if (x_low##_state) _mouse.press(MOUSE_##x_up); \ - else _mouse.release(MOUSE_##x_up); \ - } \ - } - SEND_BUTTON(left, LEFT); - SEND_BUTTON(right, RIGHT); - SEND_BUTTON(middle, MIDDLE); -# undef SEND_BUTTON - } - - void sendMove(int x, int y) override { - _mouse.move(x, y); - } - - void sendWheel(int delta_y) override { - _mouse.move(0, 0, delta_y); - } - - bool isOffline() override { - return (USBComposite == false); - } - - private: - HidWrapper& _hidWrapper; - HIDAbsMouse _mouse; - }; -} diff --git a/hid/arduino/lib/drivers-stm32/usb/mouse-relative-stm32.h b/hid/arduino/lib/drivers-stm32/usb/mouse-relative-stm32.h deleted file mode 100644 index 943857ef..00000000 --- a/hid/arduino/lib/drivers-stm32/usb/mouse-relative-stm32.h +++ /dev/null @@ -1,86 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include - -#include "mouse.h" -#include "hid-wrapper-stm32.h" - - -namespace DRIVERS { - const uint8_t reportDescriptionMouseRelative[] = { - HID_MOUSE_REPORT_DESCRIPTOR() - }; - - class UsbMouseRelative : public Mouse { - public: - UsbMouseRelative(HidWrapper& _hidWrapper) : Mouse(USB_MOUSE_RELATIVE), - _hidWrapper(_hidWrapper), _mouse(_hidWrapper.usbHid) { - _hidWrapper.addReportDescriptor(reportDescriptionMouseRelative, sizeof(reportDescriptionMouseRelative)); - } - - void begin() override { - _hidWrapper.begin(); - } - - void clear() override { - _mouse.release(0xFF); - } - - void sendButtons ( - bool left_select, bool left_state, - bool right_select, bool right_state, - bool middle_select, bool middle_state, - bool up_select, bool up_state, - bool down_select, bool down_state) override { - -# define SEND_BUTTON(x_low, x_up) { \ - if (x_low##_select) { \ - if (x_low##_state) _mouse.press(MOUSE_##x_up); \ - else _mouse.release(MOUSE_##x_up); \ - } \ - } - SEND_BUTTON(left, LEFT); - SEND_BUTTON(right, RIGHT); - SEND_BUTTON(middle, MIDDLE); -# undef SEND_BUTTON - } - - void sendRelative(int x, int y) override { - _mouse.move(x, y); - } - - void sendWheel(int delta_y) override { - _mouse.move(0, 0, delta_y); - } - - bool isOffline() override { - return (USBComposite == false); - } - - private: - HidWrapper& _hidWrapper; - HIDMouse _mouse; - }; -} diff --git a/hid/arduino/lib/drivers/aum.h b/hid/arduino/lib/drivers/aum.h deleted file mode 100644 index b211304a..00000000 --- a/hid/arduino/lib/drivers/aum.h +++ /dev/null @@ -1,48 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include - - -inline void aumInit() { - pinModeFast(AUM_IS_USB_POWERED_PIN, INPUT); - pinModeFast(AUM_SET_USB_VBUS_PIN, OUTPUT); - pinModeFast(AUM_SET_USB_CONNECTED_PIN, OUTPUT); - digitalWriteFast(AUM_SET_USB_CONNECTED_PIN, HIGH); -} - -inline void aumProxyUsbVbus() { - bool vbus = digitalReadFast(AUM_IS_USB_POWERED_PIN); - if (digitalReadFast(AUM_SET_USB_VBUS_PIN) != vbus) { - digitalWriteFast(AUM_SET_USB_VBUS_PIN, vbus); - } -} - -inline void aumSetUsbConnected(bool connected) { - digitalWriteFast(AUM_SET_USB_CONNECTED_PIN, connected); -} - -inline bool aumIsUsbConnected() { - return digitalReadFast(AUM_SET_USB_CONNECTED_PIN); -} diff --git a/hid/arduino/lib/drivers/board.h b/hid/arduino/lib/drivers/board.h deleted file mode 100644 index 069bbb0e..00000000 --- a/hid/arduino/lib/drivers/board.h +++ /dev/null @@ -1,41 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "driver.h" - - -namespace DRIVERS { - enum status { - RX_DATA = 0, - KEYBOARD_ONLINE, - MOUSE_ONLINE, - }; - - struct Board : public Driver { - using Driver::Driver; - virtual void reset() {} - virtual void periodic() {} - virtual void updateStatus(status status) {} - }; -} diff --git a/hid/arduino/lib/drivers/connection.h b/hid/arduino/lib/drivers/connection.h deleted file mode 100644 index f0e2ca0b..00000000 --- a/hid/arduino/lib/drivers/connection.h +++ /dev/null @@ -1,54 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "driver.h" -#include "stdint.h" - - -namespace DRIVERS { - typedef void (*DataHandler)(const uint8_t *data, size_t size); - typedef void (*TimeoutHandler)(); - - struct Connection : public Driver { - using Driver::Driver; - - virtual void begin() {} - - virtual void periodic() {} - - void onTimeout(TimeoutHandler cb) { - _timeout_cb = cb; - } - - void onData(DataHandler cb) { - _data_cb = cb; - } - - virtual void write(const uint8_t *data, size_t size) = 0; - - protected: - TimeoutHandler _timeout_cb = nullptr; - DataHandler _data_cb = nullptr; - }; -} diff --git a/hid/arduino/lib/drivers/driver.h b/hid/arduino/lib/drivers/driver.h deleted file mode 100644 index 7fcb72dd..00000000 --- a/hid/arduino/lib/drivers/driver.h +++ /dev/null @@ -1,49 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include - - -namespace DRIVERS { - enum type { - DUMMY = 0, - USB_MOUSE_ABSOLUTE, - USB_MOUSE_RELATIVE, - USB_MOUSE_ABSOLUTE_WIN98, - USB_KEYBOARD, - PS2_KEYBOARD, - NON_VOLATILE_STORAGE, - BOARD, - CONNECTION, - }; - - class Driver { - public: - Driver(type _type) : _type(_type) {} - uint8_t getType() { return _type; } - - private: - type _type; - }; -} diff --git a/hid/arduino/lib/drivers/factory.h b/hid/arduino/lib/drivers/factory.h deleted file mode 100644 index 5eeba394..00000000 --- a/hid/arduino/lib/drivers/factory.h +++ /dev/null @@ -1,39 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - -#pragma once - -#include "keyboard.h" -#include "mouse.h" -#include "storage.h" -#include "board.h" -#include "connection.h" - - -namespace DRIVERS { - struct Factory { - static Keyboard *makeKeyboard(type _type); - static Mouse *makeMouse(type _type); - static Storage *makeStorage(type _type); - static Board *makeBoard(type _type); - static Connection *makeConnection(type _type); - }; -} diff --git a/hid/arduino/lib/drivers/keyboard.h b/hid/arduino/lib/drivers/keyboard.h deleted file mode 100644 index b853ba67..00000000 --- a/hid/arduino/lib/drivers/keyboard.h +++ /dev/null @@ -1,68 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include - -#include "driver.h" - - -namespace DRIVERS { - typedef struct { - bool caps; - bool scroll; - bool num; - } KeyboardLedsState; - - struct Keyboard : public Driver { - using Driver::Driver; - - virtual void begin() {} - - /** - * Release all keys - */ - virtual void clear() {} - - /** - * Sends key - * @param code ??? - * @param state true pressed, false released - */ - virtual void sendKey(uint8_t code, bool state) {} - - virtual void periodic() {} - - /** - * False if online or unknown. Otherwise true. - */ - virtual bool isOffline() { - return false; - } - - virtual KeyboardLedsState getLeds() { - KeyboardLedsState result = {0}; - return result; - } - }; -} diff --git a/hid/arduino/lib/drivers/mouse.h b/hid/arduino/lib/drivers/mouse.h deleted file mode 100644 index 1877bf1e..00000000 --- a/hid/arduino/lib/drivers/mouse.h +++ /dev/null @@ -1,51 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include - -#include "driver.h" - - -namespace DRIVERS { - struct Mouse : public Driver { - using Driver::Driver; - virtual void begin() {} - - /** - * Release all keys - */ - virtual void clear() {} - virtual void sendButtons( - bool left_select, bool left_state, - bool right_select, bool right_state, - bool middle_select, bool middle_state, - bool up_select, bool up_state, - bool down_select, bool down_state) {} - virtual void sendMove(int x, int y) {} - virtual void sendRelative(int x, int y) {} - virtual void sendWheel(int delta_y) {} - virtual bool isOffline() { return false; } - virtual void periodic() {} - }; -} diff --git a/hid/arduino/lib/drivers/serial.h b/hid/arduino/lib/drivers/serial.h deleted file mode 100644 index 60ade223..00000000 --- a/hid/arduino/lib/drivers/serial.h +++ /dev/null @@ -1,68 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#ifdef CMD_SERIAL -#include "connection.h" - - -namespace DRIVERS { -#ifdef Serial -# undef Serial -#endif - struct Serial : public Connection { - Serial() : Connection(CONNECTION) {} - - void begin() override { - CMD_SERIAL.begin(CMD_SERIAL_SPEED); - } - - void periodic() override { - if (CMD_SERIAL.available() > 0) { - _buffer[_index] = (uint8_t)CMD_SERIAL.read(); - if (_index == 7) { - _data_cb(_buffer, 8); - _index = 0; - } else { - _last = micros(); - ++_index; - } - } else if (_index > 0) { - if (is_micros_timed_out(_last, CMD_SERIAL_TIMEOUT)) { - _timeout_cb(); - _index = 0; - } - } - } - - void write(const uint8_t *data, size_t size) override { - CMD_SERIAL.write(data, size); - } - - private: - unsigned long _last = 0; - uint8_t _index = 0; - uint8_t _buffer[8]; - }; -} -#endif diff --git a/hid/arduino/lib/drivers/storage.h b/hid/arduino/lib/drivers/storage.h deleted file mode 100644 index 19a5a770..00000000 --- a/hid/arduino/lib/drivers/storage.h +++ /dev/null @@ -1,35 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "driver.h" -#include "stdlib.h" - - -namespace DRIVERS { - struct Storage : public Driver { - using Driver::Driver; - virtual void readBlock(void *dest, const void *src, size_t size) {} - virtual void updateBlock(const void *src, void *dest, size_t size) {} - }; -} diff --git a/hid/arduino/lib/drivers/tools.cpp b/hid/arduino/lib/drivers/tools.cpp deleted file mode 100644 index 3dbd8470..00000000 --- a/hid/arduino/lib/drivers/tools.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include "tools.h" - - -bool is_micros_timed_out(unsigned long start_ts, unsigned long timeout) { - unsigned long now = micros(); - return ( - (now >= start_ts && now - start_ts > timeout) - || (now < start_ts && ((unsigned long)-1) - start_ts + now > timeout) - ); -} diff --git a/hid/arduino/lib/drivers/tools.h b/hid/arduino/lib/drivers/tools.h deleted file mode 100644 index ea75e378..00000000 --- a/hid/arduino/lib/drivers/tools.h +++ /dev/null @@ -1,28 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include - - -bool is_micros_timed_out(unsigned long start_ts, unsigned long timeout); diff --git a/hid/arduino/lib/drivers/usb-keymap.h b/hid/arduino/lib/drivers/usb-keymap.h deleted file mode 100644 index 95134453..00000000 --- a/hid/arduino/lib/drivers/usb-keymap.h +++ /dev/null @@ -1,141 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - - -uint8_t keymapUsb(uint8_t code) { - switch (code) { - case 1: return 4; // KeyA - case 2: return 5; // KeyB - case 3: return 6; // KeyC - case 4: return 7; // KeyD - case 5: return 8; // KeyE - case 6: return 9; // KeyF - case 7: return 10; // KeyG - case 8: return 11; // KeyH - case 9: return 12; // KeyI - case 10: return 13; // KeyJ - case 11: return 14; // KeyK - case 12: return 15; // KeyL - case 13: return 16; // KeyM - case 14: return 17; // KeyN - case 15: return 18; // KeyO - case 16: return 19; // KeyP - case 17: return 20; // KeyQ - case 18: return 21; // KeyR - case 19: return 22; // KeyS - case 20: return 23; // KeyT - case 21: return 24; // KeyU - case 22: return 25; // KeyV - case 23: return 26; // KeyW - case 24: return 27; // KeyX - case 25: return 28; // KeyY - case 26: return 29; // KeyZ - case 27: return 30; // Digit1 - case 28: return 31; // Digit2 - case 29: return 32; // Digit3 - case 30: return 33; // Digit4 - case 31: return 34; // Digit5 - case 32: return 35; // Digit6 - case 33: return 36; // Digit7 - case 34: return 37; // Digit8 - case 35: return 38; // Digit9 - case 36: return 39; // Digit0 - case 37: return 40; // Enter - case 38: return 41; // Escape - case 39: return 42; // Backspace - case 40: return 43; // Tab - case 41: return 44; // Space - case 42: return 45; // Minus - case 43: return 46; // Equal - case 44: return 47; // BracketLeft - case 45: return 48; // BracketRight - case 46: return 49; // Backslash - case 47: return 51; // Semicolon - case 48: return 52; // Quote - case 49: return 53; // Backquote - case 50: return 54; // Comma - case 51: return 55; // Period - case 52: return 56; // Slash - case 53: return 57; // CapsLock - case 54: return 58; // F1 - case 55: return 59; // F2 - case 56: return 60; // F3 - case 57: return 61; // F4 - case 58: return 62; // F5 - case 59: return 63; // F6 - case 60: return 64; // F7 - case 61: return 65; // F8 - case 62: return 66; // F9 - case 63: return 67; // F10 - case 64: return 68; // F11 - case 65: return 69; // F12 - case 66: return 70; // PrintScreen - case 67: return 73; // Insert - case 68: return 74; // Home - case 69: return 75; // PageUp - case 70: return 76; // Delete - case 71: return 77; // End - case 72: return 78; // PageDown - case 73: return 79; // ArrowRight - case 74: return 80; // ArrowLeft - case 75: return 81; // ArrowDown - case 76: return 82; // ArrowUp - case 77: return 224; // ControlLeft - case 78: return 225; // ShiftLeft - case 79: return 226; // AltLeft - case 80: return 227; // MetaLeft - case 81: return 228; // ControlRight - case 82: return 229; // ShiftRight - case 83: return 230; // AltRight - case 84: return 231; // MetaRight - case 85: return 72; // Pause - case 86: return 71; // ScrollLock - case 87: return 83; // NumLock - case 88: return 101; // ContextMenu - case 89: return 84; // NumpadDivide - case 90: return 85; // NumpadMultiply - case 91: return 86; // NumpadSubtract - case 92: return 87; // NumpadAdd - case 93: return 88; // NumpadEnter - case 94: return 89; // Numpad1 - case 95: return 90; // Numpad2 - case 96: return 91; // Numpad3 - case 97: return 92; // Numpad4 - case 98: return 93; // Numpad5 - case 99: return 94; // Numpad6 - case 100: return 95; // Numpad7 - case 101: return 96; // Numpad8 - case 102: return 97; // Numpad9 - case 103: return 98; // Numpad0 - case 104: return 99; // NumpadDecimal - case 105: return 102; // Power - case 106: return 100; // IntlBackslash - case 107: return 137; // IntlYen - case 108: return 135; // IntlRo - case 109: return 136; // KanaMode - case 110: return 138; // Convert - case 111: return 139; // NonConvert - default: return 0; - } -} diff --git a/hid/arduino/lib/drivers/usb-keymap.h.mako b/hid/arduino/lib/drivers/usb-keymap.h.mako deleted file mode 100644 index 5a10ac45..00000000 --- a/hid/arduino/lib/drivers/usb-keymap.h.mako +++ /dev/null @@ -1,37 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -<%! import operator %> -uint8_t keymapUsb(uint8_t code) { - switch (code) { -% for km in sorted(keymap, key=operator.attrgetter("mcu_code")): - % if km.usb_key.is_modifier: - case ${km.mcu_code}: return ${km.usb_key.arduino_modifier_code}; // ${km.web_name} - % else: - case ${km.mcu_code}: return ${km.usb_key.code}; // ${km.web_name} - % endif -% endfor - default: return 0; - } -} diff --git a/hid/arduino/patch.py b/hid/arduino/patch.py deleted file mode 100644 index c7674759..00000000 --- a/hid/arduino/patch.py +++ /dev/null @@ -1,49 +0,0 @@ -# https://docs.platformio.org/en/latest/projectconf/advanced_scripting.html - - -from os.path import exists -from os.path import join -from os.path import basename - -from typing import Dict - -Import("env") - - -# ===== -def _get_pkg_path(name: str) -> str: - path = env.PioPlatform().get_package_dir(name) - assert exists(path) - return path - - -def _get_libs() -> Dict[str, str]: - return { - builder.name: builder.path - for builder in env.GetLibBuilders() - } - - -def _patch(path: str, patch_path: str) -> None: - assert exists(path) - flag_path: str = join(path, f".{basename(patch_path)}.done") - if not exists(flag_path): - # TODO check for failure - env.Execute(f"patch -p1 -d {path} < {patch_path}") - env.Execute(lambda *_, **__: open(flag_path, "w").close()) - - -# ===== -if env.GetProjectOption("platform") == "ststm32": - _patch(_get_pkg_path("framework-arduinoststm32-maple"), "patches/platformio-stm32f1-no-serial-usb.patch") -elif env.GetProjectOption("platform") == "atmelavr": - _patch(_get_pkg_path("framework-arduino-avr"), "patches/arduino-main-no-usb.patch") - _patch(_get_pkg_path("framework-arduino-avr"), "patches/arduino-optional-cdc.patch") - _patch(_get_pkg_path("framework-arduino-avr"), "patches/arduino-get-plugged-endpoint.patch") - - _libs = _get_libs() - _patch(_libs["HID-Project"], "patches/hid-shut-up.patch") - _patch(_libs["HID-Project"], "patches/hid-no-singletones.patch") - _patch(_libs["HID-Project"], "patches/hid-win98.patch") -else: - assert(False) diff --git a/hid/arduino/patches/arduino-get-plugged-endpoint.patch b/hid/arduino/patches/arduino-get-plugged-endpoint.patch deleted file mode 100644 index c32131ff..00000000 --- a/hid/arduino/patches/arduino-get-plugged-endpoint.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/cores/arduino/PluggableUSB.h 2019-05-16 15:52:01.000000000 +0300 -+++ b/cores/arduino/PluggableUSB.h 2020-11-14 20:57:30.942432544 +0300 -@@ -31,6 +31,8 @@ - numEndpoints(numEps), numInterfaces(numIfs), endpointType(epType) - { } - -+ uint8_t getPluggedEndpoint() { return pluggedEndpoint; } -+ - protected: - virtual bool setup(USBSetup& setup) = 0; - virtual int getInterface(uint8_t* interfaceCount) = 0; diff --git a/hid/arduino/patches/arduino-main-no-usb.patch b/hid/arduino/patches/arduino-main-no-usb.patch deleted file mode 100644 index ab10c9a8..00000000 --- a/hid/arduino/patches/arduino-main-no-usb.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/cores/arduino/main.cpp b/cores/arduino/main.cpp -index 434cd40..7aba76f 100644 ---- a/cores/arduino/main.cpp -+++ b/cores/arduino/main.cpp -@@ -36,15 +36,15 @@ int main(void) - - initVariant(); - --#if defined(USBCON) -- USBDevice.attach(); --#endif -+// #if defined(USBCON) -+// USBDevice.attach(); -+// #endif - - setup(); - - for (;;) { - loop(); -- if (serialEventRun) serialEventRun(); -+ // if (serialEventRun) serialEventRun(); - } - - return 0; diff --git a/hid/arduino/patches/arduino-optional-cdc.patch b/hid/arduino/patches/arduino-optional-cdc.patch deleted file mode 100644 index 08d8c078..00000000 --- a/hid/arduino/patches/arduino-optional-cdc.patch +++ /dev/null @@ -1,141 +0,0 @@ -From 8e823d276f939d79b2d323fad675fb8442a718c2 Mon Sep 17 00:00:00 2001 -From: Daniel Gibson -Date: Tue, 5 Jan 2021 13:48:43 +0100 -Subject: [PATCH] Allow disabling CDC with -DCDC_DISABLED - -https://github.com/arduino/ArduinoCore-avr/pull/383 - -Sometimes Arduino-based USB devices don't work because some hardware -(like KVM switches) gets confused by the CDC sub-devices. -This change makes it relatively easy to disable CDC at compiletime. -Disabling it of course means that the serial console won't work anymore, -so you need to use the reset button when flashing. - -CDC_DISABLED is also used in ArduinoCore-samd for the same purpose. - -based on -https://github.com/gdsports/usb-metamorph/tree/master/USBSerPassThruLine - -See also https://github.com/NicoHood/HID/issues/225 and -https://github.com/arduino/Arduino/issues/6387 and -https://forum.arduino.cc/index.php?topic=545288.msg3717028#msg3717028 ---- - cores/arduino/CDC.cpp | 8 ++++++++ - cores/arduino/USBCore.cpp | 18 +++++++++++++++++- - cores/arduino/USBDesc.h | 17 +++++++++++++++++ - 3 files changed, 42 insertions(+), 1 deletion(-) - -diff --git a/cores/arduino/CDC.cpp b/cores/arduino/CDC.cpp -index 4ff6b9b4..7d5afaab 100644 ---- a/cores/arduino/CDC.cpp -+++ b/cores/arduino/CDC.cpp -@@ -22,6 +22,13 @@ - - #if defined(USBCON) - -+#ifndef CDC_ENABLED -+ -+#warning "! Disabled serial console via USB (CDC)!" -+#warning "! With this change you'll have to use the Arduino's reset button/pin to flash (upload)!" -+ -+#else // CDC not disabled -+ - typedef struct - { - u32 dwDTERate; -@@ -299,4 +306,5 @@ int32_t Serial_::readBreak() { - - Serial_ Serial; - -+#endif /* if defined(CDC_ENABLED) */ - #endif /* if defined(USBCON) */ -diff --git a/cores/arduino/USBCore.cpp b/cores/arduino/USBCore.cpp -index dc6bc387..93352387 100644 ---- a/cores/arduino/USBCore.cpp -+++ b/cores/arduino/USBCore.cpp -@@ -69,8 +69,18 @@ const u8 STRING_MANUFACTURER[] PROGMEM = USB_MANUFACTURER; - #define DEVICE_CLASS 0x02 - - // DEVICE DESCRIPTOR -+ -+#ifdef CDC_ENABLED - const DeviceDescriptor USB_DeviceDescriptorIAD = - D_DEVICE(0xEF,0x02,0x01,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,ISERIAL,1); -+#else // CDC_DISABLED -+// The default descriptor uses USB class OxEF, subclass 0x02 with protocol 1 -+// which means "Interface Association Descriptor" - that's needed for the CDC, -+// but doesn't make much sense as a default for custom devices when CDC is disabled. -+// (0x00 means "Use class information in the Interface Descriptors" which should be generally ok) -+const DeviceDescriptor USB_DeviceDescriptorIAD = -+ D_DEVICE(0x00,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,ISERIAL,1); -+#endif - - //================================================================== - //================================================================== -@@ -328,10 +338,12 @@ int USB_Send(u8 ep, const void* d, int len) - u8 _initEndpoints[USB_ENDPOINTS] = - { - 0, // Control Endpoint -- -+ -+#ifdef CDC_ENABLED - EP_TYPE_INTERRUPT_IN, // CDC_ENDPOINT_ACM - EP_TYPE_BULK_OUT, // CDC_ENDPOINT_OUT - EP_TYPE_BULK_IN, // CDC_ENDPOINT_IN -+#endif - - // Following endpoints are automatically initialized to 0 - }; -@@ -373,10 +385,12 @@ void InitEndpoints() - static - bool ClassInterfaceRequest(USBSetup& setup) - { -+#ifdef CDC_ENABLED - u8 i = setup.wIndex; - - if (CDC_ACM_INTERFACE == i) - return CDC_Setup(setup); -+#endif - - #ifdef PLUGGABLE_USB_ENABLED - return PluggableUSB().setup(setup); -@@ -466,7 +480,9 @@ static u8 SendInterfaces() - { - u8 interfaces = 0; - -+#ifdef CDC_ENABLED - CDC_GetInterface(&interfaces); -+#endif - - #ifdef PLUGGABLE_USB_ENABLED - PluggableUSB().getInterface(&interfaces); -diff --git a/cores/arduino/USBDesc.h b/cores/arduino/USBDesc.h -index c0dce079..b55ac20b 100644 ---- a/cores/arduino/USBDesc.h -+++ b/cores/arduino/USBDesc.h -@@ -26,8 +26,25 @@ - - #define ISERIAL_MAX_LEN 20 - -+// Uncomment the following line or pass -DCDC_DISABLED to the compiler -+// to disable CDC (serial console via USB). -+// That's useful if you want to create an USB device (like an USB Boot Keyboard) -+// that works even with problematic devices (like KVM switches). -+// Keep in mind that with this change you'll have to use the Arduino's -+// reset button to be able to flash it. -+//#define CDC_DISABLED -+ -+#ifndef CDC_DISABLED -+#define CDC_ENABLED -+#endif -+ -+#ifdef CDC_ENABLED - #define CDC_INTERFACE_COUNT 2 - #define CDC_ENPOINT_COUNT 3 -+#else // CDC_DISABLED -+#define CDC_INTERFACE_COUNT 0 -+#define CDC_ENPOINT_COUNT 0 -+#endif - - #define CDC_ACM_INTERFACE 0 // CDC ACM - #define CDC_DATA_INTERFACE 1 // CDC Data diff --git a/hid/arduino/patches/hid-no-singletones.patch b/hid/arduino/patches/hid-no-singletones.patch deleted file mode 100644 index 85298448..00000000 --- a/hid/arduino/patches/hid-no-singletones.patch +++ /dev/null @@ -1,66 +0,0 @@ -diff -u -r a/src/SingleReport/BootKeyboard.cpp b/src/SingleReport/BootKeyboard.cpp ---- a/src/SingleReport/BootKeyboard.cpp 2019-07-13 21:16:23.000000000 +0300 -+++ b/src/SingleReport/BootKeyboard.cpp 2020-11-17 18:59:36.618815374 +0300 -@@ -206,6 +206,6 @@ - } - - --BootKeyboard_ BootKeyboard; -+//BootKeyboard_ BootKeyboard; - - -diff -u -r a/src/SingleReport/BootKeyboard.h b/src/SingleReport/BootKeyboard.h ---- a/src/SingleReport/BootKeyboard.h 2019-07-13 21:16:23.000000000 +0300 -+++ b/src/SingleReport/BootKeyboard.h 2020-11-17 19:00:54.967113649 +0300 -@@ -80,6 +80,6 @@ - uint8_t* featureReport; - int featureLength; - }; --extern BootKeyboard_ BootKeyboard; -+//extern BootKeyboard_ BootKeyboard; - - -diff -u -r a/src/SingleReport/BootMouse.cpp b/src/SingleReport/BootMouse.cpp ---- a/src/SingleReport/BootMouse.cpp 2019-07-13 21:16:23.000000000 +0300 -+++ b/src/SingleReport/BootMouse.cpp 2020-11-17 18:59:22.859113905 +0300 -@@ -139,6 +139,6 @@ - } - } - --BootMouse_ BootMouse; -+//BootMouse_ BootMouse; - - -diff -u -r a/src/SingleReport/BootMouse.h b/src/SingleReport/BootMouse.h ---- a/src/SingleReport/BootMouse.h 2019-07-13 21:16:23.000000000 +0300 -+++ b/src/SingleReport/BootMouse.h 2020-11-17 19:01:04.076915591 +0300 -@@ -48,6 +48,6 @@ - - virtual void SendReport(void* data, int length) override; - }; --extern BootMouse_ BootMouse; -+//extern BootMouse_ BootMouse; - - -diff -u -r a/src/SingleReport/SingleAbsoluteMouse.cpp b/src/SingleReport/SingleAbsoluteMouse.cpp ---- a/src/SingleReport/SingleAbsoluteMouse.cpp 2020-11-17 18:39:35.314843889 +0300 -+++ b/src/SingleReport/SingleAbsoluteMouse.cpp 2020-11-17 18:59:12.189345326 +0300 -@@ -139,6 +139,6 @@ - USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, length); - } - --SingleAbsoluteMouse_ SingleAbsoluteMouse; -+//SingleAbsoluteMouse_ SingleAbsoluteMouse; - - -diff -u -r a/src/SingleReport/SingleAbsoluteMouse.h b/src/SingleReport/SingleAbsoluteMouse.h ---- a/src/SingleReport/SingleAbsoluteMouse.h 2019-07-13 21:16:23.000000000 +0300 -+++ b/src/SingleReport/SingleAbsoluteMouse.h 2020-11-17 19:01:21.356539808 +0300 -@@ -49,6 +49,6 @@ - - virtual inline void SendReport(void* data, int length) override; - }; --extern SingleAbsoluteMouse_ SingleAbsoluteMouse; -+//extern SingleAbsoluteMouse_ SingleAbsoluteMouse; - - diff --git a/hid/arduino/patches/hid-shut-up.patch b/hid/arduino/patches/hid-shut-up.patch deleted file mode 100644 index 1de9defb..00000000 --- a/hid/arduino/patches/hid-shut-up.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/src/KeyboardLayouts/ImprovedKeylayouts.h b/src/KeyboardLayouts/ImprovedKeylayouts.h -index 03b92a0..ee0bab4 100644 ---- a/src/KeyboardLayouts/ImprovedKeylayouts.h -+++ b/src/KeyboardLayouts/ImprovedKeylayouts.h -@@ -51,9 +51,9 @@ enum KeyboardLeds : uint8_t { - #ifndef HID_CUSTOM_LAYOUT - #define HID_CUSTOM_LAYOUT - #define LAYOUT_US_ENGLISH -- #pragma message "Using default ASCII layout for keyboard modules" -+ //#pragma message "Using default ASCII layout for keyboard modules" - #else -- #pragma message "Using custom layout for keyboard modules" -+ //#pragma message "Using custom layout for keyboard modules" - #endif - - // Hut1_12v2.pdf diff --git a/hid/arduino/patches/hid-win98.patch b/hid/arduino/patches/hid-win98.patch deleted file mode 100644 index 29670720..00000000 --- a/hid/arduino/patches/hid-win98.patch +++ /dev/null @@ -1,82 +0,0 @@ -From 82c8aee0ae3739eb22ae11b8f527d0c50b0ced31 Mon Sep 17 00:00:00 2001 -From: Maxim Devaev -Date: Sat, 14 Aug 2021 10:10:06 +0300 -Subject: [PATCH] Fixed absolute mouse positioning in Windows 98 - -Windows 98 contains a buggy HID driver. It allows you to move only -on the upper right quarter of the screen. Yes, this is indeed exceeding -the maximum value from the HID report. Yes, it really should work that way. - -VirtualBox has a similar fix, but with a shift in the other direction -(I didn't dig the details). I suppose it can be fixed somehow with a special -HID descriptor, but the proposed fix is simpler and really works. - -Related: https://github.com/pikvm/pikvm/issues/159 ---- - src/HID-APIs/AbsoluteMouseAPI.h | 3 +++ - src/HID-APIs/AbsoluteMouseAPI.hpp | 17 ++++++++++++++++- - 2 files changed, 19 insertions(+), 1 deletion(-) - -diff --git a/src/HID-APIs/AbsoluteMouseAPI.h b/src/HID-APIs/AbsoluteMouseAPI.h -index 66dbe42..c99e6a5 100644 ---- a/src/HID-APIs/AbsoluteMouseAPI.h -+++ b/src/HID-APIs/AbsoluteMouseAPI.h -@@ -57,6 +57,7 @@ class AbsoluteMouseAPI - int16_t xAxis; - int16_t yAxis; - uint8_t _buttons; -+ bool win98_fix; - inline void buttons(uint8_t b); - - inline int16_t qadd16(int16_t base, int16_t increment); -@@ -65,6 +66,8 @@ class AbsoluteMouseAPI - inline AbsoluteMouseAPI(void); - inline void begin(void); - inline void end(void); -+ inline void setWin98FixEnabled(bool enabled); -+ inline bool isWin98FixEnabled(void); - - inline void click(uint8_t b = MOUSE_LEFT); - inline void moveTo(int x, int y, signed char wheel = 0); -diff --git a/src/HID-APIs/AbsoluteMouseAPI.hpp b/src/HID-APIs/AbsoluteMouseAPI.hpp -index 0913c8a..0b9aaa2 100644 ---- a/src/HID-APIs/AbsoluteMouseAPI.hpp -+++ b/src/HID-APIs/AbsoluteMouseAPI.hpp -@@ -51,7 +51,7 @@ int16_t AbsoluteMouseAPI::qadd16(int16_t base, int16_t increment) { - } - - AbsoluteMouseAPI::AbsoluteMouseAPI(void): --xAxis(0), yAxis(0), _buttons(0) -+xAxis(0), yAxis(0), _buttons(0), win98_fix(false) - { - // Empty - } -@@ -66,6 +66,14 @@ void AbsoluteMouseAPI::end(void){ - moveTo(xAxis, yAxis, 0); - } - -+void AbsoluteMouseAPI::setWin98FixEnabled(bool enabled){ -+ win98_fix = enabled; -+} -+ -+bool AbsoluteMouseAPI::isWin98FixEnabled(void){ -+ return win98_fix; -+} -+ - void AbsoluteMouseAPI::click(uint8_t b){ - _buttons = b; - moveTo(xAxis, yAxis, 0); -@@ -84,6 +92,13 @@ void AbsoluteMouseAPI::moveTo(int x, int y, signed char wheel){ - // See detauls in AbsoluteMouse sources and here: https://github.com/NicoHood/HID/pull/306 - report.xAxis = ((int32_t)x + 32768) / 2; - report.yAxis = ((int32_t)y + 32768) / 2; -+ if (win98_fix) { -+ // Windows 98 contains a buggy driver. -+ // Yes, this is indeed exceeding the maximum value from the HID report. -+ // Yes, it really should work that way. -+ report.xAxis <<= 1; -+ report.yAxis <<= 1; -+ } - report.wheel = wheel; - SendReport(&report, sizeof(report)); - } diff --git a/hid/arduino/patches/platformio-stm32f1-no-serial-usb.patch b/hid/arduino/patches/platformio-stm32f1-no-serial-usb.patch deleted file mode 100644 index 547db168..00000000 --- a/hid/arduino/patches/platformio-stm32f1-no-serial-usb.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- aaa/tools/platformio-build-stm32f1.py 2022-07-16 18:54:42.536695468 +0200 -+++ bbb/tools/platformio-build-stm32f1.py 2022-07-16 19:03:10.988056751 +0200 -@@ -121,7 +121,7 @@ - env.Append( - CPPDEFINES=[ - ("CONFIG_MAPLE_MINI_NO_DISABLE_DEBUG", 1), -- "SERIAL_USB" -+ # "SERIAL_USB" - ]) - - is_generic = board.startswith("generic") or board == "hytiny_stm32f103t" diff --git a/hid/arduino/platformio-avr.ini b/hid/arduino/platformio-avr.ini deleted file mode 100644 index 8484ef50..00000000 --- a/hid/arduino/platformio-avr.ini +++ /dev/null @@ -1,119 +0,0 @@ -# http://docs.platformio.org/page/projectconf.html -[platformio] -core_dir = ./.platformio/ - -[env] -platform = atmelavr -board = micro -framework = arduino -lib_deps = - git+https://github.com/NicoHood/HID#2.8.2 - git+https://github.com/Harvie/ps2dev#v0.0.3 - digitalWriteFast@1.0.0 - HID@1.0 - drivers-avr -extra_scripts = - pre:avrdude.py - post:patch.py -platform_packages = - tool-avrdude - -[_common] -build_flags = - -DHID_USB_CHECK_ENDPOINT -# ----- The default config with dynamic switching ----- - -DHID_DYNAMIC - -DHID_WITH_USB - -DHID_SET_USB_KBD - -DHID_SET_USB_MOUSE_ABS -# ----- The USB ABS fix for Windows 98 (https://github.com/pikvm/pikvm/issues/159) ----- -# -DHID_WITH_USB_WIN98 -# ----- PS2 keyboard only ----- -# -DHID_WITH_PS2 -# -DHID_SET_PS2_KBD -# ----- PS2 keyboard + USB absolute mouse ----- -# -DHID_WITH_USB -# -DHID_WITH_PS2 -# -DHID_SET_PS2_KBD -# -DHID_SET_USB_MOUSE_ABS -# ----- PS2 keyboard + USB relative mouse ----- -# -DHID_WITH_USB -# -DHID_WITH_PS2 -# -DHID_SET_PS2_KBD -# -DHID_SET_USB_MOUSE_REL - -[_non_aum_pinout] = -build_flags = - -DHID_PS2_KBD_CLOCK_PIN=7 - -DHID_PS2_KBD_DATA_PIN=5 - - -# ===== Serial ===== -[env:serial] -extends = - _common - _non_aum_pinout -build_flags = - ${_common.build_flags} - ${_non_aum_pinout.build_flags} - -DCMD_SERIAL=Serial1 - -DCMD_SERIAL_SPEED=115200 - -DCMD_SERIAL_TIMEOUT=100000 -upload_port = /dev/ttyACM0 - - -# ===== RPi SPI ===== -[env:bootloader_spi] -upload_protocol = rpi -upload_flags = - -C - +avrdude-rpi.conf - -P - /dev/spidev0.0:/dev/gpiochip0 -extra_scripts = - pre:avrdude.py - -[_common_spi] -extends = - _common -build_flags = - ${_common.build_flags} - -DCMD_SPI - -DCDC_DISABLED -upload_protocol = custom -upload_flags = - -C - $PROJECT_PACKAGES_DIR/tool-avrdude/avrdude.conf - -C - +avrdude-rpi.conf - -P - /dev/spidev0.0:/dev/gpiochip0 - -c - rpi - -p - $BOARD_MCU -upload_command = avrdude $UPLOAD_FLAGS -U flash:w:$SOURCE:i - -[env:spi] -extends = - _common_spi - _non_aum_pinout -build_flags = - ${_common_spi.build_flags} - ${_non_aum_pinout.build_flags} - -[env:aum] -extends = - _common_spi -build_flags = - ${_common_spi.build_flags} - -DAUM - -DAUM_IS_USB_POWERED_PIN=A4 - -DAUM_SET_USB_VBUS_PIN=11 - -DAUM_SET_USB_CONNECTED_PIN=A5 - -DHID_PS2_KBD_VBUS_PIN=8 - -DHID_PS2_KBD_CLOCK_PIN=10 - -DHID_PS2_KBD_DATA_PIN=5 - -DHID_PS2_MOUSE_VBUS_PIN=6 - -DHID_PS2_MOUSE_CLOCK_PIN=9 - -DHID_PS2_MOUSE_DATA_PIN=13 diff --git a/hid/arduino/platformio-stm32.ini b/hid/arduino/platformio-stm32.ini deleted file mode 100644 index 94fd44d4..00000000 --- a/hid/arduino/platformio-stm32.ini +++ /dev/null @@ -1,52 +0,0 @@ -# http://docs.platformio.org/page/projectconf.html -[platformio] -core_dir = ./.platformio/ - -[env] -framework = arduino -platform = ststm32 -board = genericSTM32F103C8 -board_build.core = maple -extra_scripts = - post:patch.py - -[_common] -lib_deps = - git+https://github.com/ZulNs/STM32F1_RTC#v1.1.0 - git+https://github.com/arpruss/USBComposite_stm32f1#3c58f97eb006ee9cd1fb4fd55ac4faeeaead0974 - drivers-stm32 -build_flags = -# ----- The default config with dynamic switching ----- - -DHID_DYNAMIC - -DHID_WITH_USB - -DHID_SET_USB_KBD - -DHID_SET_USB_MOUSE_ABS - -[_serial] -extends = - _common -build_flags = - ${_common.build_flags} - -DCMD_SERIAL=Serial1 - -DCMD_SERIAL_SPEED=115200 - -DCMD_SERIAL_TIMEOUT=100000 -# ===== Serial ===== -[env:serial] -extends = - _serial -upload_flags = -c set CPUTAPID 0x2ba01477 -debug_tool= stlink -debug_build_flags = -Og -ggdb3 -g3 -debug_server = - .platformio/packages/tool-openocd/bin/openocd - -s .platformio/packages/tool-openocd/scripts - -f interface/stlink.cfg - -c "transport select hla_swd" - -c "set CPUTAPID 0x2ba01477" - -f target/stm32f1x.cfg - -c "reset_config none" -; build_type = debug -[env:patch] -; platformio-stm32f1-no-serial-usb.patch requires to running build again -; fake target was added to avoid error during first build -src_filter = - diff --git a/hid/arduino/src/main.cpp b/hid/arduino/src/main.cpp deleted file mode 100644 index aa880b10..00000000 --- a/hid/arduino/src/main.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include - -#include "tools.h" -#include "proto.h" -#include "board.h" -#include "outputs.h" -#ifdef AUM -# include "aum.h" -#endif - - -static DRIVERS::Connection *_conn; -static DRIVERS::Board *_board; -static Outputs _out; - -#ifdef HID_DYNAMIC -# define RESET_TIMEOUT 500000 -static bool _reset_required = false; -static unsigned long _reset_timestamp; -#endif - - -// ----------------------------------------------------------------------------- -#ifdef HID_DYNAMIC -static void _resetRequest() { - _reset_required = true; - _reset_timestamp = micros(); -} -#endif - -static void _cmdSetKeyboard(const uint8_t *data) { // 1 bytes -# ifdef HID_DYNAMIC - _out.writeOutputs(PROTO::OUTPUTS1::KEYBOARD::MASK, data[0], false); - _resetRequest(); -# endif -} - -static void _cmdSetMouse(const uint8_t *data) { // 1 bytes -# ifdef HID_DYNAMIC - _out.writeOutputs(PROTO::OUTPUTS1::MOUSE::MASK, data[0], false); - _resetRequest(); -# endif -} - -static void _cmdSetConnected(const uint8_t *data) { // 1 byte -# ifdef AUM - aumSetUsbConnected(data[0]); -# endif -} - -static void _cmdClearHid(const uint8_t *_) { // 0 bytes - _out.kbd->clear(); - _out.mouse->clear(); -} - -static void _cmdKeyEvent(const uint8_t *data) { // 2 bytes - _out.kbd->sendKey(data[0], data[1]); -} - -static void _cmdMouseButtonEvent(const uint8_t *data) { // 2 bytes -# define MOUSE_PAIR(_state, _button) \ - _state & PROTO::CMD::MOUSE::_button::SELECT, \ - _state & PROTO::CMD::MOUSE::_button::STATE - _out.mouse->sendButtons( - MOUSE_PAIR(data[0], LEFT), - MOUSE_PAIR(data[0], RIGHT), - MOUSE_PAIR(data[0], MIDDLE), - MOUSE_PAIR(data[1], EXTRA_UP), - MOUSE_PAIR(data[1], EXTRA_DOWN) - ); -# undef MOUSE_PAIR -} - -static void _cmdMouseMoveEvent(const uint8_t *data) { // 4 bytes - // See /kvmd/apps/otg/hid/keyboard.py for details - _out.mouse->sendMove( - PROTO::merge8_int(data[0], data[1]), - PROTO::merge8_int(data[2], data[3]) - ); -} - -static void _cmdMouseRelativeEvent(const uint8_t *data) { // 2 bytes - _out.mouse->sendRelative(data[0], data[1]); -} - -static void _cmdMouseWheelEvent(const uint8_t *data) { // 2 bytes - // Y only, X is not supported - _out.mouse->sendWheel(data[1]); -} - -static uint8_t _handleRequest(const uint8_t *data) { // 8 bytes - _board->updateStatus(DRIVERS::RX_DATA); - // FIXME: See kvmd/kvmd#80 - // Should input buffer be cleared in this case? - if (data[0] == PROTO::MAGIC && PROTO::crc16(data, 6) == PROTO::merge8(data[6], data[7])) { -# define HANDLE(_handler) { _handler(data + 2); return PROTO::PONG::OK; } - switch (data[1]) { - case PROTO::CMD::PING: return PROTO::PONG::OK; - case PROTO::CMD::SET_KEYBOARD: HANDLE(_cmdSetKeyboard); - case PROTO::CMD::SET_MOUSE: HANDLE(_cmdSetMouse); - case PROTO::CMD::SET_CONNECTED: HANDLE(_cmdSetConnected); - case PROTO::CMD::CLEAR_HID: HANDLE(_cmdClearHid); - case PROTO::CMD::KEYBOARD::KEY: HANDLE(_cmdKeyEvent); - case PROTO::CMD::MOUSE::BUTTON: HANDLE(_cmdMouseButtonEvent); - case PROTO::CMD::MOUSE::MOVE: HANDLE(_cmdMouseMoveEvent); - case PROTO::CMD::MOUSE::RELATIVE: HANDLE(_cmdMouseRelativeEvent); - case PROTO::CMD::MOUSE::WHEEL: HANDLE(_cmdMouseWheelEvent); - case PROTO::CMD::REPEAT: return 0; - default: return PROTO::RESP::INVALID_ERROR; - } -# undef HANDLE - } - return PROTO::RESP::CRC_ERROR; -} - - -// ----------------------------------------------------------------------------- -static void _sendResponse(uint8_t code) { - static uint8_t prev_code = PROTO::RESP::NONE; - if (code == 0) { - code = prev_code; // Repeat the last code - } else { - prev_code = code; - } - - uint8_t response[8] = {0}; - response[0] = PROTO::MAGIC_RESP; - if (code & PROTO::PONG::OK) { - response[1] = PROTO::PONG::OK; -# ifdef HID_DYNAMIC - if (_reset_required) { - response[1] |= PROTO::PONG::RESET_REQUIRED; - if (is_micros_timed_out(_reset_timestamp, RESET_TIMEOUT)) { - _board->reset(); - } - } - response[2] = PROTO::OUTPUTS1::DYNAMIC; -# endif - if (_out.kbd->getType() != DRIVERS::DUMMY) { - if(_out.kbd->isOffline()) { - response[1] |= PROTO::PONG::KEYBOARD_OFFLINE; - } else { - _board->updateStatus(DRIVERS::KEYBOARD_ONLINE); - } - DRIVERS::KeyboardLedsState leds = _out.kbd->getLeds(); - response[1] |= (leds.caps ? PROTO::PONG::CAPS : 0); - response[1] |= (leds.num ? PROTO::PONG::NUM : 0); - response[1] |= (leds.scroll ? PROTO::PONG::SCROLL : 0); - switch (_out.kbd->getType()) { - case DRIVERS::USB_KEYBOARD: - response[2] |= PROTO::OUTPUTS1::KEYBOARD::USB; - break; - case DRIVERS::PS2_KEYBOARD: - response[2] |= PROTO::OUTPUTS1::KEYBOARD::PS2; - break; - } - } - if (_out.mouse->getType() != DRIVERS::DUMMY) { - if(_out.mouse->isOffline()) { - response[1] |= PROTO::PONG::MOUSE_OFFLINE; - } else { - _board->updateStatus(DRIVERS::MOUSE_ONLINE); - } - switch (_out.mouse->getType()) { - case DRIVERS::USB_MOUSE_ABSOLUTE_WIN98: - response[2] |= PROTO::OUTPUTS1::MOUSE::USB_WIN98; - break; - case DRIVERS::USB_MOUSE_ABSOLUTE: - response[2] |= PROTO::OUTPUTS1::MOUSE::USB_ABS; - break; - case DRIVERS::USB_MOUSE_RELATIVE: - response[2] |= PROTO::OUTPUTS1::MOUSE::USB_REL; - break; - } - } // TODO: ps2 -# ifdef AUM - response[3] |= PROTO::OUTPUTS2::CONNECTABLE; - if (aumIsUsbConnected()) { - response[3] |= PROTO::OUTPUTS2::CONNECTED; - } -# endif -# ifdef HID_WITH_USB - response[3] |= PROTO::OUTPUTS2::HAS_USB; -# ifdef HID_WITH_USB_WIN98 - response[3] |= PROTO::OUTPUTS2::HAS_USB_WIN98; -# endif -# endif -# ifdef HID_WITH_PS2 - response[3] |= PROTO::OUTPUTS2::HAS_PS2; -# endif - } else { - response[1] = code; - } - PROTO::split16(PROTO::crc16(response, 6), &response[6], &response[7]); - - _conn->write(response, 8); -} - -static void _onTimeout() { - _sendResponse(PROTO::RESP::TIMEOUT_ERROR); -} - -static void _onData(const uint8_t *data, size_t size) { - _sendResponse(_handleRequest(data)); -} - -void setup() { - _out.initOutputs(); - -# ifdef AUM - aumInit(); -# endif - - _conn = DRIVERS::Factory::makeConnection(DRIVERS::CONNECTION); - _conn->onTimeout(_onTimeout); - _conn->onData(_onData); - _conn->begin(); - - _board = DRIVERS::Factory::makeBoard(DRIVERS::BOARD); -} - -void loop() { -# ifdef AUM - aumProxyUsbVbus(); -# endif - - _out.kbd->periodic(); - _out.mouse->periodic(); - _board->periodic(); - _conn->periodic(); -} diff --git a/hid/arduino/src/outputs.h b/hid/arduino/src/outputs.h deleted file mode 100644 index b1992fc4..00000000 --- a/hid/arduino/src/outputs.h +++ /dev/null @@ -1,122 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include "factory.h" -#include "proto.h" - - -class Outputs { - public: - void writeOutputs(uint8_t mask, uint8_t outputs, bool force) { - int old = 0; - if (!force) { - old = _readOutputs(); - if (old < 0) { - old = 0; - } - } - uint8_t data[8] = {0}; - data[0] = PROTO::MAGIC; - data[1] = (old & ~mask) | outputs; - PROTO::split16(PROTO::crc16(data, 6), &data[6], &data[7]); - _storage->updateBlock(data, 0, 8); - } - - void initOutputs() { -# ifdef HID_DYNAMIC - _storage = DRIVERS::Factory::makeStorage(DRIVERS::NON_VOLATILE_STORAGE); -# else - _storage = DRIVERS::Factory::makeStorage(DRIVERS::DUMMY); -# endif - - int outputs = _readOutputs(); - if (outputs < 0) { - outputs = 0; -# if defined(HID_WITH_USB) && defined(HID_SET_USB_KBD) - outputs |= PROTO::OUTPUTS1::KEYBOARD::USB; -# elif defined(HID_WITH_PS2) && defined(HID_SET_PS2_KBD) - outputs |= PROTO::OUTPUTS1::KEYBOARD::PS2; -# endif -# if defined(HID_WITH_USB) && defined(HID_SET_USB_MOUSE_ABS) - outputs |= PROTO::OUTPUTS1::MOUSE::USB_ABS; -# elif defined(HID_WITH_USB) && defined(HID_SET_USB_MOUSE_REL) - outputs |= PROTO::OUTPUTS1::MOUSE::USB_REL; -# elif defined(HID_WITH_PS2) && defined(HID_SET_PS2_MOUSE) - outputs |= PROTO::OUTPUTS1::MOUSE::PS2; -# elif defined(HID_WITH_USB) && defined(HID_WITH_USB_WIN98) && defined(HID_SET_USB_MOUSE_WIN98) - outputs |= PROTO::OUTPUTS1::MOUSE::USB_WIN98; -# endif - writeOutputs(0xFF, outputs, true); - } - - uint8_t kbd_type = outputs & PROTO::OUTPUTS1::KEYBOARD::MASK; - switch (kbd_type) { - case PROTO::OUTPUTS1::KEYBOARD::USB: - kbd = DRIVERS::Factory::makeKeyboard(DRIVERS::USB_KEYBOARD); - break; - case PROTO::OUTPUTS1::KEYBOARD::PS2: - kbd = DRIVERS::Factory::makeKeyboard(DRIVERS::PS2_KEYBOARD); - break; - default: - kbd = DRIVERS::Factory::makeKeyboard(DRIVERS::DUMMY); - break; - } - - uint8_t mouse_type = outputs & PROTO::OUTPUTS1::MOUSE::MASK; - switch (mouse_type) { - case PROTO::OUTPUTS1::MOUSE::USB_ABS: - mouse = DRIVERS::Factory::makeMouse(DRIVERS::USB_MOUSE_ABSOLUTE); - break; - case PROTO::OUTPUTS1::MOUSE::USB_WIN98: - mouse = DRIVERS::Factory::makeMouse(DRIVERS::USB_MOUSE_ABSOLUTE_WIN98); - break; - case PROTO::OUTPUTS1::MOUSE::USB_REL: - mouse = DRIVERS::Factory::makeMouse(DRIVERS::USB_MOUSE_RELATIVE); - break; - default: - mouse = DRIVERS::Factory::makeMouse(DRIVERS::DUMMY); - break; - } - -# ifdef ARDUINO_ARCH_AVR - USBDevice.attach(); -# endif - - kbd->begin(); - mouse->begin(); - } - - DRIVERS::Keyboard *kbd = nullptr; - DRIVERS::Mouse *mouse = nullptr; - - private: - int _readOutputs(void) { - uint8_t data[8]; - _storage->readBlock(data, 0, 8); - if (data[0] != PROTO::MAGIC || PROTO::crc16(data, 6) != PROTO::merge8(data[6], data[7])) { - return -1; - } - return data[1]; - } - - DRIVERS::Storage *_storage = nullptr; -}; diff --git a/hid/arduino/src/proto.h b/hid/arduino/src/proto.h deleted file mode 100644 index a2e95722..00000000 --- a/hid/arduino/src/proto.h +++ /dev/null @@ -1,142 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - - -namespace PROTO { - const uint8_t MAGIC = 0x33; - const uint8_t MAGIC_RESP = 0x34; - - namespace RESP { // Plain responses - // const uint8_t OK = 0x20; // Legacy - const uint8_t NONE = 0x24; - const uint8_t CRC_ERROR = 0x40; - const uint8_t INVALID_ERROR = 0x45; - const uint8_t TIMEOUT_ERROR = 0x48; - }; - - namespace PONG { // Complex response - const uint8_t OK = 0x80; - const uint8_t CAPS = 0b00000001; - const uint8_t SCROLL = 0b00000010; - const uint8_t NUM = 0b00000100; - const uint8_t KEYBOARD_OFFLINE = 0b00001000; - const uint8_t MOUSE_OFFLINE = 0b00010000; - const uint8_t RESET_REQUIRED = 0b01000000; - }; - - namespace OUTPUTS1 { // Complex request/responce flags - const uint8_t DYNAMIC = 0b10000000; - namespace KEYBOARD { - const uint8_t MASK = 0b00000111; - const uint8_t USB = 0b00000001; - const uint8_t PS2 = 0b00000011; - }; - namespace MOUSE { - const uint8_t MASK = 0b00111000; - const uint8_t USB_ABS = 0b00001000; - const uint8_t USB_REL = 0b00010000; - const uint8_t PS2 = 0b00011000; - const uint8_t USB_WIN98 = 0b00100000; - }; - }; - - namespace OUTPUTS2 { // Complex response - const uint8_t CONNECTABLE = 0b10000000; - const uint8_t CONNECTED = 0b01000000; - const uint8_t HAS_USB = 0b00000001; - const uint8_t HAS_PS2 = 0b00000010; - const uint8_t HAS_USB_WIN98 = 0b00000100; - } - - namespace CMD { - const uint8_t PING = 0x01; - const uint8_t REPEAT = 0x02; - const uint8_t SET_KEYBOARD = 0x03; - const uint8_t SET_MOUSE = 0x04; - const uint8_t SET_CONNECTED = 0x05; - const uint8_t CLEAR_HID = 0x10; - - namespace KEYBOARD { - const uint8_t KEY = 0x11; - }; - - namespace MOUSE { - const uint8_t MOVE = 0x12; - const uint8_t BUTTON = 0x13; - const uint8_t WHEEL = 0x14; - const uint8_t RELATIVE = 0x15; - namespace LEFT { - const uint8_t SELECT = 0b10000000; - const uint8_t STATE = 0b00001000; - }; - namespace RIGHT { - const uint8_t SELECT = 0b01000000; - const uint8_t STATE = 0b00000100; - }; - namespace MIDDLE { - const uint8_t SELECT = 0b00100000; - const uint8_t STATE = 0b00000010; - }; - namespace EXTRA_UP { - const uint8_t SELECT = 0b10000000; - const uint8_t STATE = 0b00001000; - }; - namespace EXTRA_DOWN { - const uint8_t SELECT = 0b01000000; - const uint8_t STATE = 0b00000100; - }; - }; - }; - - uint16_t crc16(const uint8_t *buffer, unsigned length) { - const uint16_t polinom = 0xA001; - uint16_t crc = 0xFFFF; - - for (unsigned byte_count = 0; byte_count < length; ++byte_count) { - crc = crc ^ buffer[byte_count]; - for (unsigned bit_count = 0; bit_count < 8; ++bit_count) { - if ((crc & 0x0001) == 0) { - crc = crc >> 1; - } else { - crc = crc >> 1; - crc = crc ^ polinom; - } - } - } - return crc; - } - - inline int merge8_int(uint8_t from_a, uint8_t from_b) { - return (((int)from_a << 8) | (int)from_b); - } - - inline uint16_t merge8(uint8_t from_a, uint8_t from_b) { - return (((uint16_t)from_a << 8) | (uint16_t)from_b); - } - - inline void split16(uint16_t from, uint8_t *to_a, uint8_t *to_b) { - *to_a = (uint8_t)(from >> 8); - *to_b = (uint8_t)(from & 0xFF); - } -}; diff --git a/hid/pico/.gitignore b/hid/pico/.gitignore deleted file mode 100644 index 6d0c9883..00000000 --- a/hid/pico/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/.pico-sdk* -/.tinyusb* -/.ps2x2pico* -/.build/ -/*.uf2 diff --git a/hid/pico/CMakeLists.txt b/hid/pico/CMakeLists.txt deleted file mode 100644 index 58a4c1a6..00000000 --- a/hid/pico/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -cmake_minimum_required(VERSION 3.13) - -set(PICO_SDK_PATH ${CMAKE_CURRENT_LIST_DIR}/.pico-sdk) -set(PICO_TINYUSB_PATH ${CMAKE_CURRENT_LIST_DIR}/.tinyusb) -set(PS2_PATH ${CMAKE_CURRENT_LIST_DIR}/.ps2x2pico/src) - -# For TinyUSB -set(FAMILY rp2040) - -# Include pico_sdk_import.cmake from pico-sdk (instead of copying) -include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake) - -# Generic setup -set(PROJECT hid) -project(${PROJECT}) - -# Initialize Pico-SDK -pico_sdk_init() - -# Set the path to the source code to build -set(SRC_TO_BUILD_PATH ${CMAKE_CURRENT_LIST_DIR}/src) -add_subdirectory(${SRC_TO_BUILD_PATH}) diff --git a/hid/pico/Makefile b/hid/pico/Makefile deleted file mode 100644 index cd1703d9..00000000 --- a/hid/pico/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -all: deps - rm -f hid.uf2 - cmake -B .build - cmake --build .build --config Release -- -j - ln .build/src/hid.uf2 . - - -upload: install -install: all - sudo mount /dev/sda1 /mnt - sudo cp hid.uf2 /mnt - sudo umount /mnt - - -clean: - rm -rf .build hid.uf2 -clean-all: clean - rm -rf .pico-sdk* .tinyusb* .ps2x2pico - - -define libdep - rm -rf .$(1).tmp - git clone https://github.com/$(2) .$(1).tmp - cd .$(1).tmp \ - && git checkout $(3) \ - && (test ! -f .gitmodules || git submodule update --init) - mv .$(1).tmp .$(1) -endef -.pico-sdk: - $(call libdep,pico-sdk,raspberrypi/pico-sdk,6a7db34ff63345a7badec79ebea3aaef1712f374) -.tinyusb: - $(call libdep,tinyusb,hathach/tinyusb,d713571cd44f05d2fc72efc09c670787b74106e0) -.ps2x2pico: - $(call libdep,ps2x2pico,No0ne/ps2x2pico,404aaf02949d5bee8013e3b5d0b3239abf6e13bd) -deps: .pico-sdk .tinyusb .ps2x2pico - - -.PHONY: deps diff --git a/hid/pico/src/CMakeLists.txt b/hid/pico/src/CMakeLists.txt deleted file mode 100644 index 7eeffa18..00000000 --- a/hid/pico/src/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -set(target_name hid) -add_executable(${target_name}) - -target_sources(${target_name} PRIVATE - main.c - ph_outputs.c - ph_usb.c - ph_usb_kbd.c - ph_usb_mouse.c - ph_ps2.c - ph_cmds.c - ph_com.c - ph_com_bridge.c - ph_com_spi.c - ph_com_uart.c - ph_debug.c - - ${PS2_PATH}/ps2out.c - ${PS2_PATH}/ps2in.c - ${PS2_PATH}/ps2kb.c - ${PS2_PATH}/ps2ms.c - ${PS2_PATH}/scancodesets.c -) -target_link_options(${target_name} PRIVATE -Xlinker --print-memory-usage) -target_compile_options(${target_name} PRIVATE -Wall -Wextra) -target_include_directories(${target_name} PRIVATE ${CMAKE_CURRENT_LIST_DIR} ${PS2_PATH}) - -pico_generate_pio_header(${target_name} ${PS2_PATH}/ps2out.pio) -pico_generate_pio_header(${target_name} ${PS2_PATH}/ps2in.pio) - -target_link_libraries(${target_name} PRIVATE - pico_stdlib - pico_unique_id - hardware_pio - hardware_spi - hardware_watchdog - tinyusb_device -) -pico_add_extra_outputs(${target_name}) diff --git a/hid/pico/src/main.c b/hid/pico/src/main.c deleted file mode 100644 index ee603db3..00000000 --- a/hid/pico/src/main.c +++ /dev/null @@ -1,130 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include "pico/stdlib.h" -#include "hardware/gpio.h" -#include "hardware/watchdog.h" - -#include "ph_types.h" -#include "ph_tools.h" -#include "ph_outputs.h" -#include "ph_usb.h" -#include "ph_ps2.h" -#include "ph_com.h" -#include "ph_proto.h" -#include "ph_cmds.h" -#include "ph_debug.h" - - -static bool _reset_required = false; - - -static u8 _handle_request(const u8 *data) { // 8 bytes - // FIXME: See kvmd/kvmd#80 - // Should input buffer be cleared in this case? - if (data[0] == PH_PROTO_MAGIC && ph_crc16(data, 6) == ph_merge8_u16(data[6], data[7])) { -# define HANDLE(x_handler, x_reset) { \ - x_handler(data + 2); \ - if (x_reset) { _reset_required = true; } \ - return PH_PROTO_PONG_OK; \ - } - switch (data[1]) { - case PH_PROTO_CMD_PING: return PH_PROTO_PONG_OK; - case PH_PROTO_CMD_SET_KBD: HANDLE(ph_cmd_set_kbd, true); - case PH_PROTO_CMD_SET_MOUSE: HANDLE(ph_cmd_set_mouse, true); - case PH_PROTO_CMD_SET_CONNECTED: return PH_PROTO_PONG_OK; // Arduino AUM - case PH_PROTO_CMD_CLEAR_HID: HANDLE(ph_cmd_send_clear, false); - case PH_PROTO_CMD_KBD_KEY: HANDLE(ph_cmd_kbd_send_key, false); - case PH_PROTO_CMD_MOUSE_BUTTON: HANDLE(ph_cmd_mouse_send_button, false); - case PH_PROTO_CMD_MOUSE_ABS: HANDLE(ph_cmd_mouse_send_abs, false); - case PH_PROTO_CMD_MOUSE_REL: HANDLE(ph_cmd_mouse_send_rel, false); - case PH_PROTO_CMD_MOUSE_WHEEL: HANDLE(ph_cmd_mouse_send_wheel, false); - case PH_PROTO_CMD_REPEAT: return 0; - } -# undef HANDLE - return PH_PROTO_RESP_INVALID_ERROR; - } - return PH_PROTO_RESP_CRC_ERROR; -} - -static void _send_response(u8 code) { - static u8 prev_code = PH_PROTO_RESP_NONE; - if (code == 0) { - code = prev_code; // Repeat the last code - } else { - prev_code = code; - } - - u8 resp[8] = {0}; - resp[0] = PH_PROTO_MAGIC_RESP; - - if (code & PH_PROTO_PONG_OK) { - resp[1] = PH_PROTO_PONG_OK; - if (_reset_required) { - resp[1] |= PH_PROTO_PONG_RESET_REQUIRED; - } - resp[2] = PH_PROTO_OUT1_DYNAMIC; - - resp[1] |= ph_cmd_get_offlines(); - resp[1] |= ph_cmd_kbd_get_leds(); - resp[2] |= ph_g_outputs_active; - resp[3] |= ph_g_outputs_avail; - } else { - resp[1] = code; - } - - ph_split16(ph_crc16(resp, 6), &resp[6], &resp[7]); - - ph_com_write(resp); - - if (_reset_required) { - watchdog_reboot(0, 0, 100); // Даем немного времени чтобы отправить ответ, а потом ребутимся - } -} - -static void _data_handler(const u8 *data) { - _send_response(_handle_request(data)); -} - -static void _timeout_handler(void) { - _send_response(PH_PROTO_RESP_TIMEOUT_ERROR); -} - - -int main(void) { - //ph_debug_act_init(); - //ph_debug_uart_init(); - ph_outputs_init(); - ph_ps2_init(); - ph_usb_init(); // Тут может быть инициализация USB-CDC для бриджа - ph_com_init(_data_handler, _timeout_handler); - - while (true) { - ph_usb_task(); - ph_ps2_task(); - if (!_reset_required) { - ph_com_task(); - //ph_debug_act_pulse(100); - } - } - return 0; -} diff --git a/hid/pico/src/ph_cmds.c b/hid/pico/src/ph_cmds.c deleted file mode 100644 index 52ccba16..00000000 --- a/hid/pico/src/ph_cmds.c +++ /dev/null @@ -1,131 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#include "tusb.h" - -#include "ph_types.h" -#include "ph_tools.h" -#include "ph_proto.h" -#include "ph_outputs.h" -#include "ph_usb.h" -#include "ph_usb_keymap.h" -#include "ph_ps2.h" - - -u8 ph_cmd_kbd_get_leds(void) { - u8 leds = 0; - if (PH_O_IS_KBD_USB) { - leds = ph_g_usb_kbd_leds; - } else if (PH_O_IS_KBD_PS2) { - leds = ph_g_ps2_kbd_leds; - } -# define GET(x_mod) ((leds & KEYBOARD_LED_##x_mod##LOCK) ? PH_PROTO_PONG_##x_mod : 0) - return (GET(CAPS) | GET(SCROLL) | GET(NUM)); -# undef GET -} - -u8 ph_cmd_get_offlines(void) { - bool kbd_online = true; - if (PH_O_IS_KBD_USB) { - kbd_online = ph_g_usb_kbd_online; - } else if (PH_O_IS_KBD_PS2) { - kbd_online = ph_g_ps2_kbd_online; - } - bool mouse_online = true; - if (PH_O_IS_MOUSE_USB) { - mouse_online = ph_g_usb_mouse_online; - } else if (PH_O_IS_MOUSE_PS2) { - mouse_online = ph_g_ps2_mouse_online; - } - return ( - (kbd_online ? 0 : PH_PROTO_PONG_KBD_OFFLINE) - | (mouse_online ? 0 : PH_PROTO_PONG_MOUSE_OFFLINE) - ); -} - -void ph_cmd_set_kbd(const u8 *args) { // 1 byte - ph_outputs_write(PH_PROTO_OUT1_KBD_MASK, args[0], false); -} - -void ph_cmd_set_mouse(const u8 *args) { // 1 byte - ph_outputs_write(PH_PROTO_OUT1_MOUSE_MASK, args[0], false); -} - -void ph_cmd_send_clear(const u8 *args) { // 0 bytes - (void)args; - ph_usb_send_clear(); - ph_ps2_send_clear(); -} - -void ph_cmd_kbd_send_key(const u8 *args) { // 2 bytes - const u8 key = ph_usb_keymap(args[0]); - if (key > 0) { - if (PH_O_IS_KBD_USB) { - ph_usb_kbd_send_key(key, args[1]); - } else if (PH_O_IS_KBD_PS2) { - ph_ps2_kbd_send_key(key, args[1]); - } - } -} - -void ph_cmd_mouse_send_button(const u8 *args) { // 2 bytes -# define HANDLE(x_byte_n, x_button) { \ - if (args[x_byte_n] & PH_PROTO_CMD_MOUSE_##x_button##_SELECT) { \ - const bool m_state = !!(args[x_byte_n] & PH_PROTO_CMD_MOUSE_##x_button##_STATE); \ - if (PH_O_IS_MOUSE_USB) { \ - ph_usb_mouse_send_button(MOUSE_BUTTON_##x_button, m_state); \ - } else if (PH_O_IS_MOUSE_PS2) { \ - ph_ps2_mouse_send_button(MOUSE_BUTTON_##x_button, m_state); \ - } \ - } \ - } - HANDLE(0, LEFT); - HANDLE(0, RIGHT); - HANDLE(0, MIDDLE); - HANDLE(1, BACKWARD); - HANDLE(1, FORWARD); -# undef HANDLE -} - -void ph_cmd_mouse_send_abs(const u8 *args) { // 4 bytes - if (PH_O_IS_MOUSE_USB_ABS) { - const s16 x = ph_merge8_s16(args[0], args[1]); - const s16 y = ph_merge8_s16(args[2], args[3]); - ph_usb_mouse_send_abs(x, y); - } -} - -void ph_cmd_mouse_send_rel(const u8 *args) { // 2 bytes - if (PH_O_IS_MOUSE_USB_REL) { - ph_usb_mouse_send_rel(args[0], args[1]); - } else if (PH_O_IS_MOUSE_PS2) { - ph_ps2_mouse_send_rel(args[0], args[1]); - } -} - -void ph_cmd_mouse_send_wheel(const u8 *args) { // 2 bytes - if (PH_O_IS_MOUSE_USB) { - ph_usb_mouse_send_wheel(args[0], args[1]); - } else if (PH_O_IS_MOUSE_PS2) { - ph_ps2_mouse_send_wheel(args[0], args[1]); - } -} diff --git a/hid/pico/src/ph_cmds.h b/hid/pico/src/ph_cmds.h deleted file mode 100644 index e0c4c9db..00000000 --- a/hid/pico/src/ph_cmds.h +++ /dev/null @@ -1,39 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#pragma once - -#include "ph_types.h" - - -u8 ph_cmd_kbd_get_leds(void); -u8 ph_cmd_get_offlines(void); - -void ph_cmd_set_kbd(const u8 *args); -void ph_cmd_set_mouse(const u8 *args); - -void ph_cmd_send_clear(const u8 *args); -void ph_cmd_kbd_send_key(const u8 *args); -void ph_cmd_mouse_send_button(const u8 *args); -void ph_cmd_mouse_send_abs(const u8 *args); -void ph_cmd_mouse_send_rel(const u8 *args); -void ph_cmd_mouse_send_wheel(const u8 *args); diff --git a/hid/pico/src/ph_com.c b/hid/pico/src/ph_com.c deleted file mode 100644 index 860b6202..00000000 --- a/hid/pico/src/ph_com.c +++ /dev/null @@ -1,67 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include "ph_com.h" - -#include "pico/stdlib.h" -#include "hardware/gpio.h" - -#include "ph_types.h" -#include "ph_outputs.h" -#include "ph_com_bridge.h" -#include "ph_com_spi.h" -#include "ph_com_uart.h" - - -#define _USE_SPI_PIN 22 - - -static bool _use_spi = true; - - -#define _COM(x_func, ...) { \ - if (ph_g_is_bridge) { \ - ph_com_bridge_##x_func(__VA_ARGS__); \ - } else if (_use_spi) { \ - ph_com_spi_##x_func(__VA_ARGS__); \ - } else { \ - ph_com_uart_##x_func(__VA_ARGS__); \ - } \ - } - - -void ph_com_init(void (*data_cb)(const u8 *), void (*timeout_cb)(void)) { - gpio_init(_USE_SPI_PIN); - gpio_set_dir(_USE_SPI_PIN, GPIO_IN); - gpio_pull_up(_USE_SPI_PIN); - sleep_ms(10); // Нужен небольшой слип для активации pull-up - _use_spi = gpio_get(_USE_SPI_PIN); - _COM(init, data_cb, timeout_cb); -} - -void ph_com_task(void) { - _COM(task); -} - -void ph_com_write(const u8 *data) { - _COM(write, data); -} diff --git a/hid/pico/src/ph_com.h b/hid/pico/src/ph_com.h deleted file mode 100644 index 12ca2d48..00000000 --- a/hid/pico/src/ph_com.h +++ /dev/null @@ -1,30 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "ph_types.h" - - -void ph_com_init(void (*data_cb)(const u8 *), void (*timeout_cb)(void)); -void ph_com_task(void); -void ph_com_write(const u8 *data); diff --git a/hid/pico/src/ph_com_bridge.c b/hid/pico/src/ph_com_bridge.c deleted file mode 100644 index d9fc4552..00000000 --- a/hid/pico/src/ph_com_bridge.c +++ /dev/null @@ -1,84 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include "ph_com_bridge.h" - -#include "pico/stdlib.h" - -#include "tusb.h" - -#include "ph_types.h" - - -#define _TIMEOUT_US 100000 - - -static u8 _buf[8] = {0}; -static u8 _index = 0; -static u64 _last_ts = 0; - -static void (*_data_cb)(const u8 *) = NULL; -static void (*_timeout_cb)(void) = NULL; - - -void ph_com_bridge_init(void (*data_cb)(const u8 *), void (*timeout_cb)(void)) { - _data_cb = data_cb; - _timeout_cb = timeout_cb; -} - -void ph_com_bridge_task(void) { - if (!tud_cdc_connected()) { - tud_cdc_write_clear(); - return; - } - - if (tud_cdc_available() > 0) { - const s32 ch = tud_cdc_read_char(); - if (ch < 0) { - goto no_data; - } - _buf[_index] = (u8)ch; - if (_index == 7) { - _data_cb(_buf); - _index = 0; - } else { - _last_ts = time_us_64(); - ++_index; - } - return; - } - - no_data: - if (_index > 0) { - if (_last_ts + _TIMEOUT_US < time_us_64()) { - _timeout_cb(); - _index = 0; - } - } -} - -void ph_com_bridge_write(const u8 *data) { - if (tud_cdc_connected()) { - tud_cdc_write(data, 8); - tud_cdc_write_flush(); - } -} diff --git a/hid/pico/src/ph_com_bridge.h b/hid/pico/src/ph_com_bridge.h deleted file mode 100644 index 7bc293e7..00000000 --- a/hid/pico/src/ph_com_bridge.h +++ /dev/null @@ -1,30 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "ph_types.h" - - -void ph_com_bridge_init(void (*data_cb)(const u8 *), void (*timeout_cb)(void)); -void ph_com_bridge_task(void); -void ph_com_bridge_write(const u8 *data); diff --git a/hid/pico/src/ph_com_spi.c b/hid/pico/src/ph_com_spi.c deleted file mode 100644 index e324e8dc..00000000 --- a/hid/pico/src/ph_com_spi.c +++ /dev/null @@ -1,121 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include "ph_com_spi.h" - -#include "hardware/gpio.h" -#include "hardware/irq.h" -#include "hardware/spi.h" -#include "hardware/regs/spi.h" - -#include "ph_types.h" - - -#define _BUS spi0 -#define _IRQ SPI0_IRQ -#define _FREQ (2 * 1000 * 1000) -#define _CS_PIN 21 -#define _RX_PIN 20 -#define _TX_PIN 19 -#define _CLK_PIN 18 - - -static volatile u8 _in_buf[8] = {0}; -static volatile u8 _in_index = 0; - -static volatile u8 _out_buf[8] = {0}; -static volatile u8 _out_index = 0; - -static void (*_data_cb)(const u8 *) = NULL; - - -static void _xfer_isr(void); - - -void ph_com_spi_init(void (*data_cb)(const u8 *), void (*timeout_cb)(void)) { - _data_cb = data_cb; - (void)timeout_cb; - - spi_init(_BUS, _FREQ); - spi_set_slave(_BUS, true); - spi_set_format(_BUS, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); - - gpio_set_function(_CS_PIN, GPIO_FUNC_SPI); - gpio_set_function(_RX_PIN, GPIO_FUNC_SPI); - gpio_set_function(_TX_PIN, GPIO_FUNC_SPI); - gpio_set_function(_CLK_PIN, GPIO_FUNC_SPI); - - // https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2040/hardware_regs/include/hardware/regs/spi.h - irq_set_exclusive_handler(_IRQ, _xfer_isr); - spi_get_hw(_BUS)->imsc = SPI_SSPIMSC_RXIM_BITS | SPI_SSPIMSC_TXIM_BITS; - irq_set_enabled(_IRQ, true); -} - -void ph_com_spi_task(void) { - if (!_out_buf[0] && _in_index == 8) { - _data_cb((const u8 *)_in_buf); - } -} - -void ph_com_spi_write(const u8 *data) { - // Меджик в нулевом байте разрешает начать ответ - for (s8 i = 7; i >= 0; --i) { - _out_buf[i] = data[i]; - } -} - -void __isr __not_in_flash_func(_xfer_isr)(void) { -# define SR (spi_get_hw(_BUS)->sr) -# define DR (spi_get_hw(_BUS)->dr) - - while (SR & SPI_SSPSR_TNF_BITS) { - if (_out_buf[0] && _out_index < 8) { - DR = (u32)_out_buf[_out_index]; - ++_out_index; - if (_out_index == 8) { - _out_index = 0; - _in_index = 0; - _out_buf[0] = 0; - } - } else { - DR = (u32)0; - } - } - - while (SR & SPI_SSPSR_RNE_BITS) { - static bool receiving = false; - const u8 in = DR; - if (!receiving && in != 0) { - receiving = true; - } - if (receiving && _in_index < 8) { - _in_buf[_in_index] = in; - ++_in_index; - } - if (_in_index == 8) { - receiving = false; - } - } - -# undef DR -# undef SR -} diff --git a/hid/pico/src/ph_com_spi.h b/hid/pico/src/ph_com_spi.h deleted file mode 100644 index 9a6d55b6..00000000 --- a/hid/pico/src/ph_com_spi.h +++ /dev/null @@ -1,30 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "ph_types.h" - - -void ph_com_spi_init(void (*data_cb)(const u8 *), void (*timeout_cb)(void)); -void ph_com_spi_task(void); -void ph_com_spi_write(const u8 *data); diff --git a/hid/pico/src/ph_com_uart.c b/hid/pico/src/ph_com_uart.c deleted file mode 100644 index 8d64f56b..00000000 --- a/hid/pico/src/ph_com_uart.c +++ /dev/null @@ -1,75 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include "ph_com_uart.h" - -#include "pico/stdlib.h" -#include "hardware/gpio.h" -#include "hardware/uart.h" - -#include "ph_types.h" - - -#define _BUS uart1 -#define _SPEED 115200 -#define _RX_PIN 21 -#define _TX_PIN 20 -#define _TIMEOUT_US 100000 - - -static u8 _buf[8] = {0}; -static u8 _index = 0; -static u64 _last_ts = 0; - -static void (*_data_cb)(const u8 *) = NULL; -static void (*_timeout_cb)(void) = NULL; - - -void ph_com_uart_init(void (*data_cb)(const u8 *), void (*timeout_cb)(void)) { - _data_cb = data_cb; - _timeout_cb = timeout_cb; - uart_init(_BUS, _SPEED); - gpio_set_function(_RX_PIN, GPIO_FUNC_UART); - gpio_set_function(_TX_PIN, GPIO_FUNC_UART); -} - -void ph_com_uart_task(void) { - if (uart_is_readable(_BUS)) { - _buf[_index] = (u8)uart_getc(_BUS); - if (_index == 7) { - _data_cb(_buf); - _index = 0; - } else { - _last_ts = time_us_64(); - ++_index; - } - } else if (_index > 0) { - if (_last_ts + _TIMEOUT_US < time_us_64()) { - _timeout_cb(); - _index = 0; - } - } -} - -void ph_com_uart_write(const u8 *data) { - uart_write_blocking(_BUS, data, 8); -} diff --git a/hid/pico/src/ph_com_uart.h b/hid/pico/src/ph_com_uart.h deleted file mode 100644 index b423c6c8..00000000 --- a/hid/pico/src/ph_com_uart.h +++ /dev/null @@ -1,30 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "ph_types.h" - - -void ph_com_uart_init(void (*data_cb)(const u8 *), void (*timeout_cb)(void)); -void ph_com_uart_task(void); -void ph_com_uart_write(const u8 *data); diff --git a/hid/pico/src/ph_debug.c b/hid/pico/src/ph_debug.c deleted file mode 100644 index e08b9db1..00000000 --- a/hid/pico/src/ph_debug.c +++ /dev/null @@ -1,58 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#include "pico/stdlib.h" -#include "hardware/gpio.h" - -#include "ph_types.h" - - -#define _UART uart0 -#define _SPEED 3000000 -#define _RX_PIN -1 // 1 - No stdin -#define _TX_PIN 0 -#define _ACT_PIN 25 - - -void ph_debug_uart_init(void) { - stdio_uart_init_full(_UART, _SPEED, _TX_PIN, _RX_PIN); -} - -void ph_debug_act_init(void) { - gpio_init(_ACT_PIN); - gpio_set_dir(_ACT_PIN, GPIO_OUT); -} - -void ph_debug_act(bool flag) { - gpio_put(_ACT_PIN, flag); -} - -void ph_debug_act_pulse(u64 delay_ms) { - static bool flag = false; - static u64 next_ts = 0; - const u64 now_ts = time_us_64(); - if (now_ts >= next_ts) { - ph_debug_act(flag); - flag = !flag; - next_ts = now_ts + (delay_ms * 1000); - } -} diff --git a/hid/pico/src/ph_debug.h b/hid/pico/src/ph_debug.h deleted file mode 100644 index 33d5eed8..00000000 --- a/hid/pico/src/ph_debug.h +++ /dev/null @@ -1,31 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "ph_types.h" - - -void ph_debug_uart_init(); -void ph_debug_act_init(); -void ph_debug_act(bool flag); -void ph_debug_act_pulse(u64 delay_ms); diff --git a/hid/pico/src/ph_outputs.c b/hid/pico/src/ph_outputs.c deleted file mode 100644 index 4ec40de5..00000000 --- a/hid/pico/src/ph_outputs.c +++ /dev/null @@ -1,146 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#include "ph_outputs.h" - -#include "pico/stdlib.h" -#include "hardware/gpio.h" -#include "hardware/structs/watchdog.h" - -#include "ph_types.h" -#include "ph_tools.h" -#include "ph_proto.h" - - -#define _PS2_ENABLED_PIN 2 -#define _PS2_SET_KBD_PIN 3 -#define _PS2_SET_MOUSE_PIN 4 - -#define _BRIDGE_MODE_PIN 5 - -#define _USB_DISABLED_PIN 6 -#define _USB_ENABLE_W98_PIN 7 -#define _USB_SET_MOUSE_REL_PIN 8 -#define _USB_SET_MOUSE_W98_PIN 9 - - -u8 ph_g_outputs_active = 0; -u8 ph_g_outputs_avail = 0; -bool ph_g_is_bridge = false; - - -static int _read_outputs(void); - - -void ph_outputs_init(void) { -# define INIT_SWITCH(x_pin) { gpio_init(x_pin); gpio_set_dir(x_pin, GPIO_IN); gpio_pull_up(x_pin); } - INIT_SWITCH(_PS2_ENABLED_PIN); - INIT_SWITCH(_PS2_SET_KBD_PIN); - INIT_SWITCH(_PS2_SET_MOUSE_PIN); - - INIT_SWITCH(_BRIDGE_MODE_PIN); - - INIT_SWITCH(_USB_DISABLED_PIN); - INIT_SWITCH(_USB_ENABLE_W98_PIN); - INIT_SWITCH(_USB_SET_MOUSE_REL_PIN); - INIT_SWITCH(_USB_SET_MOUSE_W98_PIN); -# undef INIT_SWITCH - sleep_ms(10); // Нужен небольшой слип для активации pull-up - - const bool o_ps2_enabled = !gpio_get(_PS2_ENABLED_PIN); // Note: all pins are pulled up! - const bool o_ps2_kbd = !gpio_get(_PS2_SET_KBD_PIN); - const bool o_ps2_mouse = !gpio_get(_PS2_SET_MOUSE_PIN); - - ph_g_is_bridge = !gpio_get(_BRIDGE_MODE_PIN); - - const bool o_usb_disabled = (ph_g_is_bridge || !gpio_get(_USB_DISABLED_PIN)); - const bool o_usb_enabled_w98 = !gpio_get(_USB_ENABLE_W98_PIN); - const bool o_usb_mouse_rel = !gpio_get(_USB_SET_MOUSE_REL_PIN); - const bool o_usb_mouse_w98 = !gpio_get(_USB_SET_MOUSE_W98_PIN); - - int outputs = _read_outputs(); - if (outputs < 0) { - outputs = 0; - - if (o_ps2_enabled && (o_ps2_kbd || o_usb_disabled)) { - outputs |= PH_PROTO_OUT1_KBD_PS2; - } else if (!o_usb_disabled) { - outputs |= PH_PROTO_OUT1_KBD_USB; - } - - if (o_ps2_enabled && (o_ps2_mouse || o_usb_disabled)) { - outputs |= PH_PROTO_OUT1_MOUSE_PS2; - } else if (!o_usb_disabled) { - if (o_usb_enabled_w98 && o_usb_mouse_w98) { - outputs |= PH_PROTO_OUT1_MOUSE_USB_W98; - } else if (o_usb_mouse_rel) { - outputs |= PH_PROTO_OUT1_MOUSE_USB_REL; - } else { - outputs |= PH_PROTO_OUT1_MOUSE_USB_ABS; - } - } - - ph_outputs_write(0xFF, outputs, true); - } - - if (!o_usb_disabled) { - ph_g_outputs_avail |= PH_PROTO_OUT2_HAS_USB; - if (o_usb_enabled_w98) { - ph_g_outputs_avail |= PH_PROTO_OUT2_HAS_USB_W98; - } - } - if (o_ps2_enabled) { - ph_g_outputs_avail |= PH_PROTO_OUT2_HAS_PS2; - } - - ph_g_outputs_active = outputs & 0xFF; -} - -void ph_outputs_write(u8 mask, u8 outputs, bool force) { - int old = 0; - if (!force) { - old = _read_outputs(); - if (old < 0) { - old = 0; - } - } - u8 data[4] = {0}; - data[0] = PH_PROTO_MAGIC; - data[1] = (old & ~mask) | outputs; - ph_split16(ph_crc16(data, 2), &data[2], &data[3]); - const u32 s0 = ((u32)data[0] << 24) | ((u32)data[1] << 16) | ((u32)data[2] << 8) | (u32)data[3]; - watchdog_hw->scratch[0] = s0; -} - -static int _read_outputs(void) { - const u32 s0 = watchdog_hw->scratch[0]; - const u8 data[4] = { - (s0 >> 24) & 0xFF, - (s0 >> 16) & 0xFF, - (s0 >> 8) & 0xFF, - s0 & 0xFF, - }; - if (data[0] != PH_PROTO_MAGIC || ph_crc16(data, 2) != ph_merge8_u16(data[2], data[3])) { - return -1; - } - return data[1]; -} diff --git a/hid/pico/src/ph_outputs.h b/hid/pico/src/ph_outputs.h deleted file mode 100644 index bddf9208..00000000 --- a/hid/pico/src/ph_outputs.h +++ /dev/null @@ -1,46 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#pragma once - -#include "ph_types.h" -#include "ph_proto.h" - - -#define PH_O_HAS_PS2 (!!(ph_g_outputs_avail & PH_PROTO_OUT2_HAS_PS2)) -#define PH_O_KBD(x_id) ((ph_g_outputs_active & PH_PROTO_OUT1_KBD_MASK) == PH_PROTO_OUT1_KBD_##x_id) -#define PH_O_MOUSE(x_id) ((ph_g_outputs_active & PH_PROTO_OUT1_MOUSE_MASK) == PH_PROTO_OUT1_MOUSE_##x_id) -#define PH_O_IS_KBD_USB PH_O_KBD(USB) -#define PH_O_IS_MOUSE_USB (PH_O_MOUSE(USB_ABS) || PH_O_MOUSE(USB_REL) || PH_O_MOUSE(USB_W98)) -#define PH_O_IS_MOUSE_USB_ABS (PH_O_MOUSE(USB_ABS) || PH_O_MOUSE(USB_W98)) -#define PH_O_IS_MOUSE_USB_REL PH_O_MOUSE(USB_REL) -#define PH_O_IS_KBD_PS2 PH_O_KBD(PS2) -#define PH_O_IS_MOUSE_PS2 PH_O_MOUSE(PS2) - - -extern bool ph_g_is_bridge; -extern u8 ph_g_outputs_active; -extern u8 ph_g_outputs_avail; - - -void ph_outputs_init(void); -void ph_outputs_write(u8 mask, u8 outputs, bool force); diff --git a/hid/pico/src/ph_proto.h b/hid/pico/src/ph_proto.h deleted file mode 100644 index 78b28d32..00000000 --- a/hid/pico/src/ph_proto.h +++ /dev/null @@ -1,92 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "ph_types.h" - - -#define PH_PROTO_MAGIC ((u8)0x33) -#define PH_PROTO_MAGIC_RESP ((u8)0x34) - -//#define PH_PROTO_RESP_OK ((u8)0x20) // Legacy -#define PH_PROTO_RESP_NONE ((u8)0x24) -#define PH_PROTO_RESP_CRC_ERROR ((u8)0x40) -#define PH_PROTO_RESP_INVALID_ERROR ((u8)0x45) -#define PH_PROTO_RESP_TIMEOUT_ERROR ((u8)0x48) - -// Complex response flags -#define PH_PROTO_PONG_OK ((u8)0b10000000) -#define PH_PROTO_PONG_CAPS ((u8)0b00000001) -#define PH_PROTO_PONG_SCROLL ((u8)0b00000010) -#define PH_PROTO_PONG_NUM ((u8)0b00000100) -#define PH_PROTO_PONG_KBD_OFFLINE ((u8)0b00001000) -#define PH_PROTO_PONG_MOUSE_OFFLINE ((u8)0b00010000) -#define PH_PROTO_PONG_RESET_REQUIRED ((u8)0b01000000) - -// Complex request/response flags -#define PH_PROTO_OUT1_DYNAMIC ((u8)0b10000000) -#define PH_PROTO_OUT1_KBD_MASK ((u8)0b00000111) -#define PH_PROTO_OUT1_KBD_USB ((u8)0b00000001) -#define PH_PROTO_OUT1_KBD_PS2 ((u8)0b00000011) -// + -#define PH_PROTO_OUT1_MOUSE_MASK ((u8)0b00111000) -#define PH_PROTO_OUT1_MOUSE_USB_ABS ((u8)0b00001000) -#define PH_PROTO_OUT1_MOUSE_USB_REL ((u8)0b00010000) -#define PH_PROTO_OUT1_MOUSE_PS2 ((u8)0b00011000) -#define PH_PROTO_OUT1_MOUSE_USB_W98 ((u8)0b00100000) - -// Complex response -#define PH_PROTO_OUT2_CONNECTABLE ((u8)0b10000000) -#define PH_PROTO_OUT2_CONNECTED ((u8)0b01000000) -#define PH_PROTO_OUT2_HAS_USB ((u8)0b00000001) -#define PH_PROTO_OUT2_HAS_PS2 ((u8)0b00000010) -#define PH_PROTO_OUT2_HAS_USB_W98 ((u8)0b00000100) - -#define PH_PROTO_CMD_PING ((u8)0x01) -#define PH_PROTO_CMD_REPEAT ((u8)0x02) -#define PH_PROTO_CMD_SET_KBD ((u8)0x03) -#define PH_PROTO_CMD_SET_MOUSE ((u8)0x04) -#define PH_PROTO_CMD_SET_CONNECTED ((u8)0x05) -#define PH_PROTO_CMD_CLEAR_HID ((u8)0x10) -// + -#define PH_PROTO_CMD_KBD_KEY ((u8)0x11) -// + -#define PH_PROTO_CMD_MOUSE_ABS ((u8)0x12) -#define PH_PROTO_CMD_MOUSE_BUTTON ((u8)0x13) -#define PH_PROTO_CMD_MOUSE_WHEEL ((u8)0x14) -#define PH_PROTO_CMD_MOUSE_REL ((u8)0x15) -// + -#define PH_PROTO_CMD_MOUSE_LEFT_SELECT ((u8)0b10000000) -#define PH_PROTO_CMD_MOUSE_LEFT_STATE ((u8)0b00001000) -// + -#define PH_PROTO_CMD_MOUSE_RIGHT_SELECT ((u8)0b01000000) -#define PH_PROTO_CMD_MOUSE_RIGHT_STATE ((u8)0b00000100) -// + -#define PH_PROTO_CMD_MOUSE_MIDDLE_SELECT ((u8)0b00100000) -#define PH_PROTO_CMD_MOUSE_MIDDLE_STATE ((u8)0b00000010) -// + -#define PH_PROTO_CMD_MOUSE_BACKWARD_SELECT ((u8)0b10000000) // Previous/Up -#define PH_PROTO_CMD_MOUSE_BACKWARD_STATE ((u8)0b00001000) // Previous/Up -// + -#define PH_PROTO_CMD_MOUSE_FORWARD_SELECT ((u8)0b01000000) // Next/Down -#define PH_PROTO_CMD_MOUSE_FORWARD_STATE ((u8)0b00000100) // Next/Down diff --git a/hid/pico/src/ph_ps2.c b/hid/pico/src/ph_ps2.c deleted file mode 100644 index fcb4dfbb..00000000 --- a/hid/pico/src/ph_ps2.c +++ /dev/null @@ -1,143 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#include "ph_ps2.h" - -#include "ph_types.h" -#include "ph_outputs.h" - -#include "hardware/gpio.h" - - -#define _LS_POWER_PIN 13 -#define _KBD_DATA_PIN 11 // CLK == 12 -#define _MOUSE_DATA_PIN 14 // CLK == 15 - -#define _KBD_IN_DATA_PIN 26 // passthru, CLK == 27 -#define _MOUSE_IN_DATA_PIN 16 // passthru, CLK == 17 - - -u8 ph_g_ps2_kbd_leds = 0; -bool ph_g_ps2_kbd_online = 0; -bool ph_g_ps2_mouse_online = 0; - -u8 ph_ps2_kbd_modifiers = 0; -u8 ph_ps2_mouse_buttons = 0; - - -void tuh_kb_set_leds(u8 leds) { - ph_g_ps2_kbd_leds = leds; -} - -void ph_ps2_init(void) { - if (PH_O_HAS_PS2) { - gpio_init(_LS_POWER_PIN); - gpio_set_dir(_LS_POWER_PIN, GPIO_OUT); - gpio_put(_LS_POWER_PIN, true); - } - -# define INIT_STUB(x_pin) { \ - gpio_init(x_pin); gpio_set_dir(x_pin, GPIO_IN); \ - gpio_init(x_pin + 1); gpio_set_dir(x_pin + 1, GPIO_IN); \ - } - - if (PH_O_IS_KBD_PS2) { - kb_init(_KBD_DATA_PIN, _KBD_IN_DATA_PIN); - } else { - INIT_STUB(_KBD_DATA_PIN); - } - - if (PH_O_IS_MOUSE_PS2) { - ms_init(_MOUSE_DATA_PIN, _MOUSE_IN_DATA_PIN); - } else { - INIT_STUB(_MOUSE_DATA_PIN); - } - -# undef INIT_STUB -} - -void ph_ps2_task(void) { - if (PH_O_IS_KBD_PS2) { - ph_g_ps2_kbd_online = kb_task(); - } - - if (PH_O_IS_MOUSE_PS2) { - ph_g_ps2_mouse_online = ms_task(); - } -} - -void ph_ps2_kbd_send_key(u8 key, bool state) { - if (PH_O_IS_KBD_PS2) { - if (key >= 0xe0 && key <= 0xe7) { - if (state) { - ph_ps2_kbd_modifiers = ph_ps2_kbd_modifiers | (1 << (key - 0xe0)); - } else { - ph_ps2_kbd_modifiers = ph_ps2_kbd_modifiers & ~(1 << (key - 0xe0)); - } - } - - kb_send_key(key, state, ph_ps2_kbd_modifiers); - } -} - -void ph_ps2_mouse_send_button(u8 button, bool state) { - if (PH_O_IS_MOUSE_PS2) { - button--; - - if (state) { - ph_ps2_mouse_buttons = ph_ps2_mouse_buttons | (1 << button); - } else { - ph_ps2_mouse_buttons = ph_ps2_mouse_buttons & ~(1 << button); - } - - ms_send_movement(ph_ps2_mouse_buttons, 0, 0, 0); - } -} - -void ph_ps2_mouse_send_rel(s8 x, s8 y) { - if (PH_O_IS_MOUSE_PS2) { - ms_send_movement(ph_ps2_mouse_buttons, x, y, 0); - } -} - -void ph_ps2_mouse_send_wheel(s8 h, s8 v) { - if (PH_O_IS_MOUSE_PS2) { - (void)h; // as far as I know there is no standard way for horizontal scrolling - ms_send_movement(ph_ps2_mouse_buttons, 0, 0, v); - } -} - -void ph_ps2_send_clear(void) { - if (PH_O_IS_KBD_PS2) { - //for(u8 key = 0xe0; key <= 0xe7; key++) { - // kb_send_key(key, false, 0); - //} - - //for(u8 key = 4; key <= 116; key++) { - // kb_send_key(key, false, 0); - //} - } - - if (PH_O_IS_MOUSE_PS2) { - ms_send_movement(0, 0, 0, 0); - } -} diff --git a/hid/pico/src/ph_ps2.h b/hid/pico/src/ph_ps2.h deleted file mode 100644 index 69f55866..00000000 --- a/hid/pico/src/ph_ps2.h +++ /dev/null @@ -1,49 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#pragma once - -#include "ph_types.h" - - -extern u8 ph_g_ps2_kbd_leds; -extern bool ph_g_ps2_kbd_online; -extern bool ph_g_ps2_mouse_online; - - -void ph_ps2_init(void); -void ph_ps2_task(void); - -void tuh_kb_set_leds(u8 leds); -void kb_init(u8 gpio_out, u8 gpio_in); -bool kb_task(); -void kb_send_key(u8 key, bool state, u8 modifiers); -void ph_ps2_kbd_send_key(u8 key, bool state); - -void ms_init(u8 gpio_out, u8 gpio_in); -bool ms_task(); -void ms_send_movement(u8 buttons, s8 x, s8 y, s8 z); -void ph_ps2_mouse_send_button(u8 button, bool state); -void ph_ps2_mouse_send_rel(s8 x, s8 y); -void ph_ps2_mouse_send_wheel(s8 h, s8 v); - -void ph_ps2_send_clear(void); diff --git a/hid/pico/src/ph_tools.h b/hid/pico/src/ph_tools.h deleted file mode 100644 index 489302c4..00000000 --- a/hid/pico/src/ph_tools.h +++ /dev/null @@ -1,57 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "ph_types.h" - - -inline u16 ph_crc16(const u8 *buf, uz len) { - const u16 polinom = 0xA001; - u16 crc = 0xFFFF; - - for (uz byte_count = 0; byte_count < len; ++byte_count) { - crc = crc ^ buf[byte_count]; - for (uz bit_count = 0; bit_count < 8; ++bit_count) { - if ((crc & 0x0001) == 0) { - crc = crc >> 1; - } else { - crc = crc >> 1; - crc = crc ^ polinom; - } - } - } - return crc; -} - -inline s16 ph_merge8_s16(u8 a, u8 b) { - return (((int)a << 8) | (int)b); -} - -inline u16 ph_merge8_u16(u8 a, u8 b) { - return (((u16)a << 8) | (u16)b); -} - -inline void ph_split16(u16 from, u8 *to_a, u8 *to_b) { - *to_a = (u8)(from >> 8); - *to_b = (u8)(from & 0xFF); -} diff --git a/hid/pico/src/ph_types.h b/hid/pico/src/ph_types.h deleted file mode 100644 index 97b89e90..00000000 --- a/hid/pico/src/ph_types.h +++ /dev/null @@ -1,38 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#pragma once - -#include -#include -#include - - -typedef int8_t s8; -typedef int16_t s16; -typedef int32_t s32; - -typedef size_t uz; -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; diff --git a/hid/pico/src/ph_usb.c b/hid/pico/src/ph_usb.c deleted file mode 100644 index a3d1da00..00000000 --- a/hid/pico/src/ph_usb.c +++ /dev/null @@ -1,437 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#include "ph_usb.h" - -#include -#include - -#include "pico/stdlib.h" -#include "pico/unique_id.h" - -#include "tusb.h" -#if TUD_OPT_HIGH_SPEED -# error "High-Speed is not supported" -#endif - -#include "ph_types.h" -#include "ph_outputs.h" -#include "ph_usb_kbd.h" -#include "ph_usb_mouse.h" - - -u8 ph_g_usb_kbd_leds = 0; -bool ph_g_usb_kbd_online = true; -bool ph_g_usb_mouse_online = true; - -static int _kbd_iface = -1; -static int _mouse_iface = -1; - -static u8 _kbd_mods = 0; -static u8 _kbd_keys[6] = {0}; -#define _KBD_CLEAR { _kbd_mods = 0; memset(_kbd_keys, 0, 6); } - -static u8 _mouse_buttons = 0; -static s16 _mouse_abs_x = 0; -static s16 _mouse_abs_y = 0; -#define _MOUSE_CLEAR { _mouse_buttons = 0; _mouse_abs_x = 0; _mouse_abs_y = 0; } - - -static void _kbd_sync_report(bool new); -static void _mouse_abs_send_report(s8 h, s8 v); -static void _mouse_rel_send_report(s8 x, s8 y, s8 h, s8 v); - - -void ph_usb_init(void) { - if (ph_g_is_bridge || PH_O_IS_KBD_USB || PH_O_IS_MOUSE_USB) { - tud_init(0); - } -} - -void ph_usb_task(void) { - if (ph_g_is_bridge || PH_O_IS_KBD_USB || PH_O_IS_MOUSE_USB) { - tud_task(); - - static u64 next_ts = 0; - const u64 now_ts = time_us_64(); - if (next_ts == 0 || now_ts >= next_ts) { -# define CHECK_IFACE(x_dev) \ - static u64 offline_ts = 0; \ - static bool prev_online = true; \ - const bool online = (tud_ready() && tud_hid_n_ready(_##x_dev##_iface)); \ - bool force = false; \ - if (online) { \ - if (!ph_g_usb_##x_dev##_online) { \ - force = true; /* Если был переход из долгого оффлайна в онлайн */ \ - } \ - ph_g_usb_##x_dev##_online = true; \ - offline_ts = 0; \ - } else if (prev_online && !online) { \ - offline_ts = now_ts; /* Начинаем отсчет для долгого оффлайна */ \ - } else if (!prev_online && !online && offline_ts + 50000 < now_ts) { \ - ph_g_usb_##x_dev##_online = false; /* Долгий оффлайн найден */ \ - } \ - prev_online = online; - - if (_kbd_iface >= 0) { - CHECK_IFACE(kbd); - _kbd_sync_report(force); - } - - if (_mouse_iface >= 0) { - CHECK_IFACE(mouse); - (void)force; - } - -# undef CHECK_IFACE - next_ts = time_us_64() + 1000; // Every 1 ms - } - } -} - -void ph_usb_kbd_send_key(u8 key, bool state) { - if (_kbd_iface < 0) { - return; // Допускаем планирование нажатия, пока устройство не готово - } - - if (key >= HID_KEY_CONTROL_LEFT && key <= HID_KEY_GUI_RIGHT) { // 0xE0...0xE7 - Modifiers - key = 1 << (key & 0x07); // Номер означает сдвиг - if (state) { - _kbd_mods |= key; - } else { - _kbd_mods &= ~key; - } - - } else { // Regular keys - if (state) { - s8 pos = -1; - for (u8 i = 0; i < 6; ++i) { - if (_kbd_keys[i] == key) { - goto already_pressed; - } else if (_kbd_keys[i] == 0) { - pos = i; - } - } - _kbd_keys[pos >= 0 ? pos : 0] = key; - // already_pressed: - } else { - for (u8 i = 0; i < 6; ++i) { - if (_kbd_keys[i] == key) { - _kbd_keys[i] = 0; - break; - } - } - } - } - already_pressed: // Old GCC doesn't like ^ that label in the end of block - - _kbd_sync_report(true); -} - -void ph_usb_mouse_send_button(u8 button, bool state) { - if (!PH_O_IS_MOUSE_USB) { - return; - } - if (state) { - _mouse_buttons |= button; - } else { - _mouse_buttons &= ~button; - } - if (PH_O_IS_MOUSE_USB_ABS) { - _mouse_abs_send_report(0, 0); - } else { // PH_O_IS_MOUSE_USB_REL - _mouse_rel_send_report(0, 0, 0, 0); - } -} - -void ph_usb_mouse_send_abs(s16 x, s16 y) { - if (PH_O_IS_MOUSE_USB_ABS) { - _mouse_abs_x = x; - _mouse_abs_y = y; - _mouse_abs_send_report(0, 0); - } -} - -void ph_usb_mouse_send_rel(s8 x, s8 y) { - if (PH_O_IS_MOUSE_USB_REL) { - _mouse_rel_send_report(x, y, 0, 0); - } -} - -void ph_usb_mouse_send_wheel(s8 h, s8 v) { - if (PH_O_IS_MOUSE_USB_ABS) { - _mouse_abs_send_report(h, v); - } else { // PH_O_IS_MOUSE_USB_REL - _mouse_rel_send_report(0, 0, h, v); - } -} - -void ph_usb_send_clear(void) { - if (PH_O_IS_KBD_USB) { - _KBD_CLEAR; - _kbd_sync_report(true); - } - if (PH_O_IS_MOUSE_USB) { - _MOUSE_CLEAR; - if (PH_O_IS_MOUSE_USB_ABS) { - _mouse_abs_send_report(0, 0); - } else { // PH_O_IS_MOUSE_USB_REL - _mouse_rel_send_report(0, 0, 0, 0); - } - } -} - -//-------------------------------------------------------------------- -// RAW report senders -//-------------------------------------------------------------------- - -static void _kbd_sync_report(bool new) { - static bool sent = true; - if (_kbd_iface < 0 || !PH_O_IS_KBD_USB) { - _KBD_CLEAR; - sent = true; - return; - } - if (new) { - sent = false; - } - if (!sent) { - if (tud_suspended()) { - tud_remote_wakeup(); - //_KBD_CLEAR; - //sent = true; - } else { - sent = tud_hid_n_keyboard_report(_kbd_iface, 0, _kbd_mods, _kbd_keys); - } - } -} - -#define _CHECK_MOUSE(x_mode) { \ - if (_mouse_iface < 0 || !PH_O_IS_MOUSE_USB_##x_mode) { _MOUSE_CLEAR; return; } \ - if (tud_suspended()) { tud_remote_wakeup(); _MOUSE_CLEAR; return; } \ - } - - -static void _mouse_abs_send_report(s8 h, s8 v) { - (void)h; // Horizontal scrolling is not supported due BIOS/UEFI compatibility reasons - _CHECK_MOUSE(ABS); - u16 x = ((s32)_mouse_abs_x + 32768) / 2; - u16 y = ((s32)_mouse_abs_y + 32768) / 2; - if (PH_O_MOUSE(USB_W98)) { - x <<= 1; - y <<= 1; - } - struct TU_ATTR_PACKED { - u8 buttons; - u16 x; - u16 y; - s8 v; - } report = {_mouse_buttons, x, y, v}; - tud_hid_n_report(_mouse_iface, 0, &report, sizeof(report)); -} - -static void _mouse_rel_send_report(s8 x, s8 y, s8 h, s8 v) { - (void)h; // Horizontal scrolling is not supported due BIOS/UEFI compatibility reasons - _CHECK_MOUSE(REL); - struct TU_ATTR_PACKED { - u8 buttons; - s8 x; - s8 y; - s8 v; - } report = {_mouse_buttons, x, y, v}; - tud_hid_n_report(_mouse_iface, 0, &report, sizeof(report)); -} - -#undef _CHECK_MOUSE - - -//-------------------------------------------------------------------- -// Device callbacks -//-------------------------------------------------------------------- - -u16 tud_hid_get_report_cb(u8 iface, u8 report_id, hid_report_type_t report_type, u8 *buf, u16 len) { - // Invoked when received GET_REPORT control request, return 0 == STALL - (void)iface; - (void)report_id; - (void)report_type; - (void)buf; - (void)len; - return 0; -} - -void tud_hid_set_report_cb(u8 iface, u8 report_id, hid_report_type_t report_type, const u8 *buf, u16 len) { - // Invoked when received SET_REPORT control request - // or received data on OUT endpoint (ReportID=0, Type=0) - (void)report_id; - if (iface == _kbd_iface && report_type == HID_REPORT_TYPE_OUTPUT && len >= 1) { - ph_g_usb_kbd_leds = buf[0]; - } -} - -const u8 *tud_hid_descriptor_report_cb(u8 iface) { - if ((int)iface == _mouse_iface) { - if (PH_O_IS_MOUSE_USB_ABS) { - return PH_USB_MOUSE_ABS_DESC; - } else { // PH_O_IS_MOUSE_USB_REL - return PH_USB_MOUSE_REL_DESC; - } - } - return PH_USB_KBD_DESC; // _kbd_iface, PH_O_IS_KBD_USB -} - -const u8 *_bridge_tud_descriptor_configuration_cb(void) { - enum {num_cdc = 0, num_cdc_data, num_total}; - static const u8 desc[] = { - TUD_CONFIG_DESCRIPTOR( - 1, // Config number - num_total,// Interface count - 0, // String index - (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN), // Total length - 0, // Attribute - 100 // Power in mA - ), - TUD_CDC_DESCRIPTOR( - num_cdc,// Interface number - 4, // String index - 0x81, // EPNUM_CDC_NOTIF - EP notification address - 8, // EP notification size - 0x02, // EPNUM_CDC_OUT - EP OUT data address - 0x82, // EPNUM_CDC_IN - EP IN data address - 64 // EP size - ), - }; - return desc; -} - -const u8 *_hid_tud_descriptor_configuration_cb(void) { - static u8 desc[TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN * 2] = {0}; - static bool filled = false; - - if (!filled) { - uz offset = TUD_CONFIG_DESC_LEN; - u8 iface = 0; - u8 ep = 0x81; - -# define APPEND_DESC(x_proto, x_desc, x_iface_to) { \ - const u8 part[] = {TUD_HID_DESCRIPTOR( \ - (x_iface_to = iface), /* Interface number */ \ - 0, x_proto, x_desc##_LEN, /* String index, protocol, report descriptor len */ \ - ep, CFG_TUD_HID_EP_BUFSIZE, 1)}; /* EP In address, size, polling interval */ \ - memcpy(desc + offset, part, TUD_HID_DESC_LEN); \ - offset += TUD_HID_DESC_LEN; ++iface; ++ep; \ - } - - if (PH_O_IS_KBD_USB) { - APPEND_DESC(HID_ITF_PROTOCOL_KEYBOARD, PH_USB_KBD_DESC, _kbd_iface); - } - if (PH_O_IS_MOUSE_USB_ABS) { - APPEND_DESC(HID_ITF_PROTOCOL_NONE, PH_USB_MOUSE_ABS_DESC, _mouse_iface); - } else if (PH_O_IS_MOUSE_USB_REL) { - APPEND_DESC(HID_ITF_PROTOCOL_MOUSE, PH_USB_MOUSE_REL_DESC, _mouse_iface); - } - -# undef APPEND_DESC - - // Config number, interface count, string index, total length, attribute, power in mA - const u8 part[] = {TUD_CONFIG_DESCRIPTOR(1, iface, 0, offset, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100)}; - memcpy(desc, part, TUD_CONFIG_DESC_LEN); - filled = true; - } - return desc; -} - -const u8 *tud_descriptor_configuration_cb(u8 index) { - // Invoked when received GET CONFIGURATION DESCRIPTOR - (void)index; - if (ph_g_is_bridge) { - return _bridge_tud_descriptor_configuration_cb(); - } - return _hid_tud_descriptor_configuration_cb(); -} - -const u8 *tud_descriptor_device_cb(void) { - // Invoked when received GET DEVICE DESCRIPTOR - static tusb_desc_device_t desc = { - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0200, - - .bDeviceClass = 0, - .bDeviceSubClass = 0, - .bDeviceProtocol = 0, - - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - - .idVendor = 0x1209, // https://pid.codes/org/Pi-KVM - .idProduct = 0xEDA2, - .bcdDevice = 0x0100, - - .iManufacturer = 1, - .iProduct = 2, - .iSerialNumber = 3, - - .bNumConfigurations = 1, - }; - if (ph_g_is_bridge) { - desc.bDeviceClass = TUSB_CLASS_MISC; - desc.bDeviceSubClass = MISC_SUBCLASS_COMMON; - desc.bDeviceProtocol = MISC_PROTOCOL_IAD; - desc.idProduct = 0xEDA3; - } - return (const u8 *)&desc; -} - -const u16 *tud_descriptor_string_cb(u8 index, u16 lang_id) { - // Invoked when received GET STRING DESCRIPTOR request. - (void)lang_id; - - static u16 desc_str[32]; - uz desc_str_len; - - if (index == 0) { - desc_str[1] = 0x0409; // Supported language is English (0x0409) - desc_str_len = 1; - } else { - char str[32]; - switch (index) { - case 1: strcpy(str, "PiKVM"); break; // Manufacturer - case 2: strcpy(str, (ph_g_is_bridge ? "PiKVM HID Bridge" : "PiKVM HID")); break; // Product - case 3: pico_get_unique_board_id_string(str, 32); break; // Serial - case 4: { - if (ph_g_is_bridge) { - strcpy(str, "PiKVM HID Bridge CDC"); - } else { - return NULL; - } - }; break; - default: return NULL; - } - desc_str_len = strlen(str); - for (uz i = 0; i < desc_str_len; ++i) { - desc_str[i + 1] = str[i]; // Convert ASCII string into UTF-16 - } - } - - // First byte is length (including header), second byte is string type - desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * desc_str_len + 2); - return desc_str; -} diff --git a/hid/pico/src/ph_usb.h b/hid/pico/src/ph_usb.h deleted file mode 100644 index 357f578d..00000000 --- a/hid/pico/src/ph_usb.h +++ /dev/null @@ -1,43 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#pragma once - -#include "ph_types.h" - - -extern u8 ph_g_usb_kbd_leds; -extern bool ph_g_usb_kbd_online; -extern bool ph_g_usb_mouse_online; - - -void ph_usb_init(void); -void ph_usb_task(void); - -void ph_usb_kbd_send_key(u8 key, bool state); - -void ph_usb_mouse_send_button(u8 button, bool state); -void ph_usb_mouse_send_abs(s16 x, s16 y); -void ph_usb_mouse_send_rel(s8 x, s8 y); -void ph_usb_mouse_send_wheel(s8 h, s8 v); - -void ph_usb_send_clear(void); diff --git a/hid/pico/src/ph_usb_kbd.c b/hid/pico/src/ph_usb_kbd.c deleted file mode 100644 index b4613c27..00000000 --- a/hid/pico/src/ph_usb_kbd.c +++ /dev/null @@ -1,78 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#include "ph_usb_kbd.h" - -#include "ph_types.h" - - -const u8 PH_USB_KBD_DESC[] = { - // Logitech descriptor. It's very similar to https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt - // Dumped using usbhid-dump; parsed using https://eleccelerator.com/usbdescreqparser - - // Keyboard - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x06, // USAGE (Keyboard) - 0xA1, 0x01, // COLLECTION (Application) - - // Modifiers - 0x05, 0x07, // USAGE_PAGE (Keyboard) - 0x19, 0xE0, // USAGE_MINIMUM (Keyboard LeftControl) - 0x29, 0xE7, // USAGE_MAXIMUM (Keyboard Right GUI) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x25, 0x01, // LOGICAL_MAXIMUM (1) - 0x75, 0x01, // REPORT_SIZE (1) - 0x95, 0x08, // REPORT_COUNT (8) - 0x81, 0x02, // INPUT (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - - // Reserved byte - 0x95, 0x01, // REPORT_COUNT (1) - 0x75, 0x08, // REPORT_SIZE (8) - 0x81, 0x01, // INPUT (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - - // LEDs output - 0x95, 0x05, // REPORT_COUNT (5) - 0x75, 0x01, // REPORT_SIZE (1) - 0x05, 0x08, // USAGE_PAGE (LEDs) - 0x19, 0x01, // USAGE_MINIMUM (Num Lock) - 0x29, 0x05, // USAGE_MAXIMUM (Kana) - 0x91, 0x02, // OUTPUT (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - - // Reserved 3 bits in output - 0x95, 0x01, // REPORT_COUNT (1) - 0x75, 0x03, // REPORT_SIZE (3) - 0x91, 0x01, // OUTPUT (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - - // 6 keys - 0x95, 0x06, // REPORT_COUNT (6) - 0x75, 0x08, // REPORT_SIZE (8) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM (0xFF) - 0x05, 0x07, // USAGE_PAGE (Keyboard) - 0x19, 0x00, // USAGE_MINIMUM (Reserved) - 0x2A, 0xFF, 0x00, // USAGE_MAXIMUM (0xFF) - 0x81, 0x00, // INPUT (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - - 0xC0, // END_COLLECTION -}; - -const uz PH_USB_KBD_DESC_LEN = sizeof(PH_USB_KBD_DESC); diff --git a/hid/pico/src/ph_usb_kbd.h b/hid/pico/src/ph_usb_kbd.h deleted file mode 100644 index 8bbdcd05..00000000 --- a/hid/pico/src/ph_usb_kbd.h +++ /dev/null @@ -1,29 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#pragma once - -#include "ph_types.h" - - -extern const u8 PH_USB_KBD_DESC[]; -extern const uz PH_USB_KBD_DESC_LEN; diff --git a/hid/pico/src/ph_usb_keymap.h b/hid/pico/src/ph_usb_keymap.h deleted file mode 100644 index f2481e41..00000000 --- a/hid/pico/src/ph_usb_keymap.h +++ /dev/null @@ -1,143 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "ph_types.h" - - -inline u8 ph_usb_keymap(u8 key) { - switch (key) { - case 1: return 4; // KeyA - case 2: return 5; // KeyB - case 3: return 6; // KeyC - case 4: return 7; // KeyD - case 5: return 8; // KeyE - case 6: return 9; // KeyF - case 7: return 10; // KeyG - case 8: return 11; // KeyH - case 9: return 12; // KeyI - case 10: return 13; // KeyJ - case 11: return 14; // KeyK - case 12: return 15; // KeyL - case 13: return 16; // KeyM - case 14: return 17; // KeyN - case 15: return 18; // KeyO - case 16: return 19; // KeyP - case 17: return 20; // KeyQ - case 18: return 21; // KeyR - case 19: return 22; // KeyS - case 20: return 23; // KeyT - case 21: return 24; // KeyU - case 22: return 25; // KeyV - case 23: return 26; // KeyW - case 24: return 27; // KeyX - case 25: return 28; // KeyY - case 26: return 29; // KeyZ - case 27: return 30; // Digit1 - case 28: return 31; // Digit2 - case 29: return 32; // Digit3 - case 30: return 33; // Digit4 - case 31: return 34; // Digit5 - case 32: return 35; // Digit6 - case 33: return 36; // Digit7 - case 34: return 37; // Digit8 - case 35: return 38; // Digit9 - case 36: return 39; // Digit0 - case 37: return 40; // Enter - case 38: return 41; // Escape - case 39: return 42; // Backspace - case 40: return 43; // Tab - case 41: return 44; // Space - case 42: return 45; // Minus - case 43: return 46; // Equal - case 44: return 47; // BracketLeft - case 45: return 48; // BracketRight - case 46: return 49; // Backslash - case 47: return 51; // Semicolon - case 48: return 52; // Quote - case 49: return 53; // Backquote - case 50: return 54; // Comma - case 51: return 55; // Period - case 52: return 56; // Slash - case 53: return 57; // CapsLock - case 54: return 58; // F1 - case 55: return 59; // F2 - case 56: return 60; // F3 - case 57: return 61; // F4 - case 58: return 62; // F5 - case 59: return 63; // F6 - case 60: return 64; // F7 - case 61: return 65; // F8 - case 62: return 66; // F9 - case 63: return 67; // F10 - case 64: return 68; // F11 - case 65: return 69; // F12 - case 66: return 70; // PrintScreen - case 67: return 73; // Insert - case 68: return 74; // Home - case 69: return 75; // PageUp - case 70: return 76; // Delete - case 71: return 77; // End - case 72: return 78; // PageDown - case 73: return 79; // ArrowRight - case 74: return 80; // ArrowLeft - case 75: return 81; // ArrowDown - case 76: return 82; // ArrowUp - case 77: return 224; // ControlLeft - case 78: return 225; // ShiftLeft - case 79: return 226; // AltLeft - case 80: return 227; // MetaLeft - case 81: return 228; // ControlRight - case 82: return 229; // ShiftRight - case 83: return 230; // AltRight - case 84: return 231; // MetaRight - case 85: return 72; // Pause - case 86: return 71; // ScrollLock - case 87: return 83; // NumLock - case 88: return 101; // ContextMenu - case 89: return 84; // NumpadDivide - case 90: return 85; // NumpadMultiply - case 91: return 86; // NumpadSubtract - case 92: return 87; // NumpadAdd - case 93: return 88; // NumpadEnter - case 94: return 89; // Numpad1 - case 95: return 90; // Numpad2 - case 96: return 91; // Numpad3 - case 97: return 92; // Numpad4 - case 98: return 93; // Numpad5 - case 99: return 94; // Numpad6 - case 100: return 95; // Numpad7 - case 101: return 96; // Numpad8 - case 102: return 97; // Numpad9 - case 103: return 98; // Numpad0 - case 104: return 99; // NumpadDecimal - case 105: return 102; // Power - case 106: return 100; // IntlBackslash - case 107: return 137; // IntlYen - case 108: return 135; // IntlRo - case 109: return 136; // KanaMode - case 110: return 138; // Convert - case 111: return 139; // NonConvert - } - return 0; -} diff --git a/hid/pico/src/ph_usb_keymap.h.mako b/hid/pico/src/ph_usb_keymap.h.mako deleted file mode 100644 index 454f9c1e..00000000 --- a/hid/pico/src/ph_usb_keymap.h.mako +++ /dev/null @@ -1,39 +0,0 @@ -/***************************************************************************** -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -*****************************************************************************/ - - -#pragma once - -#include "ph_types.h" - -<%! import operator %> -inline u8 ph_usb_keymap(u8 key) { - switch (key) { -% for km in sorted(keymap, key=operator.attrgetter("mcu_code")): - % if km.usb_key.is_modifier: - case ${km.mcu_code}: return ${km.usb_key.arduino_modifier_code}; // ${km.web_name} - % else: - case ${km.mcu_code}: return ${km.usb_key.code}; // ${km.web_name} - % endif -% endfor - } - return 0; -} diff --git a/hid/pico/src/ph_usb_mouse.c b/hid/pico/src/ph_usb_mouse.c deleted file mode 100644 index 33f35410..00000000 --- a/hid/pico/src/ph_usb_mouse.c +++ /dev/null @@ -1,120 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#include "ph_usb_mouse.h" - -#include "ph_types.h" - - -const u8 PH_USB_MOUSE_ABS_DESC[] = { - // https://github.com/NicoHood/HID/blob/0835e6a/src/SingleReport/SingleAbsoluteMouse.cpp - // Репорт взят отсюда ^^^, но изменен диапазон значений координат перемещений. - // Автор предлагает использовать -32768...32767, но семерка почему-то не хочет работать - // с отрицательными значениями координат, как не хочет хавать 65536 и 32768. - // Так что мы ей скармливаем диапазон 0...32767, и передаем рукожопам из микрософта привет, - // потому что линуксы прекрасно работают с любыми двухбайтовыми диапазонами. - - // Absolute mouse - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x02, // USAGE (Mouse) - 0xA1, 0x01, // COLLECTION (Application) - - // Pointer and Physical are required by Apple Recovery - 0x09, 0x01, // USAGE (Pointer) - 0xA1, 0x00, // COLLECTION (Physical) - - // 8 Buttons - 0x05, 0x09, // USAGE_PAGE (Button) - 0x19, 0x01, // USAGE_MINIMUM (Button 1) - 0x29, 0x08, // USAGE_MAXIMUM (Button 8) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x25, 0x01, // LOGICAL_MAXIMUM (1) - 0x95, 0x08, // REPORT_COUNT (8) - 0x75, 0x01, // REPORT_SIZE (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - // X, Y - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x30, // USAGE (X) - 0x09, 0x31, // USAGE (Y) - 0x16, 0x00, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0xFF, 0x7F, // LOGICAL_MAXIMUM (32767) - 0x75, 0x10, // REPORT_SIZE (16) - 0x95, 0x02, // REPORT_COUNT (2) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - // Wheel - 0x09, 0x38, // USAGE (Wheel) - 0x15, 0x81, // LOGICAL_MINIMUM (-127) - 0x25, 0x7F, // LOGICAL_MAXIMUM (127) - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x06, // INPUT (Data,Var,Rel) - - // End - 0xC0, // END_COLLECTION (Physical) - 0xC0, // END_COLLECTION -}; - -const uz PH_USB_MOUSE_ABS_DESC_LEN = sizeof(PH_USB_MOUSE_ABS_DESC); - -const u8 PH_USB_MOUSE_REL_DESC[] = { - // https://github.com/NicoHood/HID/blob/0835e6a/src/SingleReport/BootMouse.cpp - - // Relative mouse - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x02, // USAGE (Mouse) - 0xA1, 0x01, // COLLECTION (Application) - - // Pointer and Physical are required by Apple Recovery - 0x09, 0x01, // USAGE (Pointer) - 0xA1, 0x00, // COLLECTION (Physical) - - // 8 Buttons - 0x05, 0x09, // USAGE_PAGE (Button) - 0x19, 0x01, // USAGE_MINIMUM (Button 1) - 0x29, 0x08, // USAGE_MAXIMUM (Button 8) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x25, 0x01, // LOGICAL_MAXIMUM (1) - 0x95, 0x08, // REPORT_COUNT (8) - 0x75, 0x01, // REPORT_SIZE (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - // X, Y - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x30, // USAGE (X) - 0x09, 0x31, // USAGE (Y) - - // Wheel - 0x09, 0x38, // USAGE (Wheel) - 0x15, 0x81, // LOGICAL_MINIMUM (-127) - 0x25, 0x7F, // LOGICAL_MAXIMUM (127) - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x03, // REPORT_COUNT (3) - 0x81, 0x06, // INPUT (Data,Var,Rel) - - // End - 0xC0, // END_COLLECTION (Physical) - 0xC0, // END_COLLECTION -}; - -const uz PH_USB_MOUSE_REL_DESC_LEN = sizeof(PH_USB_MOUSE_REL_DESC); diff --git a/hid/pico/src/ph_usb_mouse.h b/hid/pico/src/ph_usb_mouse.h deleted file mode 100644 index 1d13d73d..00000000 --- a/hid/pico/src/ph_usb_mouse.h +++ /dev/null @@ -1,32 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#pragma once - -#include "ph_types.h" - - -extern const u8 PH_USB_MOUSE_ABS_DESC[]; -extern const uz PH_USB_MOUSE_ABS_DESC_LEN; - -extern const u8 PH_USB_MOUSE_REL_DESC[]; -extern const uz PH_USB_MOUSE_REL_DESC_LEN; diff --git a/hid/pico/src/tusb_config.h b/hid/pico/src/tusb_config.h deleted file mode 100644 index a3986ee1..00000000 --- a/hid/pico/src/tusb_config.h +++ /dev/null @@ -1,79 +0,0 @@ -/* ========================================================================= # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================= */ - - -#pragma once - - -//-------------------------------------------------------------------- -// Common config -//-------------------------------------------------------------------- - -//#define CFG_TUSB_DEBUG 100 - -#define CFG_TUSB_OS OPT_OS_PICO - -// Enable device stack -#define CFG_TUD_ENABLED 1 - -// CFG_TUSB_DEBUG is defined by compiler in DEBUG build -//#define CFG_TUSB_DEBUG 100 - -// USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. -// Tinyusb use follows macros to declare transferring memory so that they can be put -// into those specific section. -// - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) -// - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) -#ifndef CFG_TUSB_MEM_SECTION -# define CFG_TUSB_MEM_SECTION -#endif - -#ifndef CFG_TUSB_MEM_ALIGN -# define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4))) -#endif - - -//-------------------------------------------------------------------- -// Device config -//-------------------------------------------------------------------- - -#ifndef CFG_TUD_ENDPOINT0_SIZE -# define CFG_TUD_ENDPOINT0_SIZE 64 -#endif - -// HID: Keyboard + Mouse -#define CFG_TUD_HID 2 - -// HID buffer size Should be sufficient to hold ID (if any) + Data -#ifndef CFG_TUD_HID_EP_BUFSIZE -# define CFG_TUD_HID_EP_BUFSIZE 16 -#endif - - -// CDC for the bridge mode -#define CFG_TUD_CDC 1 - -// CDC FIFO size of TX and RX -#define CFG_TUD_CDC_RX_BUFSIZE 4096 -#define CFG_TUD_CDC_TX_BUFSIZE 4096 - -// CDC Endpoint transfer buffer size, more is faster -#define CFG_TUD_CDC_EP_BUFSIZE 64 diff --git a/kvmd/apps/kvmd/api/auth.py b/kvmd/apps/kvmd/api/auth.py index dee4a85d..28d56131 100644 --- a/kvmd/apps/kvmd/api/auth.py +++ b/kvmd/apps/kvmd/api/auth.py @@ -84,7 +84,7 @@ class AuthApi: # ===== - @exposed_http("POST", "/auth/login", auth_required=False) + @exposed_http("POST", "/api/auth/login", auth_required=False) async def __login_handler(self, req: Request) -> Response: if self.__auth_manager.is_auth_enabled(): credentials = await req.post() @@ -97,13 +97,13 @@ class AuthApi: raise ForbiddenError() return make_json_response() - @exposed_http("POST", "/auth/logout") + @exposed_http("POST", "/api/auth/logout") async def __logout_handler(self, req: Request) -> Response: if self.__auth_manager.is_auth_enabled(): token = valid_auth_token(req.cookies.get(_COOKIE_AUTH_TOKEN, "")) self.__auth_manager.logout(token) return make_json_response() - @exposed_http("GET", "/auth/check") + @exposed_http("GET", "/api/auth/check") async def __check_handler(self, _: Request) -> Response: return make_json_response() diff --git a/kvmd/apps/kvmd/api/export.py b/kvmd/apps/kvmd/api/export.py index bb048f53..23bcfd4d 100644 --- a/kvmd/apps/kvmd/api/export.py +++ b/kvmd/apps/kvmd/api/export.py @@ -49,7 +49,7 @@ class ExportApi: # ===== - @exposed_http("GET", "/export/prometheus/metrics") + @exposed_http("GET", "/api/export/prometheus/metrics") async def __prometheus_metrics_handler(self, _: Request) -> Response: return Response(text=(await self.__get_prometheus_metrics())) diff --git a/kvmd/apps/kvmd/api/hid.py b/kvmd/apps/kvmd/api/hid.py index 51b9dc00..45f619e4 100644 --- a/kvmd/apps/kvmd/api/hid.py +++ b/kvmd/apps/kvmd/api/hid.py @@ -73,7 +73,7 @@ class HidApi: async def __state_handler(self, _: Request) -> Response: return make_json_response(await self.__hid.get_state()) - @exposed_http("POST", "/hid/set_params") + @exposed_http("POST", "/api/hid/set_params") async def __set_params_handler(self, req: Request) -> Response: params = { key: validator(req.query.get(key)) @@ -87,12 +87,12 @@ class HidApi: self.__hid.set_params(**params) # type: ignore return make_json_response() - @exposed_http("POST", "/hid/set_connected") + @exposed_http("POST", "/api/hid/set_connected") async def __set_connected_handler(self, req: Request) -> Response: self.__hid.set_connected(valid_bool(req.query.get("connected"))) return make_json_response() - @exposed_http("POST", "/hid/reset") + @exposed_http("POST", "/api/hid/reset") async def __reset_handler(self, _: Request) -> Response: await self.__hid.reset() return make_json_response() @@ -112,11 +112,11 @@ class HidApi: }, } - @exposed_http("GET", "/hid/keymaps") + @exposed_http("GET", "/api/hid/keymaps") async def __keymaps_handler(self, _: Request) -> Response: return make_json_response(await self.get_keymaps()) - @exposed_http("POST", "/hid/print") + @exposed_http("POST", "/api/hid/print") async def __print_handler(self, req: Request) -> Response: text = await req.text() limit = int(valid_int_f0(req.query.get("limit", 1024))) @@ -243,7 +243,7 @@ class HidApi: # ===== - @exposed_http("POST", "/hid/events/send_key") + @exposed_http("POST", "/api/hid/events/send_key") async def __events_send_key_handler(self, req: Request) -> Response: key = valid_hid_key(req.query.get("key")) if "state" in req.query: @@ -253,7 +253,7 @@ class HidApi: self.__hid.send_key_events([(key, True), (key, False)]) return make_json_response() - @exposed_http("POST", "/hid/events/send_mouse_button") + @exposed_http("POST", "/api/hid/events/send_mouse_button") async def __events_send_mouse_button_handler(self, req: Request) -> Response: button = valid_hid_mouse_button(req.query.get("button")) if "state" in req.query: @@ -264,18 +264,18 @@ class HidApi: self.__hid.send_mouse_button_event(button, False) return make_json_response() - @exposed_http("POST", "/hid/events/send_mouse_move") + @exposed_http("POST", "/api/hid/events/send_mouse_move") async def __events_send_mouse_move_handler(self, req: Request) -> Response: to_x = valid_hid_mouse_move(req.query.get("to_x")) to_y = valid_hid_mouse_move(req.query.get("to_y")) self.__hid.send_mouse_move_event(to_x, to_y) return make_json_response() - @exposed_http("POST", "/hid/events/send_mouse_relative") + @exposed_http("POST", "/api/hid/events/send_mouse_relative") async def __events_send_mouse_relative_handler(self, req: Request) -> Response: return self.__process_http_delta_event(req, self.__hid.send_mouse_relative_event) - @exposed_http("POST", "/hid/events/send_mouse_wheel") + @exposed_http("POST", "/api/hid/events/send_mouse_wheel") async def __events_send_mouse_wheel_handler(self, req: Request) -> Response: return self.__process_http_delta_event(req, self.__hid.send_mouse_wheel_event) diff --git a/kvmd/apps/kvmd/api/info.py b/kvmd/apps/kvmd/api/info.py index 89d45a84..f2badaee 100644 --- a/kvmd/apps/kvmd/api/info.py +++ b/kvmd/apps/kvmd/api/info.py @@ -20,8 +20,18 @@ # ========================================================================== # +import os +from aiohttp import ClientConnectionError, ClientSession +from aiohttp import UnixConnector from aiohttp.web import Request from aiohttp.web import Response +from aiohttp.web import HTTPForbidden +from aiohttp.web import HTTPNotFound +from aiohttp.web import FileResponse +from aiohttp.web import HTTPInternalServerError + +from aiohttp.web import StreamResponse +from urllib.parse import urlparse, urlunparse, parse_qs, urlencode from ....htserver import exposed_http from ....htserver import make_json_response @@ -35,10 +45,13 @@ from ..info import InfoManager class InfoApi: def __init__(self, info_manager: InfoManager) -> None: self.__info_manager = info_manager + self.static_dir = 'kvmd_data/usr/share/kvmd/web' + self.target_stream_server = 'http://127.0.0.1:8081' # ===== - @exposed_http("GET", "/info") + + @exposed_http("GET", "/api/info") async def __common_state_handler(self, req: Request) -> Response: fields = self.__valid_info_fields(req) return make_json_response(await self.__info_manager.get_state(fields)) @@ -49,3 +62,52 @@ class InfoApi: arg=req.query.get("fields", ",".join(available)), variants=available, ) or available) + + @exposed_http("GET", "/streamer/stream") + async def proxy_stream_handler(self, request): + socket_path = '/home/mofeng/One-KVM/kvmd_data/run/kvmd/ustreamer.sock' + query_string = urlencode(request.query) + headers = request.headers.copy() + try: + async with ClientSession(connector=UnixConnector(path=socket_path)) as session: + backend_url = f'http://localhost/stream?{query_string}' if query_string else 'http://localhost/stream' + async with session.get(backend_url, headers=headers) as resp: + response = StreamResponse(status=resp.status, reason=resp.reason, headers=resp.headers) + await response.prepare(request) + while True: + chunk = await resp.content.read(512000) + if not chunk: + break + await response.write(chunk) + return response + except ClientConnectionError: + return Response(status=500, text="Client connection was closed") + + + @exposed_http("GET", "/{path:.*}", auth_required=False) + async def __html_file_handler(self, req: Request) -> Response: + path = req.match_info['path'] + full_path = os.path.normpath(os.path.join(self.static_dir, path)) + print("---------------") + print(full_path) + + # 安全检查:确保请求的文件在允许的基础目录内 + if not full_path.startswith(self.static_dir): + raise HTTPForbidden(text="Access denied.") + + if os.path.isdir(full_path): + index_path = os.path.join(full_path, 'index.html') + if os.path.isfile(index_path): + full_path = index_path + else: + raise HTTPNotFound(text="Directory does not contain an index.html file.") + + # 检查调整后的路径是否为现有文件 + if not (os.path.exists(full_path) and os.path.isfile(full_path)): + raise HTTPNotFound(text="File not found.") + + try: + return FileResponse(full_path) + except IOError as e: + raise HTTPInternalServerError(text=str(e)) + \ No newline at end of file diff --git a/kvmd/apps/kvmd/api/log.py b/kvmd/apps/kvmd/api/log.py index 1d31ae8f..74af8637 100644 --- a/kvmd/apps/kvmd/api/log.py +++ b/kvmd/apps/kvmd/api/log.py @@ -46,7 +46,7 @@ class LogApi: # ===== - @exposed_http("GET", "/log") + @exposed_http("GET", "/api/log") async def __log_handler(self, req: Request) -> StreamResponse: if self.__log_reader is None: raise LogReaderDisabledError() diff --git a/kvmd/apps/kvmd/api/msd.py b/kvmd/apps/kvmd/api/msd.py index ca1c6cf1..462490b3 100644 --- a/kvmd/apps/kvmd/api/msd.py +++ b/kvmd/apps/kvmd/api/msd.py @@ -61,7 +61,7 @@ class MsdApi: # ===== - @exposed_http("GET", "/msd") + @exposed_http("GET", "/api/msd") async def __state_handler(self, _: Request) -> Response: state = await self.__msd.get_state() if state["storage"] and state["storage"]["parts"]: @@ -69,7 +69,7 @@ class MsdApi: state["storage"]["free"] = state["storage"]["parts"][""]["free"] # Legacy API return make_json_response(state) - @exposed_http("POST", "/msd/set_params") + @exposed_http("POST", "/api/msd/set_params") async def __set_params_handler(self, req: Request) -> Response: params = { key: validator(req.query.get(param)) @@ -83,19 +83,19 @@ class MsdApi: await self.__msd.set_params(**params) # type: ignore return make_json_response() - @exposed_http("POST", "/msd/set_connected") + @exposed_http("POST", "/api/msd/set_connected") async def __set_connected_handler(self, req: Request) -> Response: await self.__msd.set_connected(valid_bool(req.query.get("connected"))) return make_json_response() - @exposed_http("POST", "/msd/make_image") + @exposed_http("POST", "/api/msd/make_image") async def __set_zipped_handler(self, req: Request) -> Response: await self.__msd.make_image(valid_bool(req.query.get("zipped"))) return make_json_response() # ===== - @exposed_http("GET", "/msd/read") + @exposed_http("GET", "/api/msd/read") async def __read_handler(self, req: Request) -> StreamResponse: name = valid_msd_image_name(req.query.get("image")) compressors = { @@ -143,7 +143,7 @@ class MsdApi: # ===== - @exposed_http("POST", "/msd/write") + @exposed_http("POST", "/api/msd/write") async def __write_handler(self, req: Request) -> Response: unsafe_prefix = req.query.get("prefix", "") + "/" name = valid_msd_image_name(unsafe_prefix + req.query.get("image", "")) @@ -159,7 +159,7 @@ class MsdApi: written = await writer.write_chunk(chunk) return make_json_response(self.__make_write_info(name, size, written)) - @exposed_http("POST", "/msd/write_remote") + @exposed_http("POST", "/api/msd/write_remote") async def __write_remote_handler(self, req: Request) -> (Response | StreamResponse): # pylint: disable=too-many-locals unsafe_prefix = req.query.get("prefix", "") + "/" url = valid_url(req.query.get("url")) @@ -223,12 +223,12 @@ class MsdApi: # ===== - @exposed_http("POST", "/msd/remove") + @exposed_http("POST", "/api/msd/remove") async def __remove_handler(self, req: Request) -> Response: await self.__msd.remove(valid_msd_image_name(req.query.get("image"))) return make_json_response() - @exposed_http("POST", "/msd/reset") + @exposed_http("POST", "/api/msd/reset") async def __reset_handler(self, _: Request) -> Response: await self.__msd.reset() return make_json_response() diff --git a/kvmd/apps/kvmd/api/redfish.py b/kvmd/apps/kvmd/api/redfish.py index 3b248685..5943b948 100644 --- a/kvmd/apps/kvmd/api/redfish.py +++ b/kvmd/apps/kvmd/api/redfish.py @@ -65,7 +65,7 @@ class RedfishApi: # ===== - @exposed_http("GET", "/redfish/v1", auth_required=False) + @exposed_http("GET", "/api/redfish/v1", auth_required=False) async def __root_handler(self, _: Request) -> Response: return make_json_response({ "@odata.id": "/redfish/v1", @@ -76,7 +76,7 @@ class RedfishApi: "Systems": {"@odata.id": "/redfish/v1/Systems"}, }, wrap_result=False) - @exposed_http("GET", "/redfish/v1/Systems") + @exposed_http("GET", "/api/redfish/v1/Systems") async def __systems_handler(self, _: Request) -> Response: return make_json_response({ "@odata.id": "/redfish/v1/Systems", @@ -86,7 +86,7 @@ class RedfishApi: "Name": "Computer System Collection", }, wrap_result=False) - @exposed_http("GET", "/redfish/v1/Systems/0") + @exposed_http("GET", "/api/redfish/v1/Systems/0") async def __server_handler(self, _: Request) -> Response: (atx_state, info_state) = await asyncio.gather(*[ self.__atx.get_state(), @@ -110,7 +110,7 @@ class RedfishApi: "PowerState": ("On" if atx_state["leds"]["power"] else "Off"), # type: ignore }, wrap_result=False) - @exposed_http("POST", "/redfish/v1/Systems/0/Actions/ComputerSystem.Reset") + @exposed_http("POST", "/api/redfish/v1/Systems/0/Actions/ComputerSystem.Reset") async def __power_handler(self, req: Request) -> Response: try: action = check_string_in_list( diff --git a/kvmd/apps/kvmd/api/streamer.py b/kvmd/apps/kvmd/api/streamer.py index 81f255d0..ba74755a 100644 --- a/kvmd/apps/kvmd/api/streamer.py +++ b/kvmd/apps/kvmd/api/streamer.py @@ -47,11 +47,11 @@ class StreamerApi: # ===== - @exposed_http("GET", "/streamer") + @exposed_http("GET", "/api/streamer") async def __state_handler(self, _: Request) -> Response: return make_json_response(await self.__streamer.get_state()) - @exposed_http("GET", "/streamer/snapshot") + @exposed_http("GET", "/api/streamer/snapshot") async def __take_snapshot_handler(self, req: Request) -> Response: snapshot = await self.__streamer.take_snapshot( save=valid_bool(req.query.get("save", False)), @@ -92,11 +92,11 @@ class StreamerApi: ) raise UnavailableError() - @exposed_http("DELETE", "/streamer/snapshot") + @exposed_http("DELETE", "/api/streamer/snapshot") async def __remove_snapshot_handler(self, _: Request) -> Response: self.__streamer.remove_snapshot() return make_json_response() - @exposed_http("GET", "/streamer/ocr") + @exposed_http("GET", "/api/streamer/ocr") async def __ocr_handler(self, _: Request) -> Response: return make_json_response({"ocr": (await self.__ocr.get_state())}) diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py index ed85bb24..a3595c6b 100644 --- a/kvmd/apps/kvmd/server.py +++ b/kvmd/apps/kvmd/server.py @@ -197,7 +197,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins # ===== STREAMER CONTROLLER - @exposed_http("POST", "/streamer/set_params") + @exposed_http("POST", "/api/streamer/set_params") async def __streamer_set_params_handler(self, req: Request) -> Response: current_params = self.__streamer.get_params() for (name, validator, exc_cls) in [ @@ -218,7 +218,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins self.__streamer_notifier.notify() return make_json_response() - @exposed_http("POST", "/streamer/reset") + @exposed_http("POST", "/api/streamer/reset") async def __streamer_reset_handler(self, _: Request) -> Response: self.__reset_streamer = True self.__streamer_notifier.notify() @@ -226,7 +226,7 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins # ===== WEBSOCKET - @exposed_http("GET", "/ws") + @exposed_http("GET", "/api/ws") async def __ws_handler(self, req: Request) -> WebSocketResponse: stream = valid_bool(req.query.get("stream", True)) legacy = valid_bool(req.query.get("legacy", True)) diff --git a/kvmd/clients/__init__.py b/kvmd/clients/__init__.py index e917c9f6..f0645fd2 100644 --- a/kvmd/clients/__init__.py +++ b/kvmd/clients/__init__.py @@ -72,13 +72,23 @@ class BaseHttpClient: def make_session(self) -> BaseHttpClientSession: raise NotImplementedError - def _make_http_session(self, headers: (dict[str, str] | None)=None) -> aiohttp.ClientSession: + def _make_http_session(self, headers: dict[str, str] | None = None) -> aiohttp.ClientSession: + connector = None + #这里临时使用 socket ,后期考虑是否使用 http 方式 + use_unix_socket = True + if use_unix_socket: + connector = aiohttp.UnixConnector(path=self.__unix_path) + base_url = "http://localhost:0" # 继续使用 Unix 域套接字 + else: + base_url = "http://127.0.0.1:8001" # 使用指定的 IP 和端口 + + #print("base_url:", base_url) return aiohttp.ClientSession( - base_url="http://localhost:0", + base_url=base_url, headers={ "User-Agent": self.__user_agent, **(headers or {}), }, - connector=aiohttp.UnixConnector(path=self.__unix_path), + connector=connector, timeout=aiohttp.ClientTimeout(total=self.__timeout), ) diff --git a/kvmd/htserver.py b/kvmd/htserver.py index 351c1328..6b02f36a 100644 --- a/kvmd/htserver.py +++ b/kvmd/htserver.py @@ -291,13 +291,18 @@ class HttpServer: ) -> None: self.__ws_heartbeat = heartbeat - - if unix_rm and os.path.exists(unix_path): - os.remove(unix_path) - server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - server_socket.bind(unix_path) - if unix_mode: - os.chmod(unix_path, unix_mode) + # 默认绑定到所有地址 + host = '0.0.0.0' + port = 8080 + #if unix_rm and os.path.exists(unix_path): + #os.remove(unix_path) + #server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + #server_socket.bind(unix_path) + #if unix_mode: + # os.chmod(unix_path, unix_mode) + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 允许重用地址 + server_socket.bind((host, port)) run_app( sock=server_socket, diff --git a/configs/kvmd/totp.secret b/kvmd_data/etc/kvmd/.docker_flag similarity index 100% rename from configs/kvmd/totp.secret rename to kvmd_data/etc/kvmd/.docker_flag diff --git a/configs/os/cmdline/v1-hdmiusb-rpi2.sed b/kvmd_data/etc/kvmd/.init_flag similarity index 100% rename from configs/os/cmdline/v1-hdmiusb-rpi2.sed rename to kvmd_data/etc/kvmd/.init_flag diff --git a/configs/kvmd/armbain-motd b/kvmd_data/etc/kvmd/armbain-motd similarity index 100% rename from configs/kvmd/armbain-motd rename to kvmd_data/etc/kvmd/armbain-motd diff --git a/configs/kvmd/atx.sh b/kvmd_data/etc/kvmd/atx.sh similarity index 100% rename from configs/kvmd/atx.sh rename to kvmd_data/etc/kvmd/atx.sh diff --git a/configs/kvmd/auth.yaml b/kvmd_data/etc/kvmd/auth.yaml similarity index 100% rename from configs/kvmd/auth.yaml rename to kvmd_data/etc/kvmd/auth.yaml diff --git a/configs/kvmd/clean_when_exit.sh b/kvmd_data/etc/kvmd/clean_when_exit.sh similarity index 100% rename from configs/kvmd/clean_when_exit.sh rename to kvmd_data/etc/kvmd/clean_when_exit.sh diff --git a/configs/kvmd/custom_atx/gpio.sh b/kvmd_data/etc/kvmd/custom_atx/gpio.sh similarity index 100% rename from configs/kvmd/custom_atx/gpio.sh rename to kvmd_data/etc/kvmd/custom_atx/gpio.sh diff --git a/configs/kvmd/custom_atx/usbrelay_hid.py b/kvmd_data/etc/kvmd/custom_atx/usbrelay_hid.py similarity index 100% rename from configs/kvmd/custom_atx/usbrelay_hid.py rename to kvmd_data/etc/kvmd/custom_atx/usbrelay_hid.py diff --git a/configs/kvmd/custom_atx/usbrelay_hid.sh b/kvmd_data/etc/kvmd/custom_atx/usbrelay_hid.sh similarity index 100% rename from configs/kvmd/custom_atx/usbrelay_hid.sh rename to kvmd_data/etc/kvmd/custom_atx/usbrelay_hid.sh diff --git a/configs/kvmd/htpasswd b/kvmd_data/etc/kvmd/htpasswd similarity index 100% rename from configs/kvmd/htpasswd rename to kvmd_data/etc/kvmd/htpasswd diff --git a/configs/hw_info/model b/kvmd_data/etc/kvmd/hw_info/model similarity index 100% rename from configs/hw_info/model rename to kvmd_data/etc/kvmd/hw_info/model diff --git a/configs/hw_info/serial-number b/kvmd_data/etc/kvmd/hw_info/serial-number similarity index 100% rename from configs/hw_info/serial-number rename to kvmd_data/etc/kvmd/hw_info/serial-number diff --git a/configs/kvmd/ipmipasswd b/kvmd_data/etc/kvmd/ipmipasswd similarity index 100% rename from configs/kvmd/ipmipasswd rename to kvmd_data/etc/kvmd/ipmipasswd diff --git a/configs/janus/janus.jcfg b/kvmd_data/etc/kvmd/janus/janus.jcfg similarity index 100% rename from configs/janus/janus.jcfg rename to kvmd_data/etc/kvmd/janus/janus.jcfg diff --git a/configs/janus/janus.plugin.ustreamer.jcfg b/kvmd_data/etc/kvmd/janus/janus.plugin.ustreamer.jcfg similarity index 100% rename from configs/janus/janus.plugin.ustreamer.jcfg rename to kvmd_data/etc/kvmd/janus/janus.plugin.ustreamer.jcfg diff --git a/configs/janus/janus.transport.websockets.jcfg b/kvmd_data/etc/kvmd/janus/janus.transport.websockets.jcfg similarity index 100% rename from configs/janus/janus.transport.websockets.jcfg rename to kvmd_data/etc/kvmd/janus/janus.transport.websockets.jcfg diff --git a/configs/kvmd/kvm_input.sh b/kvmd_data/etc/kvmd/kvm_input.sh similarity index 97% rename from configs/kvmd/kvm_input.sh rename to kvmd_data/etc/kvmd/kvm_input.sh index e907d7ca..d66cf3eb 100755 --- a/configs/kvmd/kvm_input.sh +++ b/kvmd_data/etc/kvmd/kvm_input.sh @@ -19,7 +19,7 @@ else fi if [[ "$2" == "" ]]; then - IP="localhost" + IP="localhost:4430" else IP="$2" fi diff --git a/configs/kvmd/logging.yaml b/kvmd_data/etc/kvmd/logging.yaml similarity index 100% rename from configs/kvmd/logging.yaml rename to kvmd_data/etc/kvmd/logging.yaml diff --git a/configs/kvmd/main.yaml b/kvmd_data/etc/kvmd/main.yaml similarity index 100% rename from configs/kvmd/main.yaml rename to kvmd_data/etc/kvmd/main.yaml diff --git a/configs/kvmd/main/v4plus-hdmi-rpi4.yaml b/kvmd_data/etc/kvmd/main/v4plus-hdmi-rpi4.yaml similarity index 100% rename from configs/kvmd/main/v4plus-hdmi-rpi4.yaml rename to kvmd_data/etc/kvmd/main/v4plus-hdmi-rpi4.yaml diff --git a/configs/kvmd/meta.yaml b/kvmd_data/etc/kvmd/meta.yaml similarity index 91% rename from configs/kvmd/meta.yaml rename to kvmd_data/etc/kvmd/meta.yaml index 4127ffd0..f024a83f 100644 --- a/configs/kvmd/meta.yaml +++ b/kvmd_data/etc/kvmd/meta.yaml @@ -4,7 +4,7 @@ # will be displayed in the web interface. server: - host: localhost.localdomain + host: docker kvm: { base_on: PiKVM, diff --git a/configs/nginx/certbot.ctx-server.conf b/kvmd_data/etc/kvmd/nginx/certbot.ctx-server.conf similarity index 68% rename from configs/nginx/certbot.ctx-server.conf rename to kvmd_data/etc/kvmd/nginx/certbot.ctx-server.conf index 6c8ea525..fbc144b8 100644 --- a/configs/nginx/certbot.ctx-server.conf +++ b/kvmd_data/etc/kvmd/nginx/certbot.ctx-server.conf @@ -1,5 +1,5 @@ location /.well-known/acme-challenge { root /run/kvmd-certbot/webroot; - include /etc/kvmd/nginx/loc-nocache.conf; + include loc-nocache.conf; auth_request off; } diff --git a/kvmd_data/etc/kvmd/nginx/kvmd.ctx-http.conf b/kvmd_data/etc/kvmd/nginx/kvmd.ctx-http.conf new file mode 100644 index 00000000..899a2eaf --- /dev/null +++ b/kvmd_data/etc/kvmd/nginx/kvmd.ctx-http.conf @@ -0,0 +1,7 @@ +upstream kvmd { + server unix:/home/mofeng/kvmd_data/run/kvmd/kvmd.sock fail_timeout=0s max_fails=0; +} + +upstream ustreamer { + server unix:/home/mofeng/kvmd_data/run/kvmd/ustreamer.sock fail_timeout=0s max_fails=0; +} diff --git a/configs/nginx/kvmd.ctx-server.conf b/kvmd_data/etc/kvmd/nginx/kvmd.ctx-server.conf similarity index 63% rename from configs/nginx/kvmd.ctx-server.conf rename to kvmd_data/etc/kvmd/nginx/kvmd.ctx-server.conf index 335849bb..05c6a11c 100644 --- a/configs/nginx/kvmd.ctx-server.conf +++ b/kvmd_data/etc/kvmd/nginx/kvmd.ctx-server.conf @@ -13,9 +13,9 @@ location = /auth_check { } location / { - root /usr/share/kvmd/web; - include /etc/kvmd/nginx/loc-login.conf; - include /etc/kvmd/nginx/loc-nocache.conf; + root /home/mofeng/kvmd_data/usr/share/kvmd/web; + include loc-login.conf; + include loc-nocache.conf; } location @login { @@ -23,30 +23,30 @@ location @login { } location /login { - root /usr/share/kvmd/web; + root /home/mofeng/kvmd_data/usr/share/kvmd/web; auth_request off; } location /share { - root /usr/share/kvmd/web; - include /etc/kvmd/nginx/loc-nocache.conf; + root /home/mofeng/kvmd_data/usr/share/kvmd/web; + include loc-nocache.conf; auth_request off; } location = /share/css/user.css { - alias /etc/kvmd/web.css; + alias /home/mofeng/kvmd_data/etc/kvmd/web.css; auth_request off; } location = /favicon.ico { - alias /usr/share/kvmd/web/favicon.ico; - include /etc/kvmd/nginx/loc-nocache.conf; + alias /home/mofeng/kvmd_data/usr/share/kvmd/web/favicon.ico; + include loc-nocache.conf; auth_request off; } location = /robots.txt { - alias /usr/share/kvmd/web/robots.txt; - include /etc/kvmd/nginx/loc-nocache.conf; + alias /home/mofeng/kvmd_data/usr/share/kvmd/web/robots.txt; + include loc-nocache.conf; auth_request off; } @@ -54,8 +54,8 @@ location /api/ws { rewrite ^/api/ws$ /ws break; rewrite ^/api/ws\?(.*)$ /ws?$1 break; proxy_pass http://kvmd; - include /etc/kvmd/nginx/loc-proxy.conf; - include /etc/kvmd/nginx/loc-websocket.conf; + include loc-proxy.conf; + include loc-websocket.conf; auth_request off; } @@ -63,8 +63,8 @@ location /api/hid/print { rewrite ^/api/hid/print$ /hid/print break; rewrite ^/api/hid/print\?(.*)$ /hid/print?$1 break; proxy_pass http://kvmd; - include /etc/kvmd/nginx/loc-proxy.conf; - include /etc/kvmd/nginx/loc-bigpost.conf; + include loc-proxy.conf; + include loc-bigpost.conf; auth_request off; } @@ -72,8 +72,8 @@ location /api/msd/read { rewrite ^/api/msd/read$ /msd/read break; rewrite ^/api/msd/read\?(.*)$ /msd/read?$1 break; proxy_pass http://kvmd; - include /etc/kvmd/nginx/loc-proxy.conf; - include /etc/kvmd/nginx/loc-nobuffering.conf; + include loc-proxy.conf; + include loc-nobuffering.conf; proxy_read_timeout 7d; auth_request off; } @@ -82,8 +82,8 @@ location /api/msd/write_remote { rewrite ^/api/msd/write_remote$ /msd/write_remote break; rewrite ^/api/msd/write_remote\?(.*)$ /msd/write_remote?$1 break; proxy_pass http://kvmd; - include /etc/kvmd/nginx/loc-proxy.conf; - include /etc/kvmd/nginx/loc-nobuffering.conf; + include loc-proxy.conf; + include loc-nobuffering.conf; proxy_read_timeout 7d; auth_request off; } @@ -92,8 +92,8 @@ location /api/msd/write { rewrite ^/api/msd/write$ /msd/write break; rewrite ^/api/msd/write\?(.*)$ /msd/write?$1 break; proxy_pass http://kvmd; - include /etc/kvmd/nginx/loc-proxy.conf; - include /etc/kvmd/nginx/loc-bigpost.conf; + include loc-proxy.conf; + include loc-bigpost.conf; auth_request off; } @@ -101,8 +101,8 @@ location /api/log { rewrite ^/api/log$ /log break; rewrite ^/api/log\?(.*)$ /log?$1 break; proxy_pass http://kvmd; - include /etc/kvmd/nginx/loc-proxy.conf; - include /etc/kvmd/nginx/loc-nobuffering.conf; + include loc-proxy.conf; + include loc-nobuffering.conf; proxy_read_timeout 7d; auth_request off; } @@ -111,7 +111,7 @@ location /api { rewrite ^/api$ / break; rewrite ^/api/(.*)$ /$1 break; proxy_pass http://kvmd; - include /etc/kvmd/nginx/loc-proxy.conf; + include loc-proxy.conf; auth_request off; } @@ -120,12 +120,12 @@ location /streamer { rewrite ^/streamer\?(.*)$ ?$1 break; rewrite ^/streamer/(.*)$ /$1 break; proxy_pass http://ustreamer; - include /etc/kvmd/nginx/loc-proxy.conf; - include /etc/kvmd/nginx/loc-nobuffering.conf; + include loc-proxy.conf; + include loc-nobuffering.conf; } location /redfish { proxy_pass http://kvmd; - include /etc/kvmd/nginx/loc-proxy.conf; + include loc-proxy.conf; auth_request off; } diff --git a/configs/nginx/loc-bigpost.conf b/kvmd_data/etc/kvmd/nginx/loc-bigpost.conf similarity index 100% rename from configs/nginx/loc-bigpost.conf rename to kvmd_data/etc/kvmd/nginx/loc-bigpost.conf diff --git a/configs/nginx/loc-login.conf b/kvmd_data/etc/kvmd/nginx/loc-login.conf similarity index 100% rename from configs/nginx/loc-login.conf rename to kvmd_data/etc/kvmd/nginx/loc-login.conf diff --git a/configs/nginx/loc-nobuffering.conf b/kvmd_data/etc/kvmd/nginx/loc-nobuffering.conf similarity index 100% rename from configs/nginx/loc-nobuffering.conf rename to kvmd_data/etc/kvmd/nginx/loc-nobuffering.conf diff --git a/configs/nginx/loc-nocache.conf b/kvmd_data/etc/kvmd/nginx/loc-nocache.conf similarity index 100% rename from configs/nginx/loc-nocache.conf rename to kvmd_data/etc/kvmd/nginx/loc-nocache.conf diff --git a/configs/nginx/loc-proxy.conf b/kvmd_data/etc/kvmd/nginx/loc-proxy.conf similarity index 100% rename from configs/nginx/loc-proxy.conf rename to kvmd_data/etc/kvmd/nginx/loc-proxy.conf diff --git a/configs/nginx/loc-websocket.conf b/kvmd_data/etc/kvmd/nginx/loc-websocket.conf similarity index 100% rename from configs/nginx/loc-websocket.conf rename to kvmd_data/etc/kvmd/nginx/loc-websocket.conf diff --git a/configs/nginx/mime-types.conf b/kvmd_data/etc/kvmd/nginx/mime-types.conf similarity index 100% rename from configs/nginx/mime-types.conf rename to kvmd_data/etc/kvmd/nginx/mime-types.conf diff --git a/kvmd_data/etc/kvmd/nginx/nginx.conf b/kvmd_data/etc/kvmd/nginx/nginx.conf new file mode 100644 index 00000000..191d57f3 --- /dev/null +++ b/kvmd_data/etc/kvmd/nginx/nginx.conf @@ -0,0 +1,55 @@ +worker_processes 4; +pid /tmp/kvmd-nginx.pid; +# error_log /tmp/kvmd-nginx.error.log; +error_log stderr; + +include /usr/share/kvmd/extras/*/nginx.ctx-main.conf; + +events { + worker_connections 1024; + use epoll; + multi_accept on; +} + +http { + types_hash_max_size 4096; + server_names_hash_bucket_size 128; + + access_log off; + + include mime-types.conf; + default_type application/octet-stream; + charset utf-8; + + sendfile on; + tcp_nodelay on; + tcp_nopush on; + keepalive_timeout 10; + client_max_body_size 4k; + + client_body_temp_path /tmp/kvmd-nginx/client_body_temp; + fastcgi_temp_path /tmp/kvmd-nginx/fastcgi_temp; + proxy_temp_path /tmp/kvmd-nginx/proxy_temp; + scgi_temp_path /tmp/kvmd-nginx/scgi_temp; + uwsgi_temp_path /tmp/kvmd-nginx/uwsgi_temp; + + include kvmd.ctx-http.conf; + include /usr/share/kvmd/extras/*/nginx.ctx-http.conf; + + + server { + listen 8080; + include certbot.ctx-server.conf; + location / { + return 301 https://$host:4430$request_uri; + } + } + + server { + listen 4430 ssl http2; + include ssl.conf; + include kvmd.ctx-server.conf; + include /usr/share/kvmd/extras/*/nginx.ctx-server.conf; + } + +} diff --git a/configs/nginx/nginx.conf.mako b/kvmd_data/etc/kvmd/nginx/nginx.conf.mako similarity index 100% rename from configs/nginx/nginx.conf.mako rename to kvmd_data/etc/kvmd/nginx/nginx.conf.mako diff --git a/configs/nginx/ssl.conf b/kvmd_data/etc/kvmd/nginx/ssl.conf similarity index 65% rename from configs/nginx/ssl.conf rename to kvmd_data/etc/kvmd/nginx/ssl.conf index 8d700f35..ffd0a018 100644 --- a/configs/nginx/ssl.conf +++ b/kvmd_data/etc/kvmd/nginx/ssl.conf @@ -1,5 +1,5 @@ ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1; ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; -ssl_certificate /etc/kvmd/nginx/ssl/server.crt; -ssl_certificate_key /etc/kvmd/nginx/ssl/server.key; +ssl_certificate ssl/server.crt; +ssl_certificate_key ssl/server.key; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; diff --git a/kvmd_data/etc/kvmd/nginx/ssl/server.crt b/kvmd_data/etc/kvmd/nginx/ssl/server.crt new file mode 100644 index 00000000..29c9fd7e --- /dev/null +++ b/kvmd_data/etc/kvmd/nginx/ssl/server.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB1zCCAX2gAwIBAgIUfHNyb1wMmgGuQnbTZUYENGtW69UwCgYIKoZIzj0EAwIw +QTELMAkGA1UEBhMCVVMxDjAMBgNVBAoMBVBpS1ZNMQ4wDAYDVQQLDAVQaUtWTTES +MBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MTIzMDE2NTkyNFoXDTM0MTIyODE2NTky +NFowQTELMAkGA1UEBhMCVVMxDjAMBgNVBAoMBVBpS1ZNMQ4wDAYDVQQLDAVQaUtW +TTESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +TrSca0xuIbYA9axtmupRnu0nG5QVgkGBwOHr44SrJe/kTkRL/p0EC0iQo2ZXwork +6Z4kBNcUlYb2QkvBboqaWqNTMFEwHQYDVR0OBBYEFBhkv8k5r6L3eqz8zO6bQ0AT +mtFAMB8GA1UdIwQYMBaAFBhkv8k5r6L3eqz8zO6bQ0ATmtFAMA8GA1UdEwEB/wQF +MAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIgbYxKLTqhxr13m75Vlil8a1uILYiv9j4D +cdTNXdoVTH0CIQCFSCXvt93o5GgNqhg0l3il66yuCqevHoDqOwQbP0/7PQ== +-----END CERTIFICATE----- diff --git a/kvmd_data/etc/kvmd/nginx/ssl/server.key b/kvmd_data/etc/kvmd/nginx/ssl/server.key new file mode 100644 index 00000000..e902a894 --- /dev/null +++ b/kvmd_data/etc/kvmd/nginx/ssl/server.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIF+vBzNTRb0YW7hzHSWPaLydFqN4TJCjPo5W+5T2u2k5oAoGCCqGSM49 +AwEHoUQDQgAETrSca0xuIbYA9axtmupRnu0nG5QVgkGBwOHr44SrJe/kTkRL/p0E +C0iQo2ZXwork6Z4kBNcUlYb2QkvBboqaWg== +-----END EC PRIVATE KEY----- diff --git a/configs/kvmd/override.yaml b/kvmd_data/etc/kvmd/override.yaml similarity index 72% rename from configs/kvmd/override.yaml rename to kvmd_data/etc/kvmd/override.yaml index a21e9064..0b9cd197 100644 --- a/configs/kvmd/override.yaml +++ b/kvmd_data/etc/kvmd/override.yaml @@ -1,6 +1,24 @@ kvmd: + info: + meta: kvmd_data/etc/kvmd/meta.yaml + extras: kvmd_data/usr/share/kvmd/extras + hw: + platform: kvmd_data/usr/share/kvmd/platform + vcgencmd_cmd: kvmd_data/usr/bin/vcgencmd auth: enabled: true + internal: + file: kvmd_data/etc/kvmd/htpasswd + + totp: + secret: + file: kvmd_data/etc/kvmd/totp.secret + + + server: + unix: kvmd_data/run/kvmd/kvmd.sock + #unix_mode: 432 + #unix_rm: true atx: type: disabled @@ -8,7 +26,7 @@ kvmd: hid: type: ch9329 device: /dev/ttyUSB0 - speed: 9600 + speed: 115200 read_timeout: 0.3 jiggler: @@ -18,6 +36,8 @@ kvmd: mouse_alt: device: /dev/kvmd-hid-mouse-alt + keymap: kvmd_data/usr/share/kvmd/keymaps/en-us + msd: #type: otg remount_cmd: /bin/true @@ -44,29 +64,27 @@ kvmd: default: 8000 cmd: - - "/usr/bin/ustreamer" + - "kvmd_data/usr/bin/ustreamer" - "--device=/dev/video0" - "--persistent" - "--format=mjpeg" - - "--encoder=LIBX264-VIDEO" - "--resolution={resolution}" - "--desired-fps={desired_fps}" - "--drop-same-frames=30" - "--last-as-blank=0" - "--unix={unix}" - "--unix-rm" - - "--unix-mode=0666" + - "--unix-mode=777" - "--exit-on-parent-death" - "--process-name-prefix={process_name_prefix}" - "--notify-parent" - "--no-log-colors" - - "--h264-sink=kvmd::ustreamer::h264" - - "--h264-sink-mode=0660" - "--jpeg-sink=kvmd::ustreamer::jpeg" - "--jpeg-sink-mode=0660" - - "--h264-bitrate={h264_bitrate}" - - "--h264-gop={h264_gop}" - "--slowdown" + + unix: kvmd_data/run/kvmd/ustreamer.sock + gpio: drivers: wol_server1: @@ -74,19 +92,19 @@ kvmd: mac: 2c:56:dc:db:7c:1e short_press: type: cmd - cmd: [/etc/kvmd/atx.sh, short] + cmd: [kvmd_data/etc/kvmd/atx.sh, short] long_press: type: cmd - cmd: [/etc/kvmd/atx.sh, long] + cmd: [kvmd_data/etc/kvmd/atx.sh, long] reset_press: type: cmd - cmd: [/etc/kvmd/atx.sh, reset] + cmd: [kvmd_data/etc/kvmd/atx.sh, reset] input1: type: cmd - cmd: [/etc/kvmd/kvm_input.sh, 1] + cmd: [kvmd_data/etc/kvmd/kvm_input.sh, 1] input2: type: cmd - cmd: [/etc/kvmd/kvm_input.sh, 2] + cmd: [kvmd_data/etc/kvmd/kvm_input.sh, 2] scheme: wol_server1: driver: wol_server1 @@ -133,21 +151,27 @@ kvmd: - ["#KVM 切换"] - ["#HDMI 1", input1-button|切换] - ["#HDMI 2", input2-button|切换] - +ipmi: + auth: + file: kvmd_data/etc/kvmd/ipmipasswd vnc: - keymap: /usr/share/kvmd/keymaps/en-us + keymap: kvmd_data/usr/share/kvmd/keymaps/en-us mouse_output: usb + server: + tls: + x509: + cert: kvmd_data/etc/kvmd/vnc/ssl/server.crt + key: kvmd_data/etc/kvmd/vnc/ssl/server.key auth: vncauth: enabled: true + file: kvmd_data/etc/kvmd/vncpasswd memsink: jpeg: sink: "kvmd::ustreamer::jpeg" - h264: - sink: "kvmd::ustreamer::h264" otgnet: commands: @@ -160,4 +184,8 @@ nginx: http: port: 8080 https: - port: 4430 \ No newline at end of file + port: 4430 + +janus: + cmd: + - "/bin/true" \ No newline at end of file diff --git a/configs/kvmd/supervisord.conf b/kvmd_data/etc/kvmd/supervisord.conf similarity index 61% rename from configs/kvmd/supervisord.conf rename to kvmd_data/etc/kvmd/supervisord.conf index 9867da32..f2e6fd63 100644 --- a/configs/kvmd/supervisord.conf +++ b/kvmd_data/etc/kvmd/supervisord.conf @@ -54,3 +54,36 @@ stdout_logfile=/dev/stdout stdout_logfile_maxbytes = 0 redirect_stderr=true + +[program:kvmd-webterm] +command=/usr/local/bin/ttyd --interface=/run/kvmd/ttyd.sock --port=0 --writable /bin/bash -c '/etc/kvmd/armbain-motd; bash' +directory=/ +autostart=true +autorestart=true +priority=14 +stopasgroup=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes = 0 +redirect_stderr=true + +[program:kvmd-vnc] +command=python -m kvmd.apps.vnc --run +directory=/ +autostart=true +autorestart=true +priority=11 +stopasgroup=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes = 0 +redirect_stderr=true + +[program:kvmd-ipmi] +command=python -m kvmd.apps.ipmi --run +directory=/ +autostart=true +autorestart=true +priority=12 +stopasgroup=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes = 0 +redirect_stderr=true diff --git a/configs/os/cmdline/v1-hdmiusb-rpi3.sed b/kvmd_data/etc/kvmd/totp.secret similarity index 100% rename from configs/os/cmdline/v1-hdmiusb-rpi3.sed rename to kvmd_data/etc/kvmd/totp.secret diff --git a/kvmd_data/etc/kvmd/vnc/ssl/server.crt b/kvmd_data/etc/kvmd/vnc/ssl/server.crt new file mode 100644 index 00000000..b967b5fe --- /dev/null +++ b/kvmd_data/etc/kvmd/vnc/ssl/server.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB2DCCAX2gAwIBAgIUfRsPya5RSXxEDeSBi1iJiYfMfnQwCgYIKoZIzj0EAwIw +QTELMAkGA1UEBhMCVVMxDjAMBgNVBAoMBVBpS1ZNMQ4wDAYDVQQLDAVQaUtWTTES +MBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MTIzMDE2NTkyNFoXDTM0MTIyODE2NTky +NFowQTELMAkGA1UEBhMCVVMxDjAMBgNVBAoMBVBpS1ZNMQ4wDAYDVQQLDAVQaUtW +TTESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +8OfAJl4ck4EnmpO9LvvBusatRFPm5qCYM1Al5nxql647sy87PjBvbSM1L9CLuj1z +3wAQnGmFD1DDGyNAFm9uZ6NTMFEwHQYDVR0OBBYEFFZxf760XzrpI9nkte8AyuHZ +qs2+MB8GA1UdIwQYMBaAFFZxf760XzrpI9nkte8AyuHZqs2+MA8GA1UdEwEB/wQF +MAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAMc/ymoJgZ+d+7UnFOhr1UfUAayxirqM ++iaa0hGmBkqmAiEAscvZYM5Le2q0KdjPydcuTPBXi13q68zSDaib1jKQ0ZM= +-----END CERTIFICATE----- diff --git a/kvmd_data/etc/kvmd/vnc/ssl/server.key b/kvmd_data/etc/kvmd/vnc/ssl/server.key new file mode 100644 index 00000000..4812f973 --- /dev/null +++ b/kvmd_data/etc/kvmd/vnc/ssl/server.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIG08yMcwf2DxWHbTand3dE0hbQgk3uAv2FRl6Umyrz0boAoGCCqGSM49 +AwEHoUQDQgAE8OfAJl4ck4EnmpO9LvvBusatRFPm5qCYM1Al5nxql647sy87PjBv +bSM1L9CLuj1z3wAQnGmFD1DDGyNAFm9uZw== +-----END EC PRIVATE KEY----- diff --git a/configs/kvmd/vncpasswd b/kvmd_data/etc/kvmd/vncpasswd similarity index 100% rename from configs/kvmd/vncpasswd rename to kvmd_data/etc/kvmd/vncpasswd diff --git a/configs/kvmd/web.css b/kvmd_data/etc/kvmd/web.css similarity index 100% rename from configs/kvmd/web.css rename to kvmd_data/etc/kvmd/web.css diff --git a/kvmd_data/usr/bin/ustreamer b/kvmd_data/usr/bin/ustreamer new file mode 100755 index 00000000..8c0a1571 Binary files /dev/null and b/kvmd_data/usr/bin/ustreamer differ diff --git a/kvmd_data/usr/bin/vcgencmd b/kvmd_data/usr/bin/vcgencmd new file mode 100755 index 00000000..3985bc1f --- /dev/null +++ b/kvmd_data/usr/bin/vcgencmd @@ -0,0 +1,4 @@ +#!/bin/sh +case $1 in + get_throttled) echo "throttled=0x0";; +esac diff --git a/extras/ipmi/manifest.yaml b/kvmd_data/usr/share/kvmd/extras/ipmi/manifest.yaml similarity index 100% rename from extras/ipmi/manifest.yaml rename to kvmd_data/usr/share/kvmd/extras/ipmi/manifest.yaml diff --git a/extras/janus-static/manifest.yaml b/kvmd_data/usr/share/kvmd/extras/janus-static/manifest.yaml similarity index 100% rename from extras/janus-static/manifest.yaml rename to kvmd_data/usr/share/kvmd/extras/janus-static/manifest.yaml diff --git a/extras/janus/manifest.yaml b/kvmd_data/usr/share/kvmd/extras/janus/manifest.yaml similarity index 100% rename from extras/janus/manifest.yaml rename to kvmd_data/usr/share/kvmd/extras/janus/manifest.yaml diff --git a/extras/janus/nginx.ctx-http.conf b/kvmd_data/usr/share/kvmd/extras/janus/nginx.ctx-http.conf similarity index 100% rename from extras/janus/nginx.ctx-http.conf rename to kvmd_data/usr/share/kvmd/extras/janus/nginx.ctx-http.conf diff --git a/extras/janus/nginx.ctx-server.conf b/kvmd_data/usr/share/kvmd/extras/janus/nginx.ctx-server.conf similarity index 100% rename from extras/janus/nginx.ctx-server.conf rename to kvmd_data/usr/share/kvmd/extras/janus/nginx.ctx-server.conf diff --git a/extras/vnc/manifest.yaml b/kvmd_data/usr/share/kvmd/extras/vnc/manifest.yaml similarity index 100% rename from extras/vnc/manifest.yaml rename to kvmd_data/usr/share/kvmd/extras/vnc/manifest.yaml diff --git a/extras/webterm/manifest.yaml b/kvmd_data/usr/share/kvmd/extras/webterm/manifest.yaml similarity index 100% rename from extras/webterm/manifest.yaml rename to kvmd_data/usr/share/kvmd/extras/webterm/manifest.yaml diff --git a/extras/webterm/nginx.ctx-http.conf b/kvmd_data/usr/share/kvmd/extras/webterm/nginx.ctx-http.conf similarity index 100% rename from extras/webterm/nginx.ctx-http.conf rename to kvmd_data/usr/share/kvmd/extras/webterm/nginx.ctx-http.conf diff --git a/extras/webterm/nginx.ctx-server.conf b/kvmd_data/usr/share/kvmd/extras/webterm/nginx.ctx-server.conf similarity index 100% rename from extras/webterm/nginx.ctx-server.conf rename to kvmd_data/usr/share/kvmd/extras/webterm/nginx.ctx-server.conf diff --git a/contrib/keymaps/ar b/kvmd_data/usr/share/kvmd/keymaps/ar similarity index 100% rename from contrib/keymaps/ar rename to kvmd_data/usr/share/kvmd/keymaps/ar diff --git a/contrib/keymaps/bepo b/kvmd_data/usr/share/kvmd/keymaps/bepo similarity index 100% rename from contrib/keymaps/bepo rename to kvmd_data/usr/share/kvmd/keymaps/bepo diff --git a/contrib/keymaps/cz b/kvmd_data/usr/share/kvmd/keymaps/cz similarity index 100% rename from contrib/keymaps/cz rename to kvmd_data/usr/share/kvmd/keymaps/cz diff --git a/contrib/keymaps/da b/kvmd_data/usr/share/kvmd/keymaps/da similarity index 100% rename from contrib/keymaps/da rename to kvmd_data/usr/share/kvmd/keymaps/da diff --git a/contrib/keymaps/de b/kvmd_data/usr/share/kvmd/keymaps/de similarity index 100% rename from contrib/keymaps/de rename to kvmd_data/usr/share/kvmd/keymaps/de diff --git a/contrib/keymaps/de-ch b/kvmd_data/usr/share/kvmd/keymaps/de-ch similarity index 100% rename from contrib/keymaps/de-ch rename to kvmd_data/usr/share/kvmd/keymaps/de-ch diff --git a/contrib/keymaps/en-gb b/kvmd_data/usr/share/kvmd/keymaps/en-gb similarity index 100% rename from contrib/keymaps/en-gb rename to kvmd_data/usr/share/kvmd/keymaps/en-gb diff --git a/contrib/keymaps/en-us b/kvmd_data/usr/share/kvmd/keymaps/en-us similarity index 100% rename from contrib/keymaps/en-us rename to kvmd_data/usr/share/kvmd/keymaps/en-us diff --git a/contrib/keymaps/en-us-altgr-intl b/kvmd_data/usr/share/kvmd/keymaps/en-us-altgr-intl similarity index 100% rename from contrib/keymaps/en-us-altgr-intl rename to kvmd_data/usr/share/kvmd/keymaps/en-us-altgr-intl diff --git a/contrib/keymaps/es b/kvmd_data/usr/share/kvmd/keymaps/es similarity index 100% rename from contrib/keymaps/es rename to kvmd_data/usr/share/kvmd/keymaps/es diff --git a/contrib/keymaps/et b/kvmd_data/usr/share/kvmd/keymaps/et similarity index 100% rename from contrib/keymaps/et rename to kvmd_data/usr/share/kvmd/keymaps/et diff --git a/contrib/keymaps/fi b/kvmd_data/usr/share/kvmd/keymaps/fi similarity index 100% rename from contrib/keymaps/fi rename to kvmd_data/usr/share/kvmd/keymaps/fi diff --git a/contrib/keymaps/fo b/kvmd_data/usr/share/kvmd/keymaps/fo similarity index 100% rename from contrib/keymaps/fo rename to kvmd_data/usr/share/kvmd/keymaps/fo diff --git a/contrib/keymaps/fr b/kvmd_data/usr/share/kvmd/keymaps/fr similarity index 100% rename from contrib/keymaps/fr rename to kvmd_data/usr/share/kvmd/keymaps/fr diff --git a/contrib/keymaps/fr-be b/kvmd_data/usr/share/kvmd/keymaps/fr-be similarity index 100% rename from contrib/keymaps/fr-be rename to kvmd_data/usr/share/kvmd/keymaps/fr-be diff --git a/contrib/keymaps/fr-ca b/kvmd_data/usr/share/kvmd/keymaps/fr-ca similarity index 100% rename from contrib/keymaps/fr-ca rename to kvmd_data/usr/share/kvmd/keymaps/fr-ca diff --git a/contrib/keymaps/fr-ch b/kvmd_data/usr/share/kvmd/keymaps/fr-ch similarity index 100% rename from contrib/keymaps/fr-ch rename to kvmd_data/usr/share/kvmd/keymaps/fr-ch diff --git a/contrib/keymaps/hr b/kvmd_data/usr/share/kvmd/keymaps/hr similarity index 100% rename from contrib/keymaps/hr rename to kvmd_data/usr/share/kvmd/keymaps/hr diff --git a/contrib/keymaps/hu b/kvmd_data/usr/share/kvmd/keymaps/hu similarity index 100% rename from contrib/keymaps/hu rename to kvmd_data/usr/share/kvmd/keymaps/hu diff --git a/contrib/keymaps/is b/kvmd_data/usr/share/kvmd/keymaps/is similarity index 100% rename from contrib/keymaps/is rename to kvmd_data/usr/share/kvmd/keymaps/is diff --git a/contrib/keymaps/it b/kvmd_data/usr/share/kvmd/keymaps/it similarity index 100% rename from contrib/keymaps/it rename to kvmd_data/usr/share/kvmd/keymaps/it diff --git a/contrib/keymaps/ja b/kvmd_data/usr/share/kvmd/keymaps/ja similarity index 100% rename from contrib/keymaps/ja rename to kvmd_data/usr/share/kvmd/keymaps/ja diff --git a/contrib/keymaps/lt b/kvmd_data/usr/share/kvmd/keymaps/lt similarity index 100% rename from contrib/keymaps/lt rename to kvmd_data/usr/share/kvmd/keymaps/lt diff --git a/contrib/keymaps/lv b/kvmd_data/usr/share/kvmd/keymaps/lv similarity index 100% rename from contrib/keymaps/lv rename to kvmd_data/usr/share/kvmd/keymaps/lv diff --git a/contrib/keymaps/mk b/kvmd_data/usr/share/kvmd/keymaps/mk similarity index 100% rename from contrib/keymaps/mk rename to kvmd_data/usr/share/kvmd/keymaps/mk diff --git a/contrib/keymaps/nl b/kvmd_data/usr/share/kvmd/keymaps/nl similarity index 100% rename from contrib/keymaps/nl rename to kvmd_data/usr/share/kvmd/keymaps/nl diff --git a/contrib/keymaps/no b/kvmd_data/usr/share/kvmd/keymaps/no similarity index 100% rename from contrib/keymaps/no rename to kvmd_data/usr/share/kvmd/keymaps/no diff --git a/contrib/keymaps/pl b/kvmd_data/usr/share/kvmd/keymaps/pl similarity index 100% rename from contrib/keymaps/pl rename to kvmd_data/usr/share/kvmd/keymaps/pl diff --git a/contrib/keymaps/pt b/kvmd_data/usr/share/kvmd/keymaps/pt similarity index 100% rename from contrib/keymaps/pt rename to kvmd_data/usr/share/kvmd/keymaps/pt diff --git a/contrib/keymaps/pt-br b/kvmd_data/usr/share/kvmd/keymaps/pt-br similarity index 100% rename from contrib/keymaps/pt-br rename to kvmd_data/usr/share/kvmd/keymaps/pt-br diff --git a/contrib/keymaps/ru b/kvmd_data/usr/share/kvmd/keymaps/ru similarity index 100% rename from contrib/keymaps/ru rename to kvmd_data/usr/share/kvmd/keymaps/ru diff --git a/contrib/keymaps/sl b/kvmd_data/usr/share/kvmd/keymaps/sl similarity index 100% rename from contrib/keymaps/sl rename to kvmd_data/usr/share/kvmd/keymaps/sl diff --git a/contrib/keymaps/sv b/kvmd_data/usr/share/kvmd/keymaps/sv similarity index 100% rename from contrib/keymaps/sv rename to kvmd_data/usr/share/kvmd/keymaps/sv diff --git a/contrib/keymaps/th b/kvmd_data/usr/share/kvmd/keymaps/th similarity index 100% rename from contrib/keymaps/th rename to kvmd_data/usr/share/kvmd/keymaps/th diff --git a/contrib/keymaps/tr b/kvmd_data/usr/share/kvmd/keymaps/tr similarity index 100% rename from contrib/keymaps/tr rename to kvmd_data/usr/share/kvmd/keymaps/tr diff --git a/scripts/kvmd-gencert b/kvmd_data/usr/share/kvmd/kvmd-gencert similarity index 100% rename from scripts/kvmd-gencert rename to kvmd_data/usr/share/kvmd/kvmd-gencert diff --git a/kvmd_data/usr/share/kvmd/platform b/kvmd_data/usr/share/kvmd/platform new file mode 100644 index 00000000..0f00370c --- /dev/null +++ b/kvmd_data/usr/share/kvmd/platform @@ -0,0 +1,3 @@ +PIKVM_MODEL=docker_model +PIKVM_VIDEO=docker_video +PIKVM_BOARD=docker_board diff --git a/web/base.pug b/kvmd_data/usr/share/kvmd/web/base.pug similarity index 100% rename from web/base.pug rename to kvmd_data/usr/share/kvmd/web/base.pug diff --git a/hid/arduino/lib/.gitignore b/kvmd_data/usr/share/kvmd/web/extras/.gitignore similarity index 100% rename from hid/arduino/lib/.gitignore rename to kvmd_data/usr/share/kvmd/web/extras/.gitignore diff --git a/web/extras/webterm/terminal.svg b/kvmd_data/usr/share/kvmd/web/extras/webterm/terminal.svg similarity index 100% rename from web/extras/webterm/terminal.svg rename to kvmd_data/usr/share/kvmd/web/extras/webterm/terminal.svg diff --git a/web/favicon.ico b/kvmd_data/usr/share/kvmd/web/favicon.ico similarity index 100% rename from web/favicon.ico rename to kvmd_data/usr/share/kvmd/web/favicon.ico diff --git a/web/index.html b/kvmd_data/usr/share/kvmd/web/index.html similarity index 100% rename from web/index.html rename to kvmd_data/usr/share/kvmd/web/index.html diff --git a/web/index.pug b/kvmd_data/usr/share/kvmd/web/index.pug similarity index 100% rename from web/index.pug rename to kvmd_data/usr/share/kvmd/web/index.pug diff --git a/web/ipmi/index.html b/kvmd_data/usr/share/kvmd/web/ipmi/index.html similarity index 100% rename from web/ipmi/index.html rename to kvmd_data/usr/share/kvmd/web/ipmi/index.html diff --git a/web/ipmi/index.pug b/kvmd_data/usr/share/kvmd/web/ipmi/index.pug similarity index 100% rename from web/ipmi/index.pug rename to kvmd_data/usr/share/kvmd/web/ipmi/index.pug diff --git a/web/kvm/index.html b/kvmd_data/usr/share/kvmd/web/kvm/index.html similarity index 100% rename from web/kvm/index.html rename to kvmd_data/usr/share/kvmd/web/kvm/index.html diff --git a/web/kvm/index.pug b/kvmd_data/usr/share/kvmd/web/kvm/index.pug similarity index 100% rename from web/kvm/index.pug rename to kvmd_data/usr/share/kvmd/web/kvm/index.pug diff --git a/web/kvm/navbar-atx.pug b/kvmd_data/usr/share/kvmd/web/kvm/navbar-atx.pug similarity index 100% rename from web/kvm/navbar-atx.pug rename to kvmd_data/usr/share/kvmd/web/kvm/navbar-atx.pug diff --git a/web/kvm/navbar-gpio.pug b/kvmd_data/usr/share/kvmd/web/kvm/navbar-gpio.pug similarity index 100% rename from web/kvm/navbar-gpio.pug rename to kvmd_data/usr/share/kvmd/web/kvm/navbar-gpio.pug diff --git a/web/kvm/navbar-health.pug b/kvmd_data/usr/share/kvmd/web/kvm/navbar-health.pug similarity index 100% rename from web/kvm/navbar-health.pug rename to kvmd_data/usr/share/kvmd/web/kvm/navbar-health.pug diff --git a/web/kvm/navbar-macro.pug b/kvmd_data/usr/share/kvmd/web/kvm/navbar-macro.pug similarity index 100% rename from web/kvm/navbar-macro.pug rename to kvmd_data/usr/share/kvmd/web/kvm/navbar-macro.pug diff --git a/web/kvm/navbar-msd.pug b/kvmd_data/usr/share/kvmd/web/kvm/navbar-msd.pug similarity index 100% rename from web/kvm/navbar-msd.pug rename to kvmd_data/usr/share/kvmd/web/kvm/navbar-msd.pug diff --git a/web/kvm/navbar-shortcuts.pug b/kvmd_data/usr/share/kvmd/web/kvm/navbar-shortcuts.pug similarity index 100% rename from web/kvm/navbar-shortcuts.pug rename to kvmd_data/usr/share/kvmd/web/kvm/navbar-shortcuts.pug diff --git a/web/kvm/navbar-system.pug b/kvmd_data/usr/share/kvmd/web/kvm/navbar-system.pug similarity index 100% rename from web/kvm/navbar-system.pug rename to kvmd_data/usr/share/kvmd/web/kvm/navbar-system.pug diff --git a/web/kvm/navbar-text.pug b/kvmd_data/usr/share/kvmd/web/kvm/navbar-text.pug similarity index 100% rename from web/kvm/navbar-text.pug rename to kvmd_data/usr/share/kvmd/web/kvm/navbar-text.pug diff --git a/web/kvm/navbar.pug b/kvmd_data/usr/share/kvmd/web/kvm/navbar.pug similarity index 100% rename from web/kvm/navbar.pug rename to kvmd_data/usr/share/kvmd/web/kvm/navbar.pug diff --git a/web/kvm/window-about.pug b/kvmd_data/usr/share/kvmd/web/kvm/window-about.pug similarity index 100% rename from web/kvm/window-about.pug rename to kvmd_data/usr/share/kvmd/web/kvm/window-about.pug diff --git a/web/kvm/window-keyboard.pug b/kvmd_data/usr/share/kvmd/web/kvm/window-keyboard.pug similarity index 100% rename from web/kvm/window-keyboard.pug rename to kvmd_data/usr/share/kvmd/web/kvm/window-keyboard.pug diff --git a/web/kvm/window-stream.pug b/kvmd_data/usr/share/kvmd/web/kvm/window-stream.pug similarity index 100% rename from web/kvm/window-stream.pug rename to kvmd_data/usr/share/kvmd/web/kvm/window-stream.pug diff --git a/web/kvm/window-webterm.pug b/kvmd_data/usr/share/kvmd/web/kvm/window-webterm.pug similarity index 100% rename from web/kvm/window-webterm.pug rename to kvmd_data/usr/share/kvmd/web/kvm/window-webterm.pug diff --git a/web/kvm/windows.pug b/kvmd_data/usr/share/kvmd/web/kvm/windows.pug similarity index 100% rename from web/kvm/windows.pug rename to kvmd_data/usr/share/kvmd/web/kvm/windows.pug diff --git a/web/login/index.html b/kvmd_data/usr/share/kvmd/web/login/index.html similarity index 100% rename from web/login/index.html rename to kvmd_data/usr/share/kvmd/web/login/index.html diff --git a/web/login/index.pug b/kvmd_data/usr/share/kvmd/web/login/index.pug similarity index 100% rename from web/login/index.pug rename to kvmd_data/usr/share/kvmd/web/login/index.pug diff --git a/web/robots.txt b/kvmd_data/usr/share/kvmd/web/robots.txt similarity index 100% rename from web/robots.txt rename to kvmd_data/usr/share/kvmd/web/robots.txt diff --git a/web/share/android-chrome-192x192.png b/kvmd_data/usr/share/kvmd/web/share/android-chrome-192x192.png similarity index 100% rename from web/share/android-chrome-192x192.png rename to kvmd_data/usr/share/kvmd/web/share/android-chrome-192x192.png diff --git a/web/share/apple-touch-icon.png b/kvmd_data/usr/share/kvmd/web/share/apple-touch-icon.png similarity index 100% rename from web/share/apple-touch-icon.png rename to kvmd_data/usr/share/kvmd/web/share/apple-touch-icon.png diff --git a/web/share/css/index/index.css b/kvmd_data/usr/share/kvmd/web/share/css/index/index.css similarity index 100% rename from web/share/css/index/index.css rename to kvmd_data/usr/share/kvmd/web/share/css/index/index.css diff --git a/web/share/css/keypad.css b/kvmd_data/usr/share/kvmd/web/share/css/keypad.css similarity index 100% rename from web/share/css/keypad.css rename to kvmd_data/usr/share/kvmd/web/share/css/keypad.css diff --git a/web/share/css/kvm/about.css b/kvmd_data/usr/share/kvmd/web/share/css/kvm/about.css similarity index 100% rename from web/share/css/kvm/about.css rename to kvmd_data/usr/share/kvmd/web/share/css/kvm/about.css diff --git a/web/share/css/kvm/hid.css b/kvmd_data/usr/share/kvmd/web/share/css/kvm/hid.css similarity index 100% rename from web/share/css/kvm/hid.css rename to kvmd_data/usr/share/kvmd/web/share/css/kvm/hid.css diff --git a/web/share/css/kvm/keyboard.css b/kvmd_data/usr/share/kvmd/web/share/css/kvm/keyboard.css similarity index 100% rename from web/share/css/kvm/keyboard.css rename to kvmd_data/usr/share/kvmd/web/share/css/kvm/keyboard.css diff --git a/web/share/css/kvm/msd.css b/kvmd_data/usr/share/kvmd/web/share/css/kvm/msd.css similarity index 100% rename from web/share/css/kvm/msd.css rename to kvmd_data/usr/share/kvmd/web/share/css/kvm/msd.css diff --git a/web/share/css/kvm/stream.css b/kvmd_data/usr/share/kvmd/web/share/css/kvm/stream.css similarity index 100% rename from web/share/css/kvm/stream.css rename to kvmd_data/usr/share/kvmd/web/share/css/kvm/stream.css diff --git a/web/share/css/kvm/system.css b/kvmd_data/usr/share/kvmd/web/share/css/kvm/system.css similarity index 100% rename from web/share/css/kvm/system.css rename to kvmd_data/usr/share/kvmd/web/share/css/kvm/system.css diff --git a/web/share/css/kvm/x-mobile.css b/kvmd_data/usr/share/kvmd/web/share/css/kvm/x-mobile.css similarity index 100% rename from web/share/css/kvm/x-mobile.css rename to kvmd_data/usr/share/kvmd/web/share/css/kvm/x-mobile.css diff --git a/web/share/css/led.css b/kvmd_data/usr/share/kvmd/web/share/css/led.css similarity index 100% rename from web/share/css/led.css rename to kvmd_data/usr/share/kvmd/web/share/css/led.css diff --git a/web/share/css/login/login.css b/kvmd_data/usr/share/kvmd/web/share/css/login/login.css similarity index 100% rename from web/share/css/login/login.css rename to kvmd_data/usr/share/kvmd/web/share/css/login/login.css diff --git a/web/share/css/main.css b/kvmd_data/usr/share/kvmd/web/share/css/main.css similarity index 100% rename from web/share/css/main.css rename to kvmd_data/usr/share/kvmd/web/share/css/main.css diff --git a/web/share/css/modal.css b/kvmd_data/usr/share/kvmd/web/share/css/modal.css similarity index 100% rename from web/share/css/modal.css rename to kvmd_data/usr/share/kvmd/web/share/css/modal.css diff --git a/web/share/css/navbar.css b/kvmd_data/usr/share/kvmd/web/share/css/navbar.css similarity index 100% rename from web/share/css/navbar.css rename to kvmd_data/usr/share/kvmd/web/share/css/navbar.css diff --git a/web/share/css/progress.css b/kvmd_data/usr/share/kvmd/web/share/css/progress.css similarity index 100% rename from web/share/css/progress.css rename to kvmd_data/usr/share/kvmd/web/share/css/progress.css diff --git a/web/share/css/radio.css b/kvmd_data/usr/share/kvmd/web/share/css/radio.css similarity index 100% rename from web/share/css/radio.css rename to kvmd_data/usr/share/kvmd/web/share/css/radio.css diff --git a/web/share/css/slider.css b/kvmd_data/usr/share/kvmd/web/share/css/slider.css similarity index 100% rename from web/share/css/slider.css rename to kvmd_data/usr/share/kvmd/web/share/css/slider.css diff --git a/web/share/css/start.css b/kvmd_data/usr/share/kvmd/web/share/css/start.css similarity index 100% rename from web/share/css/start.css rename to kvmd_data/usr/share/kvmd/web/share/css/start.css diff --git a/web/share/css/switch.css b/kvmd_data/usr/share/kvmd/web/share/css/switch.css similarity index 100% rename from web/share/css/switch.css rename to kvmd_data/usr/share/kvmd/web/share/css/switch.css diff --git a/web/share/css/tabs.css b/kvmd_data/usr/share/kvmd/web/share/css/tabs.css similarity index 100% rename from web/share/css/tabs.css rename to kvmd_data/usr/share/kvmd/web/share/css/tabs.css diff --git a/configs/os/cmdline/v1-hdmiusb-zero2w.sed b/kvmd_data/usr/share/kvmd/web/share/css/user.css similarity index 100% rename from configs/os/cmdline/v1-hdmiusb-zero2w.sed rename to kvmd_data/usr/share/kvmd/web/share/css/user.css diff --git a/web/share/css/vars.css b/kvmd_data/usr/share/kvmd/web/share/css/vars.css similarity index 100% rename from web/share/css/vars.css rename to kvmd_data/usr/share/kvmd/web/share/css/vars.css diff --git a/web/share/css/window.css b/kvmd_data/usr/share/kvmd/web/share/css/window.css similarity index 100% rename from web/share/css/window.css rename to kvmd_data/usr/share/kvmd/web/share/css/window.css diff --git a/web/share/css/x-desktop.css b/kvmd_data/usr/share/kvmd/web/share/css/x-desktop.css similarity index 100% rename from web/share/css/x-desktop.css rename to kvmd_data/usr/share/kvmd/web/share/css/x-desktop.css diff --git a/web/share/css/x-mobile.css b/kvmd_data/usr/share/kvmd/web/share/css/x-mobile.css similarity index 100% rename from web/share/css/x-mobile.css rename to kvmd_data/usr/share/kvmd/web/share/css/x-mobile.css diff --git a/web/share/favicon-16x16.png b/kvmd_data/usr/share/kvmd/web/share/favicon-16x16.png similarity index 100% rename from web/share/favicon-16x16.png rename to kvmd_data/usr/share/kvmd/web/share/favicon-16x16.png diff --git a/web/share/favicon-32x32.png b/kvmd_data/usr/share/kvmd/web/share/favicon-32x32.png similarity index 100% rename from web/share/favicon-32x32.png rename to kvmd_data/usr/share/kvmd/web/share/favicon-32x32.png diff --git a/web/share/i18n/i18n_en.json b/kvmd_data/usr/share/kvmd/web/share/i18n/i18n_en.json similarity index 100% rename from web/share/i18n/i18n_en.json rename to kvmd_data/usr/share/kvmd/web/share/i18n/i18n_en.json diff --git a/web/share/i18n/i18n_zh.json b/kvmd_data/usr/share/kvmd/web/share/i18n/i18n_zh.json similarity index 100% rename from web/share/i18n/i18n_zh.json rename to kvmd_data/usr/share/kvmd/web/share/i18n/i18n_zh.json diff --git a/web/share/js/bb.js b/kvmd_data/usr/share/kvmd/web/share/js/bb.js similarity index 100% rename from web/share/js/bb.js rename to kvmd_data/usr/share/kvmd/web/share/js/bb.js diff --git a/web/share/js/i18n/i18n.js b/kvmd_data/usr/share/kvmd/web/share/js/i18n/i18n.js similarity index 100% rename from web/share/js/i18n/i18n.js rename to kvmd_data/usr/share/kvmd/web/share/js/i18n/i18n.js diff --git a/web/share/js/i18n/jquery-3.7.1.min.js b/kvmd_data/usr/share/kvmd/web/share/js/i18n/jquery-3.7.1.min.js similarity index 100% rename from web/share/js/i18n/jquery-3.7.1.min.js rename to kvmd_data/usr/share/kvmd/web/share/js/i18n/jquery-3.7.1.min.js diff --git a/web/share/js/i18n/jquery.i18n.min.js b/kvmd_data/usr/share/kvmd/web/share/js/i18n/jquery.i18n.min.js similarity index 100% rename from web/share/js/i18n/jquery.i18n.min.js rename to kvmd_data/usr/share/kvmd/web/share/js/i18n/jquery.i18n.min.js diff --git a/web/share/js/index/main.js b/kvmd_data/usr/share/kvmd/web/share/js/index/main.js similarity index 100% rename from web/share/js/index/main.js rename to kvmd_data/usr/share/kvmd/web/share/js/index/main.js diff --git a/web/share/js/ipmi/main.js b/kvmd_data/usr/share/kvmd/web/share/js/ipmi/main.js similarity index 100% rename from web/share/js/ipmi/main.js rename to kvmd_data/usr/share/kvmd/web/share/js/ipmi/main.js diff --git a/web/share/js/keypad.js b/kvmd_data/usr/share/kvmd/web/share/js/keypad.js similarity index 100% rename from web/share/js/keypad.js rename to kvmd_data/usr/share/kvmd/web/share/js/keypad.js diff --git a/kvmd_data/usr/share/kvmd/web/share/js/kvm/adapter.js b/kvmd_data/usr/share/kvmd/web/share/js/kvm/adapter.js new file mode 100644 index 00000000..51d0f276 --- /dev/null +++ b/kvmd_data/usr/share/kvmd/web/share/js/kvm/adapter.js @@ -0,0 +1,3364 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.adapter = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0 && arguments[0] !== undefined ? arguments[0] : {}, + window = _ref.window; + var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { + shimChrome: true, + shimFirefox: true, + shimSafari: true + }; + // Utils. + var logging = utils.log; + var browserDetails = utils.detectBrowser(window); + var adapter = { + browserDetails: browserDetails, + commonShim: commonShim, + extractVersion: utils.extractVersion, + disableLog: utils.disableLog, + disableWarnings: utils.disableWarnings, + // Expose sdp as a convenience. For production apps include directly. + sdp: sdp + }; + + // Shim browser if found. + switch (browserDetails.browser) { + case 'chrome': + if (!chromeShim || !chromeShim.shimPeerConnection || !options.shimChrome) { + logging('Chrome shim is not included in this adapter release.'); + return adapter; + } + if (browserDetails.version === null) { + logging('Chrome shim can not determine version, not shimming.'); + return adapter; + } + logging('adapter.js shimming chrome.'); + // Export to the adapter global object visible in the browser. + adapter.browserShim = chromeShim; + + // Must be called before shimPeerConnection. + commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails); + commonShim.shimParameterlessSetLocalDescription(window, browserDetails); + chromeShim.shimGetUserMedia(window, browserDetails); + chromeShim.shimMediaStream(window, browserDetails); + chromeShim.shimPeerConnection(window, browserDetails); + chromeShim.shimOnTrack(window, browserDetails); + chromeShim.shimAddTrackRemoveTrack(window, browserDetails); + chromeShim.shimGetSendersWithDtmf(window, browserDetails); + chromeShim.shimGetStats(window, browserDetails); + chromeShim.shimSenderReceiverGetStats(window, browserDetails); + chromeShim.fixNegotiationNeeded(window, browserDetails); + commonShim.shimRTCIceCandidate(window, browserDetails); + commonShim.shimRTCIceCandidateRelayProtocol(window, browserDetails); + commonShim.shimConnectionState(window, browserDetails); + commonShim.shimMaxMessageSize(window, browserDetails); + commonShim.shimSendThrowTypeError(window, browserDetails); + commonShim.removeExtmapAllowMixed(window, browserDetails); + break; + case 'firefox': + if (!firefoxShim || !firefoxShim.shimPeerConnection || !options.shimFirefox) { + logging('Firefox shim is not included in this adapter release.'); + return adapter; + } + logging('adapter.js shimming firefox.'); + // Export to the adapter global object visible in the browser. + adapter.browserShim = firefoxShim; + + // Must be called before shimPeerConnection. + commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails); + commonShim.shimParameterlessSetLocalDescription(window, browserDetails); + firefoxShim.shimGetUserMedia(window, browserDetails); + firefoxShim.shimPeerConnection(window, browserDetails); + firefoxShim.shimOnTrack(window, browserDetails); + firefoxShim.shimRemoveStream(window, browserDetails); + firefoxShim.shimSenderGetStats(window, browserDetails); + firefoxShim.shimReceiverGetStats(window, browserDetails); + firefoxShim.shimRTCDataChannel(window, browserDetails); + firefoxShim.shimAddTransceiver(window, browserDetails); + firefoxShim.shimGetParameters(window, browserDetails); + firefoxShim.shimCreateOffer(window, browserDetails); + firefoxShim.shimCreateAnswer(window, browserDetails); + commonShim.shimRTCIceCandidate(window, browserDetails); + commonShim.shimConnectionState(window, browserDetails); + commonShim.shimMaxMessageSize(window, browserDetails); + commonShim.shimSendThrowTypeError(window, browserDetails); + break; + case 'safari': + if (!safariShim || !options.shimSafari) { + logging('Safari shim is not included in this adapter release.'); + return adapter; + } + logging('adapter.js shimming safari.'); + // Export to the adapter global object visible in the browser. + adapter.browserShim = safariShim; + + // Must be called before shimCallbackAPI. + commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails); + commonShim.shimParameterlessSetLocalDescription(window, browserDetails); + safariShim.shimRTCIceServerUrls(window, browserDetails); + safariShim.shimCreateOfferLegacy(window, browserDetails); + safariShim.shimCallbacksAPI(window, browserDetails); + safariShim.shimLocalStreamsAPI(window, browserDetails); + safariShim.shimRemoteStreamsAPI(window, browserDetails); + safariShim.shimTrackEventTransceiver(window, browserDetails); + safariShim.shimGetUserMedia(window, browserDetails); + safariShim.shimAudioContext(window, browserDetails); + commonShim.shimRTCIceCandidate(window, browserDetails); + commonShim.shimRTCIceCandidateRelayProtocol(window, browserDetails); + commonShim.shimMaxMessageSize(window, browserDetails); + commonShim.shimSendThrowTypeError(window, browserDetails); + commonShim.removeExtmapAllowMixed(window, browserDetails); + break; + default: + logging('Unsupported browser!'); + break; + } + return adapter; +} + +},{"./chrome/chrome_shim":3,"./common_shim":6,"./firefox/firefox_shim":7,"./safari/safari_shim":10,"./utils":11,"sdp":12}],3:[function(require,module,exports){ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ +/* eslint-env node */ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.fixNegotiationNeeded = fixNegotiationNeeded; +exports.shimAddTrackRemoveTrack = shimAddTrackRemoveTrack; +exports.shimAddTrackRemoveTrackWithNative = shimAddTrackRemoveTrackWithNative; +Object.defineProperty(exports, "shimGetDisplayMedia", { + enumerable: true, + get: function get() { + return _getdisplaymedia.shimGetDisplayMedia; + } +}); +exports.shimGetSendersWithDtmf = shimGetSendersWithDtmf; +exports.shimGetStats = shimGetStats; +Object.defineProperty(exports, "shimGetUserMedia", { + enumerable: true, + get: function get() { + return _getusermedia.shimGetUserMedia; + } +}); +exports.shimMediaStream = shimMediaStream; +exports.shimOnTrack = shimOnTrack; +exports.shimPeerConnection = shimPeerConnection; +exports.shimSenderReceiverGetStats = shimSenderReceiverGetStats; +var utils = _interopRequireWildcard(require("../utils.js")); +var _getusermedia = require("./getusermedia"); +var _getdisplaymedia = require("./getdisplaymedia"); +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } +function shimMediaStream(window) { + window.MediaStream = window.MediaStream || window.webkitMediaStream; +} +function shimOnTrack(window) { + if (_typeof(window) === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) { + Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', { + get: function get() { + return this._ontrack; + }, + set: function set(f) { + if (this._ontrack) { + this.removeEventListener('track', this._ontrack); + } + this.addEventListener('track', this._ontrack = f); + }, + enumerable: true, + configurable: true + }); + var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription; + window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() { + var _this = this; + if (!this._ontrackpoly) { + this._ontrackpoly = function (e) { + // onaddstream does not fire when a track is added to an existing + // stream. But stream.onaddtrack is implemented so we use that. + e.stream.addEventListener('addtrack', function (te) { + var receiver; + if (window.RTCPeerConnection.prototype.getReceivers) { + receiver = _this.getReceivers().find(function (r) { + return r.track && r.track.id === te.track.id; + }); + } else { + receiver = { + track: te.track + }; + } + var event = new Event('track'); + event.track = te.track; + event.receiver = receiver; + event.transceiver = { + receiver: receiver + }; + event.streams = [e.stream]; + _this.dispatchEvent(event); + }); + e.stream.getTracks().forEach(function (track) { + var receiver; + if (window.RTCPeerConnection.prototype.getReceivers) { + receiver = _this.getReceivers().find(function (r) { + return r.track && r.track.id === track.id; + }); + } else { + receiver = { + track: track + }; + } + var event = new Event('track'); + event.track = track; + event.receiver = receiver; + event.transceiver = { + receiver: receiver + }; + event.streams = [e.stream]; + _this.dispatchEvent(event); + }); + }; + this.addEventListener('addstream', this._ontrackpoly); + } + return origSetRemoteDescription.apply(this, arguments); + }; + } else { + // even if RTCRtpTransceiver is in window, it is only used and + // emitted in unified-plan. Unfortunately this means we need + // to unconditionally wrap the event. + utils.wrapPeerConnectionEvent(window, 'track', function (e) { + if (!e.transceiver) { + Object.defineProperty(e, 'transceiver', { + value: { + receiver: e.receiver + } + }); + } + return e; + }); + } +} +function shimGetSendersWithDtmf(window) { + // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack. + if (_typeof(window) === 'object' && window.RTCPeerConnection && !('getSenders' in window.RTCPeerConnection.prototype) && 'createDTMFSender' in window.RTCPeerConnection.prototype) { + var shimSenderWithDtmf = function shimSenderWithDtmf(pc, track) { + return { + track: track, + get dtmf() { + if (this._dtmf === undefined) { + if (track.kind === 'audio') { + this._dtmf = pc.createDTMFSender(track); + } else { + this._dtmf = null; + } + } + return this._dtmf; + }, + _pc: pc + }; + }; + + // augment addTrack when getSenders is not available. + if (!window.RTCPeerConnection.prototype.getSenders) { + window.RTCPeerConnection.prototype.getSenders = function getSenders() { + this._senders = this._senders || []; + return this._senders.slice(); // return a copy of the internal state. + }; + + var origAddTrack = window.RTCPeerConnection.prototype.addTrack; + window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) { + var sender = origAddTrack.apply(this, arguments); + if (!sender) { + sender = shimSenderWithDtmf(this, track); + this._senders.push(sender); + } + return sender; + }; + var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack; + window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) { + origRemoveTrack.apply(this, arguments); + var idx = this._senders.indexOf(sender); + if (idx !== -1) { + this._senders.splice(idx, 1); + } + }; + } + var origAddStream = window.RTCPeerConnection.prototype.addStream; + window.RTCPeerConnection.prototype.addStream = function addStream(stream) { + var _this2 = this; + this._senders = this._senders || []; + origAddStream.apply(this, [stream]); + stream.getTracks().forEach(function (track) { + _this2._senders.push(shimSenderWithDtmf(_this2, track)); + }); + }; + var origRemoveStream = window.RTCPeerConnection.prototype.removeStream; + window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) { + var _this3 = this; + this._senders = this._senders || []; + origRemoveStream.apply(this, [stream]); + stream.getTracks().forEach(function (track) { + var sender = _this3._senders.find(function (s) { + return s.track === track; + }); + if (sender) { + // remove sender + _this3._senders.splice(_this3._senders.indexOf(sender), 1); + } + }); + }; + } else if (_typeof(window) === 'object' && window.RTCPeerConnection && 'getSenders' in window.RTCPeerConnection.prototype && 'createDTMFSender' in window.RTCPeerConnection.prototype && window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) { + var origGetSenders = window.RTCPeerConnection.prototype.getSenders; + window.RTCPeerConnection.prototype.getSenders = function getSenders() { + var _this4 = this; + var senders = origGetSenders.apply(this, []); + senders.forEach(function (sender) { + return sender._pc = _this4; + }); + return senders; + }; + Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', { + get: function get() { + if (this._dtmf === undefined) { + if (this.track.kind === 'audio') { + this._dtmf = this._pc.createDTMFSender(this.track); + } else { + this._dtmf = null; + } + } + return this._dtmf; + } + }); + } +} +function shimGetStats(window) { + if (!window.RTCPeerConnection) { + return; + } + var origGetStats = window.RTCPeerConnection.prototype.getStats; + window.RTCPeerConnection.prototype.getStats = function getStats() { + var _this5 = this; + var _arguments = Array.prototype.slice.call(arguments), + selector = _arguments[0], + onSucc = _arguments[1], + onErr = _arguments[2]; + + // If selector is a function then we are in the old style stats so just + // pass back the original getStats format to avoid breaking old users. + if (arguments.length > 0 && typeof selector === 'function') { + return origGetStats.apply(this, arguments); + } + + // When spec-style getStats is supported, return those when called with + // either no arguments or the selector argument is null. + if (origGetStats.length === 0 && (arguments.length === 0 || typeof selector !== 'function')) { + return origGetStats.apply(this, []); + } + var fixChromeStats_ = function fixChromeStats_(response) { + var standardReport = {}; + var reports = response.result(); + reports.forEach(function (report) { + var standardStats = { + id: report.id, + timestamp: report.timestamp, + type: { + localcandidate: 'local-candidate', + remotecandidate: 'remote-candidate' + }[report.type] || report.type + }; + report.names().forEach(function (name) { + standardStats[name] = report.stat(name); + }); + standardReport[standardStats.id] = standardStats; + }); + return standardReport; + }; + + // shim getStats with maplike support + var makeMapStats = function makeMapStats(stats) { + return new Map(Object.keys(stats).map(function (key) { + return [key, stats[key]]; + })); + }; + if (arguments.length >= 2) { + var successCallbackWrapper_ = function successCallbackWrapper_(response) { + onSucc(makeMapStats(fixChromeStats_(response))); + }; + return origGetStats.apply(this, [successCallbackWrapper_, selector]); + } + + // promise-support + return new Promise(function (resolve, reject) { + origGetStats.apply(_this5, [function (response) { + resolve(makeMapStats(fixChromeStats_(response))); + }, reject]); + }).then(onSucc, onErr); + }; +} +function shimSenderReceiverGetStats(window) { + if (!(_typeof(window) === 'object' && window.RTCPeerConnection && window.RTCRtpSender && window.RTCRtpReceiver)) { + return; + } + + // shim sender stats. + if (!('getStats' in window.RTCRtpSender.prototype)) { + var origGetSenders = window.RTCPeerConnection.prototype.getSenders; + if (origGetSenders) { + window.RTCPeerConnection.prototype.getSenders = function getSenders() { + var _this6 = this; + var senders = origGetSenders.apply(this, []); + senders.forEach(function (sender) { + return sender._pc = _this6; + }); + return senders; + }; + } + var origAddTrack = window.RTCPeerConnection.prototype.addTrack; + if (origAddTrack) { + window.RTCPeerConnection.prototype.addTrack = function addTrack() { + var sender = origAddTrack.apply(this, arguments); + sender._pc = this; + return sender; + }; + } + window.RTCRtpSender.prototype.getStats = function getStats() { + var sender = this; + return this._pc.getStats().then(function (result) { + return ( + /* Note: this will include stats of all senders that + * send a track with the same id as sender.track as + * it is not possible to identify the RTCRtpSender. + */ + utils.filterStats(result, sender.track, true) + ); + }); + }; + } + + // shim receiver stats. + if (!('getStats' in window.RTCRtpReceiver.prototype)) { + var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers; + if (origGetReceivers) { + window.RTCPeerConnection.prototype.getReceivers = function getReceivers() { + var _this7 = this; + var receivers = origGetReceivers.apply(this, []); + receivers.forEach(function (receiver) { + return receiver._pc = _this7; + }); + return receivers; + }; + } + utils.wrapPeerConnectionEvent(window, 'track', function (e) { + e.receiver._pc = e.srcElement; + return e; + }); + window.RTCRtpReceiver.prototype.getStats = function getStats() { + var receiver = this; + return this._pc.getStats().then(function (result) { + return utils.filterStats(result, receiver.track, false); + }); + }; + } + if (!('getStats' in window.RTCRtpSender.prototype && 'getStats' in window.RTCRtpReceiver.prototype)) { + return; + } + + // shim RTCPeerConnection.getStats(track). + var origGetStats = window.RTCPeerConnection.prototype.getStats; + window.RTCPeerConnection.prototype.getStats = function getStats() { + if (arguments.length > 0 && arguments[0] instanceof window.MediaStreamTrack) { + var track = arguments[0]; + var sender; + var receiver; + var err; + this.getSenders().forEach(function (s) { + if (s.track === track) { + if (sender) { + err = true; + } else { + sender = s; + } + } + }); + this.getReceivers().forEach(function (r) { + if (r.track === track) { + if (receiver) { + err = true; + } else { + receiver = r; + } + } + return r.track === track; + }); + if (err || sender && receiver) { + return Promise.reject(new DOMException('There are more than one sender or receiver for the track.', 'InvalidAccessError')); + } else if (sender) { + return sender.getStats(); + } else if (receiver) { + return receiver.getStats(); + } + return Promise.reject(new DOMException('There is no sender or receiver for the track.', 'InvalidAccessError')); + } + return origGetStats.apply(this, arguments); + }; +} +function shimAddTrackRemoveTrackWithNative(window) { + // shim addTrack/removeTrack with native variants in order to make + // the interactions with legacy getLocalStreams behave as in other browsers. + // Keeps a mapping stream.id => [stream, rtpsenders...] + window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() { + var _this8 = this; + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + return Object.keys(this._shimmedLocalStreams).map(function (streamId) { + return _this8._shimmedLocalStreams[streamId][0]; + }); + }; + var origAddTrack = window.RTCPeerConnection.prototype.addTrack; + window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) { + if (!stream) { + return origAddTrack.apply(this, arguments); + } + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + var sender = origAddTrack.apply(this, arguments); + if (!this._shimmedLocalStreams[stream.id]) { + this._shimmedLocalStreams[stream.id] = [stream, sender]; + } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) { + this._shimmedLocalStreams[stream.id].push(sender); + } + return sender; + }; + var origAddStream = window.RTCPeerConnection.prototype.addStream; + window.RTCPeerConnection.prototype.addStream = function addStream(stream) { + var _this9 = this; + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + stream.getTracks().forEach(function (track) { + var alreadyExists = _this9.getSenders().find(function (s) { + return s.track === track; + }); + if (alreadyExists) { + throw new DOMException('Track already exists.', 'InvalidAccessError'); + } + }); + var existingSenders = this.getSenders(); + origAddStream.apply(this, arguments); + var newSenders = this.getSenders().filter(function (newSender) { + return existingSenders.indexOf(newSender) === -1; + }); + this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders); + }; + var origRemoveStream = window.RTCPeerConnection.prototype.removeStream; + window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) { + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + delete this._shimmedLocalStreams[stream.id]; + return origRemoveStream.apply(this, arguments); + }; + var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack; + window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) { + var _this10 = this; + this._shimmedLocalStreams = this._shimmedLocalStreams || {}; + if (sender) { + Object.keys(this._shimmedLocalStreams).forEach(function (streamId) { + var idx = _this10._shimmedLocalStreams[streamId].indexOf(sender); + if (idx !== -1) { + _this10._shimmedLocalStreams[streamId].splice(idx, 1); + } + if (_this10._shimmedLocalStreams[streamId].length === 1) { + delete _this10._shimmedLocalStreams[streamId]; + } + }); + } + return origRemoveTrack.apply(this, arguments); + }; +} +function shimAddTrackRemoveTrack(window, browserDetails) { + if (!window.RTCPeerConnection) { + return; + } + // shim addTrack and removeTrack. + if (window.RTCPeerConnection.prototype.addTrack && browserDetails.version >= 65) { + return shimAddTrackRemoveTrackWithNative(window); + } + + // also shim pc.getLocalStreams when addTrack is shimmed + // to return the original streams. + var origGetLocalStreams = window.RTCPeerConnection.prototype.getLocalStreams; + window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() { + var _this11 = this; + var nativeStreams = origGetLocalStreams.apply(this); + this._reverseStreams = this._reverseStreams || {}; + return nativeStreams.map(function (stream) { + return _this11._reverseStreams[stream.id]; + }); + }; + var origAddStream = window.RTCPeerConnection.prototype.addStream; + window.RTCPeerConnection.prototype.addStream = function addStream(stream) { + var _this12 = this; + this._streams = this._streams || {}; + this._reverseStreams = this._reverseStreams || {}; + stream.getTracks().forEach(function (track) { + var alreadyExists = _this12.getSenders().find(function (s) { + return s.track === track; + }); + if (alreadyExists) { + throw new DOMException('Track already exists.', 'InvalidAccessError'); + } + }); + // Add identity mapping for consistency with addTrack. + // Unless this is being used with a stream from addTrack. + if (!this._reverseStreams[stream.id]) { + var newStream = new window.MediaStream(stream.getTracks()); + this._streams[stream.id] = newStream; + this._reverseStreams[newStream.id] = stream; + stream = newStream; + } + origAddStream.apply(this, [stream]); + }; + var origRemoveStream = window.RTCPeerConnection.prototype.removeStream; + window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) { + this._streams = this._streams || {}; + this._reverseStreams = this._reverseStreams || {}; + origRemoveStream.apply(this, [this._streams[stream.id] || stream]); + delete this._reverseStreams[this._streams[stream.id] ? this._streams[stream.id].id : stream.id]; + delete this._streams[stream.id]; + }; + window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) { + var _this13 = this; + if (this.signalingState === 'closed') { + throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError'); + } + var streams = [].slice.call(arguments, 1); + if (streams.length !== 1 || !streams[0].getTracks().find(function (t) { + return t === track; + })) { + // this is not fully correct but all we can manage without + // [[associated MediaStreams]] internal slot. + throw new DOMException('The adapter.js addTrack polyfill only supports a single ' + ' stream which is associated with the specified track.', 'NotSupportedError'); + } + var alreadyExists = this.getSenders().find(function (s) { + return s.track === track; + }); + if (alreadyExists) { + throw new DOMException('Track already exists.', 'InvalidAccessError'); + } + this._streams = this._streams || {}; + this._reverseStreams = this._reverseStreams || {}; + var oldStream = this._streams[stream.id]; + if (oldStream) { + // this is using odd Chrome behaviour, use with caution: + // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815 + // Note: we rely on the high-level addTrack/dtmf shim to + // create the sender with a dtmf sender. + oldStream.addTrack(track); + + // Trigger ONN async. + Promise.resolve().then(function () { + _this13.dispatchEvent(new Event('negotiationneeded')); + }); + } else { + var newStream = new window.MediaStream([track]); + this._streams[stream.id] = newStream; + this._reverseStreams[newStream.id] = stream; + this.addStream(newStream); + } + return this.getSenders().find(function (s) { + return s.track === track; + }); + }; + + // replace the internal stream id with the external one and + // vice versa. + function replaceInternalStreamId(pc, description) { + var sdp = description.sdp; + Object.keys(pc._reverseStreams || []).forEach(function (internalId) { + var externalStream = pc._reverseStreams[internalId]; + var internalStream = pc._streams[externalStream.id]; + sdp = sdp.replace(new RegExp(internalStream.id, 'g'), externalStream.id); + }); + return new RTCSessionDescription({ + type: description.type, + sdp: sdp + }); + } + function replaceExternalStreamId(pc, description) { + var sdp = description.sdp; + Object.keys(pc._reverseStreams || []).forEach(function (internalId) { + var externalStream = pc._reverseStreams[internalId]; + var internalStream = pc._streams[externalStream.id]; + sdp = sdp.replace(new RegExp(externalStream.id, 'g'), internalStream.id); + }); + return new RTCSessionDescription({ + type: description.type, + sdp: sdp + }); + } + ['createOffer', 'createAnswer'].forEach(function (method) { + var nativeMethod = window.RTCPeerConnection.prototype[method]; + var methodObj = _defineProperty({}, method, function () { + var _this14 = this; + var args = arguments; + var isLegacyCall = arguments.length && typeof arguments[0] === 'function'; + if (isLegacyCall) { + return nativeMethod.apply(this, [function (description) { + var desc = replaceInternalStreamId(_this14, description); + args[0].apply(null, [desc]); + }, function (err) { + if (args[1]) { + args[1].apply(null, err); + } + }, arguments[2]]); + } + return nativeMethod.apply(this, arguments).then(function (description) { + return replaceInternalStreamId(_this14, description); + }); + }); + window.RTCPeerConnection.prototype[method] = methodObj[method]; + }); + var origSetLocalDescription = window.RTCPeerConnection.prototype.setLocalDescription; + window.RTCPeerConnection.prototype.setLocalDescription = function setLocalDescription() { + if (!arguments.length || !arguments[0].type) { + return origSetLocalDescription.apply(this, arguments); + } + arguments[0] = replaceExternalStreamId(this, arguments[0]); + return origSetLocalDescription.apply(this, arguments); + }; + + // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier + + var origLocalDescription = Object.getOwnPropertyDescriptor(window.RTCPeerConnection.prototype, 'localDescription'); + Object.defineProperty(window.RTCPeerConnection.prototype, 'localDescription', { + get: function get() { + var description = origLocalDescription.get.apply(this); + if (description.type === '') { + return description; + } + return replaceInternalStreamId(this, description); + } + }); + window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) { + var _this15 = this; + if (this.signalingState === 'closed') { + throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError'); + } + // We can not yet check for sender instanceof RTCRtpSender + // since we shim RTPSender. So we check if sender._pc is set. + if (!sender._pc) { + throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' + 'does not implement interface RTCRtpSender.', 'TypeError'); + } + var isLocal = sender._pc === this; + if (!isLocal) { + throw new DOMException('Sender was not created by this connection.', 'InvalidAccessError'); + } + + // Search for the native stream the senders track belongs to. + this._streams = this._streams || {}; + var stream; + Object.keys(this._streams).forEach(function (streamid) { + var hasTrack = _this15._streams[streamid].getTracks().find(function (track) { + return sender.track === track; + }); + if (hasTrack) { + stream = _this15._streams[streamid]; + } + }); + if (stream) { + if (stream.getTracks().length === 1) { + // if this is the last track of the stream, remove the stream. This + // takes care of any shimmed _senders. + this.removeStream(this._reverseStreams[stream.id]); + } else { + // relying on the same odd chrome behaviour as above. + stream.removeTrack(sender.track); + } + this.dispatchEvent(new Event('negotiationneeded')); + } + }; +} +function shimPeerConnection(window, browserDetails) { + if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) { + // very basic support for old versions. + window.RTCPeerConnection = window.webkitRTCPeerConnection; + } + if (!window.RTCPeerConnection) { + return; + } + + // shim implicit creation of RTCSessionDescription/RTCIceCandidate + if (browserDetails.version < 53) { + ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) { + var nativeMethod = window.RTCPeerConnection.prototype[method]; + var methodObj = _defineProperty({}, method, function () { + arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]); + return nativeMethod.apply(this, arguments); + }); + window.RTCPeerConnection.prototype[method] = methodObj[method]; + }); + } +} + +// Attempt to fix ONN in plan-b mode. +function fixNegotiationNeeded(window, browserDetails) { + utils.wrapPeerConnectionEvent(window, 'negotiationneeded', function (e) { + var pc = e.target; + if (browserDetails.version < 72 || pc.getConfiguration && pc.getConfiguration().sdpSemantics === 'plan-b') { + if (pc.signalingState !== 'stable') { + return; + } + } + return e; + }); +} + +},{"../utils.js":11,"./getdisplaymedia":4,"./getusermedia":5}],4:[function(require,module,exports){ +/* + * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ +/* eslint-env node */ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.shimGetDisplayMedia = shimGetDisplayMedia; +function shimGetDisplayMedia(window, getSourceId) { + if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) { + return; + } + if (!window.navigator.mediaDevices) { + return; + } + // getSourceId is a function that returns a promise resolving with + // the sourceId of the screen/window/tab to be shared. + if (typeof getSourceId !== 'function') { + console.error('shimGetDisplayMedia: getSourceId argument is not ' + 'a function'); + return; + } + window.navigator.mediaDevices.getDisplayMedia = function getDisplayMedia(constraints) { + return getSourceId(constraints).then(function (sourceId) { + var widthSpecified = constraints.video && constraints.video.width; + var heightSpecified = constraints.video && constraints.video.height; + var frameRateSpecified = constraints.video && constraints.video.frameRate; + constraints.video = { + mandatory: { + chromeMediaSource: 'desktop', + chromeMediaSourceId: sourceId, + maxFrameRate: frameRateSpecified || 3 + } + }; + if (widthSpecified) { + constraints.video.mandatory.maxWidth = widthSpecified; + } + if (heightSpecified) { + constraints.video.mandatory.maxHeight = heightSpecified; + } + return window.navigator.mediaDevices.getUserMedia(constraints); + }); + }; +} + +},{}],5:[function(require,module,exports){ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ +/* eslint-env node */ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.shimGetUserMedia = shimGetUserMedia; +var utils = _interopRequireWildcard(require("../utils.js")); +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } +function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } +var logging = utils.log; +function shimGetUserMedia(window, browserDetails) { + var navigator = window && window.navigator; + if (!navigator.mediaDevices) { + return; + } + var constraintsToChrome_ = function constraintsToChrome_(c) { + if (_typeof(c) !== 'object' || c.mandatory || c.optional) { + return c; + } + var cc = {}; + Object.keys(c).forEach(function (key) { + if (key === 'require' || key === 'advanced' || key === 'mediaSource') { + return; + } + var r = _typeof(c[key]) === 'object' ? c[key] : { + ideal: c[key] + }; + if (r.exact !== undefined && typeof r.exact === 'number') { + r.min = r.max = r.exact; + } + var oldname_ = function oldname_(prefix, name) { + if (prefix) { + return prefix + name.charAt(0).toUpperCase() + name.slice(1); + } + return name === 'deviceId' ? 'sourceId' : name; + }; + if (r.ideal !== undefined) { + cc.optional = cc.optional || []; + var oc = {}; + if (typeof r.ideal === 'number') { + oc[oldname_('min', key)] = r.ideal; + cc.optional.push(oc); + oc = {}; + oc[oldname_('max', key)] = r.ideal; + cc.optional.push(oc); + } else { + oc[oldname_('', key)] = r.ideal; + cc.optional.push(oc); + } + } + if (r.exact !== undefined && typeof r.exact !== 'number') { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname_('', key)] = r.exact; + } else { + ['min', 'max'].forEach(function (mix) { + if (r[mix] !== undefined) { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname_(mix, key)] = r[mix]; + } + }); + } + }); + if (c.advanced) { + cc.optional = (cc.optional || []).concat(c.advanced); + } + return cc; + }; + var shimConstraints_ = function shimConstraints_(constraints, func) { + if (browserDetails.version >= 61) { + return func(constraints); + } + constraints = JSON.parse(JSON.stringify(constraints)); + if (constraints && _typeof(constraints.audio) === 'object') { + var remap = function remap(obj, a, b) { + if (a in obj && !(b in obj)) { + obj[b] = obj[a]; + delete obj[a]; + } + }; + constraints = JSON.parse(JSON.stringify(constraints)); + remap(constraints.audio, 'autoGainControl', 'googAutoGainControl'); + remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression'); + constraints.audio = constraintsToChrome_(constraints.audio); + } + if (constraints && _typeof(constraints.video) === 'object') { + // Shim facingMode for mobile & surface pro. + var face = constraints.video.facingMode; + face = face && (_typeof(face) === 'object' ? face : { + ideal: face + }); + var getSupportedFacingModeLies = browserDetails.version < 66; + if (face && (face.exact === 'user' || face.exact === 'environment' || face.ideal === 'user' || face.ideal === 'environment') && !(navigator.mediaDevices.getSupportedConstraints && navigator.mediaDevices.getSupportedConstraints().facingMode && !getSupportedFacingModeLies)) { + delete constraints.video.facingMode; + var matches; + if (face.exact === 'environment' || face.ideal === 'environment') { + matches = ['back', 'rear']; + } else if (face.exact === 'user' || face.ideal === 'user') { + matches = ['front']; + } + if (matches) { + // Look for matches in label, or use last cam for back (typical). + return navigator.mediaDevices.enumerateDevices().then(function (devices) { + devices = devices.filter(function (d) { + return d.kind === 'videoinput'; + }); + var dev = devices.find(function (d) { + return matches.some(function (match) { + return d.label.toLowerCase().includes(match); + }); + }); + if (!dev && devices.length && matches.includes('back')) { + dev = devices[devices.length - 1]; // more likely the back cam + } + + if (dev) { + constraints.video.deviceId = face.exact ? { + exact: dev.deviceId + } : { + ideal: dev.deviceId + }; + } + constraints.video = constraintsToChrome_(constraints.video); + logging('chrome: ' + JSON.stringify(constraints)); + return func(constraints); + }); + } + } + constraints.video = constraintsToChrome_(constraints.video); + } + logging('chrome: ' + JSON.stringify(constraints)); + return func(constraints); + }; + var shimError_ = function shimError_(e) { + if (browserDetails.version >= 64) { + return e; + } + return { + name: { + PermissionDeniedError: 'NotAllowedError', + PermissionDismissedError: 'NotAllowedError', + InvalidStateError: 'NotAllowedError', + DevicesNotFoundError: 'NotFoundError', + ConstraintNotSatisfiedError: 'OverconstrainedError', + TrackStartError: 'NotReadableError', + MediaDeviceFailedDueToShutdown: 'NotAllowedError', + MediaDeviceKillSwitchOn: 'NotAllowedError', + TabCaptureError: 'AbortError', + ScreenCaptureError: 'AbortError', + DeviceCaptureError: 'AbortError' + }[e.name] || e.name, + message: e.message, + constraint: e.constraint || e.constraintName, + toString: function toString() { + return this.name + (this.message && ': ') + this.message; + } + }; + }; + var getUserMedia_ = function getUserMedia_(constraints, onSuccess, onError) { + shimConstraints_(constraints, function (c) { + navigator.webkitGetUserMedia(c, onSuccess, function (e) { + if (onError) { + onError(shimError_(e)); + } + }); + }); + }; + navigator.getUserMedia = getUserMedia_.bind(navigator); + + // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia + // function which returns a Promise, it does not accept spec-style + // constraints. + if (navigator.mediaDevices.getUserMedia) { + var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices); + navigator.mediaDevices.getUserMedia = function (cs) { + return shimConstraints_(cs, function (c) { + return origGetUserMedia(c).then(function (stream) { + if (c.audio && !stream.getAudioTracks().length || c.video && !stream.getVideoTracks().length) { + stream.getTracks().forEach(function (track) { + track.stop(); + }); + throw new DOMException('', 'NotFoundError'); + } + return stream; + }, function (e) { + return Promise.reject(shimError_(e)); + }); + }); + }; + } +} + +},{"../utils.js":11}],6:[function(require,module,exports){ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ +/* eslint-env node */ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.removeExtmapAllowMixed = removeExtmapAllowMixed; +exports.shimAddIceCandidateNullOrEmpty = shimAddIceCandidateNullOrEmpty; +exports.shimConnectionState = shimConnectionState; +exports.shimMaxMessageSize = shimMaxMessageSize; +exports.shimParameterlessSetLocalDescription = shimParameterlessSetLocalDescription; +exports.shimRTCIceCandidate = shimRTCIceCandidate; +exports.shimRTCIceCandidateRelayProtocol = shimRTCIceCandidateRelayProtocol; +exports.shimSendThrowTypeError = shimSendThrowTypeError; +var _sdp = _interopRequireDefault(require("sdp")); +var utils = _interopRequireWildcard(require("./utils")); +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } +function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } +function shimRTCIceCandidate(window) { + // foundation is arbitrarily chosen as an indicator for full support for + // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface + if (!window.RTCIceCandidate || window.RTCIceCandidate && 'foundation' in window.RTCIceCandidate.prototype) { + return; + } + var NativeRTCIceCandidate = window.RTCIceCandidate; + window.RTCIceCandidate = function RTCIceCandidate(args) { + // Remove the a= which shouldn't be part of the candidate string. + if (_typeof(args) === 'object' && args.candidate && args.candidate.indexOf('a=') === 0) { + args = JSON.parse(JSON.stringify(args)); + args.candidate = args.candidate.substring(2); + } + if (args.candidate && args.candidate.length) { + // Augment the native candidate with the parsed fields. + var nativeCandidate = new NativeRTCIceCandidate(args); + var parsedCandidate = _sdp["default"].parseCandidate(args.candidate); + for (var key in parsedCandidate) { + if (!(key in nativeCandidate)) { + Object.defineProperty(nativeCandidate, key, { + value: parsedCandidate[key] + }); + } + } + + // Override serializer to not serialize the extra attributes. + nativeCandidate.toJSON = function toJSON() { + return { + candidate: nativeCandidate.candidate, + sdpMid: nativeCandidate.sdpMid, + sdpMLineIndex: nativeCandidate.sdpMLineIndex, + usernameFragment: nativeCandidate.usernameFragment + }; + }; + return nativeCandidate; + } + return new NativeRTCIceCandidate(args); + }; + window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype; + + // Hook up the augmented candidate in onicecandidate and + // addEventListener('icecandidate', ...) + utils.wrapPeerConnectionEvent(window, 'icecandidate', function (e) { + if (e.candidate) { + Object.defineProperty(e, 'candidate', { + value: new window.RTCIceCandidate(e.candidate), + writable: 'false' + }); + } + return e; + }); +} +function shimRTCIceCandidateRelayProtocol(window) { + if (!window.RTCIceCandidate || window.RTCIceCandidate && 'relayProtocol' in window.RTCIceCandidate.prototype) { + return; + } + + // Hook up the augmented candidate in onicecandidate and + // addEventListener('icecandidate', ...) + utils.wrapPeerConnectionEvent(window, 'icecandidate', function (e) { + if (e.candidate) { + var parsedCandidate = _sdp["default"].parseCandidate(e.candidate.candidate); + if (parsedCandidate.type === 'relay') { + // This is a libwebrtc-specific mapping of local type preference + // to relayProtocol. + e.candidate.relayProtocol = { + 0: 'tls', + 1: 'tcp', + 2: 'udp' + }[parsedCandidate.priority >> 24]; + } + } + return e; + }); +} +function shimMaxMessageSize(window, browserDetails) { + if (!window.RTCPeerConnection) { + return; + } + if (!('sctp' in window.RTCPeerConnection.prototype)) { + Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', { + get: function get() { + return typeof this._sctp === 'undefined' ? null : this._sctp; + } + }); + } + var sctpInDescription = function sctpInDescription(description) { + if (!description || !description.sdp) { + return false; + } + var sections = _sdp["default"].splitSections(description.sdp); + sections.shift(); + return sections.some(function (mediaSection) { + var mLine = _sdp["default"].parseMLine(mediaSection); + return mLine && mLine.kind === 'application' && mLine.protocol.indexOf('SCTP') !== -1; + }); + }; + var getRemoteFirefoxVersion = function getRemoteFirefoxVersion(description) { + // TODO: Is there a better solution for detecting Firefox? + var match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/); + if (match === null || match.length < 2) { + return -1; + } + var version = parseInt(match[1], 10); + // Test for NaN (yes, this is ugly) + return version !== version ? -1 : version; + }; + var getCanSendMaxMessageSize = function getCanSendMaxMessageSize(remoteIsFirefox) { + // Every implementation we know can send at least 64 KiB. + // Note: Although Chrome is technically able to send up to 256 KiB, the + // data does not reach the other peer reliably. + // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419 + var canSendMaxMessageSize = 65536; + if (browserDetails.browser === 'firefox') { + if (browserDetails.version < 57) { + if (remoteIsFirefox === -1) { + // FF < 57 will send in 16 KiB chunks using the deprecated PPID + // fragmentation. + canSendMaxMessageSize = 16384; + } else { + // However, other FF (and RAWRTC) can reassemble PPID-fragmented + // messages. Thus, supporting ~2 GiB when sending. + canSendMaxMessageSize = 2147483637; + } + } else if (browserDetails.version < 60) { + // Currently, all FF >= 57 will reset the remote maximum message size + // to the default value when a data channel is created at a later + // stage. :( + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831 + canSendMaxMessageSize = browserDetails.version === 57 ? 65535 : 65536; + } else { + // FF >= 60 supports sending ~2 GiB + canSendMaxMessageSize = 2147483637; + } + } + return canSendMaxMessageSize; + }; + var getMaxMessageSize = function getMaxMessageSize(description, remoteIsFirefox) { + // Note: 65536 bytes is the default value from the SDP spec. Also, + // every implementation we know supports receiving 65536 bytes. + var maxMessageSize = 65536; + + // FF 57 has a slightly incorrect default remote max message size, so + // we need to adjust it here to avoid a failure when sending. + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697 + if (browserDetails.browser === 'firefox' && browserDetails.version === 57) { + maxMessageSize = 65535; + } + var match = _sdp["default"].matchPrefix(description.sdp, 'a=max-message-size:'); + if (match.length > 0) { + maxMessageSize = parseInt(match[0].substring(19), 10); + } else if (browserDetails.browser === 'firefox' && remoteIsFirefox !== -1) { + // If the maximum message size is not present in the remote SDP and + // both local and remote are Firefox, the remote peer can receive + // ~2 GiB. + maxMessageSize = 2147483637; + } + return maxMessageSize; + }; + var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription; + window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() { + this._sctp = null; + // Chrome decided to not expose .sctp in plan-b mode. + // As usual, adapter.js has to do an 'ugly worakaround' + // to cover up the mess. + if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) { + var _this$getConfiguratio = this.getConfiguration(), + sdpSemantics = _this$getConfiguratio.sdpSemantics; + if (sdpSemantics === 'plan-b') { + Object.defineProperty(this, 'sctp', { + get: function get() { + return typeof this._sctp === 'undefined' ? null : this._sctp; + }, + enumerable: true, + configurable: true + }); + } + } + if (sctpInDescription(arguments[0])) { + // Check if the remote is FF. + var isFirefox = getRemoteFirefoxVersion(arguments[0]); + + // Get the maximum message size the local peer is capable of sending + var canSendMMS = getCanSendMaxMessageSize(isFirefox); + + // Get the maximum message size of the remote peer. + var remoteMMS = getMaxMessageSize(arguments[0], isFirefox); + + // Determine final maximum message size + var maxMessageSize; + if (canSendMMS === 0 && remoteMMS === 0) { + maxMessageSize = Number.POSITIVE_INFINITY; + } else if (canSendMMS === 0 || remoteMMS === 0) { + maxMessageSize = Math.max(canSendMMS, remoteMMS); + } else { + maxMessageSize = Math.min(canSendMMS, remoteMMS); + } + + // Create a dummy RTCSctpTransport object and the 'maxMessageSize' + // attribute. + var sctp = {}; + Object.defineProperty(sctp, 'maxMessageSize', { + get: function get() { + return maxMessageSize; + } + }); + this._sctp = sctp; + } + return origSetRemoteDescription.apply(this, arguments); + }; +} +function shimSendThrowTypeError(window) { + if (!(window.RTCPeerConnection && 'createDataChannel' in window.RTCPeerConnection.prototype)) { + return; + } + + // Note: Although Firefox >= 57 has a native implementation, the maximum + // message size can be reset for all data channels at a later stage. + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831 + + function wrapDcSend(dc, pc) { + var origDataChannelSend = dc.send; + dc.send = function send() { + var data = arguments[0]; + var length = data.length || data.size || data.byteLength; + if (dc.readyState === 'open' && pc.sctp && length > pc.sctp.maxMessageSize) { + throw new TypeError('Message too large (can send a maximum of ' + pc.sctp.maxMessageSize + ' bytes)'); + } + return origDataChannelSend.apply(dc, arguments); + }; + } + var origCreateDataChannel = window.RTCPeerConnection.prototype.createDataChannel; + window.RTCPeerConnection.prototype.createDataChannel = function createDataChannel() { + var dataChannel = origCreateDataChannel.apply(this, arguments); + wrapDcSend(dataChannel, this); + return dataChannel; + }; + utils.wrapPeerConnectionEvent(window, 'datachannel', function (e) { + wrapDcSend(e.channel, e.target); + return e; + }); +} + +/* shims RTCConnectionState by pretending it is the same as iceConnectionState. + * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12 + * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect + * since DTLS failures would be hidden. See + * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827 + * for the Firefox tracking bug. + */ +function shimConnectionState(window) { + if (!window.RTCPeerConnection || 'connectionState' in window.RTCPeerConnection.prototype) { + return; + } + var proto = window.RTCPeerConnection.prototype; + Object.defineProperty(proto, 'connectionState', { + get: function get() { + return { + completed: 'connected', + checking: 'connecting' + }[this.iceConnectionState] || this.iceConnectionState; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(proto, 'onconnectionstatechange', { + get: function get() { + return this._onconnectionstatechange || null; + }, + set: function set(cb) { + if (this._onconnectionstatechange) { + this.removeEventListener('connectionstatechange', this._onconnectionstatechange); + delete this._onconnectionstatechange; + } + if (cb) { + this.addEventListener('connectionstatechange', this._onconnectionstatechange = cb); + } + }, + enumerable: true, + configurable: true + }); + ['setLocalDescription', 'setRemoteDescription'].forEach(function (method) { + var origMethod = proto[method]; + proto[method] = function () { + if (!this._connectionstatechangepoly) { + this._connectionstatechangepoly = function (e) { + var pc = e.target; + if (pc._lastConnectionState !== pc.connectionState) { + pc._lastConnectionState = pc.connectionState; + var newEvent = new Event('connectionstatechange', e); + pc.dispatchEvent(newEvent); + } + return e; + }; + this.addEventListener('iceconnectionstatechange', this._connectionstatechangepoly); + } + return origMethod.apply(this, arguments); + }; + }); +} +function removeExtmapAllowMixed(window, browserDetails) { + /* remove a=extmap-allow-mixed for webrtc.org < M71 */ + if (!window.RTCPeerConnection) { + return; + } + if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) { + return; + } + if (browserDetails.browser === 'safari' && browserDetails.version >= 605) { + return; + } + var nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription; + window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription(desc) { + if (desc && desc.sdp && desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) { + var sdp = desc.sdp.split('\n').filter(function (line) { + return line.trim() !== 'a=extmap-allow-mixed'; + }).join('\n'); + // Safari enforces read-only-ness of RTCSessionDescription fields. + if (window.RTCSessionDescription && desc instanceof window.RTCSessionDescription) { + arguments[0] = new window.RTCSessionDescription({ + type: desc.type, + sdp: sdp + }); + } else { + desc.sdp = sdp; + } + } + return nativeSRD.apply(this, arguments); + }; +} +function shimAddIceCandidateNullOrEmpty(window, browserDetails) { + // Support for addIceCandidate(null or undefined) + // as well as addIceCandidate({candidate: "", ...}) + // https://bugs.chromium.org/p/chromium/issues/detail?id=978582 + // Note: must be called before other polyfills which change the signature. + if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) { + return; + } + var nativeAddIceCandidate = window.RTCPeerConnection.prototype.addIceCandidate; + if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) { + return; + } + window.RTCPeerConnection.prototype.addIceCandidate = function addIceCandidate() { + if (!arguments[0]) { + if (arguments[1]) { + arguments[1].apply(null); + } + return Promise.resolve(); + } + // Firefox 68+ emits and processes {candidate: "", ...}, ignore + // in older versions. + // Native support for ignoring exists for Chrome M77+. + // Safari ignores as well, exact version unknown but works in the same + // version that also ignores addIceCandidate(null). + if ((browserDetails.browser === 'chrome' && browserDetails.version < 78 || browserDetails.browser === 'firefox' && browserDetails.version < 68 || browserDetails.browser === 'safari') && arguments[0] && arguments[0].candidate === '') { + return Promise.resolve(); + } + return nativeAddIceCandidate.apply(this, arguments); + }; +} + +// Note: Make sure to call this ahead of APIs that modify +// setLocalDescription.length +function shimParameterlessSetLocalDescription(window, browserDetails) { + if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) { + return; + } + var nativeSetLocalDescription = window.RTCPeerConnection.prototype.setLocalDescription; + if (!nativeSetLocalDescription || nativeSetLocalDescription.length === 0) { + return; + } + window.RTCPeerConnection.prototype.setLocalDescription = function setLocalDescription() { + var _this = this; + var desc = arguments[0] || {}; + if (_typeof(desc) !== 'object' || desc.type && desc.sdp) { + return nativeSetLocalDescription.apply(this, arguments); + } + // The remaining steps should technically happen when SLD comes off the + // RTCPeerConnection's operations chain (not ahead of going on it), but + // this is too difficult to shim. Instead, this shim only covers the + // common case where the operations chain is empty. This is imperfect, but + // should cover many cases. Rationale: Even if we can't reduce the glare + // window to zero on imperfect implementations, there's value in tapping + // into the perfect negotiation pattern that several browsers support. + desc = { + type: desc.type, + sdp: desc.sdp + }; + if (!desc.type) { + switch (this.signalingState) { + case 'stable': + case 'have-local-offer': + case 'have-remote-pranswer': + desc.type = 'offer'; + break; + default: + desc.type = 'answer'; + break; + } + } + if (desc.sdp || desc.type !== 'offer' && desc.type !== 'answer') { + return nativeSetLocalDescription.apply(this, [desc]); + } + var func = desc.type === 'offer' ? this.createOffer : this.createAnswer; + return func.apply(this).then(function (d) { + return nativeSetLocalDescription.apply(_this, [d]); + }); + }; +} + +},{"./utils":11,"sdp":12}],7:[function(require,module,exports){ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ +/* eslint-env node */ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.shimAddTransceiver = shimAddTransceiver; +exports.shimCreateAnswer = shimCreateAnswer; +exports.shimCreateOffer = shimCreateOffer; +Object.defineProperty(exports, "shimGetDisplayMedia", { + enumerable: true, + get: function get() { + return _getdisplaymedia.shimGetDisplayMedia; + } +}); +exports.shimGetParameters = shimGetParameters; +Object.defineProperty(exports, "shimGetUserMedia", { + enumerable: true, + get: function get() { + return _getusermedia.shimGetUserMedia; + } +}); +exports.shimOnTrack = shimOnTrack; +exports.shimPeerConnection = shimPeerConnection; +exports.shimRTCDataChannel = shimRTCDataChannel; +exports.shimReceiverGetStats = shimReceiverGetStats; +exports.shimRemoveStream = shimRemoveStream; +exports.shimSenderGetStats = shimSenderGetStats; +var utils = _interopRequireWildcard(require("../utils")); +var _getusermedia = require("./getusermedia"); +var _getdisplaymedia = require("./getdisplaymedia"); +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } +function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } +function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } +function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } +function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } +function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } +function shimOnTrack(window) { + if (_typeof(window) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) { + Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', { + get: function get() { + return { + receiver: this.receiver + }; + } + }); + } +} +function shimPeerConnection(window, browserDetails) { + if (_typeof(window) !== 'object' || !(window.RTCPeerConnection || window.mozRTCPeerConnection)) { + return; // probably media.peerconnection.enabled=false in about:config + } + + if (!window.RTCPeerConnection && window.mozRTCPeerConnection) { + // very basic support for old versions. + window.RTCPeerConnection = window.mozRTCPeerConnection; + } + if (browserDetails.version < 53) { + // shim away need for obsolete RTCIceCandidate/RTCSessionDescription. + ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) { + var nativeMethod = window.RTCPeerConnection.prototype[method]; + var methodObj = _defineProperty({}, method, function () { + arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]); + return nativeMethod.apply(this, arguments); + }); + window.RTCPeerConnection.prototype[method] = methodObj[method]; + }); + } + var modernStatsTypes = { + inboundrtp: 'inbound-rtp', + outboundrtp: 'outbound-rtp', + candidatepair: 'candidate-pair', + localcandidate: 'local-candidate', + remotecandidate: 'remote-candidate' + }; + var nativeGetStats = window.RTCPeerConnection.prototype.getStats; + window.RTCPeerConnection.prototype.getStats = function getStats() { + var _arguments = Array.prototype.slice.call(arguments), + selector = _arguments[0], + onSucc = _arguments[1], + onErr = _arguments[2]; + return nativeGetStats.apply(this, [selector || null]).then(function (stats) { + if (browserDetails.version < 53 && !onSucc) { + // Shim only promise getStats with spec-hyphens in type names + // Leave callback version alone; misc old uses of forEach before Map + try { + stats.forEach(function (stat) { + stat.type = modernStatsTypes[stat.type] || stat.type; + }); + } catch (e) { + if (e.name !== 'TypeError') { + throw e; + } + // Avoid TypeError: "type" is read-only, in old versions. 34-43ish + stats.forEach(function (stat, i) { + stats.set(i, Object.assign({}, stat, { + type: modernStatsTypes[stat.type] || stat.type + })); + }); + } + } + return stats; + }).then(onSucc, onErr); + }; +} +function shimSenderGetStats(window) { + if (!(_typeof(window) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) { + return; + } + if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) { + return; + } + var origGetSenders = window.RTCPeerConnection.prototype.getSenders; + if (origGetSenders) { + window.RTCPeerConnection.prototype.getSenders = function getSenders() { + var _this = this; + var senders = origGetSenders.apply(this, []); + senders.forEach(function (sender) { + return sender._pc = _this; + }); + return senders; + }; + } + var origAddTrack = window.RTCPeerConnection.prototype.addTrack; + if (origAddTrack) { + window.RTCPeerConnection.prototype.addTrack = function addTrack() { + var sender = origAddTrack.apply(this, arguments); + sender._pc = this; + return sender; + }; + } + window.RTCRtpSender.prototype.getStats = function getStats() { + return this.track ? this._pc.getStats(this.track) : Promise.resolve(new Map()); + }; +} +function shimReceiverGetStats(window) { + if (!(_typeof(window) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) { + return; + } + if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) { + return; + } + var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers; + if (origGetReceivers) { + window.RTCPeerConnection.prototype.getReceivers = function getReceivers() { + var _this2 = this; + var receivers = origGetReceivers.apply(this, []); + receivers.forEach(function (receiver) { + return receiver._pc = _this2; + }); + return receivers; + }; + } + utils.wrapPeerConnectionEvent(window, 'track', function (e) { + e.receiver._pc = e.srcElement; + return e; + }); + window.RTCRtpReceiver.prototype.getStats = function getStats() { + return this._pc.getStats(this.track); + }; +} +function shimRemoveStream(window) { + if (!window.RTCPeerConnection || 'removeStream' in window.RTCPeerConnection.prototype) { + return; + } + window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) { + var _this3 = this; + utils.deprecated('removeStream', 'removeTrack'); + this.getSenders().forEach(function (sender) { + if (sender.track && stream.getTracks().includes(sender.track)) { + _this3.removeTrack(sender); + } + }); + }; +} +function shimRTCDataChannel(window) { + // rename DataChannel to RTCDataChannel (native fix in FF60): + // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851 + if (window.DataChannel && !window.RTCDataChannel) { + window.RTCDataChannel = window.DataChannel; + } +} +function shimAddTransceiver(window) { + // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647 + // Firefox ignores the init sendEncodings options passed to addTransceiver + // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918 + if (!(_typeof(window) === 'object' && window.RTCPeerConnection)) { + return; + } + var origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver; + if (origAddTransceiver) { + window.RTCPeerConnection.prototype.addTransceiver = function addTransceiver() { + this.setParametersPromises = []; + // WebIDL input coercion and validation + var sendEncodings = arguments[1] && arguments[1].sendEncodings; + if (sendEncodings === undefined) { + sendEncodings = []; + } + sendEncodings = _toConsumableArray(sendEncodings); + var shouldPerformCheck = sendEncodings.length > 0; + if (shouldPerformCheck) { + // If sendEncodings params are provided, validate grammar + sendEncodings.forEach(function (encodingParam) { + if ('rid' in encodingParam) { + var ridRegex = /^[a-z0-9]{0,16}$/i; + if (!ridRegex.test(encodingParam.rid)) { + throw new TypeError('Invalid RID value provided.'); + } + } + if ('scaleResolutionDownBy' in encodingParam) { + if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) { + throw new RangeError('scale_resolution_down_by must be >= 1.0'); + } + } + if ('maxFramerate' in encodingParam) { + if (!(parseFloat(encodingParam.maxFramerate) >= 0)) { + throw new RangeError('max_framerate must be >= 0.0'); + } + } + }); + } + var transceiver = origAddTransceiver.apply(this, arguments); + if (shouldPerformCheck) { + // Check if the init options were applied. If not we do this in an + // asynchronous way and save the promise reference in a global object. + // This is an ugly hack, but at the same time is way more robust than + // checking the sender parameters before and after the createOffer + // Also note that after the createoffer we are not 100% sure that + // the params were asynchronously applied so we might miss the + // opportunity to recreate offer. + var sender = transceiver.sender; + var params = sender.getParameters(); + if (!('encodings' in params) || + // Avoid being fooled by patched getParameters() below. + params.encodings.length === 1 && Object.keys(params.encodings[0]).length === 0) { + params.encodings = sendEncodings; + sender.sendEncodings = sendEncodings; + this.setParametersPromises.push(sender.setParameters(params).then(function () { + delete sender.sendEncodings; + })["catch"](function () { + delete sender.sendEncodings; + })); + } + } + return transceiver; + }; + } +} +function shimGetParameters(window) { + if (!(_typeof(window) === 'object' && window.RTCRtpSender)) { + return; + } + var origGetParameters = window.RTCRtpSender.prototype.getParameters; + if (origGetParameters) { + window.RTCRtpSender.prototype.getParameters = function getParameters() { + var params = origGetParameters.apply(this, arguments); + if (!('encodings' in params)) { + params.encodings = [].concat(this.sendEncodings || [{}]); + } + return params; + }; + } +} +function shimCreateOffer(window) { + // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647 + // Firefox ignores the init sendEncodings options passed to addTransceiver + // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918 + if (!(_typeof(window) === 'object' && window.RTCPeerConnection)) { + return; + } + var origCreateOffer = window.RTCPeerConnection.prototype.createOffer; + window.RTCPeerConnection.prototype.createOffer = function createOffer() { + var _arguments2 = arguments, + _this4 = this; + if (this.setParametersPromises && this.setParametersPromises.length) { + return Promise.all(this.setParametersPromises).then(function () { + return origCreateOffer.apply(_this4, _arguments2); + })["finally"](function () { + _this4.setParametersPromises = []; + }); + } + return origCreateOffer.apply(this, arguments); + }; +} +function shimCreateAnswer(window) { + // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647 + // Firefox ignores the init sendEncodings options passed to addTransceiver + // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918 + if (!(_typeof(window) === 'object' && window.RTCPeerConnection)) { + return; + } + var origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer; + window.RTCPeerConnection.prototype.createAnswer = function createAnswer() { + var _arguments3 = arguments, + _this5 = this; + if (this.setParametersPromises && this.setParametersPromises.length) { + return Promise.all(this.setParametersPromises).then(function () { + return origCreateAnswer.apply(_this5, _arguments3); + })["finally"](function () { + _this5.setParametersPromises = []; + }); + } + return origCreateAnswer.apply(this, arguments); + }; +} + +},{"../utils":11,"./getdisplaymedia":8,"./getusermedia":9}],8:[function(require,module,exports){ +/* + * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ +/* eslint-env node */ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.shimGetDisplayMedia = shimGetDisplayMedia; +function shimGetDisplayMedia(window, preferredMediaSource) { + if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) { + return; + } + if (!window.navigator.mediaDevices) { + return; + } + window.navigator.mediaDevices.getDisplayMedia = function getDisplayMedia(constraints) { + if (!(constraints && constraints.video)) { + var err = new DOMException('getDisplayMedia without video ' + 'constraints is undefined'); + err.name = 'NotFoundError'; + // from https://heycam.github.io/webidl/#idl-DOMException-error-names + err.code = 8; + return Promise.reject(err); + } + if (constraints.video === true) { + constraints.video = { + mediaSource: preferredMediaSource + }; + } else { + constraints.video.mediaSource = preferredMediaSource; + } + return window.navigator.mediaDevices.getUserMedia(constraints); + }; +} + +},{}],9:[function(require,module,exports){ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ +/* eslint-env node */ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.shimGetUserMedia = shimGetUserMedia; +var utils = _interopRequireWildcard(require("../utils")); +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } +function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } +function shimGetUserMedia(window, browserDetails) { + var navigator = window && window.navigator; + var MediaStreamTrack = window && window.MediaStreamTrack; + navigator.getUserMedia = function (constraints, onSuccess, onError) { + // Replace Firefox 44+'s deprecation warning with unprefixed version. + utils.deprecated('navigator.getUserMedia', 'navigator.mediaDevices.getUserMedia'); + navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError); + }; + if (!(browserDetails.version > 55 && 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) { + var remap = function remap(obj, a, b) { + if (a in obj && !(b in obj)) { + obj[b] = obj[a]; + delete obj[a]; + } + }; + var nativeGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices); + navigator.mediaDevices.getUserMedia = function (c) { + if (_typeof(c) === 'object' && _typeof(c.audio) === 'object') { + c = JSON.parse(JSON.stringify(c)); + remap(c.audio, 'autoGainControl', 'mozAutoGainControl'); + remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression'); + } + return nativeGetUserMedia(c); + }; + if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) { + var nativeGetSettings = MediaStreamTrack.prototype.getSettings; + MediaStreamTrack.prototype.getSettings = function () { + var obj = nativeGetSettings.apply(this, arguments); + remap(obj, 'mozAutoGainControl', 'autoGainControl'); + remap(obj, 'mozNoiseSuppression', 'noiseSuppression'); + return obj; + }; + } + if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) { + var nativeApplyConstraints = MediaStreamTrack.prototype.applyConstraints; + MediaStreamTrack.prototype.applyConstraints = function (c) { + if (this.kind === 'audio' && _typeof(c) === 'object') { + c = JSON.parse(JSON.stringify(c)); + remap(c, 'autoGainControl', 'mozAutoGainControl'); + remap(c, 'noiseSuppression', 'mozNoiseSuppression'); + } + return nativeApplyConstraints.apply(this, [c]); + }; + } + } +} + +},{"../utils":11}],10:[function(require,module,exports){ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.shimAudioContext = shimAudioContext; +exports.shimCallbacksAPI = shimCallbacksAPI; +exports.shimConstraints = shimConstraints; +exports.shimCreateOfferLegacy = shimCreateOfferLegacy; +exports.shimGetUserMedia = shimGetUserMedia; +exports.shimLocalStreamsAPI = shimLocalStreamsAPI; +exports.shimRTCIceServerUrls = shimRTCIceServerUrls; +exports.shimRemoteStreamsAPI = shimRemoteStreamsAPI; +exports.shimTrackEventTransceiver = shimTrackEventTransceiver; +var utils = _interopRequireWildcard(require("../utils")); +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } +function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } +function shimLocalStreamsAPI(window) { + if (_typeof(window) !== 'object' || !window.RTCPeerConnection) { + return; + } + if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) { + window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() { + if (!this._localStreams) { + this._localStreams = []; + } + return this._localStreams; + }; + } + if (!('addStream' in window.RTCPeerConnection.prototype)) { + var _addTrack = window.RTCPeerConnection.prototype.addTrack; + window.RTCPeerConnection.prototype.addStream = function addStream(stream) { + var _this = this; + if (!this._localStreams) { + this._localStreams = []; + } + if (!this._localStreams.includes(stream)) { + this._localStreams.push(stream); + } + // Try to emulate Chrome's behaviour of adding in audio-video order. + // Safari orders by track id. + stream.getAudioTracks().forEach(function (track) { + return _addTrack.call(_this, track, stream); + }); + stream.getVideoTracks().forEach(function (track) { + return _addTrack.call(_this, track, stream); + }); + }; + window.RTCPeerConnection.prototype.addTrack = function addTrack(track) { + var _this2 = this; + for (var _len = arguments.length, streams = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + streams[_key - 1] = arguments[_key]; + } + if (streams) { + streams.forEach(function (stream) { + if (!_this2._localStreams) { + _this2._localStreams = [stream]; + } else if (!_this2._localStreams.includes(stream)) { + _this2._localStreams.push(stream); + } + }); + } + return _addTrack.apply(this, arguments); + }; + } + if (!('removeStream' in window.RTCPeerConnection.prototype)) { + window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) { + var _this3 = this; + if (!this._localStreams) { + this._localStreams = []; + } + var index = this._localStreams.indexOf(stream); + if (index === -1) { + return; + } + this._localStreams.splice(index, 1); + var tracks = stream.getTracks(); + this.getSenders().forEach(function (sender) { + if (tracks.includes(sender.track)) { + _this3.removeTrack(sender); + } + }); + }; + } +} +function shimRemoteStreamsAPI(window) { + if (_typeof(window) !== 'object' || !window.RTCPeerConnection) { + return; + } + if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) { + window.RTCPeerConnection.prototype.getRemoteStreams = function getRemoteStreams() { + return this._remoteStreams ? this._remoteStreams : []; + }; + } + if (!('onaddstream' in window.RTCPeerConnection.prototype)) { + Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', { + get: function get() { + return this._onaddstream; + }, + set: function set(f) { + var _this4 = this; + if (this._onaddstream) { + this.removeEventListener('addstream', this._onaddstream); + this.removeEventListener('track', this._onaddstreampoly); + } + this.addEventListener('addstream', this._onaddstream = f); + this.addEventListener('track', this._onaddstreampoly = function (e) { + e.streams.forEach(function (stream) { + if (!_this4._remoteStreams) { + _this4._remoteStreams = []; + } + if (_this4._remoteStreams.includes(stream)) { + return; + } + _this4._remoteStreams.push(stream); + var event = new Event('addstream'); + event.stream = stream; + _this4.dispatchEvent(event); + }); + }); + } + }); + var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription; + window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() { + var pc = this; + if (!this._onaddstreampoly) { + this.addEventListener('track', this._onaddstreampoly = function (e) { + e.streams.forEach(function (stream) { + if (!pc._remoteStreams) { + pc._remoteStreams = []; + } + if (pc._remoteStreams.indexOf(stream) >= 0) { + return; + } + pc._remoteStreams.push(stream); + var event = new Event('addstream'); + event.stream = stream; + pc.dispatchEvent(event); + }); + }); + } + return origSetRemoteDescription.apply(pc, arguments); + }; + } +} +function shimCallbacksAPI(window) { + if (_typeof(window) !== 'object' || !window.RTCPeerConnection) { + return; + } + var prototype = window.RTCPeerConnection.prototype; + var origCreateOffer = prototype.createOffer; + var origCreateAnswer = prototype.createAnswer; + var setLocalDescription = prototype.setLocalDescription; + var setRemoteDescription = prototype.setRemoteDescription; + var addIceCandidate = prototype.addIceCandidate; + prototype.createOffer = function createOffer(successCallback, failureCallback) { + var options = arguments.length >= 2 ? arguments[2] : arguments[0]; + var promise = origCreateOffer.apply(this, [options]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.createAnswer = function createAnswer(successCallback, failureCallback) { + var options = arguments.length >= 2 ? arguments[2] : arguments[0]; + var promise = origCreateAnswer.apply(this, [options]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + var withCallback = function withCallback(description, successCallback, failureCallback) { + var promise = setLocalDescription.apply(this, [description]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.setLocalDescription = withCallback; + withCallback = function withCallback(description, successCallback, failureCallback) { + var promise = setRemoteDescription.apply(this, [description]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.setRemoteDescription = withCallback; + withCallback = function withCallback(candidate, successCallback, failureCallback) { + var promise = addIceCandidate.apply(this, [candidate]); + if (!failureCallback) { + return promise; + } + promise.then(successCallback, failureCallback); + return Promise.resolve(); + }; + prototype.addIceCandidate = withCallback; +} +function shimGetUserMedia(window) { + var navigator = window && window.navigator; + if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + // shim not needed in Safari 12.1 + var mediaDevices = navigator.mediaDevices; + var _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices); + navigator.mediaDevices.getUserMedia = function (constraints) { + return _getUserMedia(shimConstraints(constraints)); + }; + } + if (!navigator.getUserMedia && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) { + navigator.mediaDevices.getUserMedia(constraints).then(cb, errcb); + }.bind(navigator); + } +} +function shimConstraints(constraints) { + if (constraints && constraints.video !== undefined) { + return Object.assign({}, constraints, { + video: utils.compactObject(constraints.video) + }); + } + return constraints; +} +function shimRTCIceServerUrls(window) { + if (!window.RTCPeerConnection) { + return; + } + // migrate from non-spec RTCIceServer.url to RTCIceServer.urls + var OrigPeerConnection = window.RTCPeerConnection; + window.RTCPeerConnection = function RTCPeerConnection(pcConfig, pcConstraints) { + if (pcConfig && pcConfig.iceServers) { + var newIceServers = []; + for (var i = 0; i < pcConfig.iceServers.length; i++) { + var server = pcConfig.iceServers[i]; + if (server.urls === undefined && server.url) { + utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls'); + server = JSON.parse(JSON.stringify(server)); + server.urls = server.url; + delete server.url; + newIceServers.push(server); + } else { + newIceServers.push(pcConfig.iceServers[i]); + } + } + pcConfig.iceServers = newIceServers; + } + return new OrigPeerConnection(pcConfig, pcConstraints); + }; + window.RTCPeerConnection.prototype = OrigPeerConnection.prototype; + // wrap static methods. Currently just generateCertificate. + if ('generateCertificate' in OrigPeerConnection) { + Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', { + get: function get() { + return OrigPeerConnection.generateCertificate; + } + }); + } +} +function shimTrackEventTransceiver(window) { + // Add event.transceiver member over deprecated event.receiver + if (_typeof(window) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) { + Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', { + get: function get() { + return { + receiver: this.receiver + }; + } + }); + } +} +function shimCreateOfferLegacy(window) { + var origCreateOffer = window.RTCPeerConnection.prototype.createOffer; + window.RTCPeerConnection.prototype.createOffer = function createOffer(offerOptions) { + if (offerOptions) { + if (typeof offerOptions.offerToReceiveAudio !== 'undefined') { + // support bit values + offerOptions.offerToReceiveAudio = !!offerOptions.offerToReceiveAudio; + } + var audioTransceiver = this.getTransceivers().find(function (transceiver) { + return transceiver.receiver.track.kind === 'audio'; + }); + if (offerOptions.offerToReceiveAudio === false && audioTransceiver) { + if (audioTransceiver.direction === 'sendrecv') { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection('sendonly'); + } else { + audioTransceiver.direction = 'sendonly'; + } + } else if (audioTransceiver.direction === 'recvonly') { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection('inactive'); + } else { + audioTransceiver.direction = 'inactive'; + } + } + } else if (offerOptions.offerToReceiveAudio === true && !audioTransceiver) { + this.addTransceiver('audio', { + direction: 'recvonly' + }); + } + if (typeof offerOptions.offerToReceiveVideo !== 'undefined') { + // support bit values + offerOptions.offerToReceiveVideo = !!offerOptions.offerToReceiveVideo; + } + var videoTransceiver = this.getTransceivers().find(function (transceiver) { + return transceiver.receiver.track.kind === 'video'; + }); + if (offerOptions.offerToReceiveVideo === false && videoTransceiver) { + if (videoTransceiver.direction === 'sendrecv') { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection('sendonly'); + } else { + videoTransceiver.direction = 'sendonly'; + } + } else if (videoTransceiver.direction === 'recvonly') { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection('inactive'); + } else { + videoTransceiver.direction = 'inactive'; + } + } + } else if (offerOptions.offerToReceiveVideo === true && !videoTransceiver) { + this.addTransceiver('video', { + direction: 'recvonly' + }); + } + } + return origCreateOffer.apply(this, arguments); + }; +} +function shimAudioContext(window) { + if (_typeof(window) !== 'object' || window.AudioContext) { + return; + } + window.AudioContext = window.webkitAudioContext; +} + +},{"../utils":11}],11:[function(require,module,exports){ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ +/* eslint-env node */ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.compactObject = compactObject; +exports.deprecated = deprecated; +exports.detectBrowser = detectBrowser; +exports.disableLog = disableLog; +exports.disableWarnings = disableWarnings; +exports.extractVersion = extractVersion; +exports.filterStats = filterStats; +exports.log = log; +exports.walkStats = walkStats; +exports.wrapPeerConnectionEvent = wrapPeerConnectionEvent; +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } +var logDisabled_ = true; +var deprecationWarnings_ = true; + +/** + * Extract browser version out of the provided user agent string. + * + * @param {!string} uastring userAgent string. + * @param {!string} expr Regular expression used as match criteria. + * @param {!number} pos position in the version string to be returned. + * @return {!number} browser version. + */ +function extractVersion(uastring, expr, pos) { + var match = uastring.match(expr); + return match && match.length >= pos && parseInt(match[pos], 10); +} + +// Wraps the peerconnection event eventNameToWrap in a function +// which returns the modified event object (or false to prevent +// the event). +function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) { + if (!window.RTCPeerConnection) { + return; + } + var proto = window.RTCPeerConnection.prototype; + var nativeAddEventListener = proto.addEventListener; + proto.addEventListener = function (nativeEventName, cb) { + if (nativeEventName !== eventNameToWrap) { + return nativeAddEventListener.apply(this, arguments); + } + var wrappedCallback = function wrappedCallback(e) { + var modifiedEvent = wrapper(e); + if (modifiedEvent) { + if (cb.handleEvent) { + cb.handleEvent(modifiedEvent); + } else { + cb(modifiedEvent); + } + } + }; + this._eventMap = this._eventMap || {}; + if (!this._eventMap[eventNameToWrap]) { + this._eventMap[eventNameToWrap] = new Map(); + } + this._eventMap[eventNameToWrap].set(cb, wrappedCallback); + return nativeAddEventListener.apply(this, [nativeEventName, wrappedCallback]); + }; + var nativeRemoveEventListener = proto.removeEventListener; + proto.removeEventListener = function (nativeEventName, cb) { + if (nativeEventName !== eventNameToWrap || !this._eventMap || !this._eventMap[eventNameToWrap]) { + return nativeRemoveEventListener.apply(this, arguments); + } + if (!this._eventMap[eventNameToWrap].has(cb)) { + return nativeRemoveEventListener.apply(this, arguments); + } + var unwrappedCb = this._eventMap[eventNameToWrap].get(cb); + this._eventMap[eventNameToWrap]["delete"](cb); + if (this._eventMap[eventNameToWrap].size === 0) { + delete this._eventMap[eventNameToWrap]; + } + if (Object.keys(this._eventMap).length === 0) { + delete this._eventMap; + } + return nativeRemoveEventListener.apply(this, [nativeEventName, unwrappedCb]); + }; + Object.defineProperty(proto, 'on' + eventNameToWrap, { + get: function get() { + return this['_on' + eventNameToWrap]; + }, + set: function set(cb) { + if (this['_on' + eventNameToWrap]) { + this.removeEventListener(eventNameToWrap, this['_on' + eventNameToWrap]); + delete this['_on' + eventNameToWrap]; + } + if (cb) { + this.addEventListener(eventNameToWrap, this['_on' + eventNameToWrap] = cb); + } + }, + enumerable: true, + configurable: true + }); +} +function disableLog(bool) { + if (typeof bool !== 'boolean') { + return new Error('Argument type: ' + _typeof(bool) + '. Please use a boolean.'); + } + logDisabled_ = bool; + return bool ? 'adapter.js logging disabled' : 'adapter.js logging enabled'; +} + +/** + * Disable or enable deprecation warnings + * @param {!boolean} bool set to true to disable warnings. + */ +function disableWarnings(bool) { + if (typeof bool !== 'boolean') { + return new Error('Argument type: ' + _typeof(bool) + '. Please use a boolean.'); + } + deprecationWarnings_ = !bool; + return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled'); +} +function log() { + if ((typeof window === "undefined" ? "undefined" : _typeof(window)) === 'object') { + if (logDisabled_) { + return; + } + if (typeof console !== 'undefined' && typeof console.log === 'function') { + console.log.apply(console, arguments); + } + } +} + +/** + * Shows a deprecation warning suggesting the modern and spec-compatible API. + */ +function deprecated(oldMethod, newMethod) { + if (!deprecationWarnings_) { + return; + } + console.warn(oldMethod + ' is deprecated, please use ' + newMethod + ' instead.'); +} + +/** + * Browser detector. + * + * @return {object} result containing browser and version + * properties. + */ +function detectBrowser(window) { + // Returned result object. + var result = { + browser: null, + version: null + }; + + // Fail early if it's not a browser + if (typeof window === 'undefined' || !window.navigator) { + result.browser = 'Not a browser.'; + return result; + } + var navigator = window.navigator; + if (navigator.mozGetUserMedia) { + // Firefox. + result.browser = 'firefox'; + result.version = extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1); + } else if (navigator.webkitGetUserMedia || window.isSecureContext === false && window.webkitRTCPeerConnection) { + // Chrome, Chromium, Webview, Opera. + // Version matches Chrome/WebRTC version. + // Chrome 74 removed webkitGetUserMedia on http as well so we need the + // more complicated fallback to webkitRTCPeerConnection. + result.browser = 'chrome'; + result.version = extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2); + } else if (window.RTCPeerConnection && navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) { + // Safari. + result.browser = 'safari'; + result.version = extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1); + result.supportsUnifiedPlan = window.RTCRtpTransceiver && 'currentDirection' in window.RTCRtpTransceiver.prototype; + } else { + // Default fallthrough: not supported. + result.browser = 'Not a supported browser.'; + return result; + } + return result; +} + +/** + * Checks if something is an object. + * + * @param {*} val The something you want to check. + * @return true if val is an object, false otherwise. + */ +function isObject(val) { + return Object.prototype.toString.call(val) === '[object Object]'; +} + +/** + * Remove all empty objects and undefined values + * from a nested object -- an enhanced and vanilla version + * of Lodash's `compact`. + */ +function compactObject(data) { + if (!isObject(data)) { + return data; + } + return Object.keys(data).reduce(function (accumulator, key) { + var isObj = isObject(data[key]); + var value = isObj ? compactObject(data[key]) : data[key]; + var isEmptyObject = isObj && !Object.keys(value).length; + if (value === undefined || isEmptyObject) { + return accumulator; + } + return Object.assign(accumulator, _defineProperty({}, key, value)); + }, {}); +} + +/* iterates the stats graph recursively. */ +function walkStats(stats, base, resultSet) { + if (!base || resultSet.has(base.id)) { + return; + } + resultSet.set(base.id, base); + Object.keys(base).forEach(function (name) { + if (name.endsWith('Id')) { + walkStats(stats, stats.get(base[name]), resultSet); + } else if (name.endsWith('Ids')) { + base[name].forEach(function (id) { + walkStats(stats, stats.get(id), resultSet); + }); + } + }); +} + +/* filter getStats for a sender/receiver track. */ +function filterStats(result, track, outbound) { + var streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp'; + var filteredResult = new Map(); + if (track === null) { + return filteredResult; + } + var trackStats = []; + result.forEach(function (value) { + if (value.type === 'track' && value.trackIdentifier === track.id) { + trackStats.push(value); + } + }); + trackStats.forEach(function (trackStat) { + result.forEach(function (stats) { + if (stats.type === streamStatsType && stats.trackId === trackStat.id) { + walkStats(result, stats, filteredResult); + } + }); + }); + return filteredResult; +} + +},{}],12:[function(require,module,exports){ +/* eslint-env node */ +'use strict'; + +// SDP helpers. + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var SDPUtils = {}; + +// Generate an alphanumeric identifier for cname or mids. +// TODO: use UUIDs instead? https://gist.github.com/jed/982883 +SDPUtils.generateIdentifier = function () { + return Math.random().toString(36).substring(2, 12); +}; + +// The RTCP CNAME used by all peerconnections from the same JS. +SDPUtils.localCName = SDPUtils.generateIdentifier(); + +// Splits SDP into lines, dealing with both CRLF and LF. +SDPUtils.splitLines = function (blob) { + return blob.trim().split('\n').map(function (line) { + return line.trim(); + }); +}; +// Splits SDP into sessionpart and mediasections. Ensures CRLF. +SDPUtils.splitSections = function (blob) { + var parts = blob.split('\nm='); + return parts.map(function (part, index) { + return (index > 0 ? 'm=' + part : part).trim() + '\r\n'; + }); +}; + +// Returns the session description. +SDPUtils.getDescription = function (blob) { + var sections = SDPUtils.splitSections(blob); + return sections && sections[0]; +}; + +// Returns the individual media sections. +SDPUtils.getMediaSections = function (blob) { + var sections = SDPUtils.splitSections(blob); + sections.shift(); + return sections; +}; + +// Returns lines that start with a certain prefix. +SDPUtils.matchPrefix = function (blob, prefix) { + return SDPUtils.splitLines(blob).filter(function (line) { + return line.indexOf(prefix) === 0; + }); +}; + +// Parses an ICE candidate line. Sample input: +// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8 +// rport 55996" +// Input can be prefixed with a=. +SDPUtils.parseCandidate = function (line) { + var parts = void 0; + // Parse both variants. + if (line.indexOf('a=candidate:') === 0) { + parts = line.substring(12).split(' '); + } else { + parts = line.substring(10).split(' '); + } + + var candidate = { + foundation: parts[0], + component: { 1: 'rtp', 2: 'rtcp' }[parts[1]] || parts[1], + protocol: parts[2].toLowerCase(), + priority: parseInt(parts[3], 10), + ip: parts[4], + address: parts[4], // address is an alias for ip. + port: parseInt(parts[5], 10), + // skip parts[6] == 'typ' + type: parts[7] + }; + + for (var i = 8; i < parts.length; i += 2) { + switch (parts[i]) { + case 'raddr': + candidate.relatedAddress = parts[i + 1]; + break; + case 'rport': + candidate.relatedPort = parseInt(parts[i + 1], 10); + break; + case 'tcptype': + candidate.tcpType = parts[i + 1]; + break; + case 'ufrag': + candidate.ufrag = parts[i + 1]; // for backward compatibility. + candidate.usernameFragment = parts[i + 1]; + break; + default: + // extension handling, in particular ufrag. Don't overwrite. + if (candidate[parts[i]] === undefined) { + candidate[parts[i]] = parts[i + 1]; + } + break; + } + } + return candidate; +}; + +// Translates a candidate object into SDP candidate attribute. +// This does not include the a= prefix! +SDPUtils.writeCandidate = function (candidate) { + var sdp = []; + sdp.push(candidate.foundation); + + var component = candidate.component; + if (component === 'rtp') { + sdp.push(1); + } else if (component === 'rtcp') { + sdp.push(2); + } else { + sdp.push(component); + } + sdp.push(candidate.protocol.toUpperCase()); + sdp.push(candidate.priority); + sdp.push(candidate.address || candidate.ip); + sdp.push(candidate.port); + + var type = candidate.type; + sdp.push('typ'); + sdp.push(type); + if (type !== 'host' && candidate.relatedAddress && candidate.relatedPort) { + sdp.push('raddr'); + sdp.push(candidate.relatedAddress); + sdp.push('rport'); + sdp.push(candidate.relatedPort); + } + if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') { + sdp.push('tcptype'); + sdp.push(candidate.tcpType); + } + if (candidate.usernameFragment || candidate.ufrag) { + sdp.push('ufrag'); + sdp.push(candidate.usernameFragment || candidate.ufrag); + } + return 'candidate:' + sdp.join(' '); +}; + +// Parses an ice-options line, returns an array of option tags. +// Sample input: +// a=ice-options:foo bar +SDPUtils.parseIceOptions = function (line) { + return line.substring(14).split(' '); +}; + +// Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input: +// a=rtpmap:111 opus/48000/2 +SDPUtils.parseRtpMap = function (line) { + var parts = line.substring(9).split(' '); + var parsed = { + payloadType: parseInt(parts.shift(), 10) // was: id + }; + + parts = parts[0].split('/'); + + parsed.name = parts[0]; + parsed.clockRate = parseInt(parts[1], 10); // was: clockrate + parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1; + // legacy alias, got renamed back to channels in ORTC. + parsed.numChannels = parsed.channels; + return parsed; +}; + +// Generates a rtpmap line from RTCRtpCodecCapability or +// RTCRtpCodecParameters. +SDPUtils.writeRtpMap = function (codec) { + var pt = codec.payloadType; + if (codec.preferredPayloadType !== undefined) { + pt = codec.preferredPayloadType; + } + var channels = codec.channels || codec.numChannels || 1; + return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate + (channels !== 1 ? '/' + channels : '') + '\r\n'; +}; + +// Parses a extmap line (headerextension from RFC 5285). Sample input: +// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset +// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset +SDPUtils.parseExtmap = function (line) { + var parts = line.substring(9).split(' '); + return { + id: parseInt(parts[0], 10), + direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv', + uri: parts[1], + attributes: parts.slice(2).join(' ') + }; +}; + +// Generates an extmap line from RTCRtpHeaderExtensionParameters or +// RTCRtpHeaderExtension. +SDPUtils.writeExtmap = function (headerExtension) { + return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) + (headerExtension.direction && headerExtension.direction !== 'sendrecv' ? '/' + headerExtension.direction : '') + ' ' + headerExtension.uri + (headerExtension.attributes ? ' ' + headerExtension.attributes : '') + '\r\n'; +}; + +// Parses a fmtp line, returns dictionary. Sample input: +// a=fmtp:96 vbr=on;cng=on +// Also deals with vbr=on; cng=on +SDPUtils.parseFmtp = function (line) { + var parsed = {}; + var kv = void 0; + var parts = line.substring(line.indexOf(' ') + 1).split(';'); + for (var j = 0; j < parts.length; j++) { + kv = parts[j].trim().split('='); + parsed[kv[0].trim()] = kv[1]; + } + return parsed; +}; + +// Generates a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters. +SDPUtils.writeFmtp = function (codec) { + var line = ''; + var pt = codec.payloadType; + if (codec.preferredPayloadType !== undefined) { + pt = codec.preferredPayloadType; + } + if (codec.parameters && Object.keys(codec.parameters).length) { + var params = []; + Object.keys(codec.parameters).forEach(function (param) { + if (codec.parameters[param] !== undefined) { + params.push(param + '=' + codec.parameters[param]); + } else { + params.push(param); + } + }); + line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n'; + } + return line; +}; + +// Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input: +// a=rtcp-fb:98 nack rpsi +SDPUtils.parseRtcpFb = function (line) { + var parts = line.substring(line.indexOf(' ') + 1).split(' '); + return { + type: parts.shift(), + parameter: parts.join(' ') + }; +}; + +// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters. +SDPUtils.writeRtcpFb = function (codec) { + var lines = ''; + var pt = codec.payloadType; + if (codec.preferredPayloadType !== undefined) { + pt = codec.preferredPayloadType; + } + if (codec.rtcpFeedback && codec.rtcpFeedback.length) { + // FIXME: special handling for trr-int? + codec.rtcpFeedback.forEach(function (fb) { + lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') + '\r\n'; + }); + } + return lines; +}; + +// Parses a RFC 5576 ssrc media attribute. Sample input: +// a=ssrc:3735928559 cname:something +SDPUtils.parseSsrcMedia = function (line) { + var sp = line.indexOf(' '); + var parts = { + ssrc: parseInt(line.substring(7, sp), 10) + }; + var colon = line.indexOf(':', sp); + if (colon > -1) { + parts.attribute = line.substring(sp + 1, colon); + parts.value = line.substring(colon + 1); + } else { + parts.attribute = line.substring(sp + 1); + } + return parts; +}; + +// Parse a ssrc-group line (see RFC 5576). Sample input: +// a=ssrc-group:semantics 12 34 +SDPUtils.parseSsrcGroup = function (line) { + var parts = line.substring(13).split(' '); + return { + semantics: parts.shift(), + ssrcs: parts.map(function (ssrc) { + return parseInt(ssrc, 10); + }) + }; +}; + +// Extracts the MID (RFC 5888) from a media section. +// Returns the MID or undefined if no mid line was found. +SDPUtils.getMid = function (mediaSection) { + var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0]; + if (mid) { + return mid.substring(6); + } +}; + +// Parses a fingerprint line for DTLS-SRTP. +SDPUtils.parseFingerprint = function (line) { + var parts = line.substring(14).split(' '); + return { + algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge. + value: parts[1].toUpperCase() // the definition is upper-case in RFC 4572. + }; +}; + +// Extracts DTLS parameters from SDP media section or sessionpart. +// FIXME: for consistency with other functions this should only +// get the fingerprint line as input. See also getIceParameters. +SDPUtils.getDtlsParameters = function (mediaSection, sessionpart) { + var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=fingerprint:'); + // Note: a=setup line is ignored since we use the 'auto' role in Edge. + return { + role: 'auto', + fingerprints: lines.map(SDPUtils.parseFingerprint) + }; +}; + +// Serializes DTLS parameters to SDP. +SDPUtils.writeDtlsParameters = function (params, setupType) { + var sdp = 'a=setup:' + setupType + '\r\n'; + params.fingerprints.forEach(function (fp) { + sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n'; + }); + return sdp; +}; + +// Parses a=crypto lines into +// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members +SDPUtils.parseCryptoLine = function (line) { + var parts = line.substring(9).split(' '); + return { + tag: parseInt(parts[0], 10), + cryptoSuite: parts[1], + keyParams: parts[2], + sessionParams: parts.slice(3) + }; +}; + +SDPUtils.writeCryptoLine = function (parameters) { + return 'a=crypto:' + parameters.tag + ' ' + parameters.cryptoSuite + ' ' + (_typeof(parameters.keyParams) === 'object' ? SDPUtils.writeCryptoKeyParams(parameters.keyParams) : parameters.keyParams) + (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') + '\r\n'; +}; + +// Parses the crypto key parameters into +// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam* +SDPUtils.parseCryptoKeyParams = function (keyParams) { + if (keyParams.indexOf('inline:') !== 0) { + return null; + } + var parts = keyParams.substring(7).split('|'); + return { + keyMethod: 'inline', + keySalt: parts[0], + lifeTime: parts[1], + mkiValue: parts[2] ? parts[2].split(':')[0] : undefined, + mkiLength: parts[2] ? parts[2].split(':')[1] : undefined + }; +}; + +SDPUtils.writeCryptoKeyParams = function (keyParams) { + return keyParams.keyMethod + ':' + keyParams.keySalt + (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') + (keyParams.mkiValue && keyParams.mkiLength ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength : ''); +}; + +// Extracts all SDES parameters. +SDPUtils.getCryptoParameters = function (mediaSection, sessionpart) { + var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=crypto:'); + return lines.map(SDPUtils.parseCryptoLine); +}; + +// Parses ICE information from SDP media section or sessionpart. +// FIXME: for consistency with other functions this should only +// get the ice-ufrag and ice-pwd lines as input. +SDPUtils.getIceParameters = function (mediaSection, sessionpart) { + var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-ufrag:')[0]; + var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-pwd:')[0]; + if (!(ufrag && pwd)) { + return null; + } + return { + usernameFragment: ufrag.substring(12), + password: pwd.substring(10) + }; +}; + +// Serializes ICE parameters to SDP. +SDPUtils.writeIceParameters = function (params) { + var sdp = 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + 'a=ice-pwd:' + params.password + '\r\n'; + if (params.iceLite) { + sdp += 'a=ice-lite\r\n'; + } + return sdp; +}; + +// Parses the SDP media section and returns RTCRtpParameters. +SDPUtils.parseRtpParameters = function (mediaSection) { + var description = { + codecs: [], + headerExtensions: [], + fecMechanisms: [], + rtcp: [] + }; + var lines = SDPUtils.splitLines(mediaSection); + var mline = lines[0].split(' '); + description.profile = mline[2]; + for (var i = 3; i < mline.length; i++) { + // find all codecs from mline[3..] + var pt = mline[i]; + var rtpmapline = SDPUtils.matchPrefix(mediaSection, 'a=rtpmap:' + pt + ' ')[0]; + if (rtpmapline) { + var codec = SDPUtils.parseRtpMap(rtpmapline); + var fmtps = SDPUtils.matchPrefix(mediaSection, 'a=fmtp:' + pt + ' '); + // Only the first a=fmtp: is considered. + codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {}; + codec.rtcpFeedback = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:' + pt + ' ').map(SDPUtils.parseRtcpFb); + description.codecs.push(codec); + // parse FEC mechanisms from rtpmap lines. + switch (codec.name.toUpperCase()) { + case 'RED': + case 'ULPFEC': + description.fecMechanisms.push(codec.name.toUpperCase()); + break; + default: + // only RED and ULPFEC are recognized as FEC mechanisms. + break; + } + } + } + SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function (line) { + description.headerExtensions.push(SDPUtils.parseExtmap(line)); + }); + var wildcardRtcpFb = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:* ').map(SDPUtils.parseRtcpFb); + description.codecs.forEach(function (codec) { + wildcardRtcpFb.forEach(function (fb) { + var duplicate = codec.rtcpFeedback.find(function (existingFeedback) { + return existingFeedback.type === fb.type && existingFeedback.parameter === fb.parameter; + }); + if (!duplicate) { + codec.rtcpFeedback.push(fb); + } + }); + }); + // FIXME: parse rtcp. + return description; +}; + +// Generates parts of the SDP media section describing the capabilities / +// parameters. +SDPUtils.writeRtpDescription = function (kind, caps) { + var sdp = ''; + + // Build the mline. + sdp += 'm=' + kind + ' '; + sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs. + sdp += ' ' + (caps.profile || 'UDP/TLS/RTP/SAVPF') + ' '; + sdp += caps.codecs.map(function (codec) { + if (codec.preferredPayloadType !== undefined) { + return codec.preferredPayloadType; + } + return codec.payloadType; + }).join(' ') + '\r\n'; + + sdp += 'c=IN IP4 0.0.0.0\r\n'; + sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n'; + + // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb. + caps.codecs.forEach(function (codec) { + sdp += SDPUtils.writeRtpMap(codec); + sdp += SDPUtils.writeFmtp(codec); + sdp += SDPUtils.writeRtcpFb(codec); + }); + var maxptime = 0; + caps.codecs.forEach(function (codec) { + if (codec.maxptime > maxptime) { + maxptime = codec.maxptime; + } + }); + if (maxptime > 0) { + sdp += 'a=maxptime:' + maxptime + '\r\n'; + } + + if (caps.headerExtensions) { + caps.headerExtensions.forEach(function (extension) { + sdp += SDPUtils.writeExtmap(extension); + }); + } + // FIXME: write fecMechanisms. + return sdp; +}; + +// Parses the SDP media section and returns an array of +// RTCRtpEncodingParameters. +SDPUtils.parseRtpEncodingParameters = function (mediaSection) { + var encodingParameters = []; + var description = SDPUtils.parseRtpParameters(mediaSection); + var hasRed = description.fecMechanisms.indexOf('RED') !== -1; + var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1; + + // filter a=ssrc:... cname:, ignore PlanB-msid + var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) { + return SDPUtils.parseSsrcMedia(line); + }).filter(function (parts) { + return parts.attribute === 'cname'; + }); + var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc; + var secondarySsrc = void 0; + + var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID').map(function (line) { + var parts = line.substring(17).split(' '); + return parts.map(function (part) { + return parseInt(part, 10); + }); + }); + if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) { + secondarySsrc = flows[0][1]; + } + + description.codecs.forEach(function (codec) { + if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) { + var encParam = { + ssrc: primarySsrc, + codecPayloadType: parseInt(codec.parameters.apt, 10) + }; + if (primarySsrc && secondarySsrc) { + encParam.rtx = { ssrc: secondarySsrc }; + } + encodingParameters.push(encParam); + if (hasRed) { + encParam = JSON.parse(JSON.stringify(encParam)); + encParam.fec = { + ssrc: primarySsrc, + mechanism: hasUlpfec ? 'red+ulpfec' : 'red' + }; + encodingParameters.push(encParam); + } + } + }); + if (encodingParameters.length === 0 && primarySsrc) { + encodingParameters.push({ + ssrc: primarySsrc + }); + } + + // we support both b=AS and b=TIAS but interpret AS as TIAS. + var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b='); + if (bandwidth.length) { + if (bandwidth[0].indexOf('b=TIAS:') === 0) { + bandwidth = parseInt(bandwidth[0].substring(7), 10); + } else if (bandwidth[0].indexOf('b=AS:') === 0) { + // use formula from JSEP to convert b=AS to TIAS value. + bandwidth = parseInt(bandwidth[0].substring(5), 10) * 1000 * 0.95 - 50 * 40 * 8; + } else { + bandwidth = undefined; + } + encodingParameters.forEach(function (params) { + params.maxBitrate = bandwidth; + }); + } + return encodingParameters; +}; + +// parses http://draft.ortc.org/#rtcrtcpparameters* +SDPUtils.parseRtcpParameters = function (mediaSection) { + var rtcpParameters = {}; + + // Gets the first SSRC. Note that with RTX there might be multiple + // SSRCs. + var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) { + return SDPUtils.parseSsrcMedia(line); + }).filter(function (obj) { + return obj.attribute === 'cname'; + })[0]; + if (remoteSsrc) { + rtcpParameters.cname = remoteSsrc.value; + rtcpParameters.ssrc = remoteSsrc.ssrc; + } + + // Edge uses the compound attribute instead of reducedSize + // compound is !reducedSize + var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize'); + rtcpParameters.reducedSize = rsize.length > 0; + rtcpParameters.compound = rsize.length === 0; + + // parses the rtcp-mux attrіbute. + // Note that Edge does not support unmuxed RTCP. + var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux'); + rtcpParameters.mux = mux.length > 0; + + return rtcpParameters; +}; + +SDPUtils.writeRtcpParameters = function (rtcpParameters) { + var sdp = ''; + if (rtcpParameters.reducedSize) { + sdp += 'a=rtcp-rsize\r\n'; + } + if (rtcpParameters.mux) { + sdp += 'a=rtcp-mux\r\n'; + } + if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) { + sdp += 'a=ssrc:' + rtcpParameters.ssrc + ' cname:' + rtcpParameters.cname + '\r\n'; + } + return sdp; +}; + +// parses either a=msid: or a=ssrc:... msid lines and returns +// the id of the MediaStream and MediaStreamTrack. +SDPUtils.parseMsid = function (mediaSection) { + var parts = void 0; + var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:'); + if (spec.length === 1) { + parts = spec[0].substring(7).split(' '); + return { stream: parts[0], track: parts[1] }; + } + var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) { + return SDPUtils.parseSsrcMedia(line); + }).filter(function (msidParts) { + return msidParts.attribute === 'msid'; + }); + if (planB.length > 0) { + parts = planB[0].value.split(' '); + return { stream: parts[0], track: parts[1] }; + } +}; + +// SCTP +// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back +// to draft-ietf-mmusic-sctp-sdp-05 +SDPUtils.parseSctpDescription = function (mediaSection) { + var mline = SDPUtils.parseMLine(mediaSection); + var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:'); + var maxMessageSize = void 0; + if (maxSizeLine.length > 0) { + maxMessageSize = parseInt(maxSizeLine[0].substring(19), 10); + } + if (isNaN(maxMessageSize)) { + maxMessageSize = 65536; + } + var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:'); + if (sctpPort.length > 0) { + return { + port: parseInt(sctpPort[0].substring(12), 10), + protocol: mline.fmt, + maxMessageSize: maxMessageSize + }; + } + var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:'); + if (sctpMapLines.length > 0) { + var parts = sctpMapLines[0].substring(10).split(' '); + return { + port: parseInt(parts[0], 10), + protocol: parts[1], + maxMessageSize: maxMessageSize + }; + } +}; + +// SCTP +// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers +// support by now receiving in this format, unless we originally parsed +// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line +// protocol of DTLS/SCTP -- without UDP/ or TCP/) +SDPUtils.writeSctpDescription = function (media, sctp) { + var output = []; + if (media.protocol !== 'DTLS/SCTP') { + output = ['m=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\r\n', 'c=IN IP4 0.0.0.0\r\n', 'a=sctp-port:' + sctp.port + '\r\n']; + } else { + output = ['m=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\r\n', 'c=IN IP4 0.0.0.0\r\n', 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\r\n']; + } + if (sctp.maxMessageSize !== undefined) { + output.push('a=max-message-size:' + sctp.maxMessageSize + '\r\n'); + } + return output.join(''); +}; + +// Generate a session ID for SDP. +// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1 +// recommends using a cryptographically random +ve 64-bit value +// but right now this should be acceptable and within the right range +SDPUtils.generateSessionId = function () { + return Math.random().toString().substr(2, 22); +}; + +// Write boiler plate for start of SDP +// sessId argument is optional - if not supplied it will +// be generated randomly +// sessVersion is optional and defaults to 2 +// sessUser is optional and defaults to 'thisisadapterortc' +SDPUtils.writeSessionBoilerplate = function (sessId, sessVer, sessUser) { + var sessionId = void 0; + var version = sessVer !== undefined ? sessVer : 2; + if (sessId) { + sessionId = sessId; + } else { + sessionId = SDPUtils.generateSessionId(); + } + var user = sessUser || 'thisisadapterortc'; + // FIXME: sess-id should be an NTP timestamp. + return 'v=0\r\n' + 'o=' + user + ' ' + sessionId + ' ' + version + ' IN IP4 127.0.0.1\r\n' + 's=-\r\n' + 't=0 0\r\n'; +}; + +// Gets the direction from the mediaSection or the sessionpart. +SDPUtils.getDirection = function (mediaSection, sessionpart) { + // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv. + var lines = SDPUtils.splitLines(mediaSection); + for (var i = 0; i < lines.length; i++) { + switch (lines[i]) { + case 'a=sendrecv': + case 'a=sendonly': + case 'a=recvonly': + case 'a=inactive': + return lines[i].substring(2); + default: + // FIXME: What should happen here? + } + } + if (sessionpart) { + return SDPUtils.getDirection(sessionpart); + } + return 'sendrecv'; +}; + +SDPUtils.getKind = function (mediaSection) { + var lines = SDPUtils.splitLines(mediaSection); + var mline = lines[0].split(' '); + return mline[0].substring(2); +}; + +SDPUtils.isRejected = function (mediaSection) { + return mediaSection.split(' ', 2)[1] === '0'; +}; + +SDPUtils.parseMLine = function (mediaSection) { + var lines = SDPUtils.splitLines(mediaSection); + var parts = lines[0].substring(2).split(' '); + return { + kind: parts[0], + port: parseInt(parts[1], 10), + protocol: parts[2], + fmt: parts.slice(3).join(' ') + }; +}; + +SDPUtils.parseOLine = function (mediaSection) { + var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0]; + var parts = line.substring(2).split(' '); + return { + username: parts[0], + sessionId: parts[1], + sessionVersion: parseInt(parts[2], 10), + netType: parts[3], + addressType: parts[4], + address: parts[5] + }; +}; + +// a very naive interpretation of a valid SDP. +SDPUtils.isValidSDP = function (blob) { + if (typeof blob !== 'string' || blob.length === 0) { + return false; + } + var lines = SDPUtils.splitLines(blob); + for (var i = 0; i < lines.length; i++) { + if (lines[i].length < 2 || lines[i].charAt(1) !== '=') { + return false; + } + // TODO: check the modifier a bit more. + } + return true; +}; + +// Expose public methods. +if ((typeof module === 'undefined' ? 'undefined' : _typeof(module)) === 'object') { + module.exports = SDPUtils; +} +},{}]},{},[1])(1) +}); diff --git a/web/share/js/kvm/atx.js b/kvmd_data/usr/share/kvmd/web/share/js/kvm/atx.js similarity index 100% rename from web/share/js/kvm/atx.js rename to kvmd_data/usr/share/kvmd/web/share/js/kvm/atx.js diff --git a/web/share/js/kvm/gpio.js b/kvmd_data/usr/share/kvmd/web/share/js/kvm/gpio.js similarity index 100% rename from web/share/js/kvm/gpio.js rename to kvmd_data/usr/share/kvmd/web/share/js/kvm/gpio.js diff --git a/web/share/js/kvm/hid.js b/kvmd_data/usr/share/kvmd/web/share/js/kvm/hid.js similarity index 100% rename from web/share/js/kvm/hid.js rename to kvmd_data/usr/share/kvmd/web/share/js/kvm/hid.js diff --git a/kvmd_data/usr/share/kvmd/web/share/js/kvm/janus.js b/kvmd_data/usr/share/kvmd/web/share/js/kvm/janus.js new file mode 100644 index 00000000..4544ccfc --- /dev/null +++ b/kvmd_data/usr/share/kvmd/web/share/js/kvm/janus.js @@ -0,0 +1,3651 @@ +import "./adapter.js" +"use strict"; + +/* + The MIT License (MIT) + + Copyright (c) 2016 Meetecho + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ + +// List of sessions +Janus.sessions = {}; + +Janus.isExtensionEnabled = function() { + if(navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) { + // No need for the extension, getDisplayMedia is supported + return true; + } + if(window.navigator.userAgent.match('Chrome')) { + var chromever = parseInt(window.navigator.userAgent.match(/Chrome\/(.*) /)[1], 10); + var maxver = 33; + if(window.navigator.userAgent.match('Linux')) + maxver = 35; // "known" crash in chrome 34 and 35 on linux + if(chromever >= 26 && chromever <= maxver) { + // Older versions of Chrome don't support this extension-based approach, so lie + return true; + } + return Janus.extension.isInstalled(); + } else { + // Firefox and others, no need for the extension (but this doesn't mean it will work) + return true; + } +}; + +var defaultExtension = { + // Screensharing Chrome Extension ID + extensionId: 'hapfgfdkleiggjjpfpenajgdnfckjpaj', + isInstalled: function() { return document.querySelector('#janus-extension-installed') !== null; }, + getScreen: function (callback) { + var pending = window.setTimeout(function () { + var error = new Error('NavigatorUserMediaError'); + error.name = 'The required Chrome extension is not installed: click here to install it. (NOTE: this will need you to refresh the page)'; + return callback(error); + }, 1000); + this.cache[pending] = callback; + window.postMessage({ type: 'janusGetScreen', id: pending }, '*'); + }, + init: function () { + var cache = {}; + this.cache = cache; + // Wait for events from the Chrome Extension + window.addEventListener('message', function (event) { + if(event.origin != window.location.origin) + return; + if(event.data.type == 'janusGotScreen' && cache[event.data.id]) { + var callback = cache[event.data.id]; + delete cache[event.data.id]; + + if (event.data.sourceId === '') { + // user canceled + var error = new Error('NavigatorUserMediaError'); + error.name = 'You cancelled the request for permission, giving up...'; + callback(error); + } else { + callback(null, event.data.sourceId); + } + } else if (event.data.type == 'janusGetScreenPending') { + console.log('clearing ', event.data.id); + window.clearTimeout(event.data.id); + } + }); + } +}; + +Janus.useDefaultDependencies = function (deps) { + var f = (deps && deps.fetch) || fetch; + var p = (deps && deps.Promise) || Promise; + var socketCls = (deps && deps.WebSocket) || WebSocket; + + return { + newWebSocket: function(server, proto) { return new socketCls(server, proto); }, + extension: (deps && deps.extension) || defaultExtension, + isArray: function(arr) { return Array.isArray(arr); }, + webRTCAdapter: (deps && deps.adapter) || adapter, + httpAPICall: function(url, options) { + var fetchOptions = { + method: options.verb, + headers: { + 'Accept': 'application/json, text/plain, */*' + }, + cache: 'no-cache' + }; + if(options.verb === "POST") { + fetchOptions.headers['Content-Type'] = 'application/json'; + } + if(options.withCredentials !== undefined) { + fetchOptions.credentials = options.withCredentials === true ? 'include' : (options.withCredentials ? options.withCredentials : 'omit'); + } + if(options.body) { + fetchOptions.body = JSON.stringify(options.body); + } + + var fetching = f(url, fetchOptions).catch(function(error) { + return p.reject({message: 'Probably a network error, is the server down?', error: error}); + }); + + /* + * fetch() does not natively support timeouts. + * Work around this by starting a timeout manually, and racing it agains the fetch() to see which thing resolves first. + */ + + if(options.timeout) { + var timeout = new p(function(resolve, reject) { + var timerId = setTimeout(function() { + clearTimeout(timerId); + return reject({message: 'Request timed out', timeout: options.timeout}); + }, options.timeout); + }); + fetching = p.race([fetching, timeout]); + } + + fetching.then(function(response) { + if(response.ok) { + if(typeof(options.success) === typeof(Janus.noop)) { + return response.json().then(function(parsed) { + try { + options.success(parsed); + } catch(error) { + Janus.error('Unhandled httpAPICall success callback error', error); + } + }, function(error) { + return p.reject({message: 'Failed to parse response body', error: error, response: response}); + }); + } + } + else { + return p.reject({message: 'API call failed', response: response}); + } + }).catch(function(error) { + if(typeof(options.error) === typeof(Janus.noop)) { + options.error(error.message || '<< internal error >>', error); + } + }); + + return fetching; + } + } +}; + +Janus.useOldDependencies = function (deps) { + var jq = (deps && deps.jQuery) || jQuery; + var socketCls = (deps && deps.WebSocket) || WebSocket; + return { + newWebSocket: function(server, proto) { return new socketCls(server, proto); }, + isArray: function(arr) { return jq.isArray(arr); }, + extension: (deps && deps.extension) || defaultExtension, + webRTCAdapter: (deps && deps.adapter) || adapter, + httpAPICall: function(url, options) { + var payload = options.body !== undefined ? { + contentType: 'application/json', + data: JSON.stringify(options.body) + } : {}; + var credentials = options.withCredentials !== undefined ? {xhrFields: {withCredentials: options.withCredentials}} : {}; + + return jq.ajax(jq.extend(payload, credentials, { + url: url, + type: options.verb, + cache: false, + dataType: 'json', + async: options.async, + timeout: options.timeout, + success: function(result) { + if(typeof(options.success) === typeof(Janus.noop)) { + options.success(result); + } + }, + error: function(xhr, status, err) { + if(typeof(options.error) === typeof(Janus.noop)) { + options.error(status, err); + } + } + })); + } + }; +}; + +Janus.noop = function() {}; + +Janus.dataChanDefaultLabel = "JanusDataChannel"; + +// Note: in the future we may want to change this, e.g., as was +// attempted in https://github.com/meetecho/janus-gateway/issues/1670 +Janus.endOfCandidates = null; + +// Stop all tracks from a given stream +Janus.stopAllTracks = function(stream) { + try { + // Try a MediaStreamTrack.stop() for each track + var tracks = stream.getTracks(); + for(var mst of tracks) { + Janus.log(mst); + if(mst) { + mst.stop(); + } + } + } catch(e) { + // Do nothing if this fails + } +} + +// Initialization +Janus.init = function(options) { + options = options || {}; + options.callback = (typeof options.callback == "function") ? options.callback : Janus.noop; + if(Janus.initDone) { + // Already initialized + options.callback(); + } else { + if(typeof console.log == "undefined") { + console.log = function() {}; + } + // Console logging (all debugging disabled by default) + Janus.trace = Janus.noop; + Janus.debug = Janus.noop; + Janus.vdebug = Janus.noop; + Janus.log = Janus.noop; + Janus.warn = Janus.noop; + Janus.error = Janus.noop; + if(options.debug === true || options.debug === "all") { + // Enable all debugging levels + Janus.trace = console.trace.bind(console); + Janus.debug = console.debug.bind(console); + Janus.vdebug = console.debug.bind(console); + Janus.log = console.log.bind(console); + Janus.warn = console.warn.bind(console); + Janus.error = console.error.bind(console); + } else if(Array.isArray(options.debug)) { + for(var d of options.debug) { + switch(d) { + case "trace": + Janus.trace = console.trace.bind(console); + break; + case "debug": + Janus.debug = console.debug.bind(console); + break; + case "vdebug": + Janus.vdebug = console.debug.bind(console); + break; + case "log": + Janus.log = console.log.bind(console); + break; + case "warn": + Janus.warn = console.warn.bind(console); + break; + case "error": + Janus.error = console.error.bind(console); + break; + default: + console.error("Unknown debugging option '" + d + "' (supported: 'trace', 'debug', 'vdebug', 'log', warn', 'error')"); + break; + } + } + } + Janus.log("Initializing library"); + + var usedDependencies = options.dependencies || Janus.useDefaultDependencies(); + Janus.isArray = usedDependencies.isArray; + Janus.webRTCAdapter = usedDependencies.webRTCAdapter; + Janus.httpAPICall = usedDependencies.httpAPICall; + Janus.newWebSocket = usedDependencies.newWebSocket; + Janus.extension = usedDependencies.extension; + Janus.extension.init(); + + // Helper method to enumerate devices + Janus.listDevices = function(callback, config) { + callback = (typeof callback == "function") ? callback : Janus.noop; + if (config == null) config = { audio: true, video: true }; + if(Janus.isGetUserMediaAvailable()) { + navigator.mediaDevices.getUserMedia(config) + .then(function(stream) { + navigator.mediaDevices.enumerateDevices().then(function(devices) { + Janus.debug(devices); + callback(devices); + // Get rid of the now useless stream + Janus.stopAllTracks(stream) + }); + }) + .catch(function(err) { + Janus.error(err); + callback([]); + }); + } else { + Janus.warn("navigator.mediaDevices unavailable"); + callback([]); + } + }; + // Helper methods to attach/reattach a stream to a video element (previously part of adapter.js) + Janus.attachMediaStream = function(element, stream) { + try { + element.srcObject = stream; + } catch (e) { + try { + element.src = URL.createObjectURL(stream); + } catch (e) { + Janus.error("Error attaching stream to element"); + } + } + }; + Janus.reattachMediaStream = function(to, from) { + try { + to.srcObject = from.srcObject; + } catch (e) { + try { + to.src = from.src; + } catch (e) { + Janus.error("Error reattaching stream to element"); + } + } + }; + // Detect tab close: make sure we don't loose existing onbeforeunload handlers + // (note: for iOS we need to subscribe to a different event, 'pagehide', see + // https://gist.github.com/thehunmonkgroup/6bee8941a49b86be31a787fe8f4b8cfe) + var iOS = ['iPad', 'iPhone', 'iPod'].indexOf(navigator.platform) >= 0; + var eventName = iOS ? 'pagehide' : 'beforeunload'; + var oldOBF = window["on" + eventName]; + window.addEventListener(eventName, function() { + Janus.log("Closing window"); + for(var s in Janus.sessions) { + if(Janus.sessions[s] && Janus.sessions[s].destroyOnUnload) { + Janus.log("Destroying session " + s); + Janus.sessions[s].destroy({unload: true, notifyDestroyed: false}); + } + } + if(oldOBF && typeof oldOBF == "function") { + oldOBF(); + } + }); + // If this is a Safari, check if VP8 or VP9 are supported + Janus.safariVp8 = false; + Janus.safariVp9 = false; + if(Janus.webRTCAdapter.browserDetails.browser === 'safari' && + Janus.webRTCAdapter.browserDetails.version >= 605) { + // Let's see if RTCRtpSender.getCapabilities() is there + if(RTCRtpSender && RTCRtpSender.getCapabilities && RTCRtpSender.getCapabilities("video") && + RTCRtpSender.getCapabilities("video").codecs && RTCRtpSender.getCapabilities("video").codecs.length) { + for(var codec of RTCRtpSender.getCapabilities("video").codecs) { + if(codec && codec.mimeType && codec.mimeType.toLowerCase() === "video/vp8") { + Janus.safariVp8 = true; + } else if(codec && codec.mimeType && codec.mimeType.toLowerCase() === "video/vp9") { + Janus.safariVp9 = true; + } + } + if(Janus.safariVp8) { + Janus.log("This version of Safari supports VP8"); + } else { + Janus.warn("This version of Safari does NOT support VP8: if you're using a Technology Preview, " + + "try enabling the 'WebRTC VP8 codec' setting in the 'Experimental Features' Develop menu"); + } + } else { + // We do it in a very ugly way, as there's no alternative... + // We create a PeerConnection to see if VP8 is in an offer + var testpc = new RTCPeerConnection({}); + testpc.createOffer({offerToReceiveVideo: true}).then(function(offer) { + Janus.safariVp8 = offer.sdp.indexOf("VP8") !== -1; + Janus.safariVp9 = offer.sdp.indexOf("VP9") !== -1; + if(Janus.safariVp8) { + Janus.log("This version of Safari supports VP8"); + } else { + Janus.warn("This version of Safari does NOT support VP8: if you're using a Technology Preview, " + + "try enabling the 'WebRTC VP8 codec' setting in the 'Experimental Features' Develop menu"); + } + testpc.close(); + testpc = null; + }); + } + } + // Check if this browser supports Unified Plan and transceivers + // Based on https://codepen.io/anon/pen/ZqLwWV?editors=0010 + Janus.unifiedPlan = false; + if(Janus.webRTCAdapter.browserDetails.browser === 'firefox' && + Janus.webRTCAdapter.browserDetails.version >= 59) { + // Firefox definitely does, starting from version 59 + Janus.unifiedPlan = true; + } else if(Janus.webRTCAdapter.browserDetails.browser === 'chrome' && + Janus.webRTCAdapter.browserDetails.version >= 72) { + // Chrome does, but it's only usable from version 72 on + Janus.unifiedPlan = true; + } else if(!window.RTCRtpTransceiver || !('currentDirection' in RTCRtpTransceiver.prototype)) { + // Safari supports addTransceiver() but not Unified Plan when + // currentDirection is not defined (see codepen above). + Janus.unifiedPlan = false; + } else { + // Check if addTransceiver() throws an exception + var tempPc = new RTCPeerConnection(); + try { + tempPc.addTransceiver('audio'); + Janus.unifiedPlan = true; + } catch (e) {} + tempPc.close(); + } + Janus.initDone = true; + options.callback(); + } +}; + +// Helper method to check whether WebRTC is supported by this browser +Janus.isWebrtcSupported = function() { + return !!window.RTCPeerConnection; +}; +// Helper method to check whether devices can be accessed by this browser (e.g., not possible via plain HTTP) +Janus.isGetUserMediaAvailable = function() { + return navigator.mediaDevices && navigator.mediaDevices.getUserMedia; +}; + +// Helper method to create random identifiers (e.g., transaction) +Janus.randomString = function(len) { + var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var randomString = ''; + for (var i = 0; i < len; i++) { + var randomPoz = Math.floor(Math.random() * charSet.length); + randomString += charSet.substring(randomPoz,randomPoz+1); + } + return randomString; +}; + +export function Janus(gatewayCallbacks) { + gatewayCallbacks = gatewayCallbacks || {}; + gatewayCallbacks.success = (typeof gatewayCallbacks.success == "function") ? gatewayCallbacks.success : Janus.noop; + gatewayCallbacks.error = (typeof gatewayCallbacks.error == "function") ? gatewayCallbacks.error : Janus.noop; + gatewayCallbacks.destroyed = (typeof gatewayCallbacks.destroyed == "function") ? gatewayCallbacks.destroyed : Janus.noop; + if(!Janus.initDone) { + gatewayCallbacks.error("Library not initialized"); + return {}; + } + if(!Janus.isWebrtcSupported()) { + gatewayCallbacks.error("WebRTC not supported by this browser"); + return {}; + } + Janus.log("Library initialized: " + Janus.initDone); + if(!gatewayCallbacks.server) { + gatewayCallbacks.error("Invalid server url"); + return {}; + } + var websockets = false; + var ws = null; + var wsHandlers = {}; + var wsKeepaliveTimeoutId = null; + var servers = null; + var serversIndex = 0; + var server = gatewayCallbacks.server; + if(Janus.isArray(server)) { + Janus.log("Multiple servers provided (" + server.length + "), will use the first that works"); + server = null; + servers = gatewayCallbacks.server; + Janus.debug(servers); + } else { + if(server.indexOf("ws") === 0) { + websockets = true; + Janus.log("Using WebSockets to contact Janus: " + server); + } else { + websockets = false; + Janus.log("Using REST API to contact Janus: " + server); + } + } + var iceServers = gatewayCallbacks.iceServers || [{urls: "stun:stun.l.google.com:19302"}]; + var iceTransportPolicy = gatewayCallbacks.iceTransportPolicy; + var bundlePolicy = gatewayCallbacks.bundlePolicy; + // Whether IPv6 candidates should be gathered + var ipv6Support = (gatewayCallbacks.ipv6 === true); + // Whether we should enable the withCredentials flag for XHR requests + var withCredentials = false; + if(gatewayCallbacks.withCredentials !== undefined && gatewayCallbacks.withCredentials !== null) + withCredentials = gatewayCallbacks.withCredentials === true; + // Optional max events + var maxev = 10; + if(gatewayCallbacks.max_poll_events !== undefined && gatewayCallbacks.max_poll_events !== null) + maxev = gatewayCallbacks.max_poll_events; + if(maxev < 1) + maxev = 1; + // Token to use (only if the token based authentication mechanism is enabled) + var token = null; + if(gatewayCallbacks.token !== undefined && gatewayCallbacks.token !== null) + token = gatewayCallbacks.token; + // API secret to use (only if the shared API secret is enabled) + var apisecret = null; + if(gatewayCallbacks.apisecret !== undefined && gatewayCallbacks.apisecret !== null) + apisecret = gatewayCallbacks.apisecret; + // Whether we should destroy this session when onbeforeunload is called + this.destroyOnUnload = true; + if(gatewayCallbacks.destroyOnUnload !== undefined && gatewayCallbacks.destroyOnUnload !== null) + this.destroyOnUnload = (gatewayCallbacks.destroyOnUnload === true); + // Some timeout-related values + var keepAlivePeriod = 25000; + if(gatewayCallbacks.keepAlivePeriod !== undefined && gatewayCallbacks.keepAlivePeriod !== null) + keepAlivePeriod = gatewayCallbacks.keepAlivePeriod; + if(isNaN(keepAlivePeriod)) + keepAlivePeriod = 25000; + var longPollTimeout = 60000; + if(gatewayCallbacks.longPollTimeout !== undefined && gatewayCallbacks.longPollTimeout !== null) + longPollTimeout = gatewayCallbacks.longPollTimeout; + if(isNaN(longPollTimeout)) + longPollTimeout = 60000; + + // overrides for default maxBitrate values for simulcasting + function getMaxBitrates(simulcastMaxBitrates) { + var maxBitrates = { + high: 900000, + medium: 300000, + low: 100000, + }; + + if (simulcastMaxBitrates !== undefined && simulcastMaxBitrates !== null) { + if (simulcastMaxBitrates.high) + maxBitrates.high = simulcastMaxBitrates.high; + if (simulcastMaxBitrates.medium) + maxBitrates.medium = simulcastMaxBitrates.medium; + if (simulcastMaxBitrates.low) + maxBitrates.low = simulcastMaxBitrates.low; + } + + return maxBitrates; + } + + var connected = false; + var sessionId = null; + var pluginHandles = {}; + var that = this; + var retries = 0; + var transactions = {}; + createSession(gatewayCallbacks); + + // Public methods + this.getServer = function() { return server; }; + this.isConnected = function() { return connected; }; + this.reconnect = function(callbacks) { + callbacks = callbacks || {}; + callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop; + callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop; + callbacks["reconnect"] = true; + createSession(callbacks); + }; + this.getSessionId = function() { return sessionId; }; + this.getInfo = function(callbacks) { getInfo(callbacks); }; + this.destroy = function(callbacks) { destroySession(callbacks); }; + this.attach = function(callbacks) { createHandle(callbacks); }; + + function eventHandler() { + if(sessionId == null) + return; + Janus.debug('Long poll...'); + if(!connected) { + Janus.warn("Is the server down? (connected=false)"); + return; + } + var longpoll = server + "/" + sessionId + "?rid=" + new Date().getTime(); + if(maxev) + longpoll = longpoll + "&maxev=" + maxev; + if(token) + longpoll = longpoll + "&token=" + encodeURIComponent(token); + if(apisecret) + longpoll = longpoll + "&apisecret=" + encodeURIComponent(apisecret); + Janus.httpAPICall(longpoll, { + verb: 'GET', + withCredentials: withCredentials, + success: handleEvent, + timeout: longPollTimeout, + error: function(textStatus, errorThrown) { + Janus.error(textStatus + ":", errorThrown); + retries++; + if(retries > 3) { + // Did we just lose the server? :-( + connected = false; + gatewayCallbacks.error("Lost connection to the server (is it down?)"); + return; + } + eventHandler(); + } + }); + } + + // Private event handler: this will trigger plugin callbacks, if set + function handleEvent(json, skipTimeout) { + retries = 0; + if(!websockets && sessionId !== undefined && sessionId !== null && skipTimeout !== true) + eventHandler(); + if(!websockets && Janus.isArray(json)) { + // We got an array: it means we passed a maxev > 1, iterate on all objects + for(var i=0; i data channel: ' + dcState); + if(dcState === 'open') { + // Any pending messages to send? + if(config.dataChannel[label].pending && config.dataChannel[label].pending.length > 0) { + Janus.log("Sending pending messages on <" + label + ">:", config.dataChannel[label].pending.length); + for(var data of config.dataChannel[label].pending) { + Janus.log("Sending data on data channel <" + label + ">"); + Janus.debug(data); + config.dataChannel[label].send(data); + } + config.dataChannel[label].pending = []; + } + // Notify the open data channel + pluginHandle.ondataopen(label, protocol); + } + }; + var onDataChannelError = function(error) { + Janus.error('Got error on data channel:', error); + // TODO + }; + if(!incoming) { + // FIXME Add options (ordered, maxRetransmits, etc.) + var dcoptions = config.dataChannelOptions; + if(dcprotocol) + dcoptions.protocol = dcprotocol; + config.dataChannel[dclabel] = config.pc.createDataChannel(dclabel, dcoptions); + } else { + // The channel was created by Janus + config.dataChannel[dclabel] = incoming; + } + config.dataChannel[dclabel].onmessage = onDataChannelMessage; + config.dataChannel[dclabel].onopen = onDataChannelStateChange; + config.dataChannel[dclabel].onclose = onDataChannelStateChange; + config.dataChannel[dclabel].onerror = onDataChannelError; + config.dataChannel[dclabel].pending = []; + if(pendingData) + config.dataChannel[dclabel].pending.push(pendingData); + } + + // Private method to send a data channel message + function sendData(handleId, callbacks) { + callbacks = callbacks || {}; + callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop; + callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop; + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || !pluginHandle.webrtcStuff) { + Janus.warn("Invalid handle"); + callbacks.error("Invalid handle"); + return; + } + var config = pluginHandle.webrtcStuff; + var data = callbacks.text || callbacks.data; + if(!data) { + Janus.warn("Invalid data"); + callbacks.error("Invalid data"); + return; + } + var label = callbacks.label ? callbacks.label : Janus.dataChanDefaultLabel; + if(!config.dataChannel[label]) { + // Create new data channel and wait for it to open + createDataChannel(handleId, label, callbacks.protocol, false, data, callbacks.protocol); + callbacks.success(); + return; + } + if(config.dataChannel[label].readyState !== "open") { + config.dataChannel[label].pending.push(data); + callbacks.success(); + return; + } + Janus.log("Sending data on data channel <" + label + ">"); + Janus.debug(data); + config.dataChannel[label].send(data); + callbacks.success(); + } + + // Private method to send a DTMF tone + function sendDtmf(handleId, callbacks) { + callbacks = callbacks || {}; + callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop; + callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop; + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || !pluginHandle.webrtcStuff) { + Janus.warn("Invalid handle"); + callbacks.error("Invalid handle"); + return; + } + var config = pluginHandle.webrtcStuff; + if(!config.dtmfSender) { + // Create the DTMF sender the proper way, if possible + if(config.pc) { + var senders = config.pc.getSenders(); + var audioSender = senders.find(function(sender) { + return sender.track && sender.track.kind === 'audio'; + }); + if(!audioSender) { + Janus.warn("Invalid DTMF configuration (no audio track)"); + callbacks.error("Invalid DTMF configuration (no audio track)"); + return; + } + config.dtmfSender = audioSender.dtmf; + if(config.dtmfSender) { + Janus.log("Created DTMF Sender"); + config.dtmfSender.ontonechange = function(tone) { Janus.debug("Sent DTMF tone: " + tone.tone); }; + } + } + if(!config.dtmfSender) { + Janus.warn("Invalid DTMF configuration"); + callbacks.error("Invalid DTMF configuration"); + return; + } + } + var dtmf = callbacks.dtmf; + if(!dtmf) { + Janus.warn("Invalid DTMF parameters"); + callbacks.error("Invalid DTMF parameters"); + return; + } + var tones = dtmf.tones; + if(!tones) { + Janus.warn("Invalid DTMF string"); + callbacks.error("Invalid DTMF string"); + return; + } + var duration = (typeof dtmf.duration === 'number') ? dtmf.duration : 500; // We choose 500ms as the default duration for a tone + var gap = (typeof dtmf.gap === 'number') ? dtmf.gap : 50; // We choose 50ms as the default gap between tones + Janus.debug("Sending DTMF string " + tones + " (duration " + duration + "ms, gap " + gap + "ms)"); + config.dtmfSender.insertDTMF(tones, duration, gap); + callbacks.success(); + } + + // Private method to destroy a plugin handle + function destroyHandle(handleId, callbacks) { + callbacks = callbacks || {}; + callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop; + callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop; + var noRequest = (callbacks.noRequest === true); + Janus.log("Destroying handle " + handleId + " (only-locally=" + noRequest + ")"); + cleanupWebrtc(handleId); + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || pluginHandle.detached) { + // Plugin was already detached by Janus, calling detach again will return a handle not found error, so just exit here + delete pluginHandles[handleId]; + callbacks.success(); + return; + } + pluginHandle.detached = true; + if(noRequest) { + // We're only removing the handle locally + delete pluginHandles[handleId]; + callbacks.success(); + return; + } + if(!connected) { + Janus.warn("Is the server down? (connected=false)"); + callbacks.error("Is the server down? (connected=false)"); + return; + } + var request = { "janus": "detach", "transaction": Janus.randomString(12) }; + if(pluginHandle.token) + request["token"] = pluginHandle.token; + if(apisecret) + request["apisecret"] = apisecret; + if(websockets) { + request["session_id"] = sessionId; + request["handle_id"] = handleId; + ws.send(JSON.stringify(request)); + delete pluginHandles[handleId]; + callbacks.success(); + return; + } + Janus.httpAPICall(server + "/" + sessionId + "/" + handleId, { + verb: 'POST', + withCredentials: withCredentials, + body: request, + success: function(json) { + Janus.log("Destroyed handle:"); + Janus.debug(json); + if(json["janus"] !== "success") { + Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME + } + delete pluginHandles[handleId]; + callbacks.success(); + }, + error: function(textStatus, errorThrown) { + Janus.error(textStatus + ":", errorThrown); // FIXME + // We cleanup anyway + delete pluginHandles[handleId]; + callbacks.success(); + } + }); + } + + // WebRTC stuff + function streamsDone(handleId, jsep, media, callbacks, stream) { + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || !pluginHandle.webrtcStuff) { + Janus.warn("Invalid handle"); + // Close all tracks if the given stream has been created internally + if(!callbacks.stream) { + Janus.stopAllTracks(stream); + } + callbacks.error("Invalid handle"); + return; + } + var config = pluginHandle.webrtcStuff; + Janus.debug("streamsDone:", stream); + if(stream) { + Janus.debug(" -- Audio tracks:", stream.getAudioTracks()); + Janus.debug(" -- Video tracks:", stream.getVideoTracks()); + } + // We're now capturing the new stream: check if we're updating or if it's a new thing + var addTracks = false; + if(!config.myStream || !media.update || (config.streamExternal && !media.replaceAudio && !media.replaceVideo)) { + config.myStream = stream; + addTracks = true; + } else { + // We only need to update the existing stream + if(((!media.update && isAudioSendEnabled(media)) || (media.update && (media.addAudio || media.replaceAudio))) && + stream.getAudioTracks() && stream.getAudioTracks().length) { + config.myStream.addTrack(stream.getAudioTracks()[0]); + if(Janus.unifiedPlan) { + // Use Transceivers + Janus.log((media.replaceAudio ? "Replacing" : "Adding") + " audio track:", stream.getAudioTracks()[0]); + var audioTransceiver = null; + const transceivers = config.pc.getTransceivers(); + if(transceivers && transceivers.length > 0) { + for(const t of transceivers) { + if((t.sender && t.sender.track && t.sender.track.kind === "audio") || + (t.receiver && t.receiver.track && t.receiver.track.kind === "audio")) { + audioTransceiver = t; + break; + } + } + } + if(audioTransceiver && audioTransceiver.sender) { + audioTransceiver.sender.replaceTrack(stream.getAudioTracks()[0]); + } else { + config.pc.addTrack(stream.getAudioTracks()[0], stream); + } + } else { + Janus.log((media.replaceAudio ? "Replacing" : "Adding") + " audio track:", stream.getAudioTracks()[0]); + config.pc.addTrack(stream.getAudioTracks()[0], stream); + } + } + if(((!media.update && isVideoSendEnabled(media)) || (media.update && (media.addVideo || media.replaceVideo))) && + stream.getVideoTracks() && stream.getVideoTracks().length) { + config.myStream.addTrack(stream.getVideoTracks()[0]); + if(Janus.unifiedPlan) { + // Use Transceivers + Janus.log((media.replaceVideo ? "Replacing" : "Adding") + " video track:", stream.getVideoTracks()[0]); + var videoTransceiver = null; + const transceivers = config.pc.getTransceivers(); + if(transceivers && transceivers.length > 0) { + for(const t of transceivers) { + if((t.sender && t.sender.track && t.sender.track.kind === "video") || + (t.receiver && t.receiver.track && t.receiver.track.kind === "video")) { + videoTransceiver = t; + break; + } + } + } + if(videoTransceiver && videoTransceiver.sender) { + videoTransceiver.sender.replaceTrack(stream.getVideoTracks()[0]); + } else { + config.pc.addTrack(stream.getVideoTracks()[0], stream); + } + } else { + Janus.log((media.replaceVideo ? "Replacing" : "Adding") + " video track:", stream.getVideoTracks()[0]); + config.pc.addTrack(stream.getVideoTracks()[0], stream); + } + } + } + // If we still need to create a PeerConnection, let's do that + if(!config.pc) { + var pc_config = {"iceServers": iceServers, "iceTransportPolicy": iceTransportPolicy, "bundlePolicy": bundlePolicy}; + if(Janus.webRTCAdapter.browserDetails.browser === "chrome") { + // For Chrome versions before 72, we force a plan-b semantic, and unified-plan otherwise + pc_config["sdpSemantics"] = (Janus.webRTCAdapter.browserDetails.version < 72) ? "plan-b" : "unified-plan"; + } + var pc_constraints = {}; + if(Janus.webRTCAdapter.browserDetails.browser === "edge") { + // This is Edge, enable BUNDLE explicitly + pc_config.bundlePolicy = "max-bundle"; + } + // Check if a sender or receiver transform has been provided + if(RTCRtpSender && (RTCRtpSender.prototype.createEncodedStreams || + (RTCRtpSender.prototype.createEncodedAudioStreams && + RTCRtpSender.prototype.createEncodedVideoStreams)) && + (callbacks.senderTransforms || callbacks.receiverTransforms)) { + config.senderTransforms = callbacks.senderTransforms; + config.receiverTransforms = callbacks.receiverTransforms; + pc_config["forceEncodedAudioInsertableStreams"] = true; + pc_config["forceEncodedVideoInsertableStreams"] = true; + pc_config["encodedInsertableStreams"] = true; + } + Janus.log("Creating PeerConnection"); + Janus.debug(pc_constraints); + config.pc = new RTCPeerConnection(pc_config, pc_constraints); + Janus.debug(config.pc); + if(config.pc.getStats) { // FIXME + config.volume = {}; + config.bitrate.value = "0 kbits/sec"; + } + Janus.log("Preparing local SDP and gathering candidates (trickle=" + config.trickle + ")"); + config.pc.oniceconnectionstatechange = function() { + if(config.pc) + pluginHandle.iceState(config.pc.iceConnectionState); + }; + config.pc.onicecandidate = function(event) { + if (!event.candidate || + (Janus.webRTCAdapter.browserDetails.browser === 'edge' && event.candidate.candidate.indexOf('endOfCandidates') > 0)) { + Janus.log("End of candidates."); + config.iceDone = true; + if(config.trickle === true) { + // Notify end of candidates + sendTrickleCandidate(handleId, {"completed": true}); + } else { + // No trickle, time to send the complete SDP (including all candidates) + sendSDP(handleId, callbacks); + } + } else { + // JSON.stringify doesn't work on some WebRTC objects anymore + // See https://code.google.com/p/chromium/issues/detail?id=467366 + var candidate = { + "candidate": event.candidate.candidate, + "sdpMid": event.candidate.sdpMid, + "sdpMLineIndex": event.candidate.sdpMLineIndex + }; + if(config.trickle === true) { + // Send candidate + sendTrickleCandidate(handleId, candidate); + } + } + }; + config.pc.ontrack = function(event) { + Janus.log("Handling Remote Track"); + Janus.debug(event); + if(!event.streams) + return; + config.remoteStream = event.streams[0]; + pluginHandle.onremotestream(config.remoteStream); + if(event.track.onended) + return; + if(config.receiverTransforms) { + var receiverStreams = null; + if(RTCRtpSender.prototype.createEncodedStreams) { + receiverStreams = event.receiver.createEncodedStreams(); + } else if(RTCRtpSender.prototype.createAudioEncodedStreams || RTCRtpSender.prototype.createEncodedVideoStreams) { + if(event.track.kind === "audio" && config.receiverTransforms["audio"]) { + receiverStreams = event.receiver.createEncodedAudioStreams(); + } else if(event.track.kind === "video" && config.receiverTransforms["video"]) { + receiverStreams = event.receiver.createEncodedVideoStreams(); + } + } + if(receiverStreams) { + console.log(receiverStreams); + if(receiverStreams.readableStream && receiverStreams.writableStream) { + receiverStreams.readableStream + .pipeThrough(config.receiverTransforms[event.track.kind]) + .pipeTo(receiverStreams.writableStream); + } else if(receiverStreams.readable && receiverStreams.writable) { + receiverStreams.readable + .pipeThrough(config.receiverTransforms[event.track.kind]) + .pipeTo(receiverStreams.writable); + } + } + } + var trackMutedTimeoutId = null; + Janus.log("Adding onended callback to track:", event.track); + event.track.onended = function(ev) { + Janus.log("Remote track removed:", ev); + if(config.remoteStream) { + clearTimeout(trackMutedTimeoutId); + config.remoteStream.removeTrack(ev.target); + pluginHandle.onremotestream(config.remoteStream); + } + }; + event.track.onmute = function(ev) { + Janus.log("Remote track muted:", ev); + if(config.remoteStream && trackMutedTimeoutId == null) { + trackMutedTimeoutId = setTimeout(function() { + Janus.log("Removing remote track"); + if (config.remoteStream) { + config.remoteStream.removeTrack(ev.target); + pluginHandle.onremotestream(config.remoteStream); + } + trackMutedTimeoutId = null; + // Chrome seems to raise mute events only at multiples of 834ms; + // we set the timeout to three times this value (rounded to 840ms) + }, 3 * 840); + } + }; + event.track.onunmute = function(ev) { + Janus.log("Remote track flowing again:", ev); + if(trackMutedTimeoutId != null) { + clearTimeout(trackMutedTimeoutId); + trackMutedTimeoutId = null; + } else { + try { + config.remoteStream.addTrack(ev.target); + pluginHandle.onremotestream(config.remoteStream); + } catch(e) { + Janus.error(e); + } + } + }; + }; + } + if(addTracks && stream) { + Janus.log('Adding local stream'); + var simulcast = (callbacks.simulcast === true || callbacks.simulcast2 === true) && Janus.unifiedPlan; + var svc = callbacks.svc; + stream.getTracks().forEach(function(track) { + Janus.log('Adding local track:', track); + var sender = null; + if((!simulcast && !svc) || track.kind === 'audio') { + sender = config.pc.addTrack(track, stream); + } else if(simulcast) { + Janus.log('Enabling rid-based simulcasting:', track); + let maxBitrates = getMaxBitrates(callbacks.simulcastMaxBitrates); + let tr = config.pc.addTransceiver(track, { + direction: "sendrecv", + streams: [stream], + sendEncodings: callbacks.sendEncodings || [ + { rid: "h", active: true, maxBitrate: maxBitrates.high }, + { rid: "m", active: true, maxBitrate: maxBitrates.medium, scaleResolutionDownBy: 2 }, + { rid: "l", active: true, maxBitrate: maxBitrates.low, scaleResolutionDownBy: 4 } + ] + }); + if(tr) + sender = tr.sender; + } else { + Janus.log('Enabling SVC (' + svc + '):', track); + let tr = config.pc.addTransceiver(track, { + direction: "sendrecv", + streams: [stream], + sendEncodings: [ + { scalabilityMode: svc } + ] + }); + if(tr) + sender = tr.sender; + } + // Check if insertable streams are involved + if(sender && config.senderTransforms) { + var senderStreams = null; + if(RTCRtpSender.prototype.createEncodedStreams) { + senderStreams = sender.createEncodedStreams(); + } else if(RTCRtpSender.prototype.createAudioEncodedStreams || RTCRtpSender.prototype.createEncodedVideoStreams) { + if(sender.track.kind === "audio" && config.senderTransforms["audio"]) { + senderStreams = sender.createEncodedAudioStreams(); + } else if(sender.track.kind === "video" && config.senderTransforms["video"]) { + senderStreams = sender.createEncodedVideoStreams(); + } + } + if(senderStreams) { + console.log(senderStreams); + if(senderStreams.readableStream && senderStreams.writableStream) { + senderStreams.readableStream + .pipeThrough(config.senderTransforms[sender.track.kind]) + .pipeTo(senderStreams.writableStream); + } else if(senderStreams.readable && senderStreams.writable) { + senderStreams.readable + .pipeThrough(config.senderTransforms[sender.track.kind]) + .pipeTo(senderStreams.writable); + } + } + } + }); + } + // Any data channel to create? + if(isDataEnabled(media) && !config.dataChannel[Janus.dataChanDefaultLabel]) { + Janus.log("Creating default data channel"); + createDataChannel(handleId, Janus.dataChanDefaultLabel, null, false); + config.pc.ondatachannel = function(event) { + Janus.log("Data channel created by Janus:", event); + createDataChannel(handleId, event.channel.label, event.channel.protocol, event.channel); + }; + } + // If there's a new local stream, let's notify the application + if(config.myStream) { + pluginHandle.onlocalstream(config.myStream); + } + // Create offer/answer now + if(!jsep) { + createOffer(handleId, media, callbacks); + } else { + config.pc.setRemoteDescription(jsep) + .then(function() { + Janus.log("Remote description accepted!"); + config.remoteSdp = jsep.sdp; + // Any trickle candidate we cached? + if(config.candidates && config.candidates.length > 0) { + for(var i = 0; i< config.candidates.length; i++) { + var candidate = config.candidates[i]; + Janus.debug("Adding remote candidate:", candidate); + if(!candidate || candidate.completed === true) { + // end-of-candidates + config.pc.addIceCandidate(Janus.endOfCandidates); + } else { + // New candidate + config.pc.addIceCandidate(candidate); + } + } + config.candidates = []; + } + // Create the answer now + createAnswer(handleId, media, callbacks); + }, callbacks.error); + } + } + + function prepareWebrtc(handleId, offer, callbacks) { + callbacks = callbacks || {}; + callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop; + callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : webrtcError; + var jsep = callbacks.jsep; + if(offer && jsep) { + Janus.error("Provided a JSEP to a createOffer"); + callbacks.error("Provided a JSEP to a createOffer"); + return; + } else if(!offer && (!jsep || !jsep.type || !jsep.sdp)) { + Janus.error("A valid JSEP is required for createAnswer"); + callbacks.error("A valid JSEP is required for createAnswer"); + return; + } + /* Check that callbacks.media is a (not null) Object */ + callbacks.media = (typeof callbacks.media === 'object' && callbacks.media) ? callbacks.media : { audio: true, video: true }; + var media = callbacks.media; + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || !pluginHandle.webrtcStuff) { + Janus.warn("Invalid handle"); + callbacks.error("Invalid handle"); + return; + } + var config = pluginHandle.webrtcStuff; + config.trickle = isTrickleEnabled(callbacks.trickle); + // Are we updating a session? + if(!config.pc) { + // Nope, new PeerConnection + media.update = false; + media.keepAudio = false; + media.keepVideo = false; + } else { + Janus.log("Updating existing media session"); + media.update = true; + // Check if there's anything to add/remove/replace, or if we + // can go directly to preparing the new SDP offer or answer + if(callbacks.stream) { + // External stream: is this the same as the one we were using before? + if(callbacks.stream !== config.myStream) { + Janus.log("Renegotiation involves a new external stream"); + } + } else { + // Check if there are changes on audio + if(media.addAudio) { + media.keepAudio = false; + media.replaceAudio = false; + media.removeAudio = false; + media.audioSend = true; + if(config.myStream && config.myStream.getAudioTracks() && config.myStream.getAudioTracks().length) { + Janus.error("Can't add audio stream, there already is one"); + callbacks.error("Can't add audio stream, there already is one"); + return; + } + } else if(media.removeAudio) { + media.keepAudio = false; + media.replaceAudio = false; + media.addAudio = false; + media.audioSend = false; + } else if(media.replaceAudio) { + media.keepAudio = false; + media.addAudio = false; + media.removeAudio = false; + media.audioSend = true; + } + if(!config.myStream) { + // No media stream: if we were asked to replace, it's actually an "add" + if(media.replaceAudio) { + media.keepAudio = false; + media.replaceAudio = false; + media.addAudio = true; + media.audioSend = true; + } + if(isAudioSendEnabled(media)) { + media.keepAudio = false; + media.addAudio = true; + } + } else { + if(!config.myStream.getAudioTracks() || config.myStream.getAudioTracks().length === 0) { + // No audio track: if we were asked to replace, it's actually an "add" + if(media.replaceAudio) { + media.keepAudio = false; + media.replaceAudio = false; + media.addAudio = true; + media.audioSend = true; + } + if(isAudioSendEnabled(media)) { + media.keepAudio = false; + media.addAudio = true; + } + } else { + // We have an audio track: should we keep it as it is? + if(isAudioSendEnabled(media) && + !media.removeAudio && !media.replaceAudio) { + media.keepAudio = true; + } + } + } + // Check if there are changes on video + if(media.addVideo) { + media.keepVideo = false; + media.replaceVideo = false; + media.removeVideo = false; + media.videoSend = true; + if(config.myStream && config.myStream.getVideoTracks() && config.myStream.getVideoTracks().length) { + Janus.error("Can't add video stream, there already is one"); + callbacks.error("Can't add video stream, there already is one"); + return; + } + } else if(media.removeVideo) { + media.keepVideo = false; + media.replaceVideo = false; + media.addVideo = false; + media.videoSend = false; + } else if(media.replaceVideo) { + media.keepVideo = false; + media.addVideo = false; + media.removeVideo = false; + media.videoSend = true; + } + if(!config.myStream) { + // No media stream: if we were asked to replace, it's actually an "add" + if(media.replaceVideo) { + media.keepVideo = false; + media.replaceVideo = false; + media.addVideo = true; + media.videoSend = true; + } + if(isVideoSendEnabled(media)) { + media.keepVideo = false; + media.addVideo = true; + } + } else { + if(!config.myStream.getVideoTracks() || config.myStream.getVideoTracks().length === 0) { + // No video track: if we were asked to replace, it's actually an "add" + if(media.replaceVideo) { + media.keepVideo = false; + media.replaceVideo = false; + media.addVideo = true; + media.videoSend = true; + } + if(isVideoSendEnabled(media)) { + media.keepVideo = false; + media.addVideo = true; + } + } else { + // We have a video track: should we keep it as it is? + if(isVideoSendEnabled(media) && !media.removeVideo && !media.replaceVideo) { + media.keepVideo = true; + } + } + } + // Data channels can only be added + if(media.addData) { + media.data = true; + } + } + // If we're updating and keeping all tracks, let's skip the getUserMedia part + if((isAudioSendEnabled(media) && media.keepAudio) && + (isVideoSendEnabled(media) && media.keepVideo)) { + pluginHandle.consentDialog(false); + streamsDone(handleId, jsep, media, callbacks, config.myStream); + return; + } + } + // If we're updating, check if we need to remove/replace one of the tracks + if(media.update && (!config.streamExternal || (config.streamExternal && (media.replaceAudio || media.replaceVideo)))) { + if(media.removeAudio || media.replaceAudio) { + if(config.myStream && config.myStream.getAudioTracks() && config.myStream.getAudioTracks().length) { + var at = config.myStream.getAudioTracks()[0]; + Janus.log("Removing audio track:", at); + config.myStream.removeTrack(at); + try { + at.stop(); + } catch(e) {} + } + if(config.pc.getSenders() && config.pc.getSenders().length) { + var ra = true; + if(media.replaceAudio && Janus.unifiedPlan) { + // We can use replaceTrack + ra = false; + } + if(ra) { + for(var asnd of config.pc.getSenders()) { + if(asnd && asnd.track && asnd.track.kind === "audio") { + Janus.log("Removing audio sender:", asnd); + config.pc.removeTrack(asnd); + } + } + } + } + } + if(media.removeVideo || media.replaceVideo) { + if(config.myStream && config.myStream.getVideoTracks() && config.myStream.getVideoTracks().length) { + var vt = config.myStream.getVideoTracks()[0]; + Janus.log("Removing video track:", vt); + config.myStream.removeTrack(vt); + try { + vt.stop(); + } catch(e) {} + } + if(config.pc.getSenders() && config.pc.getSenders().length) { + var rv = true; + if(media.replaceVideo && Janus.unifiedPlan) { + // We can use replaceTrack + rv = false; + } + if(rv) { + for(var vsnd of config.pc.getSenders()) { + if(vsnd && vsnd.track && vsnd.track.kind === "video") { + Janus.log("Removing video sender:", vsnd); + config.pc.removeTrack(vsnd); + } + } + } + } + } + } + // Was a MediaStream object passed, or do we need to take care of that? + if(callbacks.stream) { + var stream = callbacks.stream; + Janus.log("MediaStream provided by the application"); + Janus.debug(stream); + // If this is an update, let's check if we need to release the previous stream + if(media.update && config.myStream && config.myStream !== callbacks.stream && !config.streamExternal && !media.replaceAudio && !media.replaceVideo) { + // We're replacing a stream we captured ourselves with an external one + Janus.stopAllTracks(config.myStream); + config.myStream = null; + } + // Skip the getUserMedia part + config.streamExternal = true; + pluginHandle.consentDialog(false); + streamsDone(handleId, jsep, media, callbacks, stream); + return; + } + if(isAudioSendEnabled(media) || isVideoSendEnabled(media)) { + if(!Janus.isGetUserMediaAvailable()) { + callbacks.error("getUserMedia not available"); + return; + } + var constraints = { mandatory: {}, optional: []}; + pluginHandle.consentDialog(true); + var audioSupport = isAudioSendEnabled(media); + if(audioSupport && media && typeof media.audio === 'object') + audioSupport = media.audio; + var videoSupport = isVideoSendEnabled(media); + if(videoSupport && media) { + var simulcast = (callbacks.simulcast === true || callbacks.simulcast2 === true); + var svc = callbacks.svc; + if((simulcast || svc) && !jsep && !media.video) + media.video = "hires"; + if(media.video && media.video != 'screen' && media.video != 'window') { + if(typeof media.video === 'object') { + videoSupport = media.video; + } else { + var width = 0; + var height = 0; + if(media.video === 'lowres') { + // Small resolution, 4:3 + height = 240; + width = 320; + } else if(media.video === 'lowres-16:9') { + // Small resolution, 16:9 + height = 180; + width = 320; + } else if(media.video === 'hires' || media.video === 'hires-16:9' || media.video === 'hdres') { + // High(HD) resolution is only 16:9 + height = 720; + width = 1280; + } else if(media.video === 'fhdres') { + // Full HD resolution is only 16:9 + height = 1080; + width = 1920; + } else if(media.video === '4kres') { + // 4K resolution is only 16:9 + height = 2160; + width = 3840; + } else if(media.video === 'stdres') { + // Normal resolution, 4:3 + height = 480; + width = 640; + } else if(media.video === 'stdres-16:9') { + // Normal resolution, 16:9 + height = 360; + width = 640; + } else { + Janus.log("Default video setting is stdres 4:3"); + height = 480; + width = 640; + } + Janus.log("Adding media constraint:", media.video); + videoSupport = { + 'height': {'ideal': height}, + 'width': {'ideal': width} + }; + Janus.log("Adding video constraint:", videoSupport); + } + } else if(media.video === 'screen' || media.video === 'window') { + if(navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) { + // The new experimental getDisplayMedia API is available, let's use that + // https://groups.google.com/forum/#!topic/discuss-webrtc/Uf0SrR4uxzk + // https://webrtchacks.com/chrome-screensharing-getdisplaymedia/ + constraints.video = {}; + if(media.screenshareFrameRate) { + constraints.video.frameRate = media.screenshareFrameRate; + } + if(media.screenshareHeight) { + constraints.video.height = media.screenshareHeight; + } + if(media.screenshareWidth) { + constraints.video.width = media.screenshareWidth; + } + constraints.audio = media.captureDesktopAudio; + navigator.mediaDevices.getDisplayMedia(constraints) + .then(function(stream) { + pluginHandle.consentDialog(false); + if(isAudioSendEnabled(media) && !media.keepAudio) { + navigator.mediaDevices.getUserMedia({ audio: true, video: false }) + .then(function (audioStream) { + stream.addTrack(audioStream.getAudioTracks()[0]); + streamsDone(handleId, jsep, media, callbacks, stream); + }) + } else { + streamsDone(handleId, jsep, media, callbacks, stream); + } + }, function (error) { + pluginHandle.consentDialog(false); + callbacks.error(error); + }); + return; + } + // We're going to try and use the extension for Chrome 34+, the old approach + // for older versions of Chrome, or the experimental support in Firefox 33+ + const callbackUserMedia = function(error, stream) { + pluginHandle.consentDialog(false); + if(error) { + callbacks.error(error); + } else { + streamsDone(handleId, jsep, media, callbacks, stream); + } + } + const getScreenMedia = function(constraints, gsmCallback, useAudio) { + Janus.log("Adding media constraint (screen capture)"); + Janus.debug(constraints); + navigator.mediaDevices.getUserMedia(constraints) + .then(function(stream) { + if(useAudio) { + navigator.mediaDevices.getUserMedia({ audio: true, video: false }) + .then(function (audioStream) { + stream.addTrack(audioStream.getAudioTracks()[0]); + gsmCallback(null, stream); + }) + } else { + gsmCallback(null, stream); + } + }) + .catch(function(error) { pluginHandle.consentDialog(false); gsmCallback(error); }); + } + if(Janus.webRTCAdapter.browserDetails.browser === 'chrome') { + var chromever = Janus.webRTCAdapter.browserDetails.version; + var maxver = 33; + if(window.navigator.userAgent.match('Linux')) + maxver = 35; // "known" crash in chrome 34 and 35 on linux + if(chromever >= 26 && chromever <= maxver) { + // Chrome 26->33 requires some awkward chrome://flags manipulation + constraints = { + video: { + mandatory: { + googLeakyBucket: true, + maxWidth: window.screen.width, + maxHeight: window.screen.height, + minFrameRate: media.screenshareFrameRate, + maxFrameRate: media.screenshareFrameRate, + chromeMediaSource: 'screen' + } + }, + audio: isAudioSendEnabled(media) && !media.keepAudio + }; + getScreenMedia(constraints, callbackUserMedia); + } else { + // Chrome 34+ requires an extension + Janus.extension.getScreen(function (error, sourceId) { + if (error) { + pluginHandle.consentDialog(false); + return callbacks.error(error); + } + constraints = { + audio: false, + video: { + mandatory: { + chromeMediaSource: 'desktop', + maxWidth: window.screen.width, + maxHeight: window.screen.height, + minFrameRate: media.screenshareFrameRate, + maxFrameRate: media.screenshareFrameRate, + }, + optional: [ + {googLeakyBucket: true}, + {googTemporalLayeredScreencast: true} + ] + } + }; + constraints.video.mandatory.chromeMediaSourceId = sourceId; + getScreenMedia(constraints, callbackUserMedia, + isAudioSendEnabled(media) && !media.keepAudio); + }); + } + } else if(Janus.webRTCAdapter.browserDetails.browser === 'firefox') { + if(Janus.webRTCAdapter.browserDetails.version >= 33) { + // Firefox 33+ has experimental support for screen sharing + constraints = { + video: { + mozMediaSource: media.video, + mediaSource: media.video + }, + audio: isAudioSendEnabled(media) && !media.keepAudio + }; + getScreenMedia(constraints, function (err, stream) { + callbackUserMedia(err, stream); + // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1045810 + if (!err) { + var lastTime = stream.currentTime; + var polly = window.setInterval(function () { + if(!stream) + window.clearInterval(polly); + if(stream.currentTime == lastTime) { + window.clearInterval(polly); + if(stream.onended) { + stream.onended(); + } + } + lastTime = stream.currentTime; + }, 500); + } + }); + } else { + var error = new Error('NavigatorUserMediaError'); + error.name = 'Your version of Firefox does not support screen sharing, please install Firefox 33 (or more recent versions)'; + pluginHandle.consentDialog(false); + callbacks.error(error); + return; + } + } + return; + } + } + // If we got here, we're not screensharing + if(!media || media.video !== 'screen') { + // Check whether all media sources are actually available or not + navigator.mediaDevices.enumerateDevices().then(function(devices) { + var audioExist = devices.some(function(device) { + return device.kind === 'audioinput'; + }), + videoExist = isScreenSendEnabled(media) || devices.some(function(device) { + return device.kind === 'videoinput'; + }); + + // Check whether a missing device is really a problem + var audioSend = isAudioSendEnabled(media); + var videoSend = isVideoSendEnabled(media); + var needAudioDevice = isAudioSendRequired(media); + var needVideoDevice = isVideoSendRequired(media); + if(audioSend || videoSend || needAudioDevice || needVideoDevice) { + // We need to send either audio or video + var haveAudioDevice = audioSend ? audioExist : false; + var haveVideoDevice = videoSend ? videoExist : false; + if(!haveAudioDevice && !haveVideoDevice) { + // FIXME Should we really give up, or just assume recvonly for both? + pluginHandle.consentDialog(false); + callbacks.error('No capture device found'); + return false; + } else if(!haveAudioDevice && needAudioDevice) { + pluginHandle.consentDialog(false); + callbacks.error('Audio capture is required, but no capture device found'); + return false; + } else if(!haveVideoDevice && needVideoDevice) { + pluginHandle.consentDialog(false); + callbacks.error('Video capture is required, but no capture device found'); + return false; + } + } + + var gumConstraints = { + audio: (audioExist && !media.keepAudio) ? audioSupport : false, + video: (videoExist && !media.keepVideo) ? videoSupport : false + }; + Janus.debug("getUserMedia constraints", gumConstraints); + if (!gumConstraints.audio && !gumConstraints.video) { + pluginHandle.consentDialog(false); + streamsDone(handleId, jsep, media, callbacks, stream); + } else { + navigator.mediaDevices.getUserMedia(gumConstraints) + .then(function(stream) { + pluginHandle.consentDialog(false); + streamsDone(handleId, jsep, media, callbacks, stream); + }).catch(function(error) { + pluginHandle.consentDialog(false); + callbacks.error({code: error.code, name: error.name, message: error.message}); + }); + } + }) + .catch(function(error) { + pluginHandle.consentDialog(false); + callbacks.error(error); + }); + } + } else { + // No need to do a getUserMedia, create offer/answer right away + streamsDone(handleId, jsep, media, callbacks); + } + } + + function prepareWebrtcPeer(handleId, callbacks) { + callbacks = callbacks || {}; + callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop; + callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : webrtcError; + callbacks.customizeSdp = (typeof callbacks.customizeSdp == "function") ? callbacks.customizeSdp : Janus.noop; + var jsep = callbacks.jsep; + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || !pluginHandle.webrtcStuff) { + Janus.warn("Invalid handle"); + callbacks.error("Invalid handle"); + return; + } + var config = pluginHandle.webrtcStuff; + if(jsep) { + if(!config.pc) { + Janus.warn("Wait, no PeerConnection?? if this is an answer, use createAnswer and not handleRemoteJsep"); + callbacks.error("No PeerConnection: if this is an answer, use createAnswer and not handleRemoteJsep"); + return; + } + callbacks.customizeSdp(jsep); + config.pc.setRemoteDescription(jsep) + .then(function() { + Janus.log("Remote description accepted!"); + config.remoteSdp = jsep.sdp; + // Any trickle candidate we cached? + if(config.candidates && config.candidates.length > 0) { + for(var i = 0; i< config.candidates.length; i++) { + var candidate = config.candidates[i]; + Janus.debug("Adding remote candidate:", candidate); + if(!candidate || candidate.completed === true) { + // end-of-candidates + config.pc.addIceCandidate(Janus.endOfCandidates); + } else { + // New candidate + config.pc.addIceCandidate(candidate); + } + } + config.candidates = []; + } + // Done + callbacks.success(); + }, callbacks.error); + } else { + callbacks.error("Invalid JSEP"); + } + } + + function createOffer(handleId, media, callbacks) { + callbacks = callbacks || {}; + callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop; + callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop; + callbacks.customizeSdp = (typeof callbacks.customizeSdp == "function") ? callbacks.customizeSdp : Janus.noop; + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || !pluginHandle.webrtcStuff) { + Janus.warn("Invalid handle"); + callbacks.error("Invalid handle"); + return; + } + var config = pluginHandle.webrtcStuff; + var simulcast = (callbacks.simulcast === true); + if(!simulcast) { + Janus.log("Creating offer (iceDone=" + config.iceDone + ")"); + } else { + Janus.log("Creating offer (iceDone=" + config.iceDone + ", simulcast=" + simulcast + ")"); + } + // https://code.google.com/p/webrtc/issues/detail?id=3508 + var mediaConstraints = {}; + if(Janus.unifiedPlan) { + // We can use Transceivers + var audioTransceiver = null, videoTransceiver = null; + var transceivers = config.pc.getTransceivers(); + if(transceivers && transceivers.length > 0) { + for(var t of transceivers) { + if((t.sender && t.sender.track && t.sender.track.kind === "audio") || + (t.receiver && t.receiver.track && t.receiver.track.kind === "audio")) { + if(!audioTransceiver) { + audioTransceiver = t; + } + continue; + } + if((t.sender && t.sender.track && t.sender.track.kind === "video") || + (t.receiver && t.receiver.track && t.receiver.track.kind === "video")) { + if(!videoTransceiver) { + videoTransceiver = t; + } + continue; + } + } + } + // Handle audio (and related changes, if any) + var audioSend = isAudioSendEnabled(media); + var audioRecv = isAudioRecvEnabled(media); + if(!audioSend && !audioRecv) { + // Audio disabled: have we removed it? + if(media.removeAudio && audioTransceiver) { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection("inactive"); + } else { + audioTransceiver.direction = "inactive"; + } + Janus.log("Setting audio transceiver to inactive:", audioTransceiver); + } + } else { + // Take care of audio m-line + if(audioSend && audioRecv) { + if(audioTransceiver) { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection("sendrecv"); + } else { + audioTransceiver.direction = "sendrecv"; + } + Janus.log("Setting audio transceiver to sendrecv:", audioTransceiver); + } + } else if(audioSend && !audioRecv) { + if(audioTransceiver) { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection("sendonly"); + } else { + audioTransceiver.direction = "sendonly"; + } + Janus.log("Setting audio transceiver to sendonly:", audioTransceiver); + } + } else if(!audioSend && audioRecv) { + if(audioTransceiver) { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection("recvonly"); + } else { + audioTransceiver.direction = "recvonly"; + } + Janus.log("Setting audio transceiver to recvonly:", audioTransceiver); + } else { + // In theory, this is the only case where we might not have a transceiver yet + audioTransceiver = config.pc.addTransceiver("audio", { direction: "recvonly" }); + Janus.log("Adding recvonly audio transceiver:", audioTransceiver); + } + } + } + // Handle video (and related changes, if any) + var videoSend = isVideoSendEnabled(media); + var videoRecv = isVideoRecvEnabled(media); + if(!videoSend && !videoRecv) { + // Video disabled: have we removed it? + if(media.removeVideo && videoTransceiver) { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection("inactive"); + } else { + videoTransceiver.direction = "inactive"; + } + Janus.log("Setting video transceiver to inactive:", videoTransceiver); + } + } else { + // Take care of video m-line + if(videoSend && videoRecv) { + if(videoTransceiver) { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection("sendrecv"); + } else { + videoTransceiver.direction = "sendrecv"; + } + Janus.log("Setting video transceiver to sendrecv:", videoTransceiver); + } + } else if(videoSend && !videoRecv) { + if(videoTransceiver) { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection("sendonly"); + } else { + videoTransceiver.direction = "sendonly"; + } + Janus.log("Setting video transceiver to sendonly:", videoTransceiver); + } + } else if(!videoSend && videoRecv) { + if(videoTransceiver) { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection("recvonly"); + } else { + videoTransceiver.direction = "recvonly"; + } + Janus.log("Setting video transceiver to recvonly:", videoTransceiver); + } else { + // In theory, this is the only case where we might not have a transceiver yet + videoTransceiver = config.pc.addTransceiver("video", { direction: "recvonly" }); + Janus.log("Adding recvonly video transceiver:", videoTransceiver); + } + } + } + } else { + mediaConstraints["offerToReceiveAudio"] = isAudioRecvEnabled(media); + mediaConstraints["offerToReceiveVideo"] = isVideoRecvEnabled(media); + } + var iceRestart = (callbacks.iceRestart === true); + if(iceRestart) { + mediaConstraints["iceRestart"] = true; + } + Janus.debug(mediaConstraints); + // Check if this is Firefox and we've been asked to do simulcasting + var sendVideo = isVideoSendEnabled(media); + if(sendVideo && simulcast && Janus.webRTCAdapter.browserDetails.browser === "firefox") { + // FIXME Based on https://gist.github.com/voluntas/088bc3cc62094730647b + Janus.log("Enabling Simulcasting for Firefox (RID)"); + var sender = config.pc.getSenders().find(function(s) {return s.track && s.track.kind === "video"}); + if(sender) { + var parameters = sender.getParameters(); + if(!parameters) { + parameters = {}; + } + var maxBitrates = getMaxBitrates(callbacks.simulcastMaxBitrates); + parameters.encodings = callbacks.sendEncodings || [ + { rid: "h", active: true, maxBitrate: maxBitrates.high }, + { rid: "m", active: true, maxBitrate: maxBitrates.medium, scaleResolutionDownBy: 2 }, + { rid: "l", active: true, maxBitrate: maxBitrates.low, scaleResolutionDownBy: 4 } + ]; + sender.setParameters(parameters); + } + } + config.pc.createOffer(mediaConstraints) + .then(function(offer) { + Janus.debug(offer); + // JSON.stringify doesn't work on some WebRTC objects anymore + // See https://code.google.com/p/chromium/issues/detail?id=467366 + var jsep = { + "type": offer.type, + "sdp": offer.sdp + }; + callbacks.customizeSdp(jsep); + offer.sdp = jsep.sdp; + Janus.log("Setting local description"); + if(sendVideo && simulcast && !Janus.unifiedPlan) { + // We only do simulcast via SDP munging on older versions of Chrome and Safari + if(Janus.webRTCAdapter.browserDetails.browser === "chrome" || + Janus.webRTCAdapter.browserDetails.browser === "safari") { + Janus.log("Enabling Simulcasting for Chrome (SDP munging)"); + offer.sdp = mungeSdpForSimulcasting(offer.sdp); + } + } + config.mySdp = { + type: "offer", + sdp: offer.sdp + }; + config.pc.setLocalDescription(offer) + .catch(callbacks.error); + config.mediaConstraints = mediaConstraints; + if(!config.iceDone && !config.trickle) { + // Don't do anything until we have all candidates + Janus.log("Waiting for all candidates..."); + return; + } + // If transforms are present, notify Janus that the media is end-to-end encrypted + if(config.senderTransforms || config.receiverTransforms) { + offer["e2ee"] = true; + } + callbacks.success(offer); + }, callbacks.error); + } + + function createAnswer(handleId, media, callbacks) { + callbacks = callbacks || {}; + callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop; + callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop; + callbacks.customizeSdp = (typeof callbacks.customizeSdp == "function") ? callbacks.customizeSdp : Janus.noop; + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || !pluginHandle.webrtcStuff) { + Janus.warn("Invalid handle"); + callbacks.error("Invalid handle"); + return; + } + var config = pluginHandle.webrtcStuff; + var simulcast = (callbacks.simulcast === true || callbacks.simulcast2 === true); + if(!simulcast) { + Janus.log("Creating answer (iceDone=" + config.iceDone + ")"); + } else { + Janus.log("Creating answer (iceDone=" + config.iceDone + ", simulcast=" + simulcast + ")"); + } + var mediaConstraints = null; + if(Janus.unifiedPlan) { + // We can use Transceivers + mediaConstraints = {}; + var audioTransceiver = null, videoTransceiver = null; + var transceivers = config.pc.getTransceivers(); + if(transceivers && transceivers.length > 0) { + for(var t of transceivers) { + if((t.sender && t.sender.track && t.sender.track.kind === "audio") || + (t.receiver && t.receiver.track && t.receiver.track.kind === "audio")) { + if(!audioTransceiver) + audioTransceiver = t; + continue; + } + if((t.sender && t.sender.track && t.sender.track.kind === "video") || + (t.receiver && t.receiver.track && t.receiver.track.kind === "video")) { + if(!videoTransceiver) + videoTransceiver = t; + continue; + } + } + } + // Handle audio (and related changes, if any) + var audioSend = isAudioSendEnabled(media); + var audioRecv = isAudioRecvEnabled(media); + if(!audioSend && !audioRecv) { + // Audio disabled: have we removed it? + if(media.removeAudio && audioTransceiver) { + try { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection("inactive"); + } else { + audioTransceiver.direction = "inactive"; + } + Janus.log("Setting audio transceiver to inactive:", audioTransceiver); + } catch(e) { + Janus.error(e); + } + } + } else { + // Take care of audio m-line + if(audioSend && audioRecv) { + if(audioTransceiver) { + try { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection("sendrecv"); + } else { + audioTransceiver.direction = "sendrecv"; + } + Janus.log("Setting audio transceiver to sendrecv:", audioTransceiver); + } catch(e) { + Janus.error(e); + } + } + } else if(audioSend && !audioRecv) { + try { + if(audioTransceiver) { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection("sendonly"); + } else { + audioTransceiver.direction = "sendonly"; + } + Janus.log("Setting audio transceiver to sendonly:", audioTransceiver); + } + } catch(e) { + Janus.error(e); + } + } else if(!audioSend && audioRecv) { + if(audioTransceiver) { + try { + if (audioTransceiver.setDirection) { + audioTransceiver.setDirection("recvonly"); + } else { + audioTransceiver.direction = "recvonly"; + } + Janus.log("Setting audio transceiver to recvonly:", audioTransceiver); + } catch(e) { + Janus.error(e); + } + } else { + // In theory, this is the only case where we might not have a transceiver yet + audioTransceiver = config.pc.addTransceiver("audio", { direction: "recvonly" }); + Janus.log("Adding recvonly audio transceiver:", audioTransceiver); + } + } + } + // Handle video (and related changes, if any) + var videoSend = isVideoSendEnabled(media); + var videoRecv = isVideoRecvEnabled(media); + if(!videoSend && !videoRecv) { + // Video disabled: have we removed it? + if(media.removeVideo && videoTransceiver) { + try { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection("inactive"); + } else { + videoTransceiver.direction = "inactive"; + } + Janus.log("Setting video transceiver to inactive:", videoTransceiver); + } catch(e) { + Janus.error(e); + } + } + } else { + // Take care of video m-line + if(videoSend && videoRecv) { + if(videoTransceiver) { + try { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection("sendrecv"); + } else { + videoTransceiver.direction = "sendrecv"; + } + Janus.log("Setting video transceiver to sendrecv:", videoTransceiver); + } catch(e) { + Janus.error(e); + } + } + } else if(videoSend && !videoRecv) { + if(videoTransceiver) { + try { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection("sendonly"); + } else { + videoTransceiver.direction = "sendonly"; + } + Janus.log("Setting video transceiver to sendonly:", videoTransceiver); + } catch(e) { + Janus.error(e); + } + } + } else if(!videoSend && videoRecv) { + if(videoTransceiver) { + try { + if (videoTransceiver.setDirection) { + videoTransceiver.setDirection("recvonly"); + } else { + videoTransceiver.direction = "recvonly"; + } + Janus.log("Setting video transceiver to recvonly:", videoTransceiver); + } catch(e) { + Janus.error(e); + } + } else { + // In theory, this is the only case where we might not have a transceiver yet + videoTransceiver = config.pc.addTransceiver("video", { direction: "recvonly" }); + Janus.log("Adding recvonly video transceiver:", videoTransceiver); + } + } + } + } else { + if(Janus.webRTCAdapter.browserDetails.browser === "firefox" || Janus.webRTCAdapter.browserDetails.browser === "edge") { + mediaConstraints = { + offerToReceiveAudio: isAudioRecvEnabled(media), + offerToReceiveVideo: isVideoRecvEnabled(media) + }; + } else { + mediaConstraints = { + mandatory: { + OfferToReceiveAudio: isAudioRecvEnabled(media), + OfferToReceiveVideo: isVideoRecvEnabled(media) + } + }; + } + } + Janus.debug(mediaConstraints); + // Check if this is Firefox and we've been asked to do simulcasting + var sendVideo = isVideoSendEnabled(media); + if(sendVideo && simulcast && Janus.webRTCAdapter.browserDetails.browser === "firefox") { + // FIXME Based on https://gist.github.com/voluntas/088bc3cc62094730647b + Janus.log("Enabling Simulcasting for Firefox (RID)"); + var sender = config.pc.getSenders()[1]; + Janus.log(sender); + var parameters = sender.getParameters(); + Janus.log(parameters); + + var maxBitrates = getMaxBitrates(callbacks.simulcastMaxBitrates); + sender.setParameters({encodings: callbacks.sendEncodings || [ + { rid: "h", active: true, maxBitrate: maxBitrates.high }, + { rid: "m", active: true, maxBitrate: maxBitrates.medium, scaleResolutionDownBy: 2}, + { rid: "l", active: true, maxBitrate: maxBitrates.low, scaleResolutionDownBy: 4} + ]}); + } + config.pc.createAnswer(mediaConstraints) + .then(function(answer) { + Janus.debug(answer); + // JSON.stringify doesn't work on some WebRTC objects anymore + // See https://code.google.com/p/chromium/issues/detail?id=467366 + var jsep = { + "type": answer.type, + "sdp": answer.sdp + }; + callbacks.customizeSdp(jsep); + answer.sdp = jsep.sdp; + Janus.log("Setting local description"); + if(sendVideo && simulcast && !Janus.unifiedPlan) { + // We only do simulcast via SDP munging on older versions of Chrome and Safari + if(Janus.webRTCAdapter.browserDetails.browser === "chrome") { + // FIXME Apparently trying to simulcast when answering breaks video in Chrome... + //~ Janus.log("Enabling Simulcasting for Chrome (SDP munging)"); + //~ answer.sdp = mungeSdpForSimulcasting(answer.sdp); + Janus.warn("simulcast=true, but this is an answer, and video breaks in Chrome if we enable it"); + } + } + config.mySdp = { + type: "answer", + sdp: answer.sdp + }; + config.pc.setLocalDescription(answer) + .catch(callbacks.error); + config.mediaConstraints = mediaConstraints; + if(!config.iceDone && !config.trickle) { + // Don't do anything until we have all candidates + Janus.log("Waiting for all candidates..."); + return; + } + // If transforms are present, notify Janus that the media is end-to-end encrypted + if(config.senderTransforms || config.receiverTransforms) { + answer["e2ee"] = true; + } + callbacks.success(answer); + }, callbacks.error); + } + + function sendSDP(handleId, callbacks) { + callbacks = callbacks || {}; + callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop; + callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop; + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || !pluginHandle.webrtcStuff) { + Janus.warn("Invalid handle, not sending anything"); + return; + } + var config = pluginHandle.webrtcStuff; + Janus.log("Sending offer/answer SDP..."); + if(!config.mySdp) { + Janus.warn("Local SDP instance is invalid, not sending anything..."); + return; + } + config.mySdp = { + "type": config.pc.localDescription.type, + "sdp": config.pc.localDescription.sdp + }; + if(config.trickle === false) + config.mySdp["trickle"] = false; + Janus.debug(callbacks); + config.sdpSent = true; + callbacks.success(config.mySdp); + } + + function getVolume(handleId, remote) { + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || !pluginHandle.webrtcStuff) { + Janus.warn("Invalid handle"); + return 0; + } + var stream = remote ? "remote" : "local"; + var config = pluginHandle.webrtcStuff; + if(!config.volume[stream]) + config.volume[stream] = { value: 0 }; + // Start getting the volume, if audioLevel in getStats is supported (apparently + // they're only available in Chrome/Safari right now: https://webrtc-stats.callstats.io/) + if(config.pc.getStats && (Janus.webRTCAdapter.browserDetails.browser === "chrome" || + Janus.webRTCAdapter.browserDetails.browser === "safari")) { + if(remote && !config.remoteStream) { + Janus.warn("Remote stream unavailable"); + return 0; + } else if(!remote && !config.myStream) { + Janus.warn("Local stream unavailable"); + return 0; + } + if(!config.volume[stream].timer) { + Janus.log("Starting " + stream + " volume monitor"); + config.volume[stream].timer = setInterval(function() { + config.pc.getStats() + .then(function(stats) { + stats.forEach(function (res) { + if(!res || res.kind !== "audio") + return; + if((remote && !res.remoteSource) || (!remote && res.type !== "media-source")) + return; + config.volume[stream].value = (res.audioLevel ? res.audioLevel : 0); + }); + }); + }, 200); + return 0; // We don't have a volume to return yet + } + return config.volume[stream].value; + } else { + // audioInputLevel and audioOutputLevel seem only available in Chrome? audioLevel + // seems to be available on Chrome and Firefox, but they don't seem to work + Janus.warn("Getting the " + stream + " volume unsupported by browser"); + return 0; + } + } + + function isMuted(handleId, video) { + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || !pluginHandle.webrtcStuff) { + Janus.warn("Invalid handle"); + return true; + } + var config = pluginHandle.webrtcStuff; + if(!config.pc) { + Janus.warn("Invalid PeerConnection"); + return true; + } + if(!config.myStream) { + Janus.warn("Invalid local MediaStream"); + return true; + } + if(video) { + // Check video track + if(!config.myStream.getVideoTracks() || config.myStream.getVideoTracks().length === 0) { + Janus.warn("No video track"); + return true; + } + return !config.myStream.getVideoTracks()[0].enabled; + } else { + // Check audio track + if(!config.myStream.getAudioTracks() || config.myStream.getAudioTracks().length === 0) { + Janus.warn("No audio track"); + return true; + } + return !config.myStream.getAudioTracks()[0].enabled; + } + } + + function mute(handleId, video, mute) { + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || !pluginHandle.webrtcStuff) { + Janus.warn("Invalid handle"); + return false; + } + var config = pluginHandle.webrtcStuff; + if(!config.pc) { + Janus.warn("Invalid PeerConnection"); + return false; + } + if(!config.myStream) { + Janus.warn("Invalid local MediaStream"); + return false; + } + if(video) { + // Mute/unmute video track + if(!config.myStream.getVideoTracks() || config.myStream.getVideoTracks().length === 0) { + Janus.warn("No video track"); + return false; + } + config.myStream.getVideoTracks()[0].enabled = !mute; + return true; + } else { + // Mute/unmute audio track + if(!config.myStream.getAudioTracks() || config.myStream.getAudioTracks().length === 0) { + Janus.warn("No audio track"); + return false; + } + config.myStream.getAudioTracks()[0].enabled = !mute; + return true; + } + } + + function getBitrate(handleId) { + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle || !pluginHandle.webrtcStuff) { + Janus.warn("Invalid handle"); + return "Invalid handle"; + } + var config = pluginHandle.webrtcStuff; + if(!config.pc) + return "Invalid PeerConnection"; + // Start getting the bitrate, if getStats is supported + if(config.pc.getStats) { + if(!config.bitrate.timer) { + Janus.log("Starting bitrate timer (via getStats)"); + config.bitrate.timer = setInterval(function() { + config.pc.getStats() + .then(function(stats) { + stats.forEach(function (res) { + if(!res) + return; + var inStats = false; + // Check if these are statistics on incoming media + if((res.mediaType === "video" || res.id.toLowerCase().indexOf("video") > -1) && + res.type === "inbound-rtp" && res.id.indexOf("rtcp") < 0) { + // New stats + inStats = true; + } else if(res.type == 'ssrc' && res.bytesReceived && + (res.googCodecName === "VP8" || res.googCodecName === "")) { + // Older Chromer versions + inStats = true; + } + // Parse stats now + if(inStats) { + config.bitrate.bsnow = res.bytesReceived; + config.bitrate.tsnow = res.timestamp; + if(config.bitrate.bsbefore === null || config.bitrate.tsbefore === null) { + // Skip this round + config.bitrate.bsbefore = config.bitrate.bsnow; + config.bitrate.tsbefore = config.bitrate.tsnow; + } else { + // Calculate bitrate + var timePassed = config.bitrate.tsnow - config.bitrate.tsbefore; + if(Janus.webRTCAdapter.browserDetails.browser === "safari") + timePassed = timePassed/1000; // Apparently the timestamp is in microseconds, in Safari + var bitRate = Math.round((config.bitrate.bsnow - config.bitrate.bsbefore) * 8 / timePassed); + if(Janus.webRTCAdapter.browserDetails.browser === "safari") + bitRate = parseInt(bitRate/1000); + config.bitrate.value = bitRate + ' kbits/sec'; + //~ Janus.log("Estimated bitrate is " + config.bitrate.value); + config.bitrate.bsbefore = config.bitrate.bsnow; + config.bitrate.tsbefore = config.bitrate.tsnow; + } + } + }); + }); + }, 1000); + return "0 kbits/sec"; // We don't have a bitrate value yet + } + return config.bitrate.value; + } else { + Janus.warn("Getting the video bitrate unsupported by browser"); + return "Feature unsupported by browser"; + } + } + + function webrtcError(error) { + Janus.error("WebRTC error:", error); + } + + function cleanupWebrtc(handleId, hangupRequest) { + Janus.log("Cleaning WebRTC stuff"); + var pluginHandle = pluginHandles[handleId]; + if(!pluginHandle) { + // Nothing to clean + return; + } + var config = pluginHandle.webrtcStuff; + if(config) { + if(hangupRequest === true) { + // Send a hangup request (we don't really care about the response) + var request = { "janus": "hangup", "transaction": Janus.randomString(12) }; + if(pluginHandle.token) + request["token"] = pluginHandle.token; + if(apisecret) + request["apisecret"] = apisecret; + Janus.debug("Sending hangup request (handle=" + handleId + "):"); + Janus.debug(request); + if(websockets) { + request["session_id"] = sessionId; + request["handle_id"] = handleId; + ws.send(JSON.stringify(request)); + } else { + Janus.httpAPICall(server + "/" + sessionId + "/" + handleId, { + verb: 'POST', + withCredentials: withCredentials, + body: request + }); + } + } + // Cleanup stack + config.remoteStream = null; + if(config.volume) { + if(config.volume["local"] && config.volume["local"].timer) + clearInterval(config.volume["local"].timer); + if(config.volume["remote"] && config.volume["remote"].timer) + clearInterval(config.volume["remote"].timer); + } + config.volume = {}; + if(config.bitrate.timer) + clearInterval(config.bitrate.timer); + config.bitrate.timer = null; + config.bitrate.bsnow = null; + config.bitrate.bsbefore = null; + config.bitrate.tsnow = null; + config.bitrate.tsbefore = null; + config.bitrate.value = null; + if(!config.streamExternal && config.myStream) { + Janus.log("Stopping local stream tracks"); + Janus.stopAllTracks(config.myStream); + } + config.streamExternal = false; + config.myStream = null; + // Close PeerConnection + try { + config.pc.close(); + } catch(e) { + // Do nothing + } + config.pc = null; + config.candidates = null; + config.mySdp = null; + config.remoteSdp = null; + config.iceDone = false; + config.dataChannel = {}; + config.dtmfSender = null; + config.senderTransforms = null; + config.receiverTransforms = null; + } + pluginHandle.oncleanup(); + } + + // Helper method to munge an SDP to enable simulcasting (Chrome only) + function mungeSdpForSimulcasting(sdp) { + // Let's munge the SDP to add the attributes for enabling simulcasting + // (based on https://gist.github.com/ggarber/a19b4c33510028b9c657) + var lines = sdp.split("\r\n"); + var video = false; + var ssrc = [ -1 ], ssrc_fid = [ -1 ]; + var cname = null, msid = null, mslabel = null, label = null; + var insertAt = -1; + for(let i=0; i -1) { + // We're done, let's add the new attributes here + insertAt = i; + break; + } + } + continue; + } + if(!video) + continue; + var sim = lines[i].match(/a=ssrc-group:SIM (\d+) (\d+) (\d+)/); + if(sim) { + Janus.warn("The SDP already contains a SIM attribute, munging will be skipped"); + return sdp; + } + var fid = lines[i].match(/a=ssrc-group:FID (\d+) (\d+)/); + if(fid) { + ssrc[0] = fid[1]; + ssrc_fid[0] = fid[2]; + lines.splice(i, 1); i--; + continue; + } + if(ssrc[0]) { + var match = lines[i].match('a=ssrc:' + ssrc[0] + ' cname:(.+)') + if(match) { + cname = match[1]; + } + match = lines[i].match('a=ssrc:' + ssrc[0] + ' msid:(.+)') + if(match) { + msid = match[1]; + } + match = lines[i].match('a=ssrc:' + ssrc[0] + ' mslabel:(.+)') + if(match) { + mslabel = match[1]; + } + match = lines[i].match('a=ssrc:' + ssrc[0] + ' label:(.+)') + if(match) { + label = match[1]; + } + if(lines[i].indexOf('a=ssrc:' + ssrc_fid[0]) === 0) { + lines.splice(i, 1); i--; + continue; + } + if(lines[i].indexOf('a=ssrc:' + ssrc[0]) === 0) { + lines.splice(i, 1); i--; + continue; + } + } + if(lines[i].length == 0) { + lines.splice(i, 1); i--; + continue; + } + } + if(ssrc[0] < 0) { + // Couldn't find a FID attribute, let's just take the first video SSRC we find + insertAt = -1; + video = false; + for(let i=0; i -1) { + // We're done, let's add the new attributes here + insertAt = i; + break; + } + } + continue; + } + if(!video) + continue; + if(ssrc[0] < 0) { + var value = lines[i].match(/a=ssrc:(\d+)/); + if(value) { + ssrc[0] = value[1]; + lines.splice(i, 1); i--; + continue; + } + } else { + let match = lines[i].match('a=ssrc:' + ssrc[0] + ' cname:(.+)') + if(match) { + cname = match[1]; + } + match = lines[i].match('a=ssrc:' + ssrc[0] + ' msid:(.+)') + if(match) { + msid = match[1]; + } + match = lines[i].match('a=ssrc:' + ssrc[0] + ' mslabel:(.+)') + if(match) { + mslabel = match[1]; + } + match = lines[i].match('a=ssrc:' + ssrc[0] + ' label:(.+)') + if(match) { + label = match[1]; + } + if(lines[i].indexOf('a=ssrc:' + ssrc_fid[0]) === 0) { + lines.splice(i, 1); i--; + continue; + } + if(lines[i].indexOf('a=ssrc:' + ssrc[0]) === 0) { + lines.splice(i, 1); i--; + continue; + } + } + if(lines[i].length === 0) { + lines.splice(i, 1); i--; + continue; + } + } + } + if(ssrc[0] < 0) { + // Still nothing, let's just return the SDP we were asked to munge + Janus.warn("Couldn't find the video SSRC, simulcasting NOT enabled"); + return sdp; + } + if(insertAt < 0) { + // Append at the end + insertAt = lines.length; + } + // Generate a couple of SSRCs (for retransmissions too) + // Note: should we check if there are conflicts, here? + ssrc[1] = Math.floor(Math.random()*0xFFFFFFFF); + ssrc[2] = Math.floor(Math.random()*0xFFFFFFFF); + ssrc_fid[1] = Math.floor(Math.random()*0xFFFFFFFF); + ssrc_fid[2] = Math.floor(Math.random()*0xFFFFFFFF); + // Add attributes to the SDP + for(var i=0; i # -# # -# 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 . # -# # -# ========================================================================== # - - -set -ex - -if [ "$(whoami)" != root ]; then - echo "Only root can do that" - exit 1 -fi - -if [ "$1" != --do-the-thing ]; then - echo "This script will make some firstboot magic. Don't run it manually." - exit 1 -fi - - -# ========== Preparing ========== - -if [ ! -f /boot/pikvm.txt ]; then - exit 0 -fi - -if systemctl is-enabled -q kvmd-oled; then - # Stop regular kvmd-oled service and show first time setup status in oled - systemctl stop kvmd-oled || true - kvmd-oled --interval=0 --text="On-boot setup...\nDO NOT INTERRUPT!\nPlease wait" || true - has_oled=1 -fi - -# shellcheck disable=SC1090 -source <(dos2unix < /boot/pikvm.txt) - -# shellcheck disable=SC1091 -source /usr/share/kvmd/platform || true - -rw - - -# ========== First boot and/or Avahi configuration ========== - -make_avahi_service() { - local _base - local _serial - local _platform - _base=$(tr -d '\0' < /proc/device-tree/model || echo "Unknown base") - _serial=$( (cat /proc/device-tree/serial-number || echo "0000000000000000") | tr -d '\0' | tr '[:lower:]' '[:upper:]') - _platform="$PIKVM_MODEL-$PIKVM_VIDEO-$PIKVM_BOARD" - mkdir -p /etc/avahi/services - cat < /etc/avahi/services/pikvm.service - - - - pikvm-$_serial.local - - _pikvm._tcp - 443 - path=/ - protocol=https - description=PiKVM Web Server - model=$PIKVM_MODEL - video=$PIKVM_VIDEO - board=$PIKVM_BOARD - base=$_base - serial=$_serial - platform=$_platform - - - _https._tcp - 443 - path=/ - protocol=https - description=PiKVM Web Server - model=$PIKVM_MODEL - video=$PIKVM_VIDEO - board=$PIKVM_BOARD - base=$_base - serial=$_serial - model=$_platform - - -end_of_file -} - -if [ -n "$FIRSTBOOT$FIRST_BOOT" ]; then - ( \ - (umount /etc/machine-id || true) \ - && echo -n > /etc/machine-id \ - && systemd-machine-id-setup \ - ) || true - - rm -f /etc/ssh/ssh_host_* - ssh-keygen -v -A - - rm -f /etc/kvmd/nginx/ssl/* - rm -f /etc/kvmd/vnc/ssl/* - kvmd-gencert --do-the-thing - kvmd-gencert --do-the-thing --vnc - - if grep -q 'X-kvmd\.otgmsd' /etc/fstab; then - part=$(grep 'X-kvmd\.otgmsd' /etc/fstab | awk '{print $1}') - # shellcheck disable=SC2206 - splitted=(${part//=/ }) - if [ "${splitted[0]}" == LABEL ]; then - label=${splitted[1]} - part=$(blkid -c /dev/null -L "$label") - else - label=PIMSD - fi - unset splitted - disk=/dev/$(lsblk -no pkname "$part") - npart=$(cat "/sys/class/block/${part//\/dev\//}/partition") - umount "$part" - parted "$disk" -a optimal -s resizepart "$npart" 100% - yes | mkfs.ext4 -L "$label" -F -m 0 "$part" - mount "$part" - unset disk part npart label - fi - - make_avahi_service - - # fc-cache is required for installed X server - # shellcheck disable=SC2015 - which fc-cache && fc-cache || true -fi - -if [ -n "$ENABLE_AVAHI" ]; then - if [ ! -f /etc/avahi/services/pikvm.service ]; then - make_avahi_service - fi - systemctl enable avahi-daemon || true - touch /boot/pikvm-reboot.txt -fi - - -# ========== OTG serial ========== - -if [ -n "$ENABLE_OTG_SERIAL" ]; then - cat < /etc/kvmd/override.d/0000-vendor-otg-serial.yaml -# Generated by kvmd-bootconfig. Do not edit this file! -otg: - devices: - serial: - enabled: true -end_of_file - grep '^ttyGS0$' /etc/securetty || echo ttyGS0 >> /etc/securetty - mkdir -p /etc/systemd/system/getty@ttyGS0.service.d - cat < /etc/systemd/system/getty@ttyGS0.service.d/override.conf -[Service] -TTYReset=no -TTYVHangup=no -TTYVTDisallocate=no -end_of_file - systemctl enable getty@ttyGS0.service - touch /boot/pikvm-reboot.txt -fi - - -# ========== SSH ========== - -if [ -n "$SSH_PORT" ]; then - sed -i -e "s/^\s*#*\s*Port\s\+.*$/Port $SSH_PORT/g" /etc/ssh/sshd_config -fi - - -# ========== Ethernet ========== - -make_dhcp_iface() { - local _iface="$1" - local _metric="$2" - cat < "/etc/systemd/network/$_iface.network" -[Match] -Name=$_iface - -[Network] -DHCP=yes -DNSSEC=no - -[DHCP] -# Use same IP by forcing to use MAC address for clientID -ClientIdentifier=mac -# https://github.com/pikvm/pikvm/issues/583 -RouteMetric=$_metric -end_of_file -} - -make_static_iface() { - local _iface="$1" - local _addr="$2" - local _gw="$3" - local _dns="$4" - local _metric="$5" - cat < "/etc/systemd/network/$_iface.network" -[Match] -Name=$_iface - -[Network] -Address=$_addr -DNS=$_dns -DNSSEC=no - -[Route] -Gateway=$_gw -# https://github.com/pikvm/pikvm/issues/583 -Metric=$_metric -end_of_file -} - -# If the ETH_DHCP is defined, configure eth0 for DHCP -if [ -n "$ETH_DHCP" ]; then - ETH_IFACE="${ETH_IFACE:-eth0}" - make_dhcp_iface "$ETH_IFACE" 10 -fi - -# If the ETH_ADDR is defined, configure a static address on eth0 -if [ -n "$ETH_ADDR" ]; then - ETH_IFACE="${ETH_IFACE:-eth0}" - make_static_iface "$ETH_IFACE" "$ETH_ADDR" "$ETH_GW" "$ETH_DNS" 10 -fi - - -# ========== Wi-Fi ========== - -# Set the regulatory domain for wifi, if defined. -if [ -n "$WIFI_REGDOM" ]; then - sed -i \ - -e 's/^\(WIRELESS_REGDOM=.*\)$/#\1/' \ - -e 's/^#\(WIRELESS_REGDOM="'"$WIFI_REGDOM"'"\)/\1/' \ - /etc/conf.d/wireless-regdom -fi - -# If the WIFI_ESSID is defined, configure wlan0 -if [ -n "$WIFI_ESSID" ]; then - WIFI_IFACE="${WIFI_IFACE:-wlan0}" - if [ -n "$WIFI_ADDR" ]; then - make_static_iface "$WIFI_IFACE" "$WIFI_ADDR" "$WIFI_GW" "$WIFI_DNS" 50 - else - make_dhcp_iface "$WIFI_IFACE" 50 - fi - wpa_passphrase "$WIFI_ESSID" "$WIFI_PASSWD" > "/etc/wpa_supplicant/wpa_supplicant-$WIFI_IFACE.conf" - chmod 640 "/etc/wpa_supplicant/wpa_supplicant-$WIFI_IFACE.conf" - if [ -n "$WIFI_HIDDEN" ]; then - sed -i -e 's/^}/\tscan_ssid=1\n}/g' "/etc/wpa_supplicant/wpa_supplicant-$WIFI_IFACE.conf" - fi - systemctl enable "wpa_supplicant@$WIFI_IFACE.service" || true - touch /boot/pikvm-reboot.txt -fi - - -# ========== Custom scripts ========== - -if [ -d /boot/pikvm-scripts.d ]; then - run-parts --regex='^.+$' /boot/pikvm-scripts.d || true -fi - - -# ========== Finish ========== -rm -f /boot/pikvm.txt - -if [ -f /boot/pikvm-reboot.txt ]; then - rm -f /boot/pikvm-reboot.txt - ro - echo "kvmd-bootconfig: Reboot after 5 seconds" | tee /dev/kmsg - sleep 2 - reboot - sleep 3 -else - ro - if [ -n "$has_oled" ]; then - # Critical tasks have completed so start kvmd-oled service as an indicator that on-boot tasks are complete - systemctl start kvmd-oled || true - fi -fi diff --git a/scripts/kvmd-certbot b/scripts/kvmd-certbot deleted file mode 100755 index ebc94e50..00000000 --- a/scripts/kvmd-certbot +++ /dev/null @@ -1,157 +0,0 @@ -#!/bin/bash -# ========================================================================== # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================== # - - -set -e -export LC_ALL=C - -if [ "$(whoami)" != root ]; then - echo "Only root can do that" - exit 1 -fi - -user=kvmd-certbot -web=/run/kvmd-certbot/webroot -cur=/var/lib/kvmd/pst/data/certbot/runroot -new=/var/lib/kvmd/pst/data/certbot/runroot.new -tmp=/tmp/kvmd-certbot/runroot - -cur_opts=("--config-dir=$cur/config" "--work-dir=$cur/work" "--logs-dir=$cur/logs") - - -function cleanup() { - rm -rf "$tmp" -} - -function create_tmp() { - mkdir "$tmp" # Acts as a lock - chown "$user:" "$tmp" - trap cleanup EXIT -} - -function ensure_runroot() { - kvmd-pstrun -- bash -c " - set -ex - mkdir -p '$cur' - chown -R '$user:' '$cur/..' - " -} - -function restart_if_running() { - if systemctl is-active --quiet "$2"; then - echo "=> systemctl $1 $2" - systemctl "$1" "$2" || true - fi -} - -function restart_if_running_nginx() { - restart_if_running reload kvmd-nginx -} - -function restart_if_running_cloud() { - restart_if_running reload kvmd-nginx -} - -function restart_if_running_vnc() { - restart_if_running restart kvmd-vnc -} - -case "$1" in - -h|--help|help) - create_tmp - sudo --preserve-env -u "$user" certbot "$@" "${cur_opts[@]}" - ;; - - --) - shift - create_tmp - ensure_runroot - sudo --preserve-env -u "$user" kvmd-pstrun -- certbot "$@" "${cur_opts[@]}" - ;; - - certonly|certonly_webroot) - webroot_opts=() - if [ "$1" == certonly_webroot ]; then - webroot_opts=(--webroot "--webroot-path=$web") - fi - shift - create_tmp - ensure_runroot - sudo --preserve-env -u "$user" kvmd-pstrun -- certbot certonly "$@" "${cur_opts[@]}" \ - "${webroot_opts[@]}" \ - --deploy-hook="/usr/bin/bash -c ' - set -ex - chmod 755 '$cur/config/'{archive,live} - chmod 640 \"\$RENEWED_LINEAGE/privkey.pem\" - '" - ;; - - renew) - shift - create_tmp - cp -a "$cur"/. "$tmp" - chown -R "$user:" "$tmp" - sed -s -i -e "s| = $cur/| = $tmp/|g" "$tmp/config/renewal/"* - sudo --preserve-env -u "$user" certbot renew "$@" \ - --config-dir="$tmp/config" \ - --work-dir="$tmp/work" \ - --logs-dir="$tmp/logs" \ - --deploy-hook="/usr/bin/touch '$tmp/updated'" || true - if [ -f "$tmp/updated" ]; then - sudo --preserve-env -u "$user" kvmd-pstrun -- bash -c " - set -ex - rm -rf '$new' - cp -a '$tmp'/. '$new' - rm '$new/updated' - chmod 755 '$new/config/'{archive,live} - chmod 640 '$new'/config/archive/*/privkey*.pem - sed -s -i -e 's| = $tmp/| = $cur/|g' '$new/config/renewal/'* - rm -rf '$new/logs/'*.log.* '$new/config/'{csr,keys} - sync - kvmd-helper-swapfiles '$new' '$cur' - rm -rf '$new' - " - restart_if_running_nginx - restart_if_running_vnc - fi - ;; - - install_nginx|install_vnc|install_cloud) - target="${1/install_/}" - if [ -z "$2" ]; then - echo "Usage: kvmd-certbot $1 " - exit 1 - fi - set -x - rm -f "/etc/kvmd/$target/ssl/server."{crt,key} - ln -s "$cur/config/live/$2/fullchain.pem" "/etc/kvmd/$target/ssl/server.crt" - ln -s "$cur/config/live/$2/privkey.pem" "/etc/kvmd/$target/ssl/server.key" - "restart_if_running_$target" - ;; - - *) - echo "This command is not implemented by kvmd-certbot." - echo "To pass it into certbot under PST context use '--'." - echo "For example: kvmd-certbot -- $*" - exit 1 - ;; -esac diff --git a/scripts/kvmd-htpasswd b/scripts/kvmd-htpasswd deleted file mode 100755 index ef94bf4b..00000000 --- a/scripts/kvmd-htpasswd +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -cd / && python3 -m kvmd.apps.htpasswd "$@" \ No newline at end of file diff --git a/scripts/kvmd-otg b/scripts/kvmd-otg deleted file mode 100755 index 6088326f..00000000 --- a/scripts/kvmd-otg +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -cd / && python3 -m kvmd.apps.otg "$@" \ No newline at end of file diff --git a/scripts/kvmd-otgconf b/scripts/kvmd-otgconf deleted file mode 100755 index d307fbf5..00000000 --- a/scripts/kvmd-otgconf +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -cd / && python3 -m kvmd.apps.otgconf "$@" \ No newline at end of file diff --git a/scripts/kvmd-totp b/scripts/kvmd-totp deleted file mode 100755 index 9e48110a..00000000 --- a/scripts/kvmd-totp +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -cd / && python3 -m kvmd.apps.totp "$@" \ No newline at end of file diff --git a/scripts/kvmd-udev-hdmiusb-check b/scripts/kvmd-udev-hdmiusb-check deleted file mode 100755 index 12c19204..00000000 --- a/scripts/kvmd-udev-hdmiusb-check +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -# ========================================================================== # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================== # - - -set -x - -# udevadm info -a -p $(udevadm info -q path -n /dev/video0) - -board="$1" -port="$2" - -# https://gist.github.com/mdevaev/6eb65be689142e5ac16da7542f50830f -model="$(tr < /proc/device-tree/model -d '\000')" - -case "$board" in - "rpi2") - case "$model" in - "Raspberry Pi 2 Model B Rev 1.1") - if [ "$port" == "1-1.5:1.0" ]; then exit 0; else exit 1; fi;; - *) exit 0;; - esac;; - "rpi3") - case "$model" in - "Raspberry Pi 3 Model B Rev 1.2") - if [ "$port" == "1-1.5:1.0" ]; then exit 0; else exit 1; fi;; - "Raspberry Pi 3 Model B Plus Rev 1.3") - if [ "$port" == "1-1.2:1.0" ]; then exit 0; else exit 1; fi;; - *) exit 0;; - esac;; - "rpi4") - case "$model" in - "Raspberry Pi 4 Model B Rev 1.1" | "Raspberry Pi 4 Model B Rev 1.2" | "Raspberry Pi 4 Model B Rev 1.4" | "Raspberry Pi 4 Model B Rev 1.5") - if [ "$port" == "1-1.4:1.0" ] || [ "$port" == "2-1:1.0" ]; then exit 0; else exit 1; fi;; - *) exit 0;; - esac;; - *) exit 1;; -esac diff --git a/scripts/kvmd-udev-restart-pass b/scripts/kvmd-udev-restart-pass deleted file mode 100755 index 4b77897a..00000000 --- a/scripts/kvmd-udev-restart-pass +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -# ========================================================================== # -# # -# KVMD - The main PiKVM daemon. # -# # -# Copyright (C) 2018-2024 Maxim Devaev # -# # -# 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 . # -# # -# ========================================================================== # - - -set -eE -set -x - -# https://forums.raspberrypi.com/viewtopic.php?p=2202017 - -card="$1" -conn_id="$2" - -test -n "$card" -test -n "$conn_id" - -card_n="${card/card/}" -test -n "$card_n" - -port=$(grep "^connector\[$conn_id\]:" "/sys/kernel/debug/dri/$card_n/state" | awk '{print $2}') -test -n "$port" - - -if [ "$port" = "HDMI-A-1" ]; then - status=$(head -n 1 "/sys/class/drm/$card-$port/status") - if [ "$status" = "connected" ]; then - for pid in $(pgrep -f '^kvmd/streamer: ' || true); do - kill "$pid" || true - done - fi -fi diff --git a/scripts/kvmd-vnc b/scripts/kvmd-vnc deleted file mode 100755 index cef9b59f..00000000 --- a/scripts/kvmd-vnc +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -cd / && python3 -m kvmd.apps.vnc "$@" \ No newline at end of file diff --git a/web/extras/.gitignore b/web/extras/.gitignore deleted file mode 100644 index e69de29b..00000000