diff --git a/images/chromium-headful/.dockerignore b/images/chromium-headful/.dockerignore new file mode 100644 index 00000000..3c66161a --- /dev/null +++ b/images/chromium-headful/.dockerignore @@ -0,0 +1 @@ +.unikraft/ diff --git a/images/chromium-headful/.gitignore b/images/chromium-headful/.gitignore index c249e748..2c717b1d 100644 --- a/images/chromium-headful/.gitignore +++ b/images/chromium-headful/.gitignore @@ -1,2 +1,3 @@ bin/ recording/ +.tmp/ diff --git a/images/chromium-headful/Dockerfile b/images/chromium-headful/Dockerfile index 2417149d..2cb8ad83 100644 --- a/images/chromium-headful/Dockerfile +++ b/images/chromium-headful/Dockerfile @@ -13,7 +13,7 @@ ENV DEBIAN_FRONTEND=noninteractive RUN set -eux; \ apt-get update; \ apt-get install -y \ - git gcc pkgconf autoconf automake libtool make xorg-dev xutils-dev \ + git gcc pkgconf autoconf automake libtool make xorg-dev xutils-dev \ && rm -rf /var/lib/apt/lists/*; COPY xorg-deps/ /xorg/ # build xf86-video-dummy v0.3.8 with RandR support @@ -97,12 +97,12 @@ ENV USERNAME=root RUN set -eux; \ apt-get update; \ apt-get install -y --no-install-recommends \ - wget ca-certificates python2 supervisor xclip \ - pulseaudio dbus-x11 xserver-xorg-video-dummy \ - libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx7 \ - gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ - gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \ - gstreamer1.0-pulseaudio gstreamer1.0-omx; \ + wget ca-certificates python2 supervisor xclip xdotool \ + pulseaudio dbus-x11 xserver-xorg-video-dummy \ + libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx7 \ + gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ + gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \ + gstreamer1.0-pulseaudio gstreamer1.0-omx; \ # # install libxcvt0 (not available in debian:bullseye) ARCH=$(dpkg --print-architecture); \ @@ -117,9 +117,9 @@ RUN set -eux; \ # # make directories for neko mkdir -p /etc/neko /var/www /var/log/neko \ - /tmp/runtime-$USERNAME \ - /home/$USERNAME/.config/pulse \ - /home/$USERNAME/.local/share/xorg; \ + /tmp/runtime-$USERNAME \ + /home/$USERNAME/.config/pulse \ + /home/$USERNAME/.local/share/xorg; \ chmod 1777 /var/log/neko; \ chown $USERNAME /var/log/neko/ /tmp/runtime-$USERNAME; \ chown -R $USERNAME:$USERNAME /home/$USERNAME; \ @@ -156,5 +156,8 @@ COPY ./wrapper.sh /wrapper.sh COPY bin/kernel-images-api /usr/local/bin/kernel-images-api ENV WITH_KERNEL_IMAGES_API=false +RUN useradd -m -s /bin/bash kernel +RUN cp -r ./user-data /home/kernel/user-data + ENTRYPOINT [ "/wrapper.sh" ] diff --git a/images/chromium-headful/Kraftfile b/images/chromium-headful/Kraftfile index f61eac29..32df4de0 100644 --- a/images/chromium-headful/Kraftfile +++ b/images/chromium-headful/Kraftfile @@ -1,6 +1,6 @@ spec: v0.6 -runtime: base-compat:latest +runtime: index.unikraft.io/official/base-compat:latest labels: cloud.unikraft.v1.instances/scale_to_zero.policy: "on" diff --git a/images/chromium-headful/build-unikernel.sh b/images/chromium-headful/build-unikernel.sh index 5af580c5..70a0c771 100755 --- a/images/chromium-headful/build-unikernel.sh +++ b/images/chromium-headful/build-unikernel.sh @@ -1,20 +1,15 @@ #!/usr/bin/env bash -# namespace here (onkernel) should match UKC_TOKEN's username -image="onkernel/kernel-cu-test:latest" +source common.sh +set -euo pipefail -# fail if UKC_TOKEN and UKC_METRO are not set -if [ -z "$UKC_TOKEN" ] || [ -z "$UKC_METRO" ]; then - echo "UKC_TOKEN and UKC_METRO must be set" - exit 1 -fi source ../../shared/start-buildkit.sh # Build the API binary source ../../shared/build-server.sh "$(pwd)/bin" kraft pkg \ - --name index.unikraft.io/$image \ + --name $UKC_INDEX/$IMAGE \ --plat kraftcloud --arch x86_64 \ --strategy overwrite \ --push \ diff --git a/images/chromium-headful/client/src/app.vue b/images/chromium-headful/client/src/app.vue index 63717ddc..f7d629c0 100644 --- a/images/chromium-headful/client/src/app.vue +++ b/images/chromium-headful/client/src/app.vue @@ -69,7 +69,9 @@ background: $background-tertiary; height: $menu-height; flex-shrink: 0; - display: flex; + // KERNEL: hide it + // display: flex; + display: none; } .video-container { @@ -85,7 +87,9 @@ max-width: 100%; flex-shrink: 0; flex-direction: column; - display: flex; + // KERNEL: hide it + // display: flex; + display: none; .room-menu { max-width: 100%; @@ -186,9 +190,9 @@ components: { 'neko-connect': Connect, 'neko-video': Video, - 'neko-menu': Menu, + // 'neko-menu': Menu, //'neko-side': Side, - 'neko-controls': Controls, + // 'neko-controls': Controls, //'neko-members': Members, //'neko-emotes': Emotes, //'neko-about': About, @@ -251,6 +255,59 @@ } } + // KERNEL: begin custom resolution, frame rate, and readOnly control via query params + + // Add a watcher so that when we are connected we can set the resolution from query params + @Watch('connected', { immediate: true }) + onConnected(value: boolean) { + if (value) { + this.applyQueryResolution() + } + } + + // Read ?width=, ?height=, and optional ?rate= (or their short aliases w/h/r) from the URL + // and set the resolution accordingly. If the current user is an admin we also request the + // server to switch to that resolution. + private applyQueryResolution() { + const params = new URL(location.href).searchParams + + // Helper to parse integer query parameters and return `undefined` when the value is not a valid number. + const parseIntSafe = (keys: string[], fallback?: number): number | undefined => { + for (const key of keys) { + const value = params.get(key) + if (value !== null) { + const num = parseInt(value, 10) + if (!isNaN(num)) return num + } + } + return fallback + } + + const width = parseIntSafe(['width', 'w']) + const height = parseIntSafe(['height', 'h']) + const rate = parseIntSafe(['rate', 'r'], 30) as number + + if (width !== undefined && height !== undefined) { + const resolution = { width, height, rate } + this.$accessor.video.setResolution(resolution) + if (this.$accessor.user && this.$accessor.user.admin) { + this.$accessor.video.screenSet(resolution) + } + } + + // Handle readOnly query param (e.g., ?readOnly=true or ?readonly=1) + const readOnlyParam = params.get('readOnly') || params.get('readonly') || params.get('ro') + const readOnly = typeof readOnlyParam === 'string' && ['1', 'true', 'yes'].includes(readOnlyParam.toLowerCase()) + if (readOnly) { + // Disable implicit hosting so the user doesn't automatically gain control + this.$accessor.remote.setImplicitHosting(false) + // Lock the session locally to block any input even if hosting is later requested + this.$accessor.remote.setLocked(true) + } + } + + // KERNEL: end custom resolution, frame rate, and readOnly control via query params + controlAttempt() { if (this.shakeKbd || this.$accessor.remote.hosted) return diff --git a/images/chromium-headful/client/src/components/video.vue b/images/chromium-headful/client/src/components/video.vue index 13aef000..4c4fe0f8 100644 --- a/images/chromium-headful/client/src/components/video.vue +++ b/images/chromium-headful/client/src/components/video.vue @@ -38,8 +38,10 @@