Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions images/chromium-headful/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptca
apt-get update; \
apt-get --no-install-recommends -y install \
wget ca-certificates python2 supervisor xclip xdotool \
pulseaudio dbus-x11 xserver-xorg-video-dummy \
pulseaudio dbus-x11 xserver-xorg-video-dummy rtkit upower \
libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx7 \
x11-xserver-utils \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
Expand All @@ -187,7 +187,8 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptca
/home/$USERNAME/.local/share/xorg; \
chmod 1777 /var/log/neko; \
chown $USERNAME /var/log/neko/ /tmp/runtime-$USERNAME; \
chown -R $USERNAME:$USERNAME /home/$USERNAME;
chown -R $USERNAME:$USERNAME /home/$USERNAME; \
chmod 777 /etc/pulse;

# install chromium and sqlite3 for debugging the cookies file
RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptcache \
Expand Down Expand Up @@ -225,6 +226,10 @@ ENV WITHDOCKER=true

COPY images/chromium-headful/xorg.conf /etc/neko/xorg.conf
COPY images/chromium-headful/neko.yaml /etc/neko/neko.yaml
COPY images/chromium-headful/default.pa /etc/pulse/default.pa
COPY images/chromium-headful/daemon.conf /etc/pulse/daemon.conf
COPY images/chromium-headful/dbus-pulseaudio.conf /etc/dbus-1/system.d/pulseaudio.conf
COPY images/chromium-headful/dbus-mpris.conf /etc/dbus-1/system.d/mpris.conf
COPY --from=neko /usr/bin/neko /usr/bin/neko
COPY --from=client /src/dist/ /var/www
COPY --from=xorg-deps /usr/local/lib/xorg/modules/drivers/dummy_drv.so /usr/lib/xorg/modules/drivers/dummy_drv.so
Expand All @@ -244,6 +249,11 @@ COPY --from=server-builder /out/chromium-launcher /usr/local/bin/chromium-launch
# Copy the Playwright executor runtime
COPY server/runtime/playwright-executor.ts /usr/local/lib/playwright-executor.ts

RUN useradd -m -s /bin/bash kernel
RUN useradd -m -s /bin/bash kernel && \
usermod -aG audio,video,pulse,pulse-access kernel

# Environment variables for audio
ENV XDG_RUNTIME_DIR=/tmp/runtime-kernel
ENV PULSE_SERVER=unix:/tmp/runtime-kernel/pulse/native

ENTRYPOINT [ "/wrapper.sh" ]
2 changes: 1 addition & 1 deletion images/chromium-headful/client/public/browserconfig.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#19bd9c</TileColor>
<TileColor>#7B42F6</TileColor>
</tile>
</msapplication>
</browserconfig>
6 changes: 3 additions & 3 deletions images/chromium-headful/client/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
<link rel="manifest" href="site.webmanifest">
<link rel="mask-icon" href="safari-pinned-tab.svg" color="#19bd9c">
<meta name="msapplication-TileColor" content="#19bd9c">
<meta name="theme-color" content="#19bd9c">
<link rel="mask-icon" href="safari-pinned-tab.svg" color="#7B42F6">
<meta name="msapplication-TileColor" content="#7B42F6">
<meta name="theme-color" content="#7B42F6">
<style> /* weird iOS bug, if this is not set right here, video just does not start */ .video-container { width: 100%; height: 100%; } </style>
</head>
<body>
Expand Down
4 changes: 4 additions & 0 deletions images/chromium-headful/client/public/kernel.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions images/chromium-headful/client/public/site.webmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"type": "image/png"
}
],
"theme_color": "#19bd9c",
"background_color": "#19bd9c",
"theme_color": "#7B42F6",
"background_color": "#7B42F6",
"display": "standalone"
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ $background-modifier-accent: hsla(0, 0%, 100%, 0.06);
$elevation-low: 0 1px 0 rgba(4, 4, 5, 0.2), 0 1.5px 0 rgba(6, 6, 7, 0.05), 0 2px 0 rgba(4, 4, 5, 0.05);
$elevation-high: 0 8px 16px rgba(0, 0, 0, 0.24);

$style-primary: #19bd9c;
$style-primary: #7B42F6;
$style-error: #d32f2f;

$menu-height: 40px;
Expand Down
40 changes: 12 additions & 28 deletions images/chromium-headful/client/src/components/connect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
</button>
</form>
<div class="loader" v-if="connecting">
<div class="bounce1"></div>
<div class="bounce2"></div>
<img src="/kernel.svg" class="spinning-logo" alt="Loading..." />
</div>
</div>
</div>
Expand Down Expand Up @@ -103,40 +102,25 @@
.loader {
width: 90px;
height: 90px;
position: relative;
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;

.bounce1,
.bounce2 {
.spinning-logo {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: $style-primary;
opacity: 0.6;
position: absolute;
top: 0;
left: 0;

-webkit-animation: bounce 2s infinite ease-in-out;
animation: bounce 2s infinite ease-in-out;
}

.bounce2 {
-webkit-animation-delay: -1s;
animation-delay: -1s;
animation: spin 2s linear infinite;
}
}
}

@keyframes bounce {
0%,
100% {
transform: scale(0);
-webkit-transform: scale(0);
@keyframes spin {
from {
transform: rotate(0deg);
}
50% {
transform: scale(1);
-webkit-transform: scale(1);
to {
transform: rotate(360deg);
}
}
}
Expand Down Expand Up @@ -221,4 +205,4 @@
this.$accessor.client.toggleAbout()
}
}
</script>
</script>
55 changes: 54 additions & 1 deletion images/chromium-headful/client/src/components/video.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div ref="component" class="video">
<div ref="player" class="player">
<div ref="player" class="player" :class="{ 'is-muted': muted }">
<div ref="container" class="player-container">
<video ref="video" playsinline />
<div class="emotes">
Expand Down Expand Up @@ -85,6 +85,19 @@
</template>

<style lang="scss" scoped>
/* KERNEL MODIFICATION: Keyframes for the mute indicator pulse effect */
@keyframes mute-pulse {
0% {
box-shadow: inset 0 0 0 2px rgba(255, 80, 80, 0.2);
}
50% {
box-shadow: inset 0 0 0 2px rgba(255, 80, 80, 0.4);
}
100% {
box-shadow: inset 0 0 0 2px rgba(255, 80, 80, 0.2);
}
}

.video {
width: 100%;
height: 100%;
Expand All @@ -96,6 +109,11 @@
align-items: center;
background: #000;

/* KERNEL MODIFICATION: Style for the mute indicator */
&.is-muted {
animation: mute-pulse 2s infinite;
}

.video-menu {
position: absolute;
right: 20px;
Expand Down Expand Up @@ -259,6 +277,10 @@
private fullscreen = false
private mutedOverlay = true

/* KERNEL MODIFICATION: State flag to ensure unmute happens only once. */
private hasInteracted = false
private unmuteHandler: (() => void) | null = null

get admin() {
return this.$accessor.user.admin
}
Expand Down Expand Up @@ -470,6 +492,23 @@
}
}

/* KERNEL MODIFICATION: Centralized one-time unmute logic. */
_unmuteOnFirstInteraction() {
if (this.hasInteracted || !this.muted) {
return
}

this.hasInteracted = true
this.unmute()
this.$accessor.video.setVolume(100)

// Clean up global listeners if they were set
if (this.unmuteHandler) {
document.documentElement.removeEventListener('mousedown', this.unmuteHandler)
document.documentElement.removeEventListener('keydown', this.unmuteHandler)
}
}

mounted() {
this._container.addEventListener('resize', this.onResize)
this.onVolumeChanged(this.volume)
Expand Down Expand Up @@ -533,12 +572,23 @@
this.$client.sendData('keyup', { key: this.keyMap(key) })
}
this.keyboard.listenTo(this._overlay)

/* KERNEL MODIFICATION: Set up listeners for the first interaction. */
this.unmuteHandler = this._unmuteOnFirstInteraction.bind(this)
document.documentElement.addEventListener('mousedown', this.unmuteHandler, { once: true })
document.documentElement.addEventListener('keydown', this.unmuteHandler, { once: true })
}

beforeDestroy() {
this.observer.disconnect()
this.$accessor.video.setPlayable(false)
/* Guacamole Keyboard does not provide destroy functions */

/* KERNEL MODIFICATION: Clean up listeners on component destruction. */
if (this.unmuteHandler) {
document.documentElement.removeEventListener('mousedown', this.unmuteHandler)
document.documentElement.removeEventListener('keydown', this.unmuteHandler)
}
}

get hasMacOSKbd() {
Expand Down Expand Up @@ -761,6 +811,9 @@
}

onMouseDown(e: MouseEvent) {
/* KERNEL MODIFICATION: Trigger unmute on first video click. */
this._unmuteOnFirstInteraction()

if (!this.hosting) {
this.$emit('control-attempt', e)
}
Expand Down
43 changes: 43 additions & 0 deletions images/chromium-headful/daemon.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# /etc/pulse/daemon.conf
# OPTIMIZED FOR LOW-LATENCY WEBRTC

# General settings
daemonize = no
fail = yes
allow-module-loading = yes
high-priority = no
realtime-scheduling = no
exit-idle-time = -1

# Disable SHM which triggered errors in container
enable-shm = no
; enable-memfd = yes

# Resource limits
rlimit-fsize = -1
rlimit-data = -1
rlimit-stack = -1
rlimit-core = -1
rlimit-as = -1
rlimit-rss = -1
rlimit-nproc = -1
rlimit-nofile = 256
rlimit-memlock = -1
rlimit-locks = -1
rlimit-sigpending = -1
rlimit-msgqueue = -1
rlimit-nice = 31
rlimit-rtprio = 9
rlimit-rttime = 200000

# Low-latency audio settings
default-sample-format = s16le
default-sample-rate = 48000
alternate-sample-rate = 44100
default-sample-channels = 2
default-channel-map = front-left,front-right
resample-method = speex-float-1

# Critical changes for low-latency
default-fragments = 5
default-fragment-size-msec = 5
27 changes: 27 additions & 0 deletions images/chromium-headful/dbus-mpris.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Allow any user to own the MPRIS service for Chromium and all its media player variants -->
<policy context="default">
<!-- Standard Chromium MPRIS service -->
<allow own_prefix="org.mpris.MediaPlayer2.chromium"/>
<!-- Chromium Snap package variant -->
<allow own_prefix="org.mpris.MediaPlayer2.chromium_chromium"/>
<!-- Chromium Flatpak variant -->
<allow own_prefix="org.mpris.MediaPlayer2.org.chromium.Chromium"/>
<!-- Chromium-browser (Debian/Ubuntu) -->
<allow own_prefix="org.mpris.MediaPlayer2.chromium-browser"/>
<!-- Chromium (generic, fallback) -->
<allow own_prefix="org.mpris.MediaPlayer2.Chromium"/>
<!-- Chrome (in case of Chrome being used) -->
<allow own_prefix="org.mpris.MediaPlayer2.google-chrome"/>
<allow own_prefix="org.mpris.MediaPlayer2.Google-chrome"/>
<allow own_prefix="org.mpris.MediaPlayer2.chrome"/>
<!-- Chromium-based Edge (for completeness) -->
<allow own_prefix="org.mpris.MediaPlayer2.microsoft-edge"/>
<!-- Brave (another Chromium-based browser) -->
<allow own_prefix="org.mpris.MediaPlayer2.brave"/>
<!-- Vivaldi (another Chromium-based browser) -->
<allow own_prefix="org.mpris.MediaPlayer2.vivaldi"/>
</policy>
</busconfig>
27 changes: 27 additions & 0 deletions images/chromium-headful/dbus-pulseaudio.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!--
This file allows the user in the 'pulse', 'pulse-access', or 'audio'
groups to own the PulseAudio D-Bus services.
-->

<!-- Policy for 'pulse' group -->
<policy group="pulse">
<allow own="org.PulseAudio1"/>
<allow own="org.pulseaudio.Server"/>
</policy>

<!-- Policy for 'pulse-access' group -->
<policy group="pulse-access">
<allow own="org.PulseAudio1"/>
<allow own="org.pulseaudio.Server"/>
</policy>

<!-- Policy for 'audio' group -->
<policy group="audio">
<allow own="org.PulseAudio1"/>
<allow own="org.pulseaudio.Server"/>
</policy>

</busconfig>
17 changes: 17 additions & 0 deletions images/chromium-headful/default.pa
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/pulseaudio -nF

### Create virtual output device sink
load-module module-null-sink sink_name=audio_output sink_properties=device.description="Virtual_Audio_Output"

### Create virtual input device sink
load-module module-null-sink sink_name=audio_input sink_properties=device.description="Virtual_Audio_Input"

### Create a virtual audio source linked up to the virtual input device
load-module module-virtual-source source_name=microphone master=audio_input.monitor source_properties=device.description="Virtual_Microphone"

### Allow pulse audio to be accessed via a unix socket
### Important to _not_ specify socket (socket=/tmp/runtime-kernel/pulse/native)
load-module module-native-protocol-unix auth-anonymous=1

### Make sure we always have a sink around, even if it is a null sink.
load-module module-always-sink
Loading
Loading