Compare commits
264 Commits
Author | SHA1 | Date | |
---|---|---|---|
b6e668bc53 | |||
b7950b3650 | |||
6ee8bb86fe | |||
f16ffba963 | |||
334f60ac7c | |||
9be75c20ca | |||
81006e02ed | |||
78bf9e276b | |||
ecf59a39bc | |||
def879c586 | |||
b808530f0c | |||
aae55ea585 | |||
89f4e697c2 | |||
523831237d | |||
313cd55049 | |||
9e466b08cc | |||
6708ce2caf | |||
0185178234 | |||
6fcf9ff07e | |||
c2fa40d3ff | |||
d27c329fac | |||
ab05b3124f | |||
4410956eae | |||
db66cb565c | |||
85d6205823 | |||
7cfc8c4795 | |||
c3abbfd577 | |||
46b8c9f42d | |||
609b3f0927 | |||
bc2fa6cd40 | |||
1bf588511a | |||
7d94ee603d | |||
eb057f1ab2 | |||
5606bfa174 | |||
122fd8b957 | |||
b9b80ed79a | |||
2cff2a340f | |||
429fc7eb68 | |||
ffcca402ef | |||
38bed98741 | |||
45ef058bf4 | |||
e5c59faf1a | |||
c392cad046 | |||
d41580050e | |||
02a38d8805 | |||
bf428c5154 | |||
86a9effe41 | |||
733c39412b | |||
7aa7e0eacb | |||
3a79615a2b | |||
da246345e1 | |||
1800d077d8 | |||
8612286c98 | |||
5c74d304ff | |||
2f3d0d9bbd | |||
3792be85cc | |||
0319455e55 | |||
57af427c12 | |||
201197f7d3 | |||
eca75dcd07 | |||
29359baf65 | |||
305271916c | |||
93cb227a1f | |||
7e2baf432f | |||
d46c9baade | |||
8177b23557 | |||
3dbb265ef3 | |||
35a092630e | |||
ce42d81eeb | |||
e47fa91381 | |||
0acd7f5150 | |||
43c484ee48 | |||
df245924cc | |||
01ba84a417 | |||
93d3e811e0 | |||
19c21cadd9 | |||
a8f0ddc468 | |||
28dab591a4 | |||
0be72c6507 | |||
fa97f96fd5 | |||
b420931055 | |||
64e673cbd7 | |||
b875e7be13 | |||
501ee120e8 | |||
6d38ea71b9 | |||
f8ba35119e | |||
4320a2b299 | |||
b13b1907f4 | |||
1ec8bd34a6 | |||
1307236f86 | |||
285d4b630b | |||
b169195b69 | |||
7f226842df | |||
9386cbb2e2 | |||
58cb161bf3 | |||
b2effce0e7 | |||
c54e8327ac | |||
07d111af36 | |||
1038dd109a | |||
82062637ae | |||
e88521f94e | |||
79e3e153bd | |||
be3453c37d | |||
e5ca4ac184 | |||
1a09442f26 | |||
89dd22727c | |||
d154e1f6bb | |||
9a437b63ff | |||
4589f758c6 | |||
7a61f7e2da | |||
83570a15ca | |||
f85d4d312f | |||
c7a178e5a4 | |||
29b4881c08 | |||
90bc4b3280 | |||
abd1a76965 | |||
a7ab37a4f5 | |||
450cfe134d | |||
cf06641f19 | |||
e27b98d509 | |||
cf401b4187 | |||
73ea442d18 | |||
7a1716ebd0 | |||
8e18465409 | |||
9d514206ca | |||
391417d1ea | |||
94f1cd5c38 | |||
0076637353 | |||
8a0d0bac0c | |||
6c4cc0cc89 | |||
48a6cd6ad5 | |||
3c92477a05 | |||
524e70f6c4 | |||
ce52331d68 | |||
5805cbdda5 | |||
657d0f55f4 | |||
c501b108de | |||
d530be49f3 | |||
5931123bb3 | |||
f9aaabd831 | |||
208735a305 | |||
56e153b277 | |||
6cd9446ec7 | |||
b264e397d8 | |||
fd4b4bbfa2 | |||
700d43710e | |||
faab040bd5 | |||
2867b42745 | |||
70860ab83c | |||
24699ec512 | |||
b6c032921d | |||
b2f60d36a5 | |||
67c931350b | |||
b6b1cd01ac | |||
631e8775fe | |||
653e32697f | |||
5082bab6de | |||
9cf824c5ea | |||
5ff441582d | |||
a287ca1188 | |||
afbd1f6f59 | |||
3989635b90 | |||
99d528f2ea | |||
78d2d706af | |||
78a1744da4 | |||
8efc5d7094 | |||
a1fcf87345 | |||
3cb4a1795b | |||
03c991ca79 | |||
7edd9c090f | |||
23b6c82241 | |||
b88f1750f7 | |||
2b90d42f75 | |||
733cd55c3c | |||
b0884352b8 | |||
22622325ed | |||
e01b60368c | |||
6699ff1c17 | |||
2ad760c987 | |||
1b52d6db20 | |||
0d54b71055 | |||
39f5c0e7fa | |||
b96ba47224 | |||
bbae073104 | |||
9771b551df | |||
bbea5f5715 | |||
ed2cdaf71d | |||
cb586df833 | |||
d77751dabc | |||
929f315f6e | |||
5762f33e36 | |||
a961dd6428 | |||
108ac79443 | |||
577f508912 | |||
7080f6adc7 | |||
19631d8ae9 | |||
14939db65a | |||
b75303aa30 | |||
79e817e0b2 | |||
69e7990afb | |||
afb416ea57 | |||
a5c90f6198 | |||
c980409e3d | |||
085806d1b2 | |||
aace4add5b | |||
d3a16ea212 | |||
d763895665 | |||
9e64b47dea | |||
f68b8fe922 | |||
6497109292 | |||
9b9a3ad4af | |||
577cd5110e | |||
cdeeff680b | |||
dc3bb4f837 | |||
15edc0f73e | |||
0e5e1faf35 | |||
69e0979aa0 | |||
377ecd7d06 | |||
6e2b5084e7 | |||
9c3d441d16 | |||
c410134c7d | |||
72b28d69e6 | |||
2782a1dd08 | |||
6057a73a08 | |||
afcd029e4d | |||
3dd48c91ac | |||
3160382015 | |||
1834186f40 | |||
56b1aa92f4 | |||
595259b30c | |||
321e52ee4f | |||
27740b0af0 | |||
646e8af042 | |||
e57fe5efac | |||
29fc67aff9 | |||
8ef91be6ad | |||
61eed9ca98 | |||
34954d1a54 | |||
f24c99f90c | |||
b1e358c0bc | |||
405ef7b9dd | |||
d53bb724b2 | |||
4795d3ac96 | |||
82cc13b680 | |||
a362df4976 | |||
00a785f4c5 | |||
762449e46c | |||
eddd59c49d | |||
61e6f62ff5 | |||
c2e57cf51a | |||
b8b43d69b0 | |||
a67d1d0e4f | |||
78fe08a029 | |||
3b51c02486 | |||
f7ae2f3b3c | |||
8c3a54c4d5 | |||
31cd834526 | |||
43dceb363f | |||
f0e04828e8 | |||
bac0686f20 | |||
c146534ae1 | |||
6a7327c238 | |||
1ce276f313 | |||
ee2f27f80a |
76
.github/workflows/build.yml
vendored
Normal file
76
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
name: "CI for builds"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ dev ]
|
||||
#
|
||||
# Run this action periodically to keep browsers up-to-date
|
||||
# even if there is no activity in this repo.
|
||||
#
|
||||
schedule:
|
||||
- cron: "43 2 * * 1"
|
||||
|
||||
env:
|
||||
DOCKER_IMAGE: m1k1o/neko
|
||||
|
||||
jobs:
|
||||
build-base:
|
||||
runs-on: ubuntu-latest
|
||||
#
|
||||
# do not run on forks
|
||||
#
|
||||
if: github.repository_owner == 'm1k1o'
|
||||
steps:
|
||||
- name: Check Out Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
run: |
|
||||
docker login --username "${DOCKER_USERNAME}" --password-stdin "${DOCKER_REGISTRY}" <<< "${DOCKER_TOKEN}"
|
||||
env:
|
||||
DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Build base
|
||||
run: |
|
||||
BUILD_IMAGE=${DOCKER_IMAGE} .m1k1o/build ${DOCKER_TAG}
|
||||
docker push ${DOCKER_IMAGE}:${DOCKER_TAG}
|
||||
env:
|
||||
DOCKER_TAG: base
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
#
|
||||
# do not run on forks
|
||||
#
|
||||
if: github.repository_owner == 'm1k1o'
|
||||
needs: [ build-base ]
|
||||
strategy:
|
||||
matrix:
|
||||
tags: [ firefox, chromium, google-chrome, ungoogled-chromium, brave, tor-browser, vncviewer, vlc, xfce ]
|
||||
env:
|
||||
DOCKER_TAG: ${{ matrix.tags }}
|
||||
steps:
|
||||
- name: Check Out Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
run: |
|
||||
docker login --username "${DOCKER_USERNAME}" --password-stdin "${DOCKER_REGISTRY}" <<< "${DOCKER_TOKEN}"
|
||||
env:
|
||||
DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Build container
|
||||
run: |
|
||||
BUILD_IMAGE=${DOCKER_IMAGE} .m1k1o/build ${DOCKER_TAG}
|
||||
docker push ${DOCKER_IMAGE}:${DOCKER_TAG}
|
||||
|
||||
- name: Push latest tag
|
||||
if: ${{ matrix.tags == 'firefox' }}
|
||||
run: |
|
||||
docker pull ${DOCKER_IMAGE}:${DOCKER_TAG}
|
||||
docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest
|
||||
docker push ${DOCKER_IMAGE}:latest
|
18
.m1k1o/.env.default
Normal file
18
.m1k1o/.env.default
Normal file
@ -0,0 +1,18 @@
|
||||
#
|
||||
# you can copy this file to .env.local, if you don't want to have it pushed to repository
|
||||
#
|
||||
|
||||
# this is how will be your images called. you can change it to your fork.
|
||||
# only need to do this once. here.
|
||||
BUILD_IMAGE="m1k1o/neko"
|
||||
|
||||
# this is where your services will be acessible
|
||||
CLIENT_PORT=8080
|
||||
SERVER_PORT=8081
|
||||
|
||||
# on which image you want to test it
|
||||
SERVER_TAG="chromium"
|
||||
|
||||
# this is needed for WebRTC. specify your local IP address and free UDP port range.
|
||||
SERVER_EPR=55000-55009
|
||||
SERVER_IP=10.8.0.1
|
40
.m1k1o/README.md
Normal file
40
.m1k1o/README.md
Normal file
@ -0,0 +1,40 @@
|
||||
# How to contribute to neko
|
||||
|
||||
If you want to contribute, but do not want to install anything on your host system, we got you covered. You only need docker. Technically, it could be done using vs code development in container, but this is more fun:).
|
||||
|
||||
You need to copy `.env.development` to `.env` and customize values.
|
||||
|
||||
## Step 1: Building server
|
||||
|
||||
- `./build` - You can use this command to build your specified `SERVER_TAG` along with base image.
|
||||
|
||||
If you want, you can build other tags. `base` tag needs to be build first:
|
||||
|
||||
- `./build base`
|
||||
- `./build firefox`
|
||||
- `./build chromium`
|
||||
- `./build google-chrome`
|
||||
- etc...
|
||||
|
||||
## Step 2: Starting server
|
||||
|
||||
- `./start-server` - Starting server image you specified in `.env`.
|
||||
- `./start-server -r` - Shortcut for rebuilding server binary and then starting.
|
||||
|
||||
If you are changing something in the server code, you do not want to rebuild container each time. You can just rebuild your binary:
|
||||
|
||||
- `./rebuild-server` - Rebuild only server binary.
|
||||
- `./rebuild-server -f` - Force to rebuild whole Golang environment (you should do this only of you change some dependencies).
|
||||
|
||||
## Step 3: Serving client
|
||||
|
||||
- `./serve-client` - Serving vue.js client.
|
||||
- `./serve-client -i` - Install all dependencies.
|
||||
|
||||
## Debug
|
||||
|
||||
You can navigate to `CLIENT_PORT` and see live client there. It will be connected to your local server on `SERVER_PORT`.
|
||||
|
||||
If you are leaving client as is and not changing it, you don't need to start `./serve-client` and you can access server's GUI directly on `SERVER_PORT`.
|
||||
|
||||
Feel free to open new PR.
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# STAGE 1: SERVER
|
||||
#
|
||||
FROM golang:1.15-buster as server
|
||||
FROM golang:1.17-bullseye as server
|
||||
WORKDIR /src
|
||||
|
||||
#
|
||||
@ -27,12 +27,12 @@ RUN set -eux; apt-get update; \
|
||||
#
|
||||
# build server
|
||||
COPY server/ .
|
||||
RUN go get -v -t -d . && go build -o bin/neko -i cmd/neko/main.go
|
||||
RUN go get -v -t -d . && go build -o bin/neko cmd/neko/main.go
|
||||
|
||||
#
|
||||
# STAGE 2: CLIENT
|
||||
#
|
||||
FROM node:12-buster-slim as client
|
||||
FROM node:14-bullseye-slim as client
|
||||
WORKDIR /src
|
||||
|
||||
#
|
||||
@ -48,7 +48,7 @@ RUN npm run build
|
||||
#
|
||||
# STAGE 3: RUNTIME
|
||||
#
|
||||
FROM debian:buster-slim
|
||||
FROM debian:bullseye-slim
|
||||
|
||||
#
|
||||
# avoid warnings by switching to noninteractive
|
||||
@ -65,7 +65,7 @@ ARG USER_GID=$USER_UID
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends wget ca-certificates supervisor; \
|
||||
apt-get install -y --no-install-recommends pulseaudio dbus-x11 xserver-xorg-video-dummy; \
|
||||
apt-get install -y --no-install-recommends libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx5; \
|
||||
apt-get install -y --no-install-recommends libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx6; \
|
||||
#
|
||||
# gst
|
||||
apt-get install -y --no-install-recommends libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
|
||||
@ -117,6 +117,9 @@ ENV NEKO_BIND=:8080
|
||||
COPY --from=server /src/bin/neko /usr/bin/neko
|
||||
COPY --from=client /src/dist/ /var/www
|
||||
|
||||
HEALTHCHECK --interval=10s --timeout=5s --retries=8 \
|
||||
CMD wget -O - http://localhost:${NEKO_BIND#*:}/health || exit 1
|
||||
|
||||
#
|
||||
# run neko
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"]
|
||||
|
136
.m1k1o/base/Dockerfile.arm
Normal file
136
.m1k1o/base/Dockerfile.arm
Normal file
@ -0,0 +1,136 @@
|
||||
#
|
||||
# STAGE 1: SERVER
|
||||
#
|
||||
FROM arm32v7/golang:1.17-buster as server
|
||||
WORKDIR /src
|
||||
|
||||
#
|
||||
# install dependencies
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends git cmake make python2 libx11-dev libxrandr-dev libxtst-dev \
|
||||
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-omx; \
|
||||
#
|
||||
# install libclipboard
|
||||
set -eux; \
|
||||
cd /tmp; \
|
||||
git clone https://github.com/jtanx/libclipboard; \
|
||||
cd libclipboard; \
|
||||
cmake .; \
|
||||
make -j4; \
|
||||
make install; \
|
||||
rm -rf /tmp/libclipboard; \
|
||||
#
|
||||
# clean up
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
#
|
||||
# build server
|
||||
COPY server/ .
|
||||
RUN go get -v -t -d . && go build -o bin/neko cmd/neko/main.go
|
||||
|
||||
#
|
||||
# STAGE 2: CLIENT
|
||||
#
|
||||
FROM node:14-buster-slim as client
|
||||
|
||||
# install dependencies
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends python2 build-essential
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
#
|
||||
# install dependencies
|
||||
COPY client/package*.json ./
|
||||
RUN npm install
|
||||
|
||||
#
|
||||
# build client
|
||||
COPY client/ .
|
||||
RUN npm run build
|
||||
|
||||
#
|
||||
# STAGE 3: RUNTIME
|
||||
#
|
||||
FROM balenalib/raspberry-pi-debian:latest
|
||||
|
||||
#
|
||||
# avoid warnings by switching to noninteractive
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
#
|
||||
# set custom user
|
||||
ARG USERNAME=neko
|
||||
ARG USER_UID=1000
|
||||
ARG USER_GID=$USER_UID
|
||||
|
||||
#
|
||||
# install dependencies
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends wget ca-certificates supervisor; \
|
||||
apt-get install -y --no-install-recommends pulseaudio dbus-x11 xserver-xorg-video-dummy; \
|
||||
apt-get install -y --no-install-recommends libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx5; \
|
||||
#
|
||||
# gst
|
||||
apt-get install -y --no-install-recommends libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
|
||||
gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-pulseaudio gstreamer1.0-omx; \
|
||||
#
|
||||
# create a non-root user
|
||||
groupadd --gid $USER_GID $USERNAME; \
|
||||
useradd --uid $USER_UID --gid $USERNAME --shell /bin/bash --create-home $USERNAME; \
|
||||
adduser $USERNAME audio; \
|
||||
adduser $USERNAME video; \
|
||||
adduser $USERNAME pulse; \
|
||||
#
|
||||
# setup pulseaudio
|
||||
mkdir -p /home/$USERNAME/.config/pulse/; \
|
||||
echo "default-server=unix:/tmp/pulseaudio.socket" > /home/$USERNAME/.config/pulse/client.conf; \
|
||||
#
|
||||
# workaround for an X11 problem: http://blog.tigerteufel.de/?p=476
|
||||
mkdir /tmp/.X11-unix; \
|
||||
chmod 1777 /tmp/.X11-unix; \
|
||||
chown $USERNAME /tmp/.X11-unix/; \
|
||||
#
|
||||
# make directories for neko
|
||||
mkdir -p /etc/neko /var/www /var/log/neko; \
|
||||
chmod 1777 /var/log/neko; \
|
||||
chown $USERNAME /var/log/neko/; \
|
||||
chown -R $USERNAME:$USERNAME /home/$USERNAME; \
|
||||
#
|
||||
# clean up
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
#
|
||||
# copy config files
|
||||
COPY .m1k1o/base/dbus /usr/bin/dbus
|
||||
COPY .m1k1o/base/default.pa /etc/pulse/default.pa
|
||||
COPY .m1k1o/base/supervisord.conf /etc/neko/supervisord.conf
|
||||
COPY .m1k1o/base/xorg.conf /etc/neko/xorg.conf
|
||||
|
||||
#
|
||||
# set default envs
|
||||
ENV USER=$USERNAME
|
||||
ENV DISPLAY=:99.0
|
||||
ENV NEKO_PASSWORD=neko
|
||||
ENV NEKO_PASSWORD_ADMIN=admin
|
||||
ENV NEKO_BIND=:8080
|
||||
|
||||
#
|
||||
# custom arm values -> video pipeline with GPU encoding
|
||||
ENV NEKO_H264=1
|
||||
ENV NEKO_VIDEO='ximagesrc display-name=%s use-damage=0 show-pointer=true use-damage=false ! video/x-raw,framerate=30/1 ! videoconvert ! queue ! video/x-raw,framerate=30/1,format=NV12 ! v4l2h264enc extra-controls="controls,h264_profile=0,video_bitrate=1250000;" ! h264parse config-interval=3 ! video/x-h264,profile=baseline,stream-format=byte-stream'
|
||||
|
||||
#
|
||||
# copy static files from previous stages
|
||||
COPY --from=server /src/bin/neko /usr/bin/neko
|
||||
COPY --from=client /src/dist/ /var/www
|
||||
|
||||
HEALTHCHECK --interval=10s --timeout=5s --retries=8 \
|
||||
CMD wget -O - http://localhost:${NEKO_BIND#*:}/health || exit 1
|
||||
|
||||
#
|
||||
# run neko
|
||||
CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"]
|
||||
|
@ -1,5 +1,6 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
user=root
|
||||
pidfile=/var/run/supervisord.pid
|
||||
logfile=/dev/null
|
||||
logfile_maxbytes=0
|
||||
@ -44,6 +45,8 @@ redirect_stderr=true
|
||||
[program:neko]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/neko serve --static "/var/www"
|
||||
stopsignal=INT
|
||||
stopwaitsecs=5
|
||||
autorestart=true
|
||||
priority=800
|
||||
user=%(ENV_USER)s
|
||||
|
23
.m1k1o/brave/Dockerfile
Normal file
23
.m1k1o/brave/Dockerfile
Normal file
@ -0,0 +1,23 @@
|
||||
ARG BASE_IMAGE=m1k1o/neko:base
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends apt-transport-https curl openbox; \
|
||||
#
|
||||
# install brave browser
|
||||
curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg; \
|
||||
echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg arch=amd64] https://brave-browser-apt-release.s3.brave.com/ stable main" \
|
||||
| tee /etc/apt/sources.list.d/brave-browser-release.list; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends brave-browser; \
|
||||
#
|
||||
# clean up
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
#
|
||||
# copy configuation files
|
||||
COPY supervisord.conf /etc/neko/supervisord/brave.conf
|
||||
COPY --chown=neko preferences.json /home/neko/.config/brave/Default/Preferences
|
||||
COPY policies.json /etc/brave/policies/managed/policies.json
|
||||
COPY openbox.xml /etc/neko/openbox.xml
|
763
.m1k1o/brave/openbox.xml
Normal file
763
.m1k1o/brave/openbox.xml
Normal file
@ -0,0 +1,763 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- Default openbox config but all window decorations are moved
|
||||
thereby making it harder to accidentally close the virtual browser -->
|
||||
|
||||
<openbox_config xmlns="http://openbox.org/3.4/rc"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<resistance>
|
||||
<strength>10</strength>
|
||||
<screen_edge_strength>20</screen_edge_strength>
|
||||
</resistance>
|
||||
|
||||
<applications>
|
||||
<!-- Match all windows and remove their decorations (obxprop | grep "^_OB_APP") -->
|
||||
<application class="Brave-browser*" name="brave-browser*">
|
||||
<decor>no</decor>
|
||||
<maximized>true</maximized>
|
||||
<focus>yes</focus>
|
||||
<layer>normal</layer>
|
||||
</application>
|
||||
</applications>
|
||||
|
||||
<focus>
|
||||
<focusNew>yes</focusNew>
|
||||
<!-- always try to focus new windows when they appear. other rules do
|
||||
apply -->
|
||||
<followMouse>no</followMouse>
|
||||
<!-- move focus to a window when you move the mouse into it -->
|
||||
<focusLast>yes</focusLast>
|
||||
<!-- focus the last used window when changing desktops, instead of the one
|
||||
under the mouse pointer. when followMouse is enabled -->
|
||||
<underMouse>no</underMouse>
|
||||
<!-- move focus under the mouse, even when the mouse is not moving -->
|
||||
<focusDelay>200</focusDelay>
|
||||
<!-- when followMouse is enabled, the mouse must be inside the window for
|
||||
this many milliseconds (1000 = 1 sec) before moving focus to it -->
|
||||
<raiseOnFocus>no</raiseOnFocus>
|
||||
<!-- when followMouse is enabled, and a window is given focus by moving the
|
||||
mouse into it, also raise the window -->
|
||||
</focus>
|
||||
|
||||
<placement>
|
||||
<policy>Smart</policy>
|
||||
<!-- 'Smart' or 'UnderMouse' -->
|
||||
<center>yes</center>
|
||||
<!-- whether to place windows in the center of the free area found or
|
||||
the top left corner -->
|
||||
<monitor>Primary</monitor>
|
||||
<!-- with Smart placement on a multi-monitor system, try to place new windows
|
||||
on: 'Any' - any monitor, 'Mouse' - where the mouse is, 'Active' - where
|
||||
the active window is, 'Primary' - only on the primary monitor -->
|
||||
<primaryMonitor>1</primaryMonitor>
|
||||
<!-- The monitor where Openbox should place popup dialogs such as the
|
||||
focus cycling popup, or the desktop switch popup. It can be an index
|
||||
from 1, specifying a particular monitor. Or it can be one of the
|
||||
following: 'Mouse' - where the mouse is, or
|
||||
'Active' - where the active window is -->
|
||||
</placement>
|
||||
|
||||
<theme>
|
||||
<name>Clearlooks</name>
|
||||
<titleLayout>NLIMC</titleLayout>
|
||||
<!--
|
||||
available characters are NDSLIMC, each can occur at most once.
|
||||
N: window icon
|
||||
L: window label (AKA title).
|
||||
I: iconify
|
||||
M: maximize
|
||||
C: close
|
||||
S: shade (roll up/down)
|
||||
D: omnipresent (on all desktops).
|
||||
-->
|
||||
<keepBorder>yes</keepBorder>
|
||||
<animateIconify>yes</animateIconify>
|
||||
<font place="ActiveWindow">
|
||||
<name>sans</name>
|
||||
<size>8</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="InactiveWindow">
|
||||
<name>sans</name>
|
||||
<size>8</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="MenuHeader">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>normal</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="MenuItem">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>normal</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="ActiveOnScreenDisplay">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="InactiveOnScreenDisplay">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
</theme>
|
||||
|
||||
<desktops>
|
||||
<!-- this stuff is only used at startup, pagers allow you to change them
|
||||
during a session
|
||||
|
||||
these are default values to use when other ones are not already set
|
||||
by other applications, or saved in your session
|
||||
|
||||
use obconf if you want to change these without having to log out
|
||||
and back in -->
|
||||
<number>1</number>
|
||||
<firstdesk>1</firstdesk>
|
||||
<names>
|
||||
<!-- set names up here if you want to, like this:
|
||||
<name>desktop 1</name>
|
||||
<name>desktop 2</name>
|
||||
-->
|
||||
</names>
|
||||
<popupTime>875</popupTime>
|
||||
<!-- The number of milliseconds to show the popup for when switching
|
||||
desktops. Set this to 0 to disable the popup. -->
|
||||
</desktops>
|
||||
|
||||
<resize>
|
||||
<drawContents>yes</drawContents>
|
||||
<popupShow>Nonpixel</popupShow>
|
||||
<!-- 'Always', 'Never', or 'Nonpixel' (xterms and such) -->
|
||||
<popupPosition>Center</popupPosition>
|
||||
<!-- 'Center', 'Top', or 'Fixed' -->
|
||||
<popupFixedPosition>
|
||||
<!-- these are used if popupPosition is set to 'Fixed' -->
|
||||
|
||||
<x>10</x>
|
||||
<!-- positive number for distance from left edge, negative number for
|
||||
distance from right edge, or 'Center' -->
|
||||
<y>10</y>
|
||||
<!-- positive number for distance from top edge, negative number for
|
||||
distance from bottom edge, or 'Center' -->
|
||||
</popupFixedPosition>
|
||||
</resize>
|
||||
|
||||
<!-- You can reserve a portion of your screen where windows will not cover when
|
||||
they are maximized, or when they are initially placed.
|
||||
Many programs reserve space automatically, but you can use this in other
|
||||
cases. -->
|
||||
<margins>
|
||||
<top>0</top>
|
||||
<bottom>0</bottom>
|
||||
<left>0</left>
|
||||
<right>0</right>
|
||||
</margins>
|
||||
|
||||
<dock>
|
||||
<position>TopLeft</position>
|
||||
<!-- (Top|Bottom)(Left|Right|)|Top|Bottom|Left|Right|Floating -->
|
||||
<floatingX>0</floatingX>
|
||||
<floatingY>0</floatingY>
|
||||
<noStrut>no</noStrut>
|
||||
<stacking>Above</stacking>
|
||||
<!-- 'Above', 'Normal', or 'Below' -->
|
||||
<direction>Vertical</direction>
|
||||
<!-- 'Vertical' or 'Horizontal' -->
|
||||
<autoHide>no</autoHide>
|
||||
<hideDelay>300</hideDelay>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<showDelay>300</showDelay>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<moveButton>Middle</moveButton>
|
||||
<!-- 'Left', 'Middle', 'Right' -->
|
||||
</dock>
|
||||
|
||||
<keyboard>
|
||||
<chainQuitKey>C-g</chainQuitKey>
|
||||
|
||||
<!-- Keybindings for desktop switching -->
|
||||
<keybind key="C-A-Left">
|
||||
<action name="GoToDesktop"><to>left</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Right">
|
||||
<action name="GoToDesktop"><to>right</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Up">
|
||||
<action name="GoToDesktop"><to>up</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Down">
|
||||
<action name="GoToDesktop"><to>down</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Left">
|
||||
<action name="SendToDesktop"><to>left</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Right">
|
||||
<action name="SendToDesktop"><to>right</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Up">
|
||||
<action name="SendToDesktop"><to>up</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Down">
|
||||
<action name="SendToDesktop"><to>down</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="W-F1">
|
||||
<action name="GoToDesktop"><to>1</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F2">
|
||||
<action name="GoToDesktop"><to>2</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F3">
|
||||
<action name="GoToDesktop"><to>3</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F4">
|
||||
<action name="GoToDesktop"><to>4</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-d">
|
||||
<action name="ToggleShowDesktop"/>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for windows -->
|
||||
<keybind key="A-F4">
|
||||
<action name="Close"/>
|
||||
</keybind>
|
||||
<keybind key="A-Escape">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</keybind>
|
||||
<keybind key="A-space">
|
||||
<!--action name="ShowMenu"><menu>client-menu</menu></action-->
|
||||
</keybind>
|
||||
<!-- Take a screenshot of the current window with scrot when Alt+Print are pressed -->
|
||||
<keybind key="A-Print">
|
||||
<action name="Execute"><command>scrot -s</command></action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for window switching -->
|
||||
<keybind key="A-Tab">
|
||||
<action name="NextWindow">
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="A-S-Tab">
|
||||
<action name="PreviousWindow">
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Tab">
|
||||
<action name="NextWindow">
|
||||
<panels>yes</panels><desktop>yes</desktop>
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for window switching with the arrow keys -->
|
||||
<keybind key="W-S-Right">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>right</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Left">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>left</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Up">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>up</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Down">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>down</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for running applications -->
|
||||
<keybind key="W-e">
|
||||
<action name="Execute">
|
||||
<startupnotify>
|
||||
<enabled>true</enabled>
|
||||
<name>Konqueror</name>
|
||||
</startupnotify>
|
||||
<command>kfmclient openProfile filemanagement</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<!-- Launch scrot when Print is pressed -->
|
||||
<keybind key="Print">
|
||||
<action name="Execute"><command>scrot</command></action>
|
||||
</keybind>
|
||||
</keyboard>
|
||||
|
||||
<mouse>
|
||||
<dragThreshold>1</dragThreshold>
|
||||
<!-- number of pixels the mouse must move before a drag begins -->
|
||||
<doubleClickTime>500</doubleClickTime>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<screenEdgeWarpTime>400</screenEdgeWarpTime>
|
||||
<!-- Time before changing desktops when the pointer touches the edge of the
|
||||
screen while moving a window, in milliseconds (1000 = 1 second).
|
||||
Set this to 0 to disable warping -->
|
||||
<screenEdgeWarpMouse>false</screenEdgeWarpMouse>
|
||||
<!-- Set this to TRUE to move the mouse pointer across the desktop when
|
||||
switching due to hitting the edge of the screen -->
|
||||
|
||||
<context name="Frame">
|
||||
<mousebind button="A-Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Left" action="Click">
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Left" action="Drag">
|
||||
<action name="Move"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Right" action="Drag">
|
||||
<action name="Resize"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Middle" action="Press">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-S-Up" action="Click">
|
||||
<action name="SendToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-S-Down" action="Click">
|
||||
<action name="SendToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Titlebar">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Move"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="DoubleClick">
|
||||
<action name="ToggleMaximize"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="if">
|
||||
<shaded>no</shaded>
|
||||
<then>
|
||||
<action name="Shade"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
<action name="Lower"/>
|
||||
</then>
|
||||
</action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="if">
|
||||
<shaded>yes</shaded>
|
||||
<then>
|
||||
<action name="Unshade"/>
|
||||
<action name="Raise"/>
|
||||
</then>
|
||||
</action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Titlebar Top Right Bottom Left TLCorner TRCorner BRCorner BLCorner">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</mousebind>
|
||||
|
||||
<!--mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="Top">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>top</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Left">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>left</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Right">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>right</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Bottom">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>bottom</edge></action>
|
||||
</mousebind>
|
||||
|
||||
<!--mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="TRCorner BRCorner TLCorner BLCorner">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Client">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Icon">
|
||||
<!--mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="AllDesktops">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleOmnipresent"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Shade">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleShade"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Iconify">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="Iconify"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Maximize">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleMaximize"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Click">
|
||||
<action name="ToggleMaximize"><direction>vertical</direction></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Click">
|
||||
<action name="ToggleMaximize"><direction>horizontal</direction></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Close">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="Close"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Desktop">
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Root">
|
||||
<!-- Menus -->
|
||||
<!--mousebind button="Middle" action="Press">
|
||||
<action name="ShowMenu"><menu>client-list-combined-menu</menu></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="ShowMenu"><menu>root-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="MoveResize">
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
</mouse>
|
||||
|
||||
<menu>
|
||||
<!-- You can specify more than one menu file in here and they are all loaded,
|
||||
just don't make menu ids clash or, well, it'll be kind of pointless -->
|
||||
|
||||
<!-- default menu file (or custom one in $HOME/.config/openbox/) -->
|
||||
<!-- system menu files on Debian systems -->
|
||||
<!--file>/var/lib/openbox/debian-menu.xml</file-->
|
||||
<file>menu.xml</file>
|
||||
<hideDelay>200</hideDelay>
|
||||
<!-- if a press-release lasts longer than this setting (in milliseconds), the
|
||||
menu is hidden again -->
|
||||
<middle>no</middle>
|
||||
<!-- center submenus vertically about the parent entry -->
|
||||
<submenuShowDelay>100</submenuShowDelay>
|
||||
<!-- time to delay before showing a submenu after hovering over the parent
|
||||
entry.
|
||||
if this is a negative value, then the delay is infinite and the
|
||||
submenu will not be shown until it is clicked on -->
|
||||
<submenuHideDelay>400</submenuHideDelay>
|
||||
<!-- time to delay before hiding a submenu when selecting another
|
||||
entry in parent menu
|
||||
if this is a negative value, then the delay is infinite and the
|
||||
submenu will not be hidden until a different submenu is opened -->
|
||||
<showIcons>yes</showIcons>
|
||||
<!-- controls if icons appear in the client-list-(combined-)menu -->
|
||||
<manageDesktops>yes</manageDesktops>
|
||||
<!-- show the manage desktops section in the client-list-(combined-)menu -->
|
||||
</menu>
|
||||
|
||||
<applications>
|
||||
<!--
|
||||
# this is an example with comments through out. use these to make your
|
||||
# own rules, but without the comments of course.
|
||||
# you may use one or more of the name/class/role/title/type rules to specify
|
||||
# windows to match
|
||||
|
||||
<application name="the window's _OB_APP_NAME property (see obxprop)"
|
||||
class="the window's _OB_APP_CLASS property (see obxprop)"
|
||||
groupname="the window's _OB_APP_GROUP_NAME property (see obxprop)"
|
||||
groupclass="the window's _OB_APP_GROUP_CLASS property (see obxprop)"
|
||||
role="the window's _OB_APP_ROLE property (see obxprop)"
|
||||
title="the window's _OB_APP_TITLE property (see obxprop)"
|
||||
type="the window's _OB_APP_TYPE property (see obxprob)..
|
||||
(if unspecified, then it is 'dialog' for child windows)">
|
||||
# you may set only one of name/class/role/title/type, or you may use more
|
||||
# than one together to restrict your matches.
|
||||
|
||||
# the name, class, role, and title use simple wildcard matching such as those
|
||||
# used by a shell. you can use * to match any characters and ? to match
|
||||
# any single character.
|
||||
|
||||
# the type is one of: normal, dialog, splash, utility, menu, toolbar, dock,
|
||||
# or desktop
|
||||
|
||||
# when multiple rules match a window, they will all be applied, in the
|
||||
# order that they appear in this list
|
||||
|
||||
|
||||
# each rule element can be left out or set to 'default' to specify to not
|
||||
# change that attribute of the window
|
||||
|
||||
<decor>yes</decor>
|
||||
# enable or disable window decorations
|
||||
|
||||
<shade>no</shade>
|
||||
# make the window shaded when it appears, or not
|
||||
|
||||
<position force="no">
|
||||
# the position is only used if both an x and y coordinate are provided
|
||||
# (and not set to 'default')
|
||||
# when force is "yes", then the window will be placed here even if it
|
||||
# says you want it placed elsewhere. this is to override buggy
|
||||
# applications who refuse to behave
|
||||
<x>center</x>
|
||||
# a number like 50, or 'center' to center on screen. use a negative number
|
||||
# to start from the right (or bottom for <y>), ie -50 is 50 pixels from
|
||||
# the right edge (or bottom). use 'default' to specify using value
|
||||
# provided by the application, or chosen by openbox, instead.
|
||||
<y>200</y>
|
||||
<monitor>1</monitor>
|
||||
# specifies the monitor in a xinerama setup.
|
||||
# 1 is the first head, or 'mouse' for wherever the mouse is
|
||||
</position>
|
||||
|
||||
<size>
|
||||
# the size to make the window.
|
||||
<width>20</width>
|
||||
# a number like 20, or 'default' to use the size given by the application.
|
||||
# you can use fractions such as 1/2 or percentages such as 75% in which
|
||||
# case the value is relative to the size of the monitor that the window
|
||||
# appears on.
|
||||
<height>30%</height>
|
||||
</size>
|
||||
|
||||
<focus>yes</focus>
|
||||
# if the window should try be given focus when it appears. if this is set
|
||||
# to yes it doesn't guarantee the window will be given focus. some
|
||||
# restrictions may apply, but Openbox will try to
|
||||
|
||||
<desktop>1</desktop>
|
||||
# 1 is the first desktop, 'all' for all desktops
|
||||
|
||||
<layer>normal</layer>
|
||||
# 'above', 'normal', or 'below'
|
||||
|
||||
<iconic>no</iconic>
|
||||
# make the window iconified when it appears, or not
|
||||
|
||||
<skip_pager>no</skip_pager>
|
||||
# asks to not be shown in pagers
|
||||
|
||||
<skip_taskbar>no</skip_taskbar>
|
||||
# asks to not be shown in taskbars. window cycling actions will also
|
||||
# skip past such windows
|
||||
|
||||
<fullscreen>yes</fullscreen>
|
||||
# make the window in fullscreen mode when it appears
|
||||
|
||||
<maximized>true</maximized>
|
||||
# 'Horizontal', 'Vertical' or boolean (yes/no)
|
||||
</application>
|
||||
|
||||
# end of the example
|
||||
-->
|
||||
</applications>
|
||||
|
||||
</openbox_config>
|
38
.m1k1o/brave/policies.json
Normal file
38
.m1k1o/brave/policies.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"HomepageLocation": "",
|
||||
"AutoFillEnabled": false,
|
||||
"AutofillAddressEnabled": false,
|
||||
"AutofillCreditCardEnabled": false,
|
||||
"BrowserSignin": 0,
|
||||
"DefaultNotificationsSetting": 2,
|
||||
"DeveloperToolsAvailability": 2,
|
||||
"EditBookmarksEnabled": false,
|
||||
"FullscreenAllowed": true,
|
||||
"IncognitoModeAvailability": 1,
|
||||
"SyncDisabled": true,
|
||||
"AutoplayAllowed": true,
|
||||
"BrowserAddPersonEnabled": false,
|
||||
"BrowserGuestModeEnabled": false,
|
||||
"DefaultPopupsSetting": 2,
|
||||
"DownloadRestrictions": 3,
|
||||
"VideoCaptureAllowed": true,
|
||||
"AllowFileSelectionDialogs": false,
|
||||
"PromptForDownloadLocation": false,
|
||||
"BookmarkBarEnabled": false,
|
||||
"PasswordManagerEnabled": false,
|
||||
"URLBlacklist": [
|
||||
"file://*",
|
||||
"chrome://policy"
|
||||
],
|
||||
"ExtensionInstallForcelist": [
|
||||
"cjpalhdlnbpafiamejdnhcphjbkeiagm;https://clients2.google.com/service/update2/crx",
|
||||
"fjoaledfpmneenckfbpdfhkmimnjocfa;https://clients2.google.com/service/update2/crx"
|
||||
],
|
||||
"ExtensionInstallWhitelist": [
|
||||
"cjpalhdlnbpafiamejdnhcphjbkeiagm",
|
||||
"fjoaledfpmneenckfbpdfhkmimnjocfa"
|
||||
],
|
||||
"ExtensionInstallBlacklist": [
|
||||
"*"
|
||||
]
|
||||
}
|
110
.m1k1o/brave/preferences.json
Normal file
110
.m1k1o/brave/preferences.json
Normal file
@ -0,0 +1,110 @@
|
||||
{
|
||||
"homepage": "http://www.google.com",
|
||||
"homepage_is_newtabpage": false,
|
||||
"first_run_tabs": [
|
||||
"https://www.google.com/_/chrome/newtab?ie=UTF-8"
|
||||
],
|
||||
"custom_links": {
|
||||
"initialized": true,
|
||||
"list": [
|
||||
{
|
||||
"title": "YouTube",
|
||||
"url": "https://www.youtube.com/"
|
||||
},
|
||||
{
|
||||
"title": "Netflix",
|
||||
"url": "https://netflix.com"
|
||||
},
|
||||
{
|
||||
"title": "Hulu",
|
||||
"url": "https://www.hulu.com/"
|
||||
},
|
||||
{
|
||||
"title": "9Anime",
|
||||
"url": "https://9anime.to/"
|
||||
},
|
||||
{
|
||||
"title": "Crunchy Roll",
|
||||
"url": "https://www.crunchyroll.com/"
|
||||
},
|
||||
{
|
||||
"title": "Funimation",
|
||||
"url": "https://www.funimation.com/"
|
||||
},
|
||||
{
|
||||
"title": "Disney+",
|
||||
"url": "https://www.disneyplus.com/"
|
||||
},
|
||||
{
|
||||
"title": "HBO Now",
|
||||
"url": "https://play.hbonow.com/"
|
||||
},
|
||||
{
|
||||
"title": "Amazon Video",
|
||||
"url": "https://www.amazon.com/Amazon-Video/b?node=2858778011"
|
||||
},
|
||||
{
|
||||
"title": "VRV",
|
||||
"url": "https://vrv.co/"
|
||||
},
|
||||
{
|
||||
"title": "Twitch",
|
||||
"url": "https://www.twitch.tv/"
|
||||
},
|
||||
{
|
||||
"title": "Mixer",
|
||||
"url": "https://mixer.com/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"browser": {
|
||||
"custom_chrome_frame": false,
|
||||
"show_home_button": true,
|
||||
"window_placement": {
|
||||
"maximized": true
|
||||
}
|
||||
},
|
||||
"bookmark_bar": {
|
||||
"show_on_all_tabs": false
|
||||
},
|
||||
"sync_promo": {
|
||||
"show_on_first_run_allowed": false
|
||||
},
|
||||
"distribution": {
|
||||
"import_bookmarks_from_file": "bookmarks.html",
|
||||
"import_bookmarks": true,
|
||||
"import_history": true,
|
||||
"import_home_page": true,
|
||||
"import_search_engine": true,
|
||||
"ping_delay": 60,
|
||||
"do_not_create_desktop_shortcut": true,
|
||||
"do_not_create_quick_launch_shortcut": true,
|
||||
"do_not_create_taskbar_shortcut": true,
|
||||
"do_not_launch_chrome": true,
|
||||
"do_not_register_for_update_launch": true,
|
||||
"make_chrome_default": true,
|
||||
"make_chrome_default_for_user": true,
|
||||
"system_level": false,
|
||||
"verbose_logging": false
|
||||
},
|
||||
"profile": {
|
||||
"avatar_index": 19,
|
||||
"default_content_setting_values": {
|
||||
"clipboard": 2,
|
||||
"cookies": 4,
|
||||
"geolocation": 2,
|
||||
"media_stream_camera": 2,
|
||||
"media_stream_mic": 2,
|
||||
"midi_sysex": 2,
|
||||
"payment_handler": 2,
|
||||
"usb_guard": 2
|
||||
},
|
||||
"name": "neko",
|
||||
"using_default_avatar": false,
|
||||
"using_default_name": false,
|
||||
"using_gaia_avatar": false
|
||||
},
|
||||
"signin": {
|
||||
"allowed": false
|
||||
}
|
||||
}
|
21
.m1k1o/brave/supervisord.conf
Normal file
21
.m1k1o/brave/supervisord.conf
Normal file
@ -0,0 +1,21 @@
|
||||
[program:brave]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/brave-browser --window-position=0,0 --display=%(ENV_DISPLAY)s --user-data-dir=/home/neko/.config/brave --no-first-run --start-maximized --bwsi --force-dark-mode --disable-file-system --disable-gpu --disable-software-rasterizer --disable-dev-shm-usage
|
||||
autorestart=true
|
||||
priority=800
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/brave.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
||||
|
||||
[program:openbox]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/openbox --config-file /etc/neko/openbox.xml
|
||||
autorestart=true
|
||||
priority=300
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/openbox.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
89
.m1k1o/build
89
.m1k1o/build
@ -1,35 +1,88 @@
|
||||
#!/bin/sh
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
BASE=../
|
||||
BASE="${PWD}/../"
|
||||
|
||||
# BUILD_IMAGE from environment vairables has precedence
|
||||
if [ ! -z "${BUILD_IMAGE}" ]
|
||||
then
|
||||
ENV_BUILD_IMAGE="${BUILD_IMAGE}"
|
||||
fi
|
||||
|
||||
if [ -f ".env.default" ]
|
||||
then
|
||||
export $(cat .env.default | sed 's/#.*//g' | xargs)
|
||||
fi
|
||||
|
||||
if [ -f ".env" ]
|
||||
then
|
||||
export $(cat .env | sed 's/#.*//g' | xargs)
|
||||
fi
|
||||
|
||||
# BUILD_IMAGE from environment vairables has precedence
|
||||
if [ ! -z "${ENV_BUILD_IMAGE}" ]
|
||||
then
|
||||
BUILD_IMAGE="${ENV_BUILD_IMAGE}"
|
||||
unset ENV_BUILD_IMAGE
|
||||
fi
|
||||
|
||||
if [ -z "${1}" ] && [ ! -z "${SERVER_TAG}" ]
|
||||
then
|
||||
./build base
|
||||
./build ${SERVER_TAG}
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
build_client() {
|
||||
docker build -t neko-dev-client -f base/Dockerfile --target client "$BASE"
|
||||
docker run --rm -v "$BASE"/client/dist:/tmp/dist neko-dev-client sh -c "rm -rf /tmp/dist/*; cp -r /src/dist/* /tmp/dist"
|
||||
docker build -t neko-dev-client -f base/Dockerfile --target client "${BASE}"
|
||||
docker run --rm \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
-v "${BASE}client/dist:/tmp/dist" \
|
||||
neko-dev-client sh -c "rm -rf /tmp/dist/*; cp -r /src/dist/* /tmp/dist"
|
||||
}
|
||||
|
||||
build_server() {
|
||||
docker build -t neko-dev-server -f base/Dockerfile --target server "$BASE"
|
||||
docker run --rm -v "$BASE"/server/bin:/tmp/bin neko-dev-server sh -c "rm -rf /tmp/bin/neko; cp /src/bin/neko /tmp/bin"
|
||||
docker build -t neko-dev-server -f base/Dockerfile --target server "${BASE}"
|
||||
docker run --rm \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
-v "${BASE}server/bin:/tmp/bin" \
|
||||
neko-dev-server sh -c "rm -rf /tmp/bin/neko; cp /src/bin/neko /tmp/bin"
|
||||
}
|
||||
|
||||
build_base() {
|
||||
docker build -t m1k1o/neko:base -f base/Dockerfile "$BASE"
|
||||
build() {
|
||||
if [ "$1" = "base" ]
|
||||
then
|
||||
# build base
|
||||
docker build -t "${BUILD_IMAGE}:base" -f base/Dockerfile "${BASE}"
|
||||
else
|
||||
# build image
|
||||
docker build -t "${BUILD_IMAGE}:$1" --build-arg="BASE_IMAGE=${BUILD_IMAGE}:base" -f "$1/Dockerfile" "$1/"
|
||||
fi
|
||||
}
|
||||
|
||||
build_firefox() {
|
||||
docker build -t m1k1o/neko:firefox -f firefox/Dockerfile firefox/
|
||||
}
|
||||
|
||||
build_chromium() {
|
||||
docker build -t m1k1o/neko:chromium -f chromium/Dockerfile chromium/
|
||||
build_arm() {
|
||||
if [ "$1" = "base" ]
|
||||
then
|
||||
# build ARM base
|
||||
docker build -t "${BUILD_IMAGE}:arm-base" -f base/Dockerfile.arm "${BASE}"
|
||||
elif [ -f "$1/Dockerfile.arm" ]
|
||||
then
|
||||
# build dedicated ARM image
|
||||
docker build -t "${BUILD_IMAGE}:arm-$1" --build-arg="BASE_IMAGE=${BUILD_IMAGE}:arm-base" -f "$1/Dockerfile.arm" "$1/"
|
||||
else
|
||||
# try to build ARM image with common Dockerfile
|
||||
docker build -t "${BUILD_IMAGE}:arm-$1" --build-arg="BASE_IMAGE=${BUILD_IMAGE}:arm-base" -f "$1/Dockerfile" "$1/"
|
||||
fi
|
||||
}
|
||||
|
||||
case $1 in
|
||||
client) build_client;;
|
||||
serve) build_server;;
|
||||
base) build_base;;
|
||||
firefox) build_firefox;;
|
||||
chromium) build_chromium;;
|
||||
*) echo "Unknown $1";;
|
||||
server) build_server;;
|
||||
|
||||
# build arm- images
|
||||
arm-*) build_arm "${1#arm-}";;
|
||||
|
||||
# build images
|
||||
*) build "$1";;
|
||||
esac
|
||||
|
@ -1,32 +1,26 @@
|
||||
FROM m1k1o/neko:base
|
||||
|
||||
ARG SRC_URL="https://github.com/macchrome/linchrome/releases/download/v87.0.4280.88-r812852-portable-ungoogled-Lin64/ungoogled-chromium_87.0.4280.88_1.vaapi_linux.tar.xz"
|
||||
ARG BASE_IMAGE=m1k1o/neko:base
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
#
|
||||
# install custom chromium build from woolyss with support for hevc/x265
|
||||
# install neko chromium
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends libatk1.0-0 libatk-bridge2.0-0 libatomic1 \
|
||||
libcups2 libgtk-3-0 libnss3 libpci3 libxcomposite1 libxss1 openbox xz-utils; \
|
||||
wget -O - /tmp/chromium.tar.xz "${SRC_URL}" | tar -xJf- -C /tmp; \
|
||||
mv /tmp/ungoogled-chromium_* /usr/lib/chromium; \
|
||||
apt-get install -y --no-install-recommends unzip chromium chromium-sandbox openbox; \
|
||||
#
|
||||
# make required changes for sandbox mode
|
||||
mv /usr/lib/chromium/chrome_sandbox /usr/lib/chromium/chrome-sandbox; \
|
||||
chown root:root /usr/lib/chromium/chrome-sandbox; \
|
||||
chmod 4755 /usr/lib/chromium/chrome-sandbox; \
|
||||
# install widevine module
|
||||
WIDEVINE_VERSION=$(wget --quiet -O - https://dl.google.com/widevine-cdm/versions.txt | tail -n 1); \
|
||||
wget -O /tmp/widevine.zip "https://dl.google.com/widevine-cdm/$WIDEVINE_VERSION-linux-x64.zip"; \
|
||||
unzip -p /tmp/widevine.zip libwidevinecdm.so > /usr/lib/chromium/libwidevinecdm.so; \
|
||||
chmod 644 /usr/lib/chromium/libwidevinecdm.so; \
|
||||
rm /tmp/widevine.zip; \
|
||||
#
|
||||
# clean up
|
||||
apt-get --purge autoremove -y xz-utils; \
|
||||
apt-get --purge autoremove -y unzip; \
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
#
|
||||
# copy configuation files
|
||||
COPY supervisord.conf /etc/neko/supervisord/chromium.conf
|
||||
COPY preferences.json /usr/lib/chromium/master_preferences
|
||||
COPY --chown=neko preferences.json /home/neko/.config/chromium/Default/Preferences
|
||||
COPY policies.json /etc/chromium/policies/managed/policies.json
|
||||
COPY openbox.xml /etc/neko/openbox.xml
|
||||
|
||||
#
|
||||
# copy extensions and policy files
|
||||
COPY extensions /usr/share/chromium/extensions
|
||||
|
19
.m1k1o/chromium/Dockerfile.arm
Normal file
19
.m1k1o/chromium/Dockerfile.arm
Normal file
@ -0,0 +1,19 @@
|
||||
ARG BASE_IMAGE=m1k1o/neko:arm-base
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
#
|
||||
# install neko chromium
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends chromium-browser openbox libwidevinecdm0; \
|
||||
ln -s /usr/bin/chromium-browser /usr/bin/chromium; \
|
||||
#
|
||||
# clean up
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
#
|
||||
# copy configuation files
|
||||
COPY supervisord.conf /etc/neko/supervisord/chromium.conf
|
||||
COPY --chown=neko preferences.json /home/neko/.config/chromium/Default/Preferences
|
||||
COPY policies.json /etc/chromium/policies/managed/policies.json
|
||||
COPY openbox.xml /etc/neko/openbox.xml
|
Binary file not shown.
Binary file not shown.
@ -13,7 +13,7 @@
|
||||
|
||||
<applications>
|
||||
<!-- Match all windows and remove their decorations (obxprop | grep "^_OB_APP") -->
|
||||
<application class="Chromium*" name="chromium-devel">
|
||||
<application class="Chromium*" name="chromium*">
|
||||
<decor>no</decor>
|
||||
<maximized>true</maximized>
|
||||
<focus>yes</focus>
|
||||
|
@ -24,6 +24,10 @@
|
||||
"file://*",
|
||||
"chrome://policy"
|
||||
],
|
||||
"ExtensionInstallForcelist": [
|
||||
"cjpalhdlnbpafiamejdnhcphjbkeiagm;https://clients2.google.com/service/update2/crx",
|
||||
"fjoaledfpmneenckfbpdfhkmimnjocfa;https://clients2.google.com/service/update2/crx"
|
||||
],
|
||||
"ExtensionInstallWhitelist": [
|
||||
"cjpalhdlnbpafiamejdnhcphjbkeiagm",
|
||||
"fjoaledfpmneenckfbpdfhkmimnjocfa"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[program:chromium]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/lib/chromium/chrome-wrapper --window-position=0,0 --display=%(ENV_DISPLAY)s --start-maximized --bwsi --test-type --force-dark-mode --disable-file-system --disable-gpu --disable-software-rasterizer --disable-dev-shm-usage
|
||||
command=/usr/bin/chromium --window-position=0,0 --display=%(ENV_DISPLAY)s --user-data-dir=/home/neko/.config/chromium --no-first-run --start-maximized --bwsi --force-dark-mode --disable-file-system --disable-gpu --disable-software-rasterizer --disable-dev-shm-usage
|
||||
autorestart=true
|
||||
priority=800
|
||||
user=%(ENV_USER)s
|
||||
|
@ -1,22 +1,35 @@
|
||||
FROM m1k1o/neko:base
|
||||
ARG BASE_IMAGE=m1k1o/neko:base
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
ARG SRC_URL="https://download.mozilla.org/?product=firefox-latest&os=linux64&lang=en-US"
|
||||
|
||||
#
|
||||
# install firefox-esr
|
||||
# install firefox
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends openbox firefox-esr; \
|
||||
apt-get install -y --no-install-recommends openbox \
|
||||
xz-utils bzip2 libgtk-3-0 libdbus-glib-1-2; \
|
||||
#
|
||||
# install extensions
|
||||
mkdir -p /usr/lib/firefox-esr/distribution/extensions; \
|
||||
wget -O '/usr/lib/firefox-esr/distribution/extensions/uBlock0@raymondhill.net.xpi' https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi; \
|
||||
# fetch latest release
|
||||
wget -O /tmp/firefox-setup.tar.bz2 "${SRC_URL}"; \
|
||||
mkdir /usr/lib/firefox; \
|
||||
tar -xjf /tmp/firefox-setup.tar.bz2 -C /usr/lib; \
|
||||
rm -f /tmp/firefox-setup.tar.bz2; \
|
||||
ln -s /usr/lib/firefox/firefox /usr/bin/firefox; \
|
||||
#
|
||||
# create a profile directory
|
||||
mkdir -p /home/neko/.mozilla/firefox/profile.default/extensions; \
|
||||
chown -R neko:neko /home/neko/.mozilla/firefox/profile.default; \
|
||||
#
|
||||
# clean up
|
||||
apt-get --purge autoremove -y xz-utils bzip2; \
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
#
|
||||
# copy configuation files
|
||||
COPY supervisord.conf /etc/neko/supervisord/firefox.conf
|
||||
COPY neko.js /usr/lib/firefox-esr/mozilla.cfg
|
||||
COPY autoconfig.js /usr/lib/firefox-esr/defaults/pref/autoconfig.js
|
||||
COPY policies.json /usr/lib/firefox-esr/distribution/policies.json
|
||||
COPY neko.js /usr/lib/firefox/mozilla.cfg
|
||||
COPY autoconfig.js /usr/lib/firefox/defaults/pref/autoconfig.js
|
||||
COPY policies.json /usr/lib/firefox/distribution/policies.json
|
||||
COPY --chown=neko profiles.ini /home/neko/.mozilla/firefox/profiles.ini
|
||||
COPY openbox.xml /etc/neko/openbox.xml
|
||||
|
30
.m1k1o/firefox/Dockerfile.arm
Normal file
30
.m1k1o/firefox/Dockerfile.arm
Normal file
@ -0,0 +1,30 @@
|
||||
ARG BASE_IMAGE=m1k1o/neko:arm-base
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
#
|
||||
# install firefox-esr
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends openbox firefox-esr libwidevinecdm0; \
|
||||
ln -s /usr/lib/firefox-esr/firefox-esr /usr/bin/firefox; \
|
||||
#
|
||||
# install extensions
|
||||
mkdir -p /usr/lib/firefox-esr/distribution/extensions; \
|
||||
wget -O '/usr/lib/firefox-esr/distribution/extensions/uBlock0@raymondhill.net.xpi' https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi; \
|
||||
wget -O /usr/lib/firefox-esr/distribution/extensions/nordvpnproxy@nordvpn.com.xpi https://addons.mozilla.org/firefox/downloads/latest/nordvpn-proxy-extension/latest.xpi; \
|
||||
#
|
||||
# create a profile directory
|
||||
mkdir -p /home/neko/.mozilla/firefox/profile.default/extensions; \
|
||||
chown -R neko:neko /home/neko/.mozilla/firefox/profile.default; \
|
||||
#
|
||||
# clean up
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
#
|
||||
# copy configuation files
|
||||
COPY supervisord.conf /etc/neko/supervisord/firefox.conf
|
||||
COPY neko.js /usr/lib/firefox-esr/mozilla.cfg
|
||||
COPY autoconfig.js /usr/lib/firefox-esr/defaults/pref/autoconfig.js
|
||||
COPY policies.json /usr/lib/firefox-esr/distribution/policies.json
|
||||
COPY --chown=neko profiles.ini /home/neko/.mozilla/firefox/profiles.ini
|
||||
COPY openbox.xml /etc/neko/openbox.xml
|
@ -32,4 +32,6 @@ lockPref("devtools.theme", "dark");
|
||||
lockPref("ui.systemUsesDarkTheme", 1);
|
||||
lockPref("lightweightThemes.usedThemes","[]");
|
||||
lockPref("lightweightThemes.selectedThemeID", "firefox-compact-dark@mozilla.org");
|
||||
lockPref("extensions.activeThemeID", "firefox-compact-dark@mozilla.org");
|
||||
lockPref("browser.theme.toolbar-theme", 0);
|
||||
lockPref("browser.in-content.dark-mode", true);
|
||||
|
@ -56,6 +56,10 @@
|
||||
"*": {
|
||||
"installation_mode": "blocked"
|
||||
},
|
||||
"nordvpnproxy@nordvpn.com": {
|
||||
"install_url": "https://addons.mozilla.org/firefox/downloads/latest/nordvpn-proxy-extension/latest.xpi",
|
||||
"installation_mode": "force_installed"
|
||||
},
|
||||
"uBlock0@raymondhill.net": {
|
||||
"install_url": "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi",
|
||||
"installation_mode": "force_installed"
|
||||
@ -73,7 +77,7 @@
|
||||
"HardwareAcceleration": false,
|
||||
"Homepage": {
|
||||
"Additional": [],
|
||||
"StartPage": "none"
|
||||
"StartPage": "home"
|
||||
},
|
||||
"NewTabPage": true,
|
||||
"NoDefaultBookmarks": true,
|
||||
|
8
.m1k1o/firefox/profiles.ini
Normal file
8
.m1k1o/firefox/profiles.ini
Normal file
@ -0,0 +1,8 @@
|
||||
[General]
|
||||
StartWithLastProfile=1
|
||||
|
||||
[Profile0]
|
||||
Name=default
|
||||
IsRelative=1
|
||||
Path=profile.default
|
||||
Default=1
|
@ -1,10 +1,10 @@
|
||||
[program:firefox-esr]
|
||||
[program:firefox]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/lib/firefox-esr/firefox-esr --display=%(ENV_DISPLAY)s -setDefaultBrowser -width 1280 -height 720
|
||||
command=/usr/bin/firefox --no-remote -P default --display=%(ENV_DISPLAY)s -setDefaultBrowser -width 1280 -height 720
|
||||
autorestart=true
|
||||
priority=800
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/firefox-esr.log
|
||||
stdout_logfile=/var/log/neko/firefox.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
||||
|
21
.m1k1o/google-chrome/Dockerfile
Normal file
21
.m1k1o/google-chrome/Dockerfile
Normal file
@ -0,0 +1,21 @@
|
||||
ARG BASE_IMAGE=m1k1o/neko:base
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
ARG SRC_URL="https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb"
|
||||
|
||||
#
|
||||
# install google chrome
|
||||
RUN set -eux; apt-get update; \
|
||||
wget -O /tmp/google-chrome.deb "${SRC_URL}"; \
|
||||
apt-get install -y --no-install-recommends openbox /tmp/google-chrome.deb; \
|
||||
#
|
||||
# clean up
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
#
|
||||
# copy configuation files
|
||||
COPY supervisord.conf /etc/neko/supervisord/google-chrome.conf
|
||||
COPY --chown=neko preferences.json /home/neko/.config/google-chrome/Default/Preferences
|
||||
COPY policies.json /etc/opt/chrome/policies/managed/policies.json
|
||||
COPY openbox.xml /etc/neko/openbox.xml
|
763
.m1k1o/google-chrome/openbox.xml
Normal file
763
.m1k1o/google-chrome/openbox.xml
Normal file
@ -0,0 +1,763 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- Default openbox config but all window decorations are moved
|
||||
thereby making it harder to accidentally close the virtual browser -->
|
||||
|
||||
<openbox_config xmlns="http://openbox.org/3.4/rc"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<resistance>
|
||||
<strength>10</strength>
|
||||
<screen_edge_strength>20</screen_edge_strength>
|
||||
</resistance>
|
||||
|
||||
<applications>
|
||||
<!-- Match all windows and remove their decorations (obxprop | grep "^_OB_APP") -->
|
||||
<application class="Google-chrome*" name="google-chrome*">
|
||||
<decor>no</decor>
|
||||
<maximized>true</maximized>
|
||||
<focus>yes</focus>
|
||||
<layer>normal</layer>
|
||||
</application>
|
||||
</applications>
|
||||
|
||||
<focus>
|
||||
<focusNew>yes</focusNew>
|
||||
<!-- always try to focus new windows when they appear. other rules do
|
||||
apply -->
|
||||
<followMouse>no</followMouse>
|
||||
<!-- move focus to a window when you move the mouse into it -->
|
||||
<focusLast>yes</focusLast>
|
||||
<!-- focus the last used window when changing desktops, instead of the one
|
||||
under the mouse pointer. when followMouse is enabled -->
|
||||
<underMouse>no</underMouse>
|
||||
<!-- move focus under the mouse, even when the mouse is not moving -->
|
||||
<focusDelay>200</focusDelay>
|
||||
<!-- when followMouse is enabled, the mouse must be inside the window for
|
||||
this many milliseconds (1000 = 1 sec) before moving focus to it -->
|
||||
<raiseOnFocus>no</raiseOnFocus>
|
||||
<!-- when followMouse is enabled, and a window is given focus by moving the
|
||||
mouse into it, also raise the window -->
|
||||
</focus>
|
||||
|
||||
<placement>
|
||||
<policy>Smart</policy>
|
||||
<!-- 'Smart' or 'UnderMouse' -->
|
||||
<center>yes</center>
|
||||
<!-- whether to place windows in the center of the free area found or
|
||||
the top left corner -->
|
||||
<monitor>Primary</monitor>
|
||||
<!-- with Smart placement on a multi-monitor system, try to place new windows
|
||||
on: 'Any' - any monitor, 'Mouse' - where the mouse is, 'Active' - where
|
||||
the active window is, 'Primary' - only on the primary monitor -->
|
||||
<primaryMonitor>1</primaryMonitor>
|
||||
<!-- The monitor where Openbox should place popup dialogs such as the
|
||||
focus cycling popup, or the desktop switch popup. It can be an index
|
||||
from 1, specifying a particular monitor. Or it can be one of the
|
||||
following: 'Mouse' - where the mouse is, or
|
||||
'Active' - where the active window is -->
|
||||
</placement>
|
||||
|
||||
<theme>
|
||||
<name>Clearlooks</name>
|
||||
<titleLayout>NLIMC</titleLayout>
|
||||
<!--
|
||||
available characters are NDSLIMC, each can occur at most once.
|
||||
N: window icon
|
||||
L: window label (AKA title).
|
||||
I: iconify
|
||||
M: maximize
|
||||
C: close
|
||||
S: shade (roll up/down)
|
||||
D: omnipresent (on all desktops).
|
||||
-->
|
||||
<keepBorder>yes</keepBorder>
|
||||
<animateIconify>yes</animateIconify>
|
||||
<font place="ActiveWindow">
|
||||
<name>sans</name>
|
||||
<size>8</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="InactiveWindow">
|
||||
<name>sans</name>
|
||||
<size>8</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="MenuHeader">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>normal</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="MenuItem">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>normal</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="ActiveOnScreenDisplay">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="InactiveOnScreenDisplay">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
</theme>
|
||||
|
||||
<desktops>
|
||||
<!-- this stuff is only used at startup, pagers allow you to change them
|
||||
during a session
|
||||
|
||||
these are default values to use when other ones are not already set
|
||||
by other applications, or saved in your session
|
||||
|
||||
use obconf if you want to change these without having to log out
|
||||
and back in -->
|
||||
<number>1</number>
|
||||
<firstdesk>1</firstdesk>
|
||||
<names>
|
||||
<!-- set names up here if you want to, like this:
|
||||
<name>desktop 1</name>
|
||||
<name>desktop 2</name>
|
||||
-->
|
||||
</names>
|
||||
<popupTime>875</popupTime>
|
||||
<!-- The number of milliseconds to show the popup for when switching
|
||||
desktops. Set this to 0 to disable the popup. -->
|
||||
</desktops>
|
||||
|
||||
<resize>
|
||||
<drawContents>yes</drawContents>
|
||||
<popupShow>Nonpixel</popupShow>
|
||||
<!-- 'Always', 'Never', or 'Nonpixel' (xterms and such) -->
|
||||
<popupPosition>Center</popupPosition>
|
||||
<!-- 'Center', 'Top', or 'Fixed' -->
|
||||
<popupFixedPosition>
|
||||
<!-- these are used if popupPosition is set to 'Fixed' -->
|
||||
|
||||
<x>10</x>
|
||||
<!-- positive number for distance from left edge, negative number for
|
||||
distance from right edge, or 'Center' -->
|
||||
<y>10</y>
|
||||
<!-- positive number for distance from top edge, negative number for
|
||||
distance from bottom edge, or 'Center' -->
|
||||
</popupFixedPosition>
|
||||
</resize>
|
||||
|
||||
<!-- You can reserve a portion of your screen where windows will not cover when
|
||||
they are maximized, or when they are initially placed.
|
||||
Many programs reserve space automatically, but you can use this in other
|
||||
cases. -->
|
||||
<margins>
|
||||
<top>0</top>
|
||||
<bottom>0</bottom>
|
||||
<left>0</left>
|
||||
<right>0</right>
|
||||
</margins>
|
||||
|
||||
<dock>
|
||||
<position>TopLeft</position>
|
||||
<!-- (Top|Bottom)(Left|Right|)|Top|Bottom|Left|Right|Floating -->
|
||||
<floatingX>0</floatingX>
|
||||
<floatingY>0</floatingY>
|
||||
<noStrut>no</noStrut>
|
||||
<stacking>Above</stacking>
|
||||
<!-- 'Above', 'Normal', or 'Below' -->
|
||||
<direction>Vertical</direction>
|
||||
<!-- 'Vertical' or 'Horizontal' -->
|
||||
<autoHide>no</autoHide>
|
||||
<hideDelay>300</hideDelay>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<showDelay>300</showDelay>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<moveButton>Middle</moveButton>
|
||||
<!-- 'Left', 'Middle', 'Right' -->
|
||||
</dock>
|
||||
|
||||
<keyboard>
|
||||
<chainQuitKey>C-g</chainQuitKey>
|
||||
|
||||
<!-- Keybindings for desktop switching -->
|
||||
<keybind key="C-A-Left">
|
||||
<action name="GoToDesktop"><to>left</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Right">
|
||||
<action name="GoToDesktop"><to>right</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Up">
|
||||
<action name="GoToDesktop"><to>up</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Down">
|
||||
<action name="GoToDesktop"><to>down</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Left">
|
||||
<action name="SendToDesktop"><to>left</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Right">
|
||||
<action name="SendToDesktop"><to>right</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Up">
|
||||
<action name="SendToDesktop"><to>up</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Down">
|
||||
<action name="SendToDesktop"><to>down</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="W-F1">
|
||||
<action name="GoToDesktop"><to>1</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F2">
|
||||
<action name="GoToDesktop"><to>2</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F3">
|
||||
<action name="GoToDesktop"><to>3</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F4">
|
||||
<action name="GoToDesktop"><to>4</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-d">
|
||||
<action name="ToggleShowDesktop"/>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for windows -->
|
||||
<keybind key="A-F4">
|
||||
<action name="Close"/>
|
||||
</keybind>
|
||||
<keybind key="A-Escape">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</keybind>
|
||||
<keybind key="A-space">
|
||||
<!--action name="ShowMenu"><menu>client-menu</menu></action-->
|
||||
</keybind>
|
||||
<!-- Take a screenshot of the current window with scrot when Alt+Print are pressed -->
|
||||
<keybind key="A-Print">
|
||||
<action name="Execute"><command>scrot -s</command></action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for window switching -->
|
||||
<keybind key="A-Tab">
|
||||
<action name="NextWindow">
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="A-S-Tab">
|
||||
<action name="PreviousWindow">
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Tab">
|
||||
<action name="NextWindow">
|
||||
<panels>yes</panels><desktop>yes</desktop>
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for window switching with the arrow keys -->
|
||||
<keybind key="W-S-Right">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>right</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Left">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>left</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Up">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>up</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Down">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>down</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for running applications -->
|
||||
<keybind key="W-e">
|
||||
<action name="Execute">
|
||||
<startupnotify>
|
||||
<enabled>true</enabled>
|
||||
<name>Konqueror</name>
|
||||
</startupnotify>
|
||||
<command>kfmclient openProfile filemanagement</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<!-- Launch scrot when Print is pressed -->
|
||||
<keybind key="Print">
|
||||
<action name="Execute"><command>scrot</command></action>
|
||||
</keybind>
|
||||
</keyboard>
|
||||
|
||||
<mouse>
|
||||
<dragThreshold>1</dragThreshold>
|
||||
<!-- number of pixels the mouse must move before a drag begins -->
|
||||
<doubleClickTime>500</doubleClickTime>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<screenEdgeWarpTime>400</screenEdgeWarpTime>
|
||||
<!-- Time before changing desktops when the pointer touches the edge of the
|
||||
screen while moving a window, in milliseconds (1000 = 1 second).
|
||||
Set this to 0 to disable warping -->
|
||||
<screenEdgeWarpMouse>false</screenEdgeWarpMouse>
|
||||
<!-- Set this to TRUE to move the mouse pointer across the desktop when
|
||||
switching due to hitting the edge of the screen -->
|
||||
|
||||
<context name="Frame">
|
||||
<mousebind button="A-Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Left" action="Click">
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Left" action="Drag">
|
||||
<action name="Move"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Right" action="Drag">
|
||||
<action name="Resize"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Middle" action="Press">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-S-Up" action="Click">
|
||||
<action name="SendToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-S-Down" action="Click">
|
||||
<action name="SendToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Titlebar">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Move"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="DoubleClick">
|
||||
<action name="ToggleMaximize"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="if">
|
||||
<shaded>no</shaded>
|
||||
<then>
|
||||
<action name="Shade"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
<action name="Lower"/>
|
||||
</then>
|
||||
</action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="if">
|
||||
<shaded>yes</shaded>
|
||||
<then>
|
||||
<action name="Unshade"/>
|
||||
<action name="Raise"/>
|
||||
</then>
|
||||
</action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Titlebar Top Right Bottom Left TLCorner TRCorner BRCorner BLCorner">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</mousebind>
|
||||
|
||||
<!--mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="Top">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>top</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Left">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>left</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Right">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>right</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Bottom">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>bottom</edge></action>
|
||||
</mousebind>
|
||||
|
||||
<!--mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="TRCorner BRCorner TLCorner BLCorner">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Client">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Icon">
|
||||
<!--mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="AllDesktops">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleOmnipresent"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Shade">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleShade"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Iconify">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="Iconify"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Maximize">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleMaximize"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Click">
|
||||
<action name="ToggleMaximize"><direction>vertical</direction></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Click">
|
||||
<action name="ToggleMaximize"><direction>horizontal</direction></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Close">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="Close"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Desktop">
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Root">
|
||||
<!-- Menus -->
|
||||
<!--mousebind button="Middle" action="Press">
|
||||
<action name="ShowMenu"><menu>client-list-combined-menu</menu></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="ShowMenu"><menu>root-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="MoveResize">
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
</mouse>
|
||||
|
||||
<menu>
|
||||
<!-- You can specify more than one menu file in here and they are all loaded,
|
||||
just don't make menu ids clash or, well, it'll be kind of pointless -->
|
||||
|
||||
<!-- default menu file (or custom one in $HOME/.config/openbox/) -->
|
||||
<!-- system menu files on Debian systems -->
|
||||
<!--file>/var/lib/openbox/debian-menu.xml</file-->
|
||||
<file>menu.xml</file>
|
||||
<hideDelay>200</hideDelay>
|
||||
<!-- if a press-release lasts longer than this setting (in milliseconds), the
|
||||
menu is hidden again -->
|
||||
<middle>no</middle>
|
||||
<!-- center submenus vertically about the parent entry -->
|
||||
<submenuShowDelay>100</submenuShowDelay>
|
||||
<!-- time to delay before showing a submenu after hovering over the parent
|
||||
entry.
|
||||
if this is a negative value, then the delay is infinite and the
|
||||
submenu will not be shown until it is clicked on -->
|
||||
<submenuHideDelay>400</submenuHideDelay>
|
||||
<!-- time to delay before hiding a submenu when selecting another
|
||||
entry in parent menu
|
||||
if this is a negative value, then the delay is infinite and the
|
||||
submenu will not be hidden until a different submenu is opened -->
|
||||
<showIcons>yes</showIcons>
|
||||
<!-- controls if icons appear in the client-list-(combined-)menu -->
|
||||
<manageDesktops>yes</manageDesktops>
|
||||
<!-- show the manage desktops section in the client-list-(combined-)menu -->
|
||||
</menu>
|
||||
|
||||
<applications>
|
||||
<!--
|
||||
# this is an example with comments through out. use these to make your
|
||||
# own rules, but without the comments of course.
|
||||
# you may use one or more of the name/class/role/title/type rules to specify
|
||||
# windows to match
|
||||
|
||||
<application name="the window's _OB_APP_NAME property (see obxprop)"
|
||||
class="the window's _OB_APP_CLASS property (see obxprop)"
|
||||
groupname="the window's _OB_APP_GROUP_NAME property (see obxprop)"
|
||||
groupclass="the window's _OB_APP_GROUP_CLASS property (see obxprop)"
|
||||
role="the window's _OB_APP_ROLE property (see obxprop)"
|
||||
title="the window's _OB_APP_TITLE property (see obxprop)"
|
||||
type="the window's _OB_APP_TYPE property (see obxprob)..
|
||||
(if unspecified, then it is 'dialog' for child windows)">
|
||||
# you may set only one of name/class/role/title/type, or you may use more
|
||||
# than one together to restrict your matches.
|
||||
|
||||
# the name, class, role, and title use simple wildcard matching such as those
|
||||
# used by a shell. you can use * to match any characters and ? to match
|
||||
# any single character.
|
||||
|
||||
# the type is one of: normal, dialog, splash, utility, menu, toolbar, dock,
|
||||
# or desktop
|
||||
|
||||
# when multiple rules match a window, they will all be applied, in the
|
||||
# order that they appear in this list
|
||||
|
||||
|
||||
# each rule element can be left out or set to 'default' to specify to not
|
||||
# change that attribute of the window
|
||||
|
||||
<decor>yes</decor>
|
||||
# enable or disable window decorations
|
||||
|
||||
<shade>no</shade>
|
||||
# make the window shaded when it appears, or not
|
||||
|
||||
<position force="no">
|
||||
# the position is only used if both an x and y coordinate are provided
|
||||
# (and not set to 'default')
|
||||
# when force is "yes", then the window will be placed here even if it
|
||||
# says you want it placed elsewhere. this is to override buggy
|
||||
# applications who refuse to behave
|
||||
<x>center</x>
|
||||
# a number like 50, or 'center' to center on screen. use a negative number
|
||||
# to start from the right (or bottom for <y>), ie -50 is 50 pixels from
|
||||
# the right edge (or bottom). use 'default' to specify using value
|
||||
# provided by the application, or chosen by openbox, instead.
|
||||
<y>200</y>
|
||||
<monitor>1</monitor>
|
||||
# specifies the monitor in a xinerama setup.
|
||||
# 1 is the first head, or 'mouse' for wherever the mouse is
|
||||
</position>
|
||||
|
||||
<size>
|
||||
# the size to make the window.
|
||||
<width>20</width>
|
||||
# a number like 20, or 'default' to use the size given by the application.
|
||||
# you can use fractions such as 1/2 or percentages such as 75% in which
|
||||
# case the value is relative to the size of the monitor that the window
|
||||
# appears on.
|
||||
<height>30%</height>
|
||||
</size>
|
||||
|
||||
<focus>yes</focus>
|
||||
# if the window should try be given focus when it appears. if this is set
|
||||
# to yes it doesn't guarantee the window will be given focus. some
|
||||
# restrictions may apply, but Openbox will try to
|
||||
|
||||
<desktop>1</desktop>
|
||||
# 1 is the first desktop, 'all' for all desktops
|
||||
|
||||
<layer>normal</layer>
|
||||
# 'above', 'normal', or 'below'
|
||||
|
||||
<iconic>no</iconic>
|
||||
# make the window iconified when it appears, or not
|
||||
|
||||
<skip_pager>no</skip_pager>
|
||||
# asks to not be shown in pagers
|
||||
|
||||
<skip_taskbar>no</skip_taskbar>
|
||||
# asks to not be shown in taskbars. window cycling actions will also
|
||||
# skip past such windows
|
||||
|
||||
<fullscreen>yes</fullscreen>
|
||||
# make the window in fullscreen mode when it appears
|
||||
|
||||
<maximized>true</maximized>
|
||||
# 'Horizontal', 'Vertical' or boolean (yes/no)
|
||||
</application>
|
||||
|
||||
# end of the example
|
||||
-->
|
||||
</applications>
|
||||
|
||||
</openbox_config>
|
38
.m1k1o/google-chrome/policies.json
Normal file
38
.m1k1o/google-chrome/policies.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"HomepageLocation": "",
|
||||
"AutoFillEnabled": false,
|
||||
"AutofillAddressEnabled": false,
|
||||
"AutofillCreditCardEnabled": false,
|
||||
"BrowserSignin": 0,
|
||||
"DefaultNotificationsSetting": 2,
|
||||
"DeveloperToolsAvailability": 2,
|
||||
"EditBookmarksEnabled": false,
|
||||
"FullscreenAllowed": true,
|
||||
"IncognitoModeAvailability": 1,
|
||||
"SyncDisabled": true,
|
||||
"AutoplayAllowed": true,
|
||||
"BrowserAddPersonEnabled": false,
|
||||
"BrowserGuestModeEnabled": false,
|
||||
"DefaultPopupsSetting": 2,
|
||||
"DownloadRestrictions": 3,
|
||||
"VideoCaptureAllowed": true,
|
||||
"AllowFileSelectionDialogs": false,
|
||||
"PromptForDownloadLocation": false,
|
||||
"BookmarkBarEnabled": false,
|
||||
"PasswordManagerEnabled": false,
|
||||
"URLBlacklist": [
|
||||
"file://*",
|
||||
"chrome://policy"
|
||||
],
|
||||
"ExtensionInstallForcelist": [
|
||||
"cjpalhdlnbpafiamejdnhcphjbkeiagm;https://clients2.google.com/service/update2/crx",
|
||||
"fjoaledfpmneenckfbpdfhkmimnjocfa;https://clients2.google.com/service/update2/crx"
|
||||
],
|
||||
"ExtensionInstallWhitelist": [
|
||||
"cjpalhdlnbpafiamejdnhcphjbkeiagm",
|
||||
"fjoaledfpmneenckfbpdfhkmimnjocfa"
|
||||
],
|
||||
"ExtensionInstallBlacklist": [
|
||||
"*"
|
||||
]
|
||||
}
|
110
.m1k1o/google-chrome/preferences.json
Normal file
110
.m1k1o/google-chrome/preferences.json
Normal file
@ -0,0 +1,110 @@
|
||||
{
|
||||
"homepage": "http://www.google.com",
|
||||
"homepage_is_newtabpage": false,
|
||||
"first_run_tabs": [
|
||||
"https://www.google.com/_/chrome/newtab?ie=UTF-8"
|
||||
],
|
||||
"custom_links": {
|
||||
"initialized": true,
|
||||
"list": [
|
||||
{
|
||||
"title": "YouTube",
|
||||
"url": "https://www.youtube.com/"
|
||||
},
|
||||
{
|
||||
"title": "Netflix",
|
||||
"url": "https://netflix.com"
|
||||
},
|
||||
{
|
||||
"title": "Hulu",
|
||||
"url": "https://www.hulu.com/"
|
||||
},
|
||||
{
|
||||
"title": "9Anime",
|
||||
"url": "https://9anime.to/"
|
||||
},
|
||||
{
|
||||
"title": "Crunchy Roll",
|
||||
"url": "https://www.crunchyroll.com/"
|
||||
},
|
||||
{
|
||||
"title": "Funimation",
|
||||
"url": "https://www.funimation.com/"
|
||||
},
|
||||
{
|
||||
"title": "Disney+",
|
||||
"url": "https://www.disneyplus.com/"
|
||||
},
|
||||
{
|
||||
"title": "HBO Now",
|
||||
"url": "https://play.hbonow.com/"
|
||||
},
|
||||
{
|
||||
"title": "Amazon Video",
|
||||
"url": "https://www.amazon.com/Amazon-Video/b?node=2858778011"
|
||||
},
|
||||
{
|
||||
"title": "VRV",
|
||||
"url": "https://vrv.co/"
|
||||
},
|
||||
{
|
||||
"title": "Twitch",
|
||||
"url": "https://www.twitch.tv/"
|
||||
},
|
||||
{
|
||||
"title": "Mixer",
|
||||
"url": "https://mixer.com/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"browser": {
|
||||
"custom_chrome_frame": false,
|
||||
"show_home_button": true,
|
||||
"window_placement": {
|
||||
"maximized": true
|
||||
}
|
||||
},
|
||||
"bookmark_bar": {
|
||||
"show_on_all_tabs": false
|
||||
},
|
||||
"sync_promo": {
|
||||
"show_on_first_run_allowed": false
|
||||
},
|
||||
"distribution": {
|
||||
"import_bookmarks_from_file": "bookmarks.html",
|
||||
"import_bookmarks": true,
|
||||
"import_history": true,
|
||||
"import_home_page": true,
|
||||
"import_search_engine": true,
|
||||
"ping_delay": 60,
|
||||
"do_not_create_desktop_shortcut": true,
|
||||
"do_not_create_quick_launch_shortcut": true,
|
||||
"do_not_create_taskbar_shortcut": true,
|
||||
"do_not_launch_chrome": true,
|
||||
"do_not_register_for_update_launch": true,
|
||||
"make_chrome_default": true,
|
||||
"make_chrome_default_for_user": true,
|
||||
"system_level": false,
|
||||
"verbose_logging": false
|
||||
},
|
||||
"profile": {
|
||||
"avatar_index": 19,
|
||||
"default_content_setting_values": {
|
||||
"clipboard": 2,
|
||||
"cookies": 4,
|
||||
"geolocation": 2,
|
||||
"media_stream_camera": 2,
|
||||
"media_stream_mic": 2,
|
||||
"midi_sysex": 2,
|
||||
"payment_handler": 2,
|
||||
"usb_guard": 2
|
||||
},
|
||||
"name": "neko",
|
||||
"using_default_avatar": false,
|
||||
"using_default_name": false,
|
||||
"using_gaia_avatar": false
|
||||
},
|
||||
"signin": {
|
||||
"allowed": false
|
||||
}
|
||||
}
|
21
.m1k1o/google-chrome/supervisord.conf
Normal file
21
.m1k1o/google-chrome/supervisord.conf
Normal file
@ -0,0 +1,21 @@
|
||||
[program:google-chrome]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/google-chrome --window-position=0,0 --display=%(ENV_DISPLAY)s --user-data-dir=/home/neko/.config/google-chrome --no-first-run --start-maximized --bwsi --force-dark-mode --disable-file-system --disable-gpu --disable-software-rasterizer --disable-dev-shm-usage
|
||||
autorestart=true
|
||||
priority=800
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/google-chrome.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
||||
|
||||
[program:openbox]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/openbox --config-file /etc/neko/openbox.xml
|
||||
autorestart=true
|
||||
priority=300
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/openbox.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
21
.m1k1o/rebuild-server
Executable file
21
.m1k1o/rebuild-server
Executable file
@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -f ".env.default" ]
|
||||
then
|
||||
export $(cat .env.default | sed 's/#.*//g' | xargs)
|
||||
fi
|
||||
|
||||
if [ -f ".env" ]
|
||||
then
|
||||
export $(cat .env | sed 's/#.*//g' | xargs)
|
||||
fi
|
||||
|
||||
# use -f to force rebuild
|
||||
if [ "$(docker images -q neko_dev_server 2> /dev/null)" == "" ] || [ "$1" == "-f" ]; then
|
||||
docker build -t neko_dev_server -f base/Dockerfile --target server ../
|
||||
fi
|
||||
|
||||
docker run --rm -it \
|
||||
-v "${PWD}/../server:/src" \
|
||||
--entrypoint="go" \
|
||||
neko_dev_server build -o "bin/neko" "cmd/neko/main.go"
|
29
.m1k1o/serve-client
Executable file
29
.m1k1o/serve-client
Executable file
@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -f ".env.default" ]
|
||||
then
|
||||
export $(cat .env.default | sed 's/#.*//g' | xargs)
|
||||
fi
|
||||
|
||||
if [ -f ".env" ]
|
||||
then
|
||||
export $(cat .env | sed 's/#.*//g' | xargs)
|
||||
fi
|
||||
|
||||
# use -i to install
|
||||
if [ ! -d "${PWD}/../client/node_modules" ] || [ "$1" == "-i" ]; then
|
||||
docker run --rm -it \
|
||||
-v "${PWD}/../client:/app" \
|
||||
--workdir="/app" \
|
||||
--entrypoint="npm" \
|
||||
node:14-buster-slim install
|
||||
fi
|
||||
|
||||
docker run --rm -it \
|
||||
-p "${CLIENT_PORT}:8080" \
|
||||
-v "${PWD}/../client:/app" \
|
||||
-e "VUE_APP_SERVER_PORT=${SERVER_PORT}" \
|
||||
--workdir="/app" \
|
||||
--entrypoint="npm" \
|
||||
node:14-buster-slim run serve
|
||||
|
32
.m1k1o/start-server
Executable file
32
.m1k1o/start-server
Executable file
@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -f ".env.default" ]
|
||||
then
|
||||
export $(cat .env.default | sed 's/#.*//g' | xargs)
|
||||
fi
|
||||
|
||||
if [ -f ".env" ]
|
||||
then
|
||||
export $(cat .env | sed 's/#.*//g' | xargs)
|
||||
fi
|
||||
|
||||
BINARY_PATH="${PWD}/../server/bin/neko"
|
||||
|
||||
# use -r to rebuild
|
||||
if [ ! -f "${BINARY_PATH}" ] || [ "$1" == "-r" ]; then
|
||||
./rebuild-server
|
||||
fi
|
||||
|
||||
docker run --rm -it \
|
||||
--name "neko_dev" \
|
||||
-p "${SERVER_PORT}:8080" \
|
||||
-p "${SERVER_EPR}:${SERVER_EPR}/udp" \
|
||||
-e "NEKO_SCREEN=1920x1080@60" \
|
||||
-e "NEKO_EPR=${SERVER_EPR}" \
|
||||
-e "NEKO_NAT1TO1=${SERVER_IP}" \
|
||||
-e "NEKO_ICELITE=true" \
|
||||
-e "NEKO_MAX_FPS=25" \
|
||||
-v "${BINARY_PATH}:/usr/bin/neko" \
|
||||
--shm-size=2G \
|
||||
--cap-add SYS_ADMIN \
|
||||
${BUILD_IMAGE}:${SERVER_TAG}
|
25
.m1k1o/tor-browser/Dockerfile
Normal file
25
.m1k1o/tor-browser/Dockerfile
Normal file
@ -0,0 +1,25 @@
|
||||
ARG BASE_IMAGE=m1k1o/neko:base
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
#
|
||||
# install dependencies
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends openbox curl xz-utils file libgtk-3-0 libdbus-glib-1-2; \
|
||||
#
|
||||
# download TOR browser
|
||||
DOWNLOAD_URI="$(curl -s -N https://www.torproject.org/download/ | grep -Po -m 1 '(?=(dist/torbrowser)).*(?<=.tar.xz)')"; \
|
||||
echo "Downloading $DOWNLOAD_URI"; \
|
||||
curl -sSL -o /tmp/tor.tar.xz "https://www.torproject.org/$DOWNLOAD_URI"; \
|
||||
tar -xvJf /tmp/tor.tar.xz -C /opt; \
|
||||
chown -R neko:neko /opt/tor-browser_en-US/; \
|
||||
rm -f /tmp/tor.tar.xz; \
|
||||
#
|
||||
# clean up
|
||||
apt-get --purge autoremove -y curl xz-utils; \
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*;
|
||||
|
||||
#
|
||||
# copy configuation file
|
||||
COPY supervisord.conf /etc/neko/supervisord/tor-browser.conf
|
||||
COPY openbox.xml /etc/neko/openbox.xml
|
763
.m1k1o/tor-browser/openbox.xml
Normal file
763
.m1k1o/tor-browser/openbox.xml
Normal file
@ -0,0 +1,763 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- Default openbox config but all window decorations are moved
|
||||
thereby making it harder to accidentally close the virtual browser -->
|
||||
|
||||
<openbox_config xmlns="http://openbox.org/3.4/rc"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<resistance>
|
||||
<strength>10</strength>
|
||||
<screen_edge_strength>20</screen_edge_strength>
|
||||
</resistance>
|
||||
|
||||
<applications>
|
||||
<!-- Match all windows and remove their decorations (obxprop | grep "^_OB_APP") -->
|
||||
<application class="Tor*" name="Navigator">
|
||||
<decor>no</decor>
|
||||
<maximized>true</maximized>
|
||||
<focus>yes</focus>
|
||||
<layer>normal</layer>
|
||||
</application>
|
||||
</applications>
|
||||
|
||||
<focus>
|
||||
<focusNew>yes</focusNew>
|
||||
<!-- always try to focus new windows when they appear. other rules do
|
||||
apply -->
|
||||
<followMouse>no</followMouse>
|
||||
<!-- move focus to a window when you move the mouse into it -->
|
||||
<focusLast>yes</focusLast>
|
||||
<!-- focus the last used window when changing desktops, instead of the one
|
||||
under the mouse pointer. when followMouse is enabled -->
|
||||
<underMouse>no</underMouse>
|
||||
<!-- move focus under the mouse, even when the mouse is not moving -->
|
||||
<focusDelay>200</focusDelay>
|
||||
<!-- when followMouse is enabled, the mouse must be inside the window for
|
||||
this many milliseconds (1000 = 1 sec) before moving focus to it -->
|
||||
<raiseOnFocus>no</raiseOnFocus>
|
||||
<!-- when followMouse is enabled, and a window is given focus by moving the
|
||||
mouse into it, also raise the window -->
|
||||
</focus>
|
||||
|
||||
<placement>
|
||||
<policy>Smart</policy>
|
||||
<!-- 'Smart' or 'UnderMouse' -->
|
||||
<center>yes</center>
|
||||
<!-- whether to place windows in the center of the free area found or
|
||||
the top left corner -->
|
||||
<monitor>Primary</monitor>
|
||||
<!-- with Smart placement on a multi-monitor system, try to place new windows
|
||||
on: 'Any' - any monitor, 'Mouse' - where the mouse is, 'Active' - where
|
||||
the active window is, 'Primary' - only on the primary monitor -->
|
||||
<primaryMonitor>1</primaryMonitor>
|
||||
<!-- The monitor where Openbox should place popup dialogs such as the
|
||||
focus cycling popup, or the desktop switch popup. It can be an index
|
||||
from 1, specifying a particular monitor. Or it can be one of the
|
||||
following: 'Mouse' - where the mouse is, or
|
||||
'Active' - where the active window is -->
|
||||
</placement>
|
||||
|
||||
<theme>
|
||||
<name>Clearlooks</name>
|
||||
<titleLayout>NLIMC</titleLayout>
|
||||
<!--
|
||||
available characters are NDSLIMC, each can occur at most once.
|
||||
N: window icon
|
||||
L: window label (AKA title).
|
||||
I: iconify
|
||||
M: maximize
|
||||
C: close
|
||||
S: shade (roll up/down)
|
||||
D: omnipresent (on all desktops).
|
||||
-->
|
||||
<keepBorder>yes</keepBorder>
|
||||
<animateIconify>yes</animateIconify>
|
||||
<font place="ActiveWindow">
|
||||
<name>sans</name>
|
||||
<size>8</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="InactiveWindow">
|
||||
<name>sans</name>
|
||||
<size>8</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="MenuHeader">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>normal</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="MenuItem">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>normal</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="ActiveOnScreenDisplay">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="InactiveOnScreenDisplay">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
</theme>
|
||||
|
||||
<desktops>
|
||||
<!-- this stuff is only used at startup, pagers allow you to change them
|
||||
during a session
|
||||
|
||||
these are default values to use when other ones are not already set
|
||||
by other applications, or saved in your session
|
||||
|
||||
use obconf if you want to change these without having to log out
|
||||
and back in -->
|
||||
<number>1</number>
|
||||
<firstdesk>1</firstdesk>
|
||||
<names>
|
||||
<!-- set names up here if you want to, like this:
|
||||
<name>desktop 1</name>
|
||||
<name>desktop 2</name>
|
||||
-->
|
||||
</names>
|
||||
<popupTime>875</popupTime>
|
||||
<!-- The number of milliseconds to show the popup for when switching
|
||||
desktops. Set this to 0 to disable the popup. -->
|
||||
</desktops>
|
||||
|
||||
<resize>
|
||||
<drawContents>yes</drawContents>
|
||||
<popupShow>Nonpixel</popupShow>
|
||||
<!-- 'Always', 'Never', or 'Nonpixel' (xterms and such) -->
|
||||
<popupPosition>Center</popupPosition>
|
||||
<!-- 'Center', 'Top', or 'Fixed' -->
|
||||
<popupFixedPosition>
|
||||
<!-- these are used if popupPosition is set to 'Fixed' -->
|
||||
|
||||
<x>10</x>
|
||||
<!-- positive number for distance from left edge, negative number for
|
||||
distance from right edge, or 'Center' -->
|
||||
<y>10</y>
|
||||
<!-- positive number for distance from top edge, negative number for
|
||||
distance from bottom edge, or 'Center' -->
|
||||
</popupFixedPosition>
|
||||
</resize>
|
||||
|
||||
<!-- You can reserve a portion of your screen where windows will not cover when
|
||||
they are maximized, or when they are initially placed.
|
||||
Many programs reserve space automatically, but you can use this in other
|
||||
cases. -->
|
||||
<margins>
|
||||
<top>0</top>
|
||||
<bottom>0</bottom>
|
||||
<left>0</left>
|
||||
<right>0</right>
|
||||
</margins>
|
||||
|
||||
<dock>
|
||||
<position>TopLeft</position>
|
||||
<!-- (Top|Bottom)(Left|Right|)|Top|Bottom|Left|Right|Floating -->
|
||||
<floatingX>0</floatingX>
|
||||
<floatingY>0</floatingY>
|
||||
<noStrut>no</noStrut>
|
||||
<stacking>Above</stacking>
|
||||
<!-- 'Above', 'Normal', or 'Below' -->
|
||||
<direction>Vertical</direction>
|
||||
<!-- 'Vertical' or 'Horizontal' -->
|
||||
<autoHide>no</autoHide>
|
||||
<hideDelay>300</hideDelay>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<showDelay>300</showDelay>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<moveButton>Middle</moveButton>
|
||||
<!-- 'Left', 'Middle', 'Right' -->
|
||||
</dock>
|
||||
|
||||
<keyboard>
|
||||
<chainQuitKey>C-g</chainQuitKey>
|
||||
|
||||
<!-- Keybindings for desktop switching -->
|
||||
<keybind key="C-A-Left">
|
||||
<action name="GoToDesktop"><to>left</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Right">
|
||||
<action name="GoToDesktop"><to>right</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Up">
|
||||
<action name="GoToDesktop"><to>up</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Down">
|
||||
<action name="GoToDesktop"><to>down</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Left">
|
||||
<action name="SendToDesktop"><to>left</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Right">
|
||||
<action name="SendToDesktop"><to>right</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Up">
|
||||
<action name="SendToDesktop"><to>up</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Down">
|
||||
<action name="SendToDesktop"><to>down</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="W-F1">
|
||||
<action name="GoToDesktop"><to>1</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F2">
|
||||
<action name="GoToDesktop"><to>2</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F3">
|
||||
<action name="GoToDesktop"><to>3</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F4">
|
||||
<action name="GoToDesktop"><to>4</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-d">
|
||||
<action name="ToggleShowDesktop"/>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for windows -->
|
||||
<keybind key="A-F4">
|
||||
<action name="Close"/>
|
||||
</keybind>
|
||||
<keybind key="A-Escape">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</keybind>
|
||||
<keybind key="A-space">
|
||||
<!--action name="ShowMenu"><menu>client-menu</menu></action-->
|
||||
</keybind>
|
||||
<!-- Take a screenshot of the current window with scrot when Alt+Print are pressed -->
|
||||
<keybind key="A-Print">
|
||||
<action name="Execute"><command>scrot -s</command></action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for window switching -->
|
||||
<keybind key="A-Tab">
|
||||
<action name="NextWindow">
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="A-S-Tab">
|
||||
<action name="PreviousWindow">
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Tab">
|
||||
<action name="NextWindow">
|
||||
<panels>yes</panels><desktop>yes</desktop>
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for window switching with the arrow keys -->
|
||||
<keybind key="W-S-Right">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>right</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Left">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>left</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Up">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>up</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Down">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>down</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for running applications -->
|
||||
<keybind key="W-e">
|
||||
<action name="Execute">
|
||||
<startupnotify>
|
||||
<enabled>true</enabled>
|
||||
<name>Konqueror</name>
|
||||
</startupnotify>
|
||||
<command>kfmclient openProfile filemanagement</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<!-- Launch scrot when Print is pressed -->
|
||||
<keybind key="Print">
|
||||
<action name="Execute"><command>scrot</command></action>
|
||||
</keybind>
|
||||
</keyboard>
|
||||
|
||||
<mouse>
|
||||
<dragThreshold>1</dragThreshold>
|
||||
<!-- number of pixels the mouse must move before a drag begins -->
|
||||
<doubleClickTime>500</doubleClickTime>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<screenEdgeWarpTime>400</screenEdgeWarpTime>
|
||||
<!-- Time before changing desktops when the pointer touches the edge of the
|
||||
screen while moving a window, in milliseconds (1000 = 1 second).
|
||||
Set this to 0 to disable warping -->
|
||||
<screenEdgeWarpMouse>false</screenEdgeWarpMouse>
|
||||
<!-- Set this to TRUE to move the mouse pointer across the desktop when
|
||||
switching due to hitting the edge of the screen -->
|
||||
|
||||
<context name="Frame">
|
||||
<mousebind button="A-Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Left" action="Click">
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Left" action="Drag">
|
||||
<action name="Move"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Right" action="Drag">
|
||||
<action name="Resize"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Middle" action="Press">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-S-Up" action="Click">
|
||||
<action name="SendToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-S-Down" action="Click">
|
||||
<action name="SendToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Titlebar">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Move"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="DoubleClick">
|
||||
<action name="ToggleMaximize"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="if">
|
||||
<shaded>no</shaded>
|
||||
<then>
|
||||
<action name="Shade"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
<action name="Lower"/>
|
||||
</then>
|
||||
</action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="if">
|
||||
<shaded>yes</shaded>
|
||||
<then>
|
||||
<action name="Unshade"/>
|
||||
<action name="Raise"/>
|
||||
</then>
|
||||
</action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Titlebar Top Right Bottom Left TLCorner TRCorner BRCorner BLCorner">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</mousebind>
|
||||
|
||||
<!--mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="Top">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>top</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Left">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>left</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Right">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>right</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Bottom">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>bottom</edge></action>
|
||||
</mousebind>
|
||||
|
||||
<!--mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="TRCorner BRCorner TLCorner BLCorner">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Client">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Icon">
|
||||
<!--mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="AllDesktops">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleOmnipresent"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Shade">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleShade"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Iconify">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="Iconify"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Maximize">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleMaximize"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Click">
|
||||
<action name="ToggleMaximize"><direction>vertical</direction></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Click">
|
||||
<action name="ToggleMaximize"><direction>horizontal</direction></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Close">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="Close"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Desktop">
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Root">
|
||||
<!-- Menus -->
|
||||
<!--mousebind button="Middle" action="Press">
|
||||
<action name="ShowMenu"><menu>client-list-combined-menu</menu></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="ShowMenu"><menu>root-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="MoveResize">
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
</mouse>
|
||||
|
||||
<menu>
|
||||
<!-- You can specify more than one menu file in here and they are all loaded,
|
||||
just don't make menu ids clash or, well, it'll be kind of pointless -->
|
||||
|
||||
<!-- default menu file (or custom one in $HOME/.config/openbox/) -->
|
||||
<!-- system menu files on Debian systems -->
|
||||
<!--file>/var/lib/openbox/debian-menu.xml</file-->
|
||||
<file>menu.xml</file>
|
||||
<hideDelay>200</hideDelay>
|
||||
<!-- if a press-release lasts longer than this setting (in milliseconds), the
|
||||
menu is hidden again -->
|
||||
<middle>no</middle>
|
||||
<!-- center submenus vertically about the parent entry -->
|
||||
<submenuShowDelay>100</submenuShowDelay>
|
||||
<!-- time to delay before showing a submenu after hovering over the parent
|
||||
entry.
|
||||
if this is a negative value, then the delay is infinite and the
|
||||
submenu will not be shown until it is clicked on -->
|
||||
<submenuHideDelay>400</submenuHideDelay>
|
||||
<!-- time to delay before hiding a submenu when selecting another
|
||||
entry in parent menu
|
||||
if this is a negative value, then the delay is infinite and the
|
||||
submenu will not be hidden until a different submenu is opened -->
|
||||
<showIcons>yes</showIcons>
|
||||
<!-- controls if icons appear in the client-list-(combined-)menu -->
|
||||
<manageDesktops>yes</manageDesktops>
|
||||
<!-- show the manage desktops section in the client-list-(combined-)menu -->
|
||||
</menu>
|
||||
|
||||
<applications>
|
||||
<!--
|
||||
# this is an example with comments through out. use these to make your
|
||||
# own rules, but without the comments of course.
|
||||
# you may use one or more of the name/class/role/title/type rules to specify
|
||||
# windows to match
|
||||
|
||||
<application name="the window's _OB_APP_NAME property (see obxprop)"
|
||||
class="the window's _OB_APP_CLASS property (see obxprop)"
|
||||
groupname="the window's _OB_APP_GROUP_NAME property (see obxprop)"
|
||||
groupclass="the window's _OB_APP_GROUP_CLASS property (see obxprop)"
|
||||
role="the window's _OB_APP_ROLE property (see obxprop)"
|
||||
title="the window's _OB_APP_TITLE property (see obxprop)"
|
||||
type="the window's _OB_APP_TYPE property (see obxprob)..
|
||||
(if unspecified, then it is 'dialog' for child windows)">
|
||||
# you may set only one of name/class/role/title/type, or you may use more
|
||||
# than one together to restrict your matches.
|
||||
|
||||
# the name, class, role, and title use simple wildcard matching such as those
|
||||
# used by a shell. you can use * to match any characters and ? to match
|
||||
# any single character.
|
||||
|
||||
# the type is one of: normal, dialog, splash, utility, menu, toolbar, dock,
|
||||
# or desktop
|
||||
|
||||
# when multiple rules match a window, they will all be applied, in the
|
||||
# order that they appear in this list
|
||||
|
||||
|
||||
# each rule element can be left out or set to 'default' to specify to not
|
||||
# change that attribute of the window
|
||||
|
||||
<decor>yes</decor>
|
||||
# enable or disable window decorations
|
||||
|
||||
<shade>no</shade>
|
||||
# make the window shaded when it appears, or not
|
||||
|
||||
<position force="no">
|
||||
# the position is only used if both an x and y coordinate are provided
|
||||
# (and not set to 'default')
|
||||
# when force is "yes", then the window will be placed here even if it
|
||||
# says you want it placed elsewhere. this is to override buggy
|
||||
# applications who refuse to behave
|
||||
<x>center</x>
|
||||
# a number like 50, or 'center' to center on screen. use a negative number
|
||||
# to start from the right (or bottom for <y>), ie -50 is 50 pixels from
|
||||
# the right edge (or bottom). use 'default' to specify using value
|
||||
# provided by the application, or chosen by openbox, instead.
|
||||
<y>200</y>
|
||||
<monitor>1</monitor>
|
||||
# specifies the monitor in a xinerama setup.
|
||||
# 1 is the first head, or 'mouse' for wherever the mouse is
|
||||
</position>
|
||||
|
||||
<size>
|
||||
# the size to make the window.
|
||||
<width>20</width>
|
||||
# a number like 20, or 'default' to use the size given by the application.
|
||||
# you can use fractions such as 1/2 or percentages such as 75% in which
|
||||
# case the value is relative to the size of the monitor that the window
|
||||
# appears on.
|
||||
<height>30%</height>
|
||||
</size>
|
||||
|
||||
<focus>yes</focus>
|
||||
# if the window should try be given focus when it appears. if this is set
|
||||
# to yes it doesn't guarantee the window will be given focus. some
|
||||
# restrictions may apply, but Openbox will try to
|
||||
|
||||
<desktop>1</desktop>
|
||||
# 1 is the first desktop, 'all' for all desktops
|
||||
|
||||
<layer>normal</layer>
|
||||
# 'above', 'normal', or 'below'
|
||||
|
||||
<iconic>no</iconic>
|
||||
# make the window iconified when it appears, or not
|
||||
|
||||
<skip_pager>no</skip_pager>
|
||||
# asks to not be shown in pagers
|
||||
|
||||
<skip_taskbar>no</skip_taskbar>
|
||||
# asks to not be shown in taskbars. window cycling actions will also
|
||||
# skip past such windows
|
||||
|
||||
<fullscreen>yes</fullscreen>
|
||||
# make the window in fullscreen mode when it appears
|
||||
|
||||
<maximized>true</maximized>
|
||||
# 'Horizontal', 'Vertical' or boolean (yes/no)
|
||||
</application>
|
||||
|
||||
# end of the example
|
||||
-->
|
||||
</applications>
|
||||
|
||||
</openbox_config>
|
24
.m1k1o/tor-browser/supervisord.conf
Normal file
24
.m1k1o/tor-browser/supervisord.conf
Normal file
@ -0,0 +1,24 @@
|
||||
[program:tor-browser]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/opt/tor-browser_en-US/Browser/start-tor-browser --display=%(ENV_DISPLAY)s --setDefaultBrowser --window-size 1280,720
|
||||
autorestart=true
|
||||
priority=800
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/tor-browser.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
||||
stderr_logfile=/var/log/neko/tor-browser.err.log
|
||||
stderr_logfile_maxbytes=100MB
|
||||
stderr_logfile_backups=10
|
||||
|
||||
[program:openbox]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/openbox --config-file /etc/neko/openbox.xml
|
||||
autorestart=true
|
||||
priority=300
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/openbox.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
43
.m1k1o/ungoogled-chromium/Dockerfile
Normal file
43
.m1k1o/ungoogled-chromium/Dockerfile
Normal file
@ -0,0 +1,43 @@
|
||||
ARG BASE_IMAGE=m1k1o/neko:base
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
ARG API_URL="https://api.github.com/repos/macchrome/linchrome/releases/latest"
|
||||
|
||||
#
|
||||
# install custom chromium build from woolyss with support for hevc/x265
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends wget unzip libatk1.0-0 libatk-bridge2.0-0 libatomic1 \
|
||||
libcups2 libgtk-3-0 libnss3 libpci3 libxcomposite1 libxss1 openbox xz-utils jq; \
|
||||
#
|
||||
# fetch latest release
|
||||
SRC_URL="$(wget -O - "${API_URL}" 2>/dev/null | jq -r ".assets[] | .browser_download_url" | grep tar.xz)"; \
|
||||
wget -O - /tmp/chromium.tar.xz "${SRC_URL}" | tar -xJf- -C /tmp; \
|
||||
mv /tmp/ungoogled-chromium_* /usr/lib/chromium; \
|
||||
#
|
||||
# make required changes for sandbox mode
|
||||
mv /usr/lib/chromium/chrome_sandbox /usr/lib/chromium/chrome-sandbox; \
|
||||
chown root:root /usr/lib/chromium/chrome-sandbox; \
|
||||
chmod 4755 /usr/lib/chromium/chrome-sandbox; \
|
||||
#
|
||||
# install widevine module
|
||||
WIDEVINE_VERSION=$(wget --quiet -O - https://dl.google.com/widevine-cdm/versions.txt | tail -n 1); \
|
||||
wget -O /tmp/widevine.zip "https://dl.google.com/widevine-cdm/$WIDEVINE_VERSION-linux-x64.zip"; \
|
||||
unzip -p /tmp/widevine.zip libwidevinecdm.so > /usr/lib/chromium/libwidevinecdm.so; \
|
||||
chmod 644 /usr/lib/chromium/libwidevinecdm.so; \
|
||||
rm /tmp/widevine.zip; \
|
||||
#
|
||||
# clean up
|
||||
apt-get --purge autoremove -y xz-utils jq; \
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
#
|
||||
# copy configuation files
|
||||
COPY supervisord.conf /etc/neko/supervisord/ungoogled-chromium.conf
|
||||
COPY preferences.json /usr/lib/chromium/master_preferences
|
||||
COPY policies.json /etc/chromium/policies/managed/policies.json
|
||||
COPY openbox.xml /etc/neko/openbox.xml
|
||||
|
||||
#
|
||||
# copy extensions and policy files
|
||||
COPY extensions /usr/share/chromium/extensions
|
Binary file not shown.
@ -1,4 +1,4 @@
|
||||
{
|
||||
"external_crx": "/usr/share/chromium/extensions/cjpalhdlnbpafiamejdnhcphjbkeiagm.crx",
|
||||
"external_version": "1.30.6"
|
||||
"external_version": "1.37.2"
|
||||
}
|
Binary file not shown.
@ -1,4 +1,4 @@
|
||||
{
|
||||
"external_crx": "/usr/share/chromium/extensions/fjoaledfpmneenckfbpdfhkmimnjocfa.crx",
|
||||
"external_version": "2.21.0"
|
||||
"external_version": "2.31.0"
|
||||
}
|
763
.m1k1o/ungoogled-chromium/openbox.xml
Normal file
763
.m1k1o/ungoogled-chromium/openbox.xml
Normal file
@ -0,0 +1,763 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- Default openbox config but all window decorations are moved
|
||||
thereby making it harder to accidentally close the virtual browser -->
|
||||
|
||||
<openbox_config xmlns="http://openbox.org/3.4/rc"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<resistance>
|
||||
<strength>10</strength>
|
||||
<screen_edge_strength>20</screen_edge_strength>
|
||||
</resistance>
|
||||
|
||||
<applications>
|
||||
<!-- Match all windows and remove their decorations (obxprop | grep "^_OB_APP") -->
|
||||
<application class="Chromium*" name="chromium-devel">
|
||||
<decor>no</decor>
|
||||
<maximized>true</maximized>
|
||||
<focus>yes</focus>
|
||||
<layer>normal</layer>
|
||||
</application>
|
||||
</applications>
|
||||
|
||||
<focus>
|
||||
<focusNew>yes</focusNew>
|
||||
<!-- always try to focus new windows when they appear. other rules do
|
||||
apply -->
|
||||
<followMouse>no</followMouse>
|
||||
<!-- move focus to a window when you move the mouse into it -->
|
||||
<focusLast>yes</focusLast>
|
||||
<!-- focus the last used window when changing desktops, instead of the one
|
||||
under the mouse pointer. when followMouse is enabled -->
|
||||
<underMouse>no</underMouse>
|
||||
<!-- move focus under the mouse, even when the mouse is not moving -->
|
||||
<focusDelay>200</focusDelay>
|
||||
<!-- when followMouse is enabled, the mouse must be inside the window for
|
||||
this many milliseconds (1000 = 1 sec) before moving focus to it -->
|
||||
<raiseOnFocus>no</raiseOnFocus>
|
||||
<!-- when followMouse is enabled, and a window is given focus by moving the
|
||||
mouse into it, also raise the window -->
|
||||
</focus>
|
||||
|
||||
<placement>
|
||||
<policy>Smart</policy>
|
||||
<!-- 'Smart' or 'UnderMouse' -->
|
||||
<center>yes</center>
|
||||
<!-- whether to place windows in the center of the free area found or
|
||||
the top left corner -->
|
||||
<monitor>Primary</monitor>
|
||||
<!-- with Smart placement on a multi-monitor system, try to place new windows
|
||||
on: 'Any' - any monitor, 'Mouse' - where the mouse is, 'Active' - where
|
||||
the active window is, 'Primary' - only on the primary monitor -->
|
||||
<primaryMonitor>1</primaryMonitor>
|
||||
<!-- The monitor where Openbox should place popup dialogs such as the
|
||||
focus cycling popup, or the desktop switch popup. It can be an index
|
||||
from 1, specifying a particular monitor. Or it can be one of the
|
||||
following: 'Mouse' - where the mouse is, or
|
||||
'Active' - where the active window is -->
|
||||
</placement>
|
||||
|
||||
<theme>
|
||||
<name>Clearlooks</name>
|
||||
<titleLayout>NLIMC</titleLayout>
|
||||
<!--
|
||||
available characters are NDSLIMC, each can occur at most once.
|
||||
N: window icon
|
||||
L: window label (AKA title).
|
||||
I: iconify
|
||||
M: maximize
|
||||
C: close
|
||||
S: shade (roll up/down)
|
||||
D: omnipresent (on all desktops).
|
||||
-->
|
||||
<keepBorder>yes</keepBorder>
|
||||
<animateIconify>yes</animateIconify>
|
||||
<font place="ActiveWindow">
|
||||
<name>sans</name>
|
||||
<size>8</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="InactiveWindow">
|
||||
<name>sans</name>
|
||||
<size>8</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="MenuHeader">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>normal</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="MenuItem">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>normal</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="ActiveOnScreenDisplay">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="InactiveOnScreenDisplay">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
</theme>
|
||||
|
||||
<desktops>
|
||||
<!-- this stuff is only used at startup, pagers allow you to change them
|
||||
during a session
|
||||
|
||||
these are default values to use when other ones are not already set
|
||||
by other applications, or saved in your session
|
||||
|
||||
use obconf if you want to change these without having to log out
|
||||
and back in -->
|
||||
<number>1</number>
|
||||
<firstdesk>1</firstdesk>
|
||||
<names>
|
||||
<!-- set names up here if you want to, like this:
|
||||
<name>desktop 1</name>
|
||||
<name>desktop 2</name>
|
||||
-->
|
||||
</names>
|
||||
<popupTime>875</popupTime>
|
||||
<!-- The number of milliseconds to show the popup for when switching
|
||||
desktops. Set this to 0 to disable the popup. -->
|
||||
</desktops>
|
||||
|
||||
<resize>
|
||||
<drawContents>yes</drawContents>
|
||||
<popupShow>Nonpixel</popupShow>
|
||||
<!-- 'Always', 'Never', or 'Nonpixel' (xterms and such) -->
|
||||
<popupPosition>Center</popupPosition>
|
||||
<!-- 'Center', 'Top', or 'Fixed' -->
|
||||
<popupFixedPosition>
|
||||
<!-- these are used if popupPosition is set to 'Fixed' -->
|
||||
|
||||
<x>10</x>
|
||||
<!-- positive number for distance from left edge, negative number for
|
||||
distance from right edge, or 'Center' -->
|
||||
<y>10</y>
|
||||
<!-- positive number for distance from top edge, negative number for
|
||||
distance from bottom edge, or 'Center' -->
|
||||
</popupFixedPosition>
|
||||
</resize>
|
||||
|
||||
<!-- You can reserve a portion of your screen where windows will not cover when
|
||||
they are maximized, or when they are initially placed.
|
||||
Many programs reserve space automatically, but you can use this in other
|
||||
cases. -->
|
||||
<margins>
|
||||
<top>0</top>
|
||||
<bottom>0</bottom>
|
||||
<left>0</left>
|
||||
<right>0</right>
|
||||
</margins>
|
||||
|
||||
<dock>
|
||||
<position>TopLeft</position>
|
||||
<!-- (Top|Bottom)(Left|Right|)|Top|Bottom|Left|Right|Floating -->
|
||||
<floatingX>0</floatingX>
|
||||
<floatingY>0</floatingY>
|
||||
<noStrut>no</noStrut>
|
||||
<stacking>Above</stacking>
|
||||
<!-- 'Above', 'Normal', or 'Below' -->
|
||||
<direction>Vertical</direction>
|
||||
<!-- 'Vertical' or 'Horizontal' -->
|
||||
<autoHide>no</autoHide>
|
||||
<hideDelay>300</hideDelay>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<showDelay>300</showDelay>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<moveButton>Middle</moveButton>
|
||||
<!-- 'Left', 'Middle', 'Right' -->
|
||||
</dock>
|
||||
|
||||
<keyboard>
|
||||
<chainQuitKey>C-g</chainQuitKey>
|
||||
|
||||
<!-- Keybindings for desktop switching -->
|
||||
<keybind key="C-A-Left">
|
||||
<action name="GoToDesktop"><to>left</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Right">
|
||||
<action name="GoToDesktop"><to>right</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Up">
|
||||
<action name="GoToDesktop"><to>up</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Down">
|
||||
<action name="GoToDesktop"><to>down</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Left">
|
||||
<action name="SendToDesktop"><to>left</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Right">
|
||||
<action name="SendToDesktop"><to>right</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Up">
|
||||
<action name="SendToDesktop"><to>up</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Down">
|
||||
<action name="SendToDesktop"><to>down</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="W-F1">
|
||||
<action name="GoToDesktop"><to>1</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F2">
|
||||
<action name="GoToDesktop"><to>2</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F3">
|
||||
<action name="GoToDesktop"><to>3</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F4">
|
||||
<action name="GoToDesktop"><to>4</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-d">
|
||||
<action name="ToggleShowDesktop"/>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for windows -->
|
||||
<keybind key="A-F4">
|
||||
<action name="Close"/>
|
||||
</keybind>
|
||||
<keybind key="A-Escape">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</keybind>
|
||||
<keybind key="A-space">
|
||||
<!--action name="ShowMenu"><menu>client-menu</menu></action-->
|
||||
</keybind>
|
||||
<!-- Take a screenshot of the current window with scrot when Alt+Print are pressed -->
|
||||
<keybind key="A-Print">
|
||||
<action name="Execute"><command>scrot -s</command></action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for window switching -->
|
||||
<keybind key="A-Tab">
|
||||
<action name="NextWindow">
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="A-S-Tab">
|
||||
<action name="PreviousWindow">
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Tab">
|
||||
<action name="NextWindow">
|
||||
<panels>yes</panels><desktop>yes</desktop>
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for window switching with the arrow keys -->
|
||||
<keybind key="W-S-Right">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>right</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Left">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>left</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Up">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>up</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Down">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>down</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for running applications -->
|
||||
<keybind key="W-e">
|
||||
<action name="Execute">
|
||||
<startupnotify>
|
||||
<enabled>true</enabled>
|
||||
<name>Konqueror</name>
|
||||
</startupnotify>
|
||||
<command>kfmclient openProfile filemanagement</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<!-- Launch scrot when Print is pressed -->
|
||||
<keybind key="Print">
|
||||
<action name="Execute"><command>scrot</command></action>
|
||||
</keybind>
|
||||
</keyboard>
|
||||
|
||||
<mouse>
|
||||
<dragThreshold>1</dragThreshold>
|
||||
<!-- number of pixels the mouse must move before a drag begins -->
|
||||
<doubleClickTime>500</doubleClickTime>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<screenEdgeWarpTime>400</screenEdgeWarpTime>
|
||||
<!-- Time before changing desktops when the pointer touches the edge of the
|
||||
screen while moving a window, in milliseconds (1000 = 1 second).
|
||||
Set this to 0 to disable warping -->
|
||||
<screenEdgeWarpMouse>false</screenEdgeWarpMouse>
|
||||
<!-- Set this to TRUE to move the mouse pointer across the desktop when
|
||||
switching due to hitting the edge of the screen -->
|
||||
|
||||
<context name="Frame">
|
||||
<mousebind button="A-Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Left" action="Click">
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Left" action="Drag">
|
||||
<action name="Move"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Right" action="Drag">
|
||||
<action name="Resize"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Middle" action="Press">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-S-Up" action="Click">
|
||||
<action name="SendToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-S-Down" action="Click">
|
||||
<action name="SendToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Titlebar">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Move"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="DoubleClick">
|
||||
<action name="ToggleMaximize"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="if">
|
||||
<shaded>no</shaded>
|
||||
<then>
|
||||
<action name="Shade"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
<action name="Lower"/>
|
||||
</then>
|
||||
</action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="if">
|
||||
<shaded>yes</shaded>
|
||||
<then>
|
||||
<action name="Unshade"/>
|
||||
<action name="Raise"/>
|
||||
</then>
|
||||
</action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Titlebar Top Right Bottom Left TLCorner TRCorner BRCorner BLCorner">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</mousebind>
|
||||
|
||||
<!--mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="Top">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>top</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Left">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>left</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Right">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>right</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Bottom">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>bottom</edge></action>
|
||||
</mousebind>
|
||||
|
||||
<!--mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="TRCorner BRCorner TLCorner BLCorner">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Client">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Icon">
|
||||
<!--mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="AllDesktops">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleOmnipresent"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Shade">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleShade"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Iconify">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="Iconify"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Maximize">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleMaximize"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Click">
|
||||
<action name="ToggleMaximize"><direction>vertical</direction></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Click">
|
||||
<action name="ToggleMaximize"><direction>horizontal</direction></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Close">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="Close"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Desktop">
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Root">
|
||||
<!-- Menus -->
|
||||
<!--mousebind button="Middle" action="Press">
|
||||
<action name="ShowMenu"><menu>client-list-combined-menu</menu></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="ShowMenu"><menu>root-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="MoveResize">
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
</mouse>
|
||||
|
||||
<menu>
|
||||
<!-- You can specify more than one menu file in here and they are all loaded,
|
||||
just don't make menu ids clash or, well, it'll be kind of pointless -->
|
||||
|
||||
<!-- default menu file (or custom one in $HOME/.config/openbox/) -->
|
||||
<!-- system menu files on Debian systems -->
|
||||
<!--file>/var/lib/openbox/debian-menu.xml</file-->
|
||||
<file>menu.xml</file>
|
||||
<hideDelay>200</hideDelay>
|
||||
<!-- if a press-release lasts longer than this setting (in milliseconds), the
|
||||
menu is hidden again -->
|
||||
<middle>no</middle>
|
||||
<!-- center submenus vertically about the parent entry -->
|
||||
<submenuShowDelay>100</submenuShowDelay>
|
||||
<!-- time to delay before showing a submenu after hovering over the parent
|
||||
entry.
|
||||
if this is a negative value, then the delay is infinite and the
|
||||
submenu will not be shown until it is clicked on -->
|
||||
<submenuHideDelay>400</submenuHideDelay>
|
||||
<!-- time to delay before hiding a submenu when selecting another
|
||||
entry in parent menu
|
||||
if this is a negative value, then the delay is infinite and the
|
||||
submenu will not be hidden until a different submenu is opened -->
|
||||
<showIcons>yes</showIcons>
|
||||
<!-- controls if icons appear in the client-list-(combined-)menu -->
|
||||
<manageDesktops>yes</manageDesktops>
|
||||
<!-- show the manage desktops section in the client-list-(combined-)menu -->
|
||||
</menu>
|
||||
|
||||
<applications>
|
||||
<!--
|
||||
# this is an example with comments through out. use these to make your
|
||||
# own rules, but without the comments of course.
|
||||
# you may use one or more of the name/class/role/title/type rules to specify
|
||||
# windows to match
|
||||
|
||||
<application name="the window's _OB_APP_NAME property (see obxprop)"
|
||||
class="the window's _OB_APP_CLASS property (see obxprop)"
|
||||
groupname="the window's _OB_APP_GROUP_NAME property (see obxprop)"
|
||||
groupclass="the window's _OB_APP_GROUP_CLASS property (see obxprop)"
|
||||
role="the window's _OB_APP_ROLE property (see obxprop)"
|
||||
title="the window's _OB_APP_TITLE property (see obxprop)"
|
||||
type="the window's _OB_APP_TYPE property (see obxprob)..
|
||||
(if unspecified, then it is 'dialog' for child windows)">
|
||||
# you may set only one of name/class/role/title/type, or you may use more
|
||||
# than one together to restrict your matches.
|
||||
|
||||
# the name, class, role, and title use simple wildcard matching such as those
|
||||
# used by a shell. you can use * to match any characters and ? to match
|
||||
# any single character.
|
||||
|
||||
# the type is one of: normal, dialog, splash, utility, menu, toolbar, dock,
|
||||
# or desktop
|
||||
|
||||
# when multiple rules match a window, they will all be applied, in the
|
||||
# order that they appear in this list
|
||||
|
||||
|
||||
# each rule element can be left out or set to 'default' to specify to not
|
||||
# change that attribute of the window
|
||||
|
||||
<decor>yes</decor>
|
||||
# enable or disable window decorations
|
||||
|
||||
<shade>no</shade>
|
||||
# make the window shaded when it appears, or not
|
||||
|
||||
<position force="no">
|
||||
# the position is only used if both an x and y coordinate are provided
|
||||
# (and not set to 'default')
|
||||
# when force is "yes", then the window will be placed here even if it
|
||||
# says you want it placed elsewhere. this is to override buggy
|
||||
# applications who refuse to behave
|
||||
<x>center</x>
|
||||
# a number like 50, or 'center' to center on screen. use a negative number
|
||||
# to start from the right (or bottom for <y>), ie -50 is 50 pixels from
|
||||
# the right edge (or bottom). use 'default' to specify using value
|
||||
# provided by the application, or chosen by openbox, instead.
|
||||
<y>200</y>
|
||||
<monitor>1</monitor>
|
||||
# specifies the monitor in a xinerama setup.
|
||||
# 1 is the first head, or 'mouse' for wherever the mouse is
|
||||
</position>
|
||||
|
||||
<size>
|
||||
# the size to make the window.
|
||||
<width>20</width>
|
||||
# a number like 20, or 'default' to use the size given by the application.
|
||||
# you can use fractions such as 1/2 or percentages such as 75% in which
|
||||
# case the value is relative to the size of the monitor that the window
|
||||
# appears on.
|
||||
<height>30%</height>
|
||||
</size>
|
||||
|
||||
<focus>yes</focus>
|
||||
# if the window should try be given focus when it appears. if this is set
|
||||
# to yes it doesn't guarantee the window will be given focus. some
|
||||
# restrictions may apply, but Openbox will try to
|
||||
|
||||
<desktop>1</desktop>
|
||||
# 1 is the first desktop, 'all' for all desktops
|
||||
|
||||
<layer>normal</layer>
|
||||
# 'above', 'normal', or 'below'
|
||||
|
||||
<iconic>no</iconic>
|
||||
# make the window iconified when it appears, or not
|
||||
|
||||
<skip_pager>no</skip_pager>
|
||||
# asks to not be shown in pagers
|
||||
|
||||
<skip_taskbar>no</skip_taskbar>
|
||||
# asks to not be shown in taskbars. window cycling actions will also
|
||||
# skip past such windows
|
||||
|
||||
<fullscreen>yes</fullscreen>
|
||||
# make the window in fullscreen mode when it appears
|
||||
|
||||
<maximized>true</maximized>
|
||||
# 'Horizontal', 'Vertical' or boolean (yes/no)
|
||||
</application>
|
||||
|
||||
# end of the example
|
||||
-->
|
||||
</applications>
|
||||
|
||||
</openbox_config>
|
34
.m1k1o/ungoogled-chromium/policies.json
Normal file
34
.m1k1o/ungoogled-chromium/policies.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"HomepageLocation": "",
|
||||
"AutoFillEnabled": false,
|
||||
"AutofillAddressEnabled": false,
|
||||
"AutofillCreditCardEnabled": false,
|
||||
"BrowserSignin": 0,
|
||||
"DefaultNotificationsSetting": 2,
|
||||
"DeveloperToolsAvailability": 2,
|
||||
"EditBookmarksEnabled": false,
|
||||
"FullscreenAllowed": true,
|
||||
"IncognitoModeAvailability": 1,
|
||||
"SyncDisabled": true,
|
||||
"AutoplayAllowed": true,
|
||||
"BrowserAddPersonEnabled": false,
|
||||
"BrowserGuestModeEnabled": false,
|
||||
"DefaultPopupsSetting": 2,
|
||||
"DownloadRestrictions": 3,
|
||||
"VideoCaptureAllowed": true,
|
||||
"AllowFileSelectionDialogs": false,
|
||||
"PromptForDownloadLocation": false,
|
||||
"BookmarkBarEnabled": false,
|
||||
"PasswordManagerEnabled": false,
|
||||
"URLBlacklist": [
|
||||
"file://*",
|
||||
"chrome://policy"
|
||||
],
|
||||
"ExtensionInstallWhitelist": [
|
||||
"cjpalhdlnbpafiamejdnhcphjbkeiagm",
|
||||
"fjoaledfpmneenckfbpdfhkmimnjocfa"
|
||||
],
|
||||
"ExtensionInstallBlacklist": [
|
||||
"*"
|
||||
]
|
||||
}
|
110
.m1k1o/ungoogled-chromium/preferences.json
Normal file
110
.m1k1o/ungoogled-chromium/preferences.json
Normal file
@ -0,0 +1,110 @@
|
||||
{
|
||||
"homepage": "http://www.google.com",
|
||||
"homepage_is_newtabpage": false,
|
||||
"first_run_tabs": [
|
||||
"https://www.google.com/_/chrome/newtab?ie=UTF-8"
|
||||
],
|
||||
"custom_links": {
|
||||
"initialized": true,
|
||||
"list": [
|
||||
{
|
||||
"title": "YouTube",
|
||||
"url": "https://www.youtube.com/"
|
||||
},
|
||||
{
|
||||
"title": "Netflix",
|
||||
"url": "https://netflix.com"
|
||||
},
|
||||
{
|
||||
"title": "Hulu",
|
||||
"url": "https://www.hulu.com/"
|
||||
},
|
||||
{
|
||||
"title": "9Anime",
|
||||
"url": "https://9anime.to/"
|
||||
},
|
||||
{
|
||||
"title": "Crunchy Roll",
|
||||
"url": "https://www.crunchyroll.com/"
|
||||
},
|
||||
{
|
||||
"title": "Funimation",
|
||||
"url": "https://www.funimation.com/"
|
||||
},
|
||||
{
|
||||
"title": "Disney+",
|
||||
"url": "https://www.disneyplus.com/"
|
||||
},
|
||||
{
|
||||
"title": "HBO Now",
|
||||
"url": "https://play.hbonow.com/"
|
||||
},
|
||||
{
|
||||
"title": "Amazon Video",
|
||||
"url": "https://www.amazon.com/Amazon-Video/b?node=2858778011"
|
||||
},
|
||||
{
|
||||
"title": "VRV",
|
||||
"url": "https://vrv.co/"
|
||||
},
|
||||
{
|
||||
"title": "Twitch",
|
||||
"url": "https://www.twitch.tv/"
|
||||
},
|
||||
{
|
||||
"title": "Mixer",
|
||||
"url": "https://mixer.com/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"browser": {
|
||||
"custom_chrome_frame": false,
|
||||
"show_home_button": true,
|
||||
"window_placement": {
|
||||
"maximized": true
|
||||
}
|
||||
},
|
||||
"bookmark_bar": {
|
||||
"show_on_all_tabs": false
|
||||
},
|
||||
"sync_promo": {
|
||||
"show_on_first_run_allowed": false
|
||||
},
|
||||
"distribution": {
|
||||
"import_bookmarks_from_file": "bookmarks.html",
|
||||
"import_bookmarks": true,
|
||||
"import_history": true,
|
||||
"import_home_page": true,
|
||||
"import_search_engine": true,
|
||||
"ping_delay": 60,
|
||||
"do_not_create_desktop_shortcut": true,
|
||||
"do_not_create_quick_launch_shortcut": true,
|
||||
"do_not_create_taskbar_shortcut": true,
|
||||
"do_not_launch_chrome": true,
|
||||
"do_not_register_for_update_launch": true,
|
||||
"make_chrome_default": true,
|
||||
"make_chrome_default_for_user": true,
|
||||
"system_level": false,
|
||||
"verbose_logging": false
|
||||
},
|
||||
"profile": {
|
||||
"avatar_index": 19,
|
||||
"default_content_setting_values": {
|
||||
"clipboard": 2,
|
||||
"cookies": 4,
|
||||
"geolocation": 2,
|
||||
"media_stream_camera": 2,
|
||||
"media_stream_mic": 2,
|
||||
"midi_sysex": 2,
|
||||
"payment_handler": 2,
|
||||
"usb_guard": 2
|
||||
},
|
||||
"name": "neko",
|
||||
"using_default_avatar": false,
|
||||
"using_default_name": false,
|
||||
"using_gaia_avatar": false
|
||||
},
|
||||
"signin": {
|
||||
"allowed": false
|
||||
}
|
||||
}
|
21
.m1k1o/ungoogled-chromium/supervisord.conf
Normal file
21
.m1k1o/ungoogled-chromium/supervisord.conf
Normal file
@ -0,0 +1,21 @@
|
||||
[program:ungoogled-chromium]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/lib/chromium/chrome-wrapper --window-position=0,0 --display=%(ENV_DISPLAY)s --start-maximized --bwsi --test-type --force-dark-mode --disable-file-system --disable-gpu --disable-software-rasterizer --disable-dev-shm-usage
|
||||
autorestart=true
|
||||
priority=800
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/ungoogled-chromium.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
||||
|
||||
[program:openbox]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/openbox --config-file /etc/neko/openbox.xml
|
||||
autorestart=true
|
||||
priority=300
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/openbox.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
18
.m1k1o/vlc/Dockerfile
Normal file
18
.m1k1o/vlc/Dockerfile
Normal file
@ -0,0 +1,18 @@
|
||||
ARG BASE_IMAGE=m1k1o/neko:base
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
#
|
||||
# install vlc
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends openbox vlc; \
|
||||
#
|
||||
# clean up
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
ENV VLC_MEDIA="/media"
|
||||
|
||||
#
|
||||
# copy configuation files
|
||||
COPY supervisord.conf /etc/neko/supervisord/vlc.conf
|
||||
COPY openbox.xml /etc/neko/openbox.xml
|
763
.m1k1o/vlc/openbox.xml
Normal file
763
.m1k1o/vlc/openbox.xml
Normal file
@ -0,0 +1,763 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- Default openbox config but all window decorations are moved
|
||||
thereby making it harder to accidentally close the virtual browser -->
|
||||
|
||||
<openbox_config xmlns="http://openbox.org/3.4/rc"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<resistance>
|
||||
<strength>10</strength>
|
||||
<screen_edge_strength>20</screen_edge_strength>
|
||||
</resistance>
|
||||
|
||||
<applications>
|
||||
<!-- Match all windows and remove their decorations (obxprop | grep "^_OB_APP") -->
|
||||
<application class="vlc" name="vlc" role="vlc-main">
|
||||
<decor>no</decor>
|
||||
<maximized>true</maximized>
|
||||
<focus>yes</focus>
|
||||
<layer>normal</layer>
|
||||
</application>
|
||||
</applications>
|
||||
|
||||
<focus>
|
||||
<focusNew>yes</focusNew>
|
||||
<!-- always try to focus new windows when they appear. other rules do
|
||||
apply -->
|
||||
<followMouse>no</followMouse>
|
||||
<!-- move focus to a window when you move the mouse into it -->
|
||||
<focusLast>yes</focusLast>
|
||||
<!-- focus the last used window when changing desktops, instead of the one
|
||||
under the mouse pointer. when followMouse is enabled -->
|
||||
<underMouse>no</underMouse>
|
||||
<!-- move focus under the mouse, even when the mouse is not moving -->
|
||||
<focusDelay>200</focusDelay>
|
||||
<!-- when followMouse is enabled, the mouse must be inside the window for
|
||||
this many milliseconds (1000 = 1 sec) before moving focus to it -->
|
||||
<raiseOnFocus>no</raiseOnFocus>
|
||||
<!-- when followMouse is enabled, and a window is given focus by moving the
|
||||
mouse into it, also raise the window -->
|
||||
</focus>
|
||||
|
||||
<placement>
|
||||
<policy>Smart</policy>
|
||||
<!-- 'Smart' or 'UnderMouse' -->
|
||||
<center>yes</center>
|
||||
<!-- whether to place windows in the center of the free area found or
|
||||
the top left corner -->
|
||||
<monitor>Primary</monitor>
|
||||
<!-- with Smart placement on a multi-monitor system, try to place new windows
|
||||
on: 'Any' - any monitor, 'Mouse' - where the mouse is, 'Active' - where
|
||||
the active window is, 'Primary' - only on the primary monitor -->
|
||||
<primaryMonitor>1</primaryMonitor>
|
||||
<!-- The monitor where Openbox should place popup dialogs such as the
|
||||
focus cycling popup, or the desktop switch popup. It can be an index
|
||||
from 1, specifying a particular monitor. Or it can be one of the
|
||||
following: 'Mouse' - where the mouse is, or
|
||||
'Active' - where the active window is -->
|
||||
</placement>
|
||||
|
||||
<theme>
|
||||
<name>Clearlooks</name>
|
||||
<titleLayout>NLIMC</titleLayout>
|
||||
<!--
|
||||
available characters are NDSLIMC, each can occur at most once.
|
||||
N: window icon
|
||||
L: window label (AKA title).
|
||||
I: iconify
|
||||
M: maximize
|
||||
C: close
|
||||
S: shade (roll up/down)
|
||||
D: omnipresent (on all desktops).
|
||||
-->
|
||||
<keepBorder>yes</keepBorder>
|
||||
<animateIconify>yes</animateIconify>
|
||||
<font place="ActiveWindow">
|
||||
<name>sans</name>
|
||||
<size>8</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="InactiveWindow">
|
||||
<name>sans</name>
|
||||
<size>8</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="MenuHeader">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>normal</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="MenuItem">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>normal</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="ActiveOnScreenDisplay">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="InactiveOnScreenDisplay">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
</theme>
|
||||
|
||||
<desktops>
|
||||
<!-- this stuff is only used at startup, pagers allow you to change them
|
||||
during a session
|
||||
|
||||
these are default values to use when other ones are not already set
|
||||
by other applications, or saved in your session
|
||||
|
||||
use obconf if you want to change these without having to log out
|
||||
and back in -->
|
||||
<number>1</number>
|
||||
<firstdesk>1</firstdesk>
|
||||
<names>
|
||||
<!-- set names up here if you want to, like this:
|
||||
<name>desktop 1</name>
|
||||
<name>desktop 2</name>
|
||||
-->
|
||||
</names>
|
||||
<popupTime>875</popupTime>
|
||||
<!-- The number of milliseconds to show the popup for when switching
|
||||
desktops. Set this to 0 to disable the popup. -->
|
||||
</desktops>
|
||||
|
||||
<resize>
|
||||
<drawContents>yes</drawContents>
|
||||
<popupShow>Nonpixel</popupShow>
|
||||
<!-- 'Always', 'Never', or 'Nonpixel' (xterms and such) -->
|
||||
<popupPosition>Center</popupPosition>
|
||||
<!-- 'Center', 'Top', or 'Fixed' -->
|
||||
<popupFixedPosition>
|
||||
<!-- these are used if popupPosition is set to 'Fixed' -->
|
||||
|
||||
<x>10</x>
|
||||
<!-- positive number for distance from left edge, negative number for
|
||||
distance from right edge, or 'Center' -->
|
||||
<y>10</y>
|
||||
<!-- positive number for distance from top edge, negative number for
|
||||
distance from bottom edge, or 'Center' -->
|
||||
</popupFixedPosition>
|
||||
</resize>
|
||||
|
||||
<!-- You can reserve a portion of your screen where windows will not cover when
|
||||
they are maximized, or when they are initially placed.
|
||||
Many programs reserve space automatically, but you can use this in other
|
||||
cases. -->
|
||||
<margins>
|
||||
<top>0</top>
|
||||
<bottom>0</bottom>
|
||||
<left>0</left>
|
||||
<right>0</right>
|
||||
</margins>
|
||||
|
||||
<dock>
|
||||
<position>TopLeft</position>
|
||||
<!-- (Top|Bottom)(Left|Right|)|Top|Bottom|Left|Right|Floating -->
|
||||
<floatingX>0</floatingX>
|
||||
<floatingY>0</floatingY>
|
||||
<noStrut>no</noStrut>
|
||||
<stacking>Above</stacking>
|
||||
<!-- 'Above', 'Normal', or 'Below' -->
|
||||
<direction>Vertical</direction>
|
||||
<!-- 'Vertical' or 'Horizontal' -->
|
||||
<autoHide>no</autoHide>
|
||||
<hideDelay>300</hideDelay>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<showDelay>300</showDelay>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<moveButton>Middle</moveButton>
|
||||
<!-- 'Left', 'Middle', 'Right' -->
|
||||
</dock>
|
||||
|
||||
<keyboard>
|
||||
<chainQuitKey>C-g</chainQuitKey>
|
||||
|
||||
<!-- Keybindings for desktop switching -->
|
||||
<keybind key="C-A-Left">
|
||||
<action name="GoToDesktop"><to>left</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Right">
|
||||
<action name="GoToDesktop"><to>right</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Up">
|
||||
<action name="GoToDesktop"><to>up</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Down">
|
||||
<action name="GoToDesktop"><to>down</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Left">
|
||||
<action name="SendToDesktop"><to>left</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Right">
|
||||
<action name="SendToDesktop"><to>right</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Up">
|
||||
<action name="SendToDesktop"><to>up</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Down">
|
||||
<action name="SendToDesktop"><to>down</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="W-F1">
|
||||
<action name="GoToDesktop"><to>1</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F2">
|
||||
<action name="GoToDesktop"><to>2</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F3">
|
||||
<action name="GoToDesktop"><to>3</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F4">
|
||||
<action name="GoToDesktop"><to>4</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-d">
|
||||
<action name="ToggleShowDesktop"/>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for windows -->
|
||||
<keybind key="A-F4">
|
||||
<action name="Close"/>
|
||||
</keybind>
|
||||
<keybind key="A-Escape">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</keybind>
|
||||
<keybind key="A-space">
|
||||
<!--action name="ShowMenu"><menu>client-menu</menu></action-->
|
||||
</keybind>
|
||||
<!-- Take a screenshot of the current window with scrot when Alt+Print are pressed -->
|
||||
<keybind key="A-Print">
|
||||
<action name="Execute"><command>scrot -s</command></action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for window switching -->
|
||||
<keybind key="A-Tab">
|
||||
<action name="NextWindow">
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="A-S-Tab">
|
||||
<action name="PreviousWindow">
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Tab">
|
||||
<action name="NextWindow">
|
||||
<panels>yes</panels><desktop>yes</desktop>
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for window switching with the arrow keys -->
|
||||
<keybind key="W-S-Right">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>right</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Left">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>left</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Up">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>up</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Down">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>down</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for running applications -->
|
||||
<keybind key="W-e">
|
||||
<action name="Execute">
|
||||
<startupnotify>
|
||||
<enabled>true</enabled>
|
||||
<name>Konqueror</name>
|
||||
</startupnotify>
|
||||
<command>kfmclient openProfile filemanagement</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<!-- Launch scrot when Print is pressed -->
|
||||
<keybind key="Print">
|
||||
<action name="Execute"><command>scrot</command></action>
|
||||
</keybind>
|
||||
</keyboard>
|
||||
|
||||
<mouse>
|
||||
<dragThreshold>1</dragThreshold>
|
||||
<!-- number of pixels the mouse must move before a drag begins -->
|
||||
<doubleClickTime>500</doubleClickTime>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<screenEdgeWarpTime>400</screenEdgeWarpTime>
|
||||
<!-- Time before changing desktops when the pointer touches the edge of the
|
||||
screen while moving a window, in milliseconds (1000 = 1 second).
|
||||
Set this to 0 to disable warping -->
|
||||
<screenEdgeWarpMouse>false</screenEdgeWarpMouse>
|
||||
<!-- Set this to TRUE to move the mouse pointer across the desktop when
|
||||
switching due to hitting the edge of the screen -->
|
||||
|
||||
<context name="Frame">
|
||||
<mousebind button="A-Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Left" action="Click">
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Left" action="Drag">
|
||||
<action name="Move"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Right" action="Drag">
|
||||
<action name="Resize"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Middle" action="Press">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-S-Up" action="Click">
|
||||
<action name="SendToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-S-Down" action="Click">
|
||||
<action name="SendToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Titlebar">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Move"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="DoubleClick">
|
||||
<action name="ToggleMaximize"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="if">
|
||||
<shaded>no</shaded>
|
||||
<then>
|
||||
<action name="Shade"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
<action name="Lower"/>
|
||||
</then>
|
||||
</action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="if">
|
||||
<shaded>yes</shaded>
|
||||
<then>
|
||||
<action name="Unshade"/>
|
||||
<action name="Raise"/>
|
||||
</then>
|
||||
</action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Titlebar Top Right Bottom Left TLCorner TRCorner BRCorner BLCorner">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</mousebind>
|
||||
|
||||
<!--mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="Top">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>top</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Left">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>left</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Right">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>right</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Bottom">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>bottom</edge></action>
|
||||
</mousebind>
|
||||
|
||||
<!--mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="TRCorner BRCorner TLCorner BLCorner">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Client">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Icon">
|
||||
<!--mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="AllDesktops">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleOmnipresent"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Shade">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleShade"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Iconify">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="Iconify"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Maximize">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleMaximize"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Click">
|
||||
<action name="ToggleMaximize"><direction>vertical</direction></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Click">
|
||||
<action name="ToggleMaximize"><direction>horizontal</direction></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Close">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="Close"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Desktop">
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Root">
|
||||
<!-- Menus -->
|
||||
<!--mousebind button="Middle" action="Press">
|
||||
<action name="ShowMenu"><menu>client-list-combined-menu</menu></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="ShowMenu"><menu>root-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="MoveResize">
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
</mouse>
|
||||
|
||||
<menu>
|
||||
<!-- You can specify more than one menu file in here and they are all loaded,
|
||||
just don't make menu ids clash or, well, it'll be kind of pointless -->
|
||||
|
||||
<!-- default menu file (or custom one in $HOME/.config/openbox/) -->
|
||||
<!-- system menu files on Debian systems -->
|
||||
<!--file>/var/lib/openbox/debian-menu.xml</file-->
|
||||
<file>menu.xml</file>
|
||||
<hideDelay>200</hideDelay>
|
||||
<!-- if a press-release lasts longer than this setting (in milliseconds), the
|
||||
menu is hidden again -->
|
||||
<middle>no</middle>
|
||||
<!-- center submenus vertically about the parent entry -->
|
||||
<submenuShowDelay>100</submenuShowDelay>
|
||||
<!-- time to delay before showing a submenu after hovering over the parent
|
||||
entry.
|
||||
if this is a negative value, then the delay is infinite and the
|
||||
submenu will not be shown until it is clicked on -->
|
||||
<submenuHideDelay>400</submenuHideDelay>
|
||||
<!-- time to delay before hiding a submenu when selecting another
|
||||
entry in parent menu
|
||||
if this is a negative value, then the delay is infinite and the
|
||||
submenu will not be hidden until a different submenu is opened -->
|
||||
<showIcons>yes</showIcons>
|
||||
<!-- controls if icons appear in the client-list-(combined-)menu -->
|
||||
<manageDesktops>yes</manageDesktops>
|
||||
<!-- show the manage desktops section in the client-list-(combined-)menu -->
|
||||
</menu>
|
||||
|
||||
<applications>
|
||||
<!--
|
||||
# this is an example with comments through out. use these to make your
|
||||
# own rules, but without the comments of course.
|
||||
# you may use one or more of the name/class/role/title/type rules to specify
|
||||
# windows to match
|
||||
|
||||
<application name="the window's _OB_APP_NAME property (see obxprop)"
|
||||
class="the window's _OB_APP_CLASS property (see obxprop)"
|
||||
groupname="the window's _OB_APP_GROUP_NAME property (see obxprop)"
|
||||
groupclass="the window's _OB_APP_GROUP_CLASS property (see obxprop)"
|
||||
role="the window's _OB_APP_ROLE property (see obxprop)"
|
||||
title="the window's _OB_APP_TITLE property (see obxprop)"
|
||||
type="the window's _OB_APP_TYPE property (see obxprob)..
|
||||
(if unspecified, then it is 'dialog' for child windows)">
|
||||
# you may set only one of name/class/role/title/type, or you may use more
|
||||
# than one together to restrict your matches.
|
||||
|
||||
# the name, class, role, and title use simple wildcard matching such as those
|
||||
# used by a shell. you can use * to match any characters and ? to match
|
||||
# any single character.
|
||||
|
||||
# the type is one of: normal, dialog, splash, utility, menu, toolbar, dock,
|
||||
# or desktop
|
||||
|
||||
# when multiple rules match a window, they will all be applied, in the
|
||||
# order that they appear in this list
|
||||
|
||||
|
||||
# each rule element can be left out or set to 'default' to specify to not
|
||||
# change that attribute of the window
|
||||
|
||||
<decor>yes</decor>
|
||||
# enable or disable window decorations
|
||||
|
||||
<shade>no</shade>
|
||||
# make the window shaded when it appears, or not
|
||||
|
||||
<position force="no">
|
||||
# the position is only used if both an x and y coordinate are provided
|
||||
# (and not set to 'default')
|
||||
# when force is "yes", then the window will be placed here even if it
|
||||
# says you want it placed elsewhere. this is to override buggy
|
||||
# applications who refuse to behave
|
||||
<x>center</x>
|
||||
# a number like 50, or 'center' to center on screen. use a negative number
|
||||
# to start from the right (or bottom for <y>), ie -50 is 50 pixels from
|
||||
# the right edge (or bottom). use 'default' to specify using value
|
||||
# provided by the application, or chosen by openbox, instead.
|
||||
<y>200</y>
|
||||
<monitor>1</monitor>
|
||||
# specifies the monitor in a xinerama setup.
|
||||
# 1 is the first head, or 'mouse' for wherever the mouse is
|
||||
</position>
|
||||
|
||||
<size>
|
||||
# the size to make the window.
|
||||
<width>20</width>
|
||||
# a number like 20, or 'default' to use the size given by the application.
|
||||
# you can use fractions such as 1/2 or percentages such as 75% in which
|
||||
# case the value is relative to the size of the monitor that the window
|
||||
# appears on.
|
||||
<height>30%</height>
|
||||
</size>
|
||||
|
||||
<focus>yes</focus>
|
||||
# if the window should try be given focus when it appears. if this is set
|
||||
# to yes it doesn't guarantee the window will be given focus. some
|
||||
# restrictions may apply, but Openbox will try to
|
||||
|
||||
<desktop>1</desktop>
|
||||
# 1 is the first desktop, 'all' for all desktops
|
||||
|
||||
<layer>normal</layer>
|
||||
# 'above', 'normal', or 'below'
|
||||
|
||||
<iconic>no</iconic>
|
||||
# make the window iconified when it appears, or not
|
||||
|
||||
<skip_pager>no</skip_pager>
|
||||
# asks to not be shown in pagers
|
||||
|
||||
<skip_taskbar>no</skip_taskbar>
|
||||
# asks to not be shown in taskbars. window cycling actions will also
|
||||
# skip past such windows
|
||||
|
||||
<fullscreen>yes</fullscreen>
|
||||
# make the window in fullscreen mode when it appears
|
||||
|
||||
<maximized>true</maximized>
|
||||
# 'Horizontal', 'Vertical' or boolean (yes/no)
|
||||
</application>
|
||||
|
||||
# end of the example
|
||||
-->
|
||||
</applications>
|
||||
|
||||
</openbox_config>
|
21
.m1k1o/vlc/supervisord.conf
Normal file
21
.m1k1o/vlc/supervisord.conf
Normal file
@ -0,0 +1,21 @@
|
||||
[program:vlc]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/vlc --x11-display=%(ENV_DISPLAY)s --no-qt-privacy-ask %(ENV_VLC_MEDIA)s
|
||||
autorestart=true
|
||||
priority=800
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/vlc.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
||||
|
||||
[program:openbox]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/openbox --config-file /etc/neko/openbox.xml
|
||||
autorestart=true
|
||||
priority=300
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/openbox.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
18
.m1k1o/vncviewer/Dockerfile
Normal file
18
.m1k1o/vncviewer/Dockerfile
Normal file
@ -0,0 +1,18 @@
|
||||
ARG BASE_IMAGE=m1k1o/neko:base
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
#
|
||||
# install vncviewer
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends openbox xtightvncviewer; \
|
||||
#
|
||||
# clean up
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
ENV NEKO_VNC_URL=""
|
||||
|
||||
#
|
||||
# copy configuation files
|
||||
COPY supervisord.conf /etc/neko/supervisord/vncviewer.conf
|
||||
COPY openbox.xml /etc/neko/openbox.xml
|
763
.m1k1o/vncviewer/openbox.xml
Normal file
763
.m1k1o/vncviewer/openbox.xml
Normal file
@ -0,0 +1,763 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- Default openbox config but all window decorations are moved
|
||||
thereby making it harder to accidentally close the virtual browser -->
|
||||
|
||||
<openbox_config xmlns="http://openbox.org/3.4/rc"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<resistance>
|
||||
<strength>10</strength>
|
||||
<screen_edge_strength>20</screen_edge_strength>
|
||||
</resistance>
|
||||
|
||||
<applications>
|
||||
<!-- Match all windows and remove their decorations (obxprop | grep "^_OB_APP") -->
|
||||
<application class="Vncviewer" name="vncviewer">
|
||||
<decor>no</decor>
|
||||
<maximized>true</maximized>
|
||||
<focus>yes</focus>
|
||||
<layer>normal</layer>
|
||||
</application>
|
||||
</applications>
|
||||
|
||||
<focus>
|
||||
<focusNew>yes</focusNew>
|
||||
<!-- always try to focus new windows when they appear. other rules do
|
||||
apply -->
|
||||
<followMouse>no</followMouse>
|
||||
<!-- move focus to a window when you move the mouse into it -->
|
||||
<focusLast>yes</focusLast>
|
||||
<!-- focus the last used window when changing desktops, instead of the one
|
||||
under the mouse pointer. when followMouse is enabled -->
|
||||
<underMouse>no</underMouse>
|
||||
<!-- move focus under the mouse, even when the mouse is not moving -->
|
||||
<focusDelay>200</focusDelay>
|
||||
<!-- when followMouse is enabled, the mouse must be inside the window for
|
||||
this many milliseconds (1000 = 1 sec) before moving focus to it -->
|
||||
<raiseOnFocus>no</raiseOnFocus>
|
||||
<!-- when followMouse is enabled, and a window is given focus by moving the
|
||||
mouse into it, also raise the window -->
|
||||
</focus>
|
||||
|
||||
<placement>
|
||||
<policy>Smart</policy>
|
||||
<!-- 'Smart' or 'UnderMouse' -->
|
||||
<center>yes</center>
|
||||
<!-- whether to place windows in the center of the free area found or
|
||||
the top left corner -->
|
||||
<monitor>Primary</monitor>
|
||||
<!-- with Smart placement on a multi-monitor system, try to place new windows
|
||||
on: 'Any' - any monitor, 'Mouse' - where the mouse is, 'Active' - where
|
||||
the active window is, 'Primary' - only on the primary monitor -->
|
||||
<primaryMonitor>1</primaryMonitor>
|
||||
<!-- The monitor where Openbox should place popup dialogs such as the
|
||||
focus cycling popup, or the desktop switch popup. It can be an index
|
||||
from 1, specifying a particular monitor. Or it can be one of the
|
||||
following: 'Mouse' - where the mouse is, or
|
||||
'Active' - where the active window is -->
|
||||
</placement>
|
||||
|
||||
<theme>
|
||||
<name>Clearlooks</name>
|
||||
<titleLayout>NLIMC</titleLayout>
|
||||
<!--
|
||||
available characters are NDSLIMC, each can occur at most once.
|
||||
N: window icon
|
||||
L: window label (AKA title).
|
||||
I: iconify
|
||||
M: maximize
|
||||
C: close
|
||||
S: shade (roll up/down)
|
||||
D: omnipresent (on all desktops).
|
||||
-->
|
||||
<keepBorder>yes</keepBorder>
|
||||
<animateIconify>yes</animateIconify>
|
||||
<font place="ActiveWindow">
|
||||
<name>sans</name>
|
||||
<size>8</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="InactiveWindow">
|
||||
<name>sans</name>
|
||||
<size>8</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="MenuHeader">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>normal</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="MenuItem">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>normal</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="ActiveOnScreenDisplay">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
<font place="InactiveOnScreenDisplay">
|
||||
<name>sans</name>
|
||||
<size>9</size>
|
||||
<!-- font size in points -->
|
||||
<weight>bold</weight>
|
||||
<!-- 'bold' or 'normal' -->
|
||||
<slant>normal</slant>
|
||||
<!-- 'italic' or 'normal' -->
|
||||
</font>
|
||||
</theme>
|
||||
|
||||
<desktops>
|
||||
<!-- this stuff is only used at startup, pagers allow you to change them
|
||||
during a session
|
||||
|
||||
these are default values to use when other ones are not already set
|
||||
by other applications, or saved in your session
|
||||
|
||||
use obconf if you want to change these without having to log out
|
||||
and back in -->
|
||||
<number>1</number>
|
||||
<firstdesk>1</firstdesk>
|
||||
<names>
|
||||
<!-- set names up here if you want to, like this:
|
||||
<name>desktop 1</name>
|
||||
<name>desktop 2</name>
|
||||
-->
|
||||
</names>
|
||||
<popupTime>875</popupTime>
|
||||
<!-- The number of milliseconds to show the popup for when switching
|
||||
desktops. Set this to 0 to disable the popup. -->
|
||||
</desktops>
|
||||
|
||||
<resize>
|
||||
<drawContents>yes</drawContents>
|
||||
<popupShow>Nonpixel</popupShow>
|
||||
<!-- 'Always', 'Never', or 'Nonpixel' (xterms and such) -->
|
||||
<popupPosition>Center</popupPosition>
|
||||
<!-- 'Center', 'Top', or 'Fixed' -->
|
||||
<popupFixedPosition>
|
||||
<!-- these are used if popupPosition is set to 'Fixed' -->
|
||||
|
||||
<x>10</x>
|
||||
<!-- positive number for distance from left edge, negative number for
|
||||
distance from right edge, or 'Center' -->
|
||||
<y>10</y>
|
||||
<!-- positive number for distance from top edge, negative number for
|
||||
distance from bottom edge, or 'Center' -->
|
||||
</popupFixedPosition>
|
||||
</resize>
|
||||
|
||||
<!-- You can reserve a portion of your screen where windows will not cover when
|
||||
they are maximized, or when they are initially placed.
|
||||
Many programs reserve space automatically, but you can use this in other
|
||||
cases. -->
|
||||
<margins>
|
||||
<top>0</top>
|
||||
<bottom>0</bottom>
|
||||
<left>0</left>
|
||||
<right>0</right>
|
||||
</margins>
|
||||
|
||||
<dock>
|
||||
<position>TopLeft</position>
|
||||
<!-- (Top|Bottom)(Left|Right|)|Top|Bottom|Left|Right|Floating -->
|
||||
<floatingX>0</floatingX>
|
||||
<floatingY>0</floatingY>
|
||||
<noStrut>no</noStrut>
|
||||
<stacking>Above</stacking>
|
||||
<!-- 'Above', 'Normal', or 'Below' -->
|
||||
<direction>Vertical</direction>
|
||||
<!-- 'Vertical' or 'Horizontal' -->
|
||||
<autoHide>no</autoHide>
|
||||
<hideDelay>300</hideDelay>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<showDelay>300</showDelay>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<moveButton>Middle</moveButton>
|
||||
<!-- 'Left', 'Middle', 'Right' -->
|
||||
</dock>
|
||||
|
||||
<keyboard>
|
||||
<chainQuitKey>C-g</chainQuitKey>
|
||||
|
||||
<!-- Keybindings for desktop switching -->
|
||||
<keybind key="C-A-Left">
|
||||
<action name="GoToDesktop"><to>left</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Right">
|
||||
<action name="GoToDesktop"><to>right</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Up">
|
||||
<action name="GoToDesktop"><to>up</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Down">
|
||||
<action name="GoToDesktop"><to>down</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Left">
|
||||
<action name="SendToDesktop"><to>left</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Right">
|
||||
<action name="SendToDesktop"><to>right</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Up">
|
||||
<action name="SendToDesktop"><to>up</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="S-A-Down">
|
||||
<action name="SendToDesktop"><to>down</to><wrap>no</wrap></action>
|
||||
</keybind>
|
||||
<keybind key="W-F1">
|
||||
<action name="GoToDesktop"><to>1</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F2">
|
||||
<action name="GoToDesktop"><to>2</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F3">
|
||||
<action name="GoToDesktop"><to>3</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-F4">
|
||||
<action name="GoToDesktop"><to>4</to></action>
|
||||
</keybind>
|
||||
<keybind key="W-d">
|
||||
<action name="ToggleShowDesktop"/>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for windows -->
|
||||
<keybind key="A-F4">
|
||||
<action name="Close"/>
|
||||
</keybind>
|
||||
<keybind key="A-Escape">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</keybind>
|
||||
<keybind key="A-space">
|
||||
<!--action name="ShowMenu"><menu>client-menu</menu></action-->
|
||||
</keybind>
|
||||
<!-- Take a screenshot of the current window with scrot when Alt+Print are pressed -->
|
||||
<keybind key="A-Print">
|
||||
<action name="Execute"><command>scrot -s</command></action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for window switching -->
|
||||
<keybind key="A-Tab">
|
||||
<action name="NextWindow">
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="A-S-Tab">
|
||||
<action name="PreviousWindow">
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="C-A-Tab">
|
||||
<action name="NextWindow">
|
||||
<panels>yes</panels><desktop>yes</desktop>
|
||||
<finalactions>
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</finalactions>
|
||||
</action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for window switching with the arrow keys -->
|
||||
<keybind key="W-S-Right">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>right</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Left">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>left</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Up">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>up</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
<keybind key="W-S-Down">
|
||||
<action name="DirectionalCycleWindows">
|
||||
<direction>down</direction>
|
||||
</action>
|
||||
</keybind>
|
||||
|
||||
<!-- Keybindings for running applications -->
|
||||
<keybind key="W-e">
|
||||
<action name="Execute">
|
||||
<startupnotify>
|
||||
<enabled>true</enabled>
|
||||
<name>Konqueror</name>
|
||||
</startupnotify>
|
||||
<command>kfmclient openProfile filemanagement</command>
|
||||
</action>
|
||||
</keybind>
|
||||
<!-- Launch scrot when Print is pressed -->
|
||||
<keybind key="Print">
|
||||
<action name="Execute"><command>scrot</command></action>
|
||||
</keybind>
|
||||
</keyboard>
|
||||
|
||||
<mouse>
|
||||
<dragThreshold>1</dragThreshold>
|
||||
<!-- number of pixels the mouse must move before a drag begins -->
|
||||
<doubleClickTime>500</doubleClickTime>
|
||||
<!-- in milliseconds (1000 = 1 second) -->
|
||||
<screenEdgeWarpTime>400</screenEdgeWarpTime>
|
||||
<!-- Time before changing desktops when the pointer touches the edge of the
|
||||
screen while moving a window, in milliseconds (1000 = 1 second).
|
||||
Set this to 0 to disable warping -->
|
||||
<screenEdgeWarpMouse>false</screenEdgeWarpMouse>
|
||||
<!-- Set this to TRUE to move the mouse pointer across the desktop when
|
||||
switching due to hitting the edge of the screen -->
|
||||
|
||||
<context name="Frame">
|
||||
<mousebind button="A-Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Left" action="Click">
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Left" action="Drag">
|
||||
<action name="Move"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="A-Right" action="Drag">
|
||||
<action name="Resize"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Middle" action="Press">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-S-Up" action="Click">
|
||||
<action name="SendToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-S-Down" action="Click">
|
||||
<action name="SendToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Titlebar">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Move"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="DoubleClick">
|
||||
<action name="ToggleMaximize"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="if">
|
||||
<shaded>no</shaded>
|
||||
<then>
|
||||
<action name="Shade"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
<action name="Lower"/>
|
||||
</then>
|
||||
</action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="if">
|
||||
<shaded>yes</shaded>
|
||||
<then>
|
||||
<action name="Unshade"/>
|
||||
<action name="Raise"/>
|
||||
</then>
|
||||
</action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Titlebar Top Right Bottom Left TLCorner TRCorner BRCorner BLCorner">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Lower"/>
|
||||
<action name="FocusToBottom"/>
|
||||
<action name="Unfocus"/>
|
||||
</mousebind>
|
||||
|
||||
<!--mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="Top">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>top</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Left">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>left</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Right">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>right</edge></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Bottom">
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"><edge>bottom</edge></action>
|
||||
</mousebind>
|
||||
|
||||
<!--mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="TRCorner BRCorner TLCorner BLCorner">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Drag">
|
||||
<action name="Resize"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Client">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Icon">
|
||||
<!--mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="ShowMenu"><menu>client-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="AllDesktops">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleOmnipresent"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Shade">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleShade"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Iconify">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="Iconify"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Maximize">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="ToggleMaximize"/>
|
||||
</mousebind>
|
||||
<mousebind button="Middle" action="Click">
|
||||
<action name="ToggleMaximize"><direction>vertical</direction></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Click">
|
||||
<action name="ToggleMaximize"><direction>horizontal</direction></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Close">
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
<action name="Unshade"/>
|
||||
</mousebind>
|
||||
<mousebind button="Left" action="Click">
|
||||
<action name="Close"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Desktop">
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="C-A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
|
||||
<mousebind button="Left" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="Focus"/>
|
||||
<action name="Raise"/>
|
||||
</mousebind>
|
||||
</context>
|
||||
|
||||
<context name="Root">
|
||||
<!-- Menus -->
|
||||
<!--mousebind button="Middle" action="Press">
|
||||
<action name="ShowMenu"><menu>client-list-combined-menu</menu></action>
|
||||
</mousebind>
|
||||
<mousebind button="Right" action="Press">
|
||||
<action name="ShowMenu"><menu>root-menu</menu></action>
|
||||
</mousebind-->
|
||||
</context>
|
||||
|
||||
<context name="MoveResize">
|
||||
<mousebind button="Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Up" action="Click">
|
||||
<action name="GoToDesktop"><to>previous</to></action>
|
||||
</mousebind>
|
||||
<mousebind button="A-Down" action="Click">
|
||||
<action name="GoToDesktop"><to>next</to></action>
|
||||
</mousebind>
|
||||
</context>
|
||||
</mouse>
|
||||
|
||||
<menu>
|
||||
<!-- You can specify more than one menu file in here and they are all loaded,
|
||||
just don't make menu ids clash or, well, it'll be kind of pointless -->
|
||||
|
||||
<!-- default menu file (or custom one in $HOME/.config/openbox/) -->
|
||||
<!-- system menu files on Debian systems -->
|
||||
<!--file>/var/lib/openbox/debian-menu.xml</file-->
|
||||
<file>menu.xml</file>
|
||||
<hideDelay>200</hideDelay>
|
||||
<!-- if a press-release lasts longer than this setting (in milliseconds), the
|
||||
menu is hidden again -->
|
||||
<middle>no</middle>
|
||||
<!-- center submenus vertically about the parent entry -->
|
||||
<submenuShowDelay>100</submenuShowDelay>
|
||||
<!-- time to delay before showing a submenu after hovering over the parent
|
||||
entry.
|
||||
if this is a negative value, then the delay is infinite and the
|
||||
submenu will not be shown until it is clicked on -->
|
||||
<submenuHideDelay>400</submenuHideDelay>
|
||||
<!-- time to delay before hiding a submenu when selecting another
|
||||
entry in parent menu
|
||||
if this is a negative value, then the delay is infinite and the
|
||||
submenu will not be hidden until a different submenu is opened -->
|
||||
<showIcons>yes</showIcons>
|
||||
<!-- controls if icons appear in the client-list-(combined-)menu -->
|
||||
<manageDesktops>yes</manageDesktops>
|
||||
<!-- show the manage desktops section in the client-list-(combined-)menu -->
|
||||
</menu>
|
||||
|
||||
<applications>
|
||||
<!--
|
||||
# this is an example with comments through out. use these to make your
|
||||
# own rules, but without the comments of course.
|
||||
# you may use one or more of the name/class/role/title/type rules to specify
|
||||
# windows to match
|
||||
|
||||
<application name="the window's _OB_APP_NAME property (see obxprop)"
|
||||
class="the window's _OB_APP_CLASS property (see obxprop)"
|
||||
groupname="the window's _OB_APP_GROUP_NAME property (see obxprop)"
|
||||
groupclass="the window's _OB_APP_GROUP_CLASS property (see obxprop)"
|
||||
role="the window's _OB_APP_ROLE property (see obxprop)"
|
||||
title="the window's _OB_APP_TITLE property (see obxprop)"
|
||||
type="the window's _OB_APP_TYPE property (see obxprob)..
|
||||
(if unspecified, then it is 'dialog' for child windows)">
|
||||
# you may set only one of name/class/role/title/type, or you may use more
|
||||
# than one together to restrict your matches.
|
||||
|
||||
# the name, class, role, and title use simple wildcard matching such as those
|
||||
# used by a shell. you can use * to match any characters and ? to match
|
||||
# any single character.
|
||||
|
||||
# the type is one of: normal, dialog, splash, utility, menu, toolbar, dock,
|
||||
# or desktop
|
||||
|
||||
# when multiple rules match a window, they will all be applied, in the
|
||||
# order that they appear in this list
|
||||
|
||||
|
||||
# each rule element can be left out or set to 'default' to specify to not
|
||||
# change that attribute of the window
|
||||
|
||||
<decor>yes</decor>
|
||||
# enable or disable window decorations
|
||||
|
||||
<shade>no</shade>
|
||||
# make the window shaded when it appears, or not
|
||||
|
||||
<position force="no">
|
||||
# the position is only used if both an x and y coordinate are provided
|
||||
# (and not set to 'default')
|
||||
# when force is "yes", then the window will be placed here even if it
|
||||
# says you want it placed elsewhere. this is to override buggy
|
||||
# applications who refuse to behave
|
||||
<x>center</x>
|
||||
# a number like 50, or 'center' to center on screen. use a negative number
|
||||
# to start from the right (or bottom for <y>), ie -50 is 50 pixels from
|
||||
# the right edge (or bottom). use 'default' to specify using value
|
||||
# provided by the application, or chosen by openbox, instead.
|
||||
<y>200</y>
|
||||
<monitor>1</monitor>
|
||||
# specifies the monitor in a xinerama setup.
|
||||
# 1 is the first head, or 'mouse' for wherever the mouse is
|
||||
</position>
|
||||
|
||||
<size>
|
||||
# the size to make the window.
|
||||
<width>20</width>
|
||||
# a number like 20, or 'default' to use the size given by the application.
|
||||
# you can use fractions such as 1/2 or percentages such as 75% in which
|
||||
# case the value is relative to the size of the monitor that the window
|
||||
# appears on.
|
||||
<height>30%</height>
|
||||
</size>
|
||||
|
||||
<focus>yes</focus>
|
||||
# if the window should try be given focus when it appears. if this is set
|
||||
# to yes it doesn't guarantee the window will be given focus. some
|
||||
# restrictions may apply, but Openbox will try to
|
||||
|
||||
<desktop>1</desktop>
|
||||
# 1 is the first desktop, 'all' for all desktops
|
||||
|
||||
<layer>normal</layer>
|
||||
# 'above', 'normal', or 'below'
|
||||
|
||||
<iconic>no</iconic>
|
||||
# make the window iconified when it appears, or not
|
||||
|
||||
<skip_pager>no</skip_pager>
|
||||
# asks to not be shown in pagers
|
||||
|
||||
<skip_taskbar>no</skip_taskbar>
|
||||
# asks to not be shown in taskbars. window cycling actions will also
|
||||
# skip past such windows
|
||||
|
||||
<fullscreen>yes</fullscreen>
|
||||
# make the window in fullscreen mode when it appears
|
||||
|
||||
<maximized>true</maximized>
|
||||
# 'Horizontal', 'Vertical' or boolean (yes/no)
|
||||
</application>
|
||||
|
||||
# end of the example
|
||||
-->
|
||||
</applications>
|
||||
|
||||
</openbox_config>
|
21
.m1k1o/vncviewer/supervisord.conf
Normal file
21
.m1k1o/vncviewer/supervisord.conf
Normal file
@ -0,0 +1,21 @@
|
||||
[program:vncviewer]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/vncviewer -autopass -x11cursor -nojpeg -quality 9 -compresslevel 9 %(ENV_NEKO_VNC_URL)s
|
||||
autorestart=true
|
||||
priority=800
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/vncviewer.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
||||
|
||||
[program:openbox]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/openbox --config-file /etc/neko/openbox.xml
|
||||
autorestart=true
|
||||
priority=300
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/openbox.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
20
.m1k1o/xfce/Dockerfile
Normal file
20
.m1k1o/xfce/Dockerfile
Normal file
@ -0,0 +1,20 @@
|
||||
ARG BASE_IMAGE=m1k1o/neko:base
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
#
|
||||
# install xfce
|
||||
RUN set -eux; apt-get update; \
|
||||
apt-get install -y --no-install-recommends xfce4 xfce4-terminal sudo; \
|
||||
#
|
||||
# add user to sudoers
|
||||
usermod -aG sudo neko; \
|
||||
echo "neko:neko" | chpasswd; \
|
||||
echo "%sudo ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers; \
|
||||
# clean up
|
||||
apt-get clean -y; \
|
||||
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
|
||||
|
||||
#
|
||||
# copy configuation files
|
||||
COPY supervisord.conf /etc/neko/supervisord/xfce.conf
|
||||
|
11
.m1k1o/xfce/supervisord.conf
Normal file
11
.m1k1o/xfce/supervisord.conf
Normal file
@ -0,0 +1,11 @@
|
||||
[program:xfce]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/startxfce4
|
||||
autorestart=true
|
||||
priority=500
|
||||
user=%(ENV_USER)s
|
||||
stdout_logfile=/var/log/neko/xfce.log
|
||||
stdout_logfile_maxbytes=100MB
|
||||
stdout_logfile_backups=10
|
||||
redirect_stderr=true
|
||||
|
391
README.md
391
README.md
@ -1,12 +1,18 @@
|
||||
<div align="center">
|
||||
<a href="https://n.eko.moe/#/" ><img src="https://raw.githubusercontent.com/nurdism/neko/master/docs/_media/logo.png" width="450" height="auto"/></a>
|
||||
<a href="https://github.com/m1k1o/neko" title="Neko's Github repository.">
|
||||
<img src="https://raw.githubusercontent.com/m1k1o/neko/master/docs/_media/logo.png" width="450" height="auto"/>
|
||||
</a>
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/github/v/release/nurdism/neko" alt="release">
|
||||
<img src="https://img.shields.io/github/license/nurdism/neko" alt="license">
|
||||
<img src="https://img.shields.io/docker/pulls/nurdism/neko" alt="pulls">
|
||||
<img src="https://img.shields.io/github/issues/nurdism/neko" alt="issues">
|
||||
<a href="https://discord.gg/3U6hWpC" ><img src="https://discordapp.com/api/guilds/665851821906067466/widget.png" alt="Chat on discord"><a/>
|
||||
<a href="https://github.com/nurdism/neko/actions" ><img src="https://github.com/nurdism/neko/workflows/deploy/badge.svg" alt="build"><a/>
|
||||
<img src="https://img.shields.io/github/v/release/m1k1o/neko" alt="release">
|
||||
<img src="https://img.shields.io/github/license/m1k1o/neko" alt="license">
|
||||
<img src="https://img.shields.io/docker/pulls/m1k1o/neko" alt="pulls">
|
||||
<img src="https://img.shields.io/github/issues/m1k1o/neko" alt="issues">
|
||||
<a href="https://discord.gg/3U6hWpC">
|
||||
<img src="https://discordapp.com/api/guilds/665851821906067466/widget.png" alt="Chat on discord">
|
||||
</a>
|
||||
<a href="https://github.com/m1k1o/neko/actions">
|
||||
<img src="https://github.com/m1k1o/neko/actions/workflows/build.yml/badge.svg" alt="build">
|
||||
</a>
|
||||
</p>
|
||||
<br/>
|
||||
<br/>
|
||||
@ -16,65 +22,247 @@
|
||||
</div>
|
||||
|
||||
# n.eko (m1k1o fork)
|
||||
This app uses Web RTC to stream a desktop inside of a docker container. This is fork of https://github.com/nurdism/neko.
|
||||
This app uses Web-RTC to stream a desktop inside a docker container. This is a fork of https://github.com/nurdism/neko.
|
||||
|
||||
For n.eko room management software, visit https://github.com/m1k1o/neko-rooms.
|
||||
|
||||
## Differences to original repository.
|
||||
|
||||
### New Features
|
||||
- Clipboard button with text area - for browsers, that don't support clipboard syncing or for HTTP.
|
||||
- Keyboard modifier state synchronization (Num Lock, Caps Lock, Scroll Lock) for each hosting.
|
||||
- Added chromium ungoogled (with h265 support) an kept up to date by @whalehub.
|
||||
- Clipboard button with text area - for browsers, that don't support clipboard syncing (Firefox, what a shame...) or for HTTP.
|
||||
- Keyboard modifier state synchronization (Num-Lock, Caps-Lock, Scroll-Lock) for each hosting.
|
||||
- Added chromium ungoogled (with h265 support) an kept up to date (by @whalehub).
|
||||
- Added Picture in Picture button (only for watching screen, controlling not possible).
|
||||
- Added RTMP broadcast. Enables broadcasting neko screen to local RTMP server, YouTube or Twitch.
|
||||
- Added RTMP broadcast. Enables broadcasting n.eko screen to local RTMP server, YouTube or Twitch. Example: `rtmp://a.rtmp.youtube.com/live2/<your-streaming-key>`.
|
||||
- Stereo sound (works properly only in Firefox host).
|
||||
- Added limited support for some mobile browsers with `playsinline` attribute.
|
||||
- Added `VIDEO_BITRATE` and `AUDIO_BITRATE` in kbit/s to control stream quality (in collaboration with @mbattista).
|
||||
- Added `MAX_FPS`, where you can specify max WebRTC frame rate. When set to `0`, frame rate won't be capped, and you can enjoy your real `60fps` experience. Originally, it was constant at `25fps`.
|
||||
- Invite links. You can invite people, and they don't need to enter passwords by themselves (and get confused about user accounts that do not exist). You can put your password in URL using `?pwd=<your-password>` and it will be automatically used when logging in.
|
||||
- Added `/stats?pwd=<admin>` endpoint to get total active connections, host and members.
|
||||
- Added `m1k1o/neko:vlc` tag, use VLC to watch local files together (by @mbattista).
|
||||
- Added `m1k1o/neko:xfce` tag, as a non-video related showcase (by @mbattista).
|
||||
- Added ARM-based images, for Raspberry Pi support (by @mbattista).
|
||||
- Added simple language picker.
|
||||
- Added `?usr=<display-name>` that will prefill username. This allows creating auto-join links.
|
||||
- Added `?cast=1` that will hide all control and show only video.
|
||||
- Shake keyboard icon if someone attempted to control when is nobody hosting.
|
||||
- Support for password protected `NEKO_ICESERVERS` (by @mbattista).
|
||||
- Added bunch of translations (🇸🇰, 🇪🇸, 🇸🇪, 🇳🇴, 🇫🇷) by various people.
|
||||
- Added `m1k1o/neko:google-chrome` tag.
|
||||
- Show red dot badge on sidebar toggle if there are new messages, and user can't see them.
|
||||
- Added `m1k1o/neko:brave` tag.
|
||||
|
||||
### Bugs
|
||||
- Fixed minor gst pipeline bug.
|
||||
- Locked screen only for users, admins can still join.
|
||||
- Fixed h264 pipelines bugs (by @mbattista).
|
||||
- Fixed sessions manager thread safety by adding mutexes (caused panic in rare edge cases).
|
||||
- Now when user gets kicked, he won't join as a ghost user again but will be logged out.
|
||||
- **iOS compatibility!** Fixed a really strange CSS bug, which prevented iOS from loading the video.
|
||||
- Proper disconnect only once with unsubscribing events. When Web-RTC fails, user won't be logged in without username again.
|
||||
- Upgraded and fixed emojis to a new major version.
|
||||
- Fixed bad `keymap -> keysym` translation to respect active modifiers (#45, with @mbattista).
|
||||
- Respecting `NEKO_DEBUG` env variable.
|
||||
- Full-screen support for iOS devices.
|
||||
- Added `chrome-sandbox` to fix weird bug when chromium didn't start.
|
||||
- Fixed keyboard mapping on macOS, when CMD could not be used for copy & paste.
|
||||
- Fixed stop signal sent by supervisor to gracefully shut down neko server.
|
||||
|
||||
### Misc
|
||||
- Custom docker workflow.
|
||||
- Based on debian buster instead of stretch.
|
||||
- Custom avatars without any 3rd party depenency.
|
||||
- Based on Debian buster instead of stretch.
|
||||
- Versions bumped: Go 16, Node.js 14 (by @mbattista).
|
||||
- Custom avatars without any 3rd party dependency.
|
||||
- Ignore duplicate notify bars.
|
||||
- No pointer events for notify bars.
|
||||
- Disable debug mode by default.
|
||||
- Remove HTML tags from username.
|
||||
- Upgraded `pion/webrtc` to v3 (by @mbattista).
|
||||
- Added `requestFullscreen` compatibility for older browsers and iOS devices.
|
||||
- Fixed small lags in video and improved video UX (by @mbattista).
|
||||
- Added `m1k1o/neko:vncviewer` tag, use `NEKO_VNC_URL` to specify VNC target and use n.eko as a bridge.
|
||||
- Ability to include n.eko as a component in another Vue.Js project (by @gbrian).
|
||||
- Added HEALTHCHECK to Dockerfile.
|
||||
- Arguments in broadcast pipeline are optional, not positional and can be repeated `{url} {device} {display}`.
|
||||
- Chat messages are dense, when repeated, they are joined together.
|
||||
- While IP address fetching is now proxy ignored.
|
||||
- Start unmuted on reconnects.
|
||||
- Switched to the latest Firefox version instead of esr.
|
||||
- Fixed very fast scroll speed on macOS.
|
||||
- Broadcast pipeline errors are reported to the user.
|
||||
- On stopping server all websocket connections are going to be gracefully disconnected.
|
||||
|
||||
# Getting started & FAQ
|
||||
|
||||
Use following docker images:
|
||||
Use the following docker images:
|
||||
- `m1k1o/neko:latest` - for Firefox.
|
||||
- `m1k1o/neko:chromium` - for Chromium Ungoogled (needs `--cap-add=SYS_ADMIN`).
|
||||
- `m1k1o/neko:chromium` - for Chromium (needs `--cap-add=SYS_ADMIN`).
|
||||
- `m1k1o/neko:google-chrome` - for Google Chrome (needs `--cap-add=SYS_ADMIN`).
|
||||
- `m1k1o/neko:ungoogled-chromium` - for [Ungoogled Chromium](https://github.com/Eloston/ungoogled-chromium) (needs `--cap-add=SYS_ADMIN`) (by @whalehub).
|
||||
- `m1k1o/neko:brave` - for [Brave Browser](https://brave.com) (needs `--cap-add=SYS_ADMIN`).
|
||||
- `m1k1o/neko:tor-browser` - for Tor Browser.
|
||||
- `m1k1o/neko:vncviewer` - for simple VNC viewer (specify `NEKO_VNC_URL` to your VNC target).
|
||||
- `m1k1o/neko:vlc` - for VLC Video player (needs volume mounted to `/media` with local video files, or setting `VLC_MEDIA=/media` path).
|
||||
- `m1k1o/neko:xfce` - for a shared desktop / installing shared software.
|
||||
- `m1k1o/neko:base` - for custom base.
|
||||
|
||||
Networking:
|
||||
For ARM-based devices (like Raspberry Pi, with GPU hardware acceleration):
|
||||
- `m1k1o/neko:arm-firefox` - for Firefox.
|
||||
- `m1k1o/neko:arm-chromium` - for Chromium.
|
||||
- `m1k1o/neko:arm-base` - for custom arm based.
|
||||
|
||||
Images are built using GitHub actions on every push and on weekly basis to keep all browsers up-to-date,
|
||||
|
||||
### Networking:
|
||||
- If you want to use n.eko in **external** network, you can omit `NEKO_NAT1TO1`. It will automatically get your Public IP.
|
||||
- If you want to use n.eko in **internal** network, set `NEKO_NAT1TO1` to your local IP address (e.g. `NEKO_NAT1TO1: 192.168.1.20`)-
|
||||
- Currently, it is not supported to supply multiple NAT addresses (see https://github.com/m1k1o/neko/issues/47).
|
||||
|
||||
Why so many ports?
|
||||
- WebRTC needs UDP ports for each channel it creates towards users.
|
||||
- Every user will need 2 UDP ports (for getting audio/video and sending mouse positions).
|
||||
- You can freely limit number of UDP ports. But you can't map them to diferent ports.
|
||||
- This **WONT** work: `32000-32100:52000-52100/udp`
|
||||
### Why so many ports?
|
||||
- WebRTC needs UDP ports in order to transfer Audio/Video towards user and Mouse/Keyboard events to the server in real time.
|
||||
- If you don't set `NEKO_ICELITE=true`, every user will need 2 UDP ports.
|
||||
- If you set `NEKO_ICELITE=true`, every user will need only 1 UDP port. It is **recommended** to use *ice-lite*.
|
||||
- Do not forget, they are **UDP** ports, that configuration must be correct in your firewall/router/docker.
|
||||
- You can freely limit number of UDP ports. But you can't map them to different ports.
|
||||
- This **WON'T** work: `32000-32100:52000-52100/udp`
|
||||
- You can change API port (8080).
|
||||
- This **WILL** work: `3000:8080`
|
||||
|
||||
Behind reverse proxy?
|
||||
- Nginx configuration: https://github.com/nurdism/neko/issues/111#issuecomment-742656957
|
||||
- Apache configuration: https://github.com/nurdism/neko/blob/cad98a62a5bd7f1daf2c11980631bb14ba81a1f6/docs/apache-proxypass-config.md#example-apache-config
|
||||
- Traefik configuration: https://github.com/m1k1o/neko-vpn/blob/a1b934515dcf597992a515d61d307c2450a11002/docker-compose.yml#L38-L43
|
||||
### Behind reverse proxy?
|
||||
|
||||
Want to use VPN for your neko browsing?
|
||||
<details>
|
||||
<summary>Traefik2 configuration</summary>
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.neko-frontend.loadbalancer.server.port=8080"
|
||||
- "traefik.http.routers.neko.rule=${TRAEFIK_RULE}"
|
||||
- "traefik.http.routers.neko.entrypoints=${TRAEFIK_ENTRYPOINTS}"
|
||||
- "traefik.http.routers.neko.tls.certresolver=${TRAEFIK_CERTRESOLVER}"
|
||||
```
|
||||
|
||||
(by @m1k1o, [example](https://github.com/m1k1o/neko-vpn/blob/a1b934515dcf597992a515d61d307c2450a11002/docker-compose.yml#L38-L43))
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Nginx configuration</summary>
|
||||
|
||||
```conf
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name example.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 86400;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-Protocol $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
(by @GigaFyde, [source](https://github.com/nurdism/neko/issues/111#issuecomment-742656957))
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Apache configuration</summary>
|
||||
|
||||
```xml
|
||||
<VirtualHost *:80>
|
||||
# The ServerName directive sets the request scheme, hostname and port that
|
||||
# the server uses to identify itself. This is used when creating
|
||||
# redirection URLs. In the context of virtual hosts, the ServerName
|
||||
# specifies what hostname must appear in the request's Host: header to
|
||||
# match this virtual host. For the default virtual host (this file) this
|
||||
# value is not decisive as it is used as a last resort host regardless.
|
||||
# However, you must set it for any further virtual host explicitly.
|
||||
|
||||
# Paths of those modules might vary across different distros.
|
||||
LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so
|
||||
LoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so
|
||||
LoadModule proxy_wstunnel_module /usr/lib/apache2/modules/mod_proxy_wstunnel.so
|
||||
|
||||
ServerName example.com
|
||||
ServerAlias www.example.com
|
||||
|
||||
ProxyRequests Off
|
||||
ProxyPass / http://localhost:8080/
|
||||
ProxyPassReverse / http://localhost:8080/
|
||||
|
||||
RewriteEngine on
|
||||
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
||||
RewriteCond %{HTTP:Connection} upgrade [NC]
|
||||
RewriteRule /ws(.*) "ws://localhost:8080/ws$1" [P,L]
|
||||
|
||||
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
|
||||
# error, crit, alert, emerg.
|
||||
# It is also possible to configure the loglevel for particular
|
||||
# modules, e.g.
|
||||
#LogLevel info ssl:warn
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
|
||||
# For most configuration files from conf-available/, which are
|
||||
# enabled or disabled at a global level, it is possible to
|
||||
# include a line for only one particular virtual host. For example the
|
||||
# following line enables the CGI configuration for this host only
|
||||
# after it has been globally disabled with "a2disconf".
|
||||
#Include conf-available/serve-cgi-bin.conf
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
(by @DarkReaper231, [source](https://github.com/nurdism/neko/blob/cad98a62a5bd7f1daf2c11980631bb14ba81a1f6/docs/apache-proxypass-config.md#example-apache-config))
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Caddy configuration</summary>
|
||||
|
||||
```conf
|
||||
https://example.com {
|
||||
reverse_proxy localhost:8080 {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
(by @ccallahan, [source](https://github.com/nurdism/neko/pull/125/commits/eb4ceda75423b0d960c8aea0240acf6d7a10fef4))
|
||||
</details>
|
||||
|
||||
### Want to customize and install own add-ons, set custom bookmarks?
|
||||
- You would need to modify the existing policy file and mount it to your container.
|
||||
- For Firefox, copy [this](https://github.com/m1k1o/neko/blob/dev/.m1k1o/firefox/policies.json) file, modify and mount it as: ` -v '${PWD}/policies.json:/usr/share/firefox-esr/distribution/policies.json'`
|
||||
- For Chromium, copy [this](https://github.com/m1k1o/neko/blob/dev/.m1k1o/chromium/policies.json) file, modify and mount it as: ` -v '${PWD}/policies.json:/etc/chromium/policies/managed/policies.json'`
|
||||
|
||||
### Want to use VPN for your n.eko browsing?
|
||||
- Check this out: https://github.com/m1k1o/neko-vpn
|
||||
|
||||
Accounts:
|
||||
- There are no accounts, display name (a.k.a. username) can be freely chosen. Only paword needs to match. Depeding on which password matches, visitor gets its privilege:
|
||||
### Want to have multiple rooms on demand?
|
||||
- Check this out: https://github.com/m1k1o/neko-rooms
|
||||
|
||||
### Want to use different Apps than Browser?
|
||||
- Check this out: https://github.com/m1k1o/neko-apps
|
||||
|
||||
### Accounts:
|
||||
- There are no accounts, display name (a.k.a. username) can be freely chosen. Only password needs to match. Depending on which password matches, the visitor gets its privilege:
|
||||
- Anyone, who enters with `NEKO_PASSWORD` will be **user**.
|
||||
- Anyone, who enters with `NEKO_PASSWORD_ADMIN` will be **admin**.
|
||||
|
||||
Screen size
|
||||
### Screen size
|
||||
- Only admins can change screen size.
|
||||
- You can set default screen size, but this size **MUST** be one from list, that your server supports.
|
||||
- You can set a default screen size, but this size **MUST** be one from the list, that your server supports.
|
||||
- You will get this list in frontend, where you can choose from.
|
||||
|
||||
## Firefox
|
||||
@ -97,7 +285,7 @@ services:
|
||||
NEKO_NAT1TO1: <your-IP>
|
||||
```
|
||||
|
||||
## Chromium Ungoogled
|
||||
## Chromium
|
||||
|
||||
```yaml
|
||||
version: "3.4"
|
||||
@ -118,3 +306,142 @@ services:
|
||||
NEKO_EPR: 52000-52100
|
||||
NEKO_NAT1TO1: <your-IP>
|
||||
```
|
||||
|
||||
## VLC
|
||||
|
||||
```yaml
|
||||
version: "3.4"
|
||||
services:
|
||||
neko:
|
||||
image: "m1k1o/neko:vlc"
|
||||
restart: "unless-stopped"
|
||||
shm_size: "2gb"
|
||||
volumes:
|
||||
- "<your-video-folder>:/video"
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "52000-52100:52000-52100/udp"
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
environment:
|
||||
NEKO_SCREEN: '1920x1080@30'
|
||||
NEKO_PASSWORD: neko
|
||||
NEKO_PASSWORD_ADMIN: admin
|
||||
NEKO_EPR: 52000-52100
|
||||
NEKO_NAT1TO1: <your-IP>
|
||||
```
|
||||
|
||||
## Raspberry Pi
|
||||
|
||||
Note! Since this pipeline is using H264, that enables GPU HW acceleration for Raspberry Pi, you are only able to connect from browsers supporting H264 for WebRTC. At the time of implementing, [Firefox does not support this](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#supported-foot-1).
|
||||
|
||||
```yaml
|
||||
version: "3.4"
|
||||
services:
|
||||
neko:
|
||||
image: "m1k1o/neko:arm-chromium"
|
||||
restart: "unless-stopped"
|
||||
# increase on rpi's with more then 1gb ram.
|
||||
shm_size: "520mb"
|
||||
ports:
|
||||
- "8088:8080"
|
||||
- "52000-52100:52000-52100/udp"
|
||||
# this is important since we need a GPU for hardware acceleration alternatively mount the devices into the docker.
|
||||
privileged: true
|
||||
environment:
|
||||
NEKO_SCREEN: '1280x720@30'
|
||||
NEKO_PASSWORD: 'neko'
|
||||
NEKO_PASSWORD_ADMIN: 'admin'
|
||||
NEKO_EPR: 52000-52100
|
||||
# optional: change target bitrate and framerate on this parameter.
|
||||
NEKO_VIDEO: |
|
||||
ximagesrc display-name=%s use-damage=0 show-pointer=true use-damage=false
|
||||
! video/x-raw,framerate=30/1
|
||||
! videoconvert
|
||||
! queue
|
||||
! video/x-raw,framerate=30/1,format=NV12
|
||||
! v4l2h264enc extra-controls="controls,h264_profile=0,video_bitrate=1250000;"
|
||||
! h264parse config-interval=3
|
||||
! video/x-h264,profile=baseline,stream-format=byte-stream
|
||||
```
|
||||
|
||||
## Not using docker?
|
||||
|
||||
You can execute `neko --help` to see available arguments. In [Dockerfile](https://github.com/m1k1o/neko/blob/dev/.m1k1o/base/Dockerfile) you can find required dependencies and install them manually.
|
||||
|
||||
## Mobile support
|
||||
|
||||
N.eko is now working on iOS and Android! Also, the UI screens have been fixed for small screens.
|
||||
|
||||

|
||||
|
||||
## Docker-Compose Environment Options
|
||||
|
||||
```code
|
||||
NEKO_SCREEN:
|
||||
- Resolution after startup. Only Admins can change this later.
|
||||
- e.g. '1920x1080@30'
|
||||
NEKO_PASSWORD:
|
||||
- Password for the user login
|
||||
- e.g. 'user_password'
|
||||
NEKO_PASSWORD_ADMIN
|
||||
- Password for the admin login
|
||||
- e.g. 'admin_password'
|
||||
NEKO_EPR:
|
||||
- For WebRTC needed range of ports
|
||||
- e.g. 52000-52100
|
||||
NEKO_VP8:
|
||||
- If vp8 should be used as video encoder for the stream (default encoder)
|
||||
- e.g. 'true'
|
||||
NEKO_VP9:
|
||||
- If vp9 should be used as video encoder for the stream (Parameter not optimized yet)
|
||||
- e.g. 'false'
|
||||
NEKO_H264:
|
||||
- If h264 should be used as video encoder for the stream (second best option)
|
||||
- e.g. 'false'
|
||||
NEKO_VIDEO_BITRATE:
|
||||
- Bitrate of the video stream in kb/s
|
||||
- e.g. 3500
|
||||
NEKO_VIDEO:
|
||||
- Makes it possible to create custom gstreamer pipelines. With this you could find the best quality for your CPU
|
||||
- Installed are gstreamer1.0-plugins-base / gstreamer1.0-plugins-good / gstreamer1.0-plugins-bad / gstreamer1.0-plugins-ugly
|
||||
- e.g. ' ximagesrc display-name=%s show-pointer=true use-damage=false ! video/x-raw,framerate=30/1 ! videoconvert ! queue ! video/x-raw,format=NV12 ! x264enc threads=4 bitrate=3500 key-int-max=60 vbv-buf-capacity=4000 byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream '
|
||||
NEKO_MAX_FPS:
|
||||
- The resulting stream frames per seconds should be capped (0 for uncapped)
|
||||
- e.g. 0
|
||||
NEKO_OPUS:
|
||||
- If opus should be used as audio encoder for the stream (default encoder)
|
||||
- e.g. 'true'
|
||||
NEKO_G722:
|
||||
- If g722 should be used as audio encoder for the stream
|
||||
- e.g. 'false'
|
||||
NEKO_PCMU:
|
||||
- If pcmu should be used as audio encoder for the stream
|
||||
- e.g. 'false'
|
||||
NEKO_PCMA:
|
||||
- If pcma should be used as audio encoder for the stream
|
||||
- e.g. 'false'
|
||||
NEKO_AUDIO_BITRATE:
|
||||
- Bitrate of the audio stream in kb/s
|
||||
- e.g. 196
|
||||
NEKO_CERT:
|
||||
- Path to the SSL-Certificate
|
||||
- e.g. '/certs/cert.pem'
|
||||
NEKO_KEY:
|
||||
- Path to the SSL-Certificate private key
|
||||
- e.g. '/certs/key.pem'
|
||||
NEKO_ICELITE:
|
||||
- Use the ice lite protocol
|
||||
- e.g. false
|
||||
NEKO_ICESERVER:
|
||||
- Describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer (simple usage for server without authentication)
|
||||
- e.g. 'stun:stun.l.google.com:19302'
|
||||
NEKO_ICESERVERS:
|
||||
- Describes multiple STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer
|
||||
- e.g. '[{"urls": ["turn:turn.example.com:19302", "stun:stun.example.com:19302"], "username": "name", "credential": "password"}, {"urls": ["stun:stun.example2.com:19302"]}]'
|
||||
- [More information](https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer)
|
||||
```
|
||||
|
||||
# How to contribute?
|
||||
|
||||
Navigate to [.m1k1o/README.md](.m1k1o/README.md) for further information.
|
||||
|
@ -15,53 +15,55 @@
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve --mode development",
|
||||
"build": "vue-cli-service build",
|
||||
"build:lib": "vue-cli-service build --target lib --name neko-lib 'src/lib.ts'",
|
||||
"build:emoji": "ts-node --files --project tools/tsconfig.json tools/emoji.ts",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.14.0",
|
||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||
"animejs": "^3.2.0",
|
||||
"axios": "^0.19.1",
|
||||
"date-fns": "^2.16.1",
|
||||
"emoji-datasource": "^5.0.1",
|
||||
"emojilib": "^2.4.0",
|
||||
"axios": "^0.21.1",
|
||||
"date-fns": "^2.23.0",
|
||||
"emoji-datasource": "^6.0.1",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"simple-markdown": "^0.7.2",
|
||||
"sweetalert2": "^9.17.2",
|
||||
"sweetalert2": "^10.15.7",
|
||||
"typed-vuex": "^0.1.21",
|
||||
"v-tooltip": "^2.0.3",
|
||||
"vue": "^2.6.12",
|
||||
"vue": "^2.6.14",
|
||||
"vue-class-component": "^7.2.6",
|
||||
"vue-clickaway": "^2.2.2",
|
||||
"vue-context": "^5.2.0",
|
||||
"vue-i18n": "^8.21.1",
|
||||
"vue-i18n": "^8.25.0",
|
||||
"vue-notification": "^1.3.20",
|
||||
"vue-property-decorator": "^8.5.1",
|
||||
"vue-property-decorator": "^9.1.2",
|
||||
"vuex": "^3.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/animejs": "^3.1.2",
|
||||
"@types/node": "^13.13.21",
|
||||
"@types/animejs": "^3.1.4",
|
||||
"@types/node": "^14.17.12",
|
||||
"@types/vue": "^2.0.0",
|
||||
"@types/vue-clickaway": "^2.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.34.0",
|
||||
"@typescript-eslint/parser": "^2.34.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.30.0",
|
||||
"@typescript-eslint/parser": "^4.30.0",
|
||||
"@vue/cli-plugin-babel": "^4.5.6",
|
||||
"@vue/cli-plugin-eslint": "^4.5.6",
|
||||
"@vue/cli-plugin-typescript": "^4.5.6",
|
||||
"@vue/cli-plugin-vuex": "^4.5.6",
|
||||
"@vue/cli-service": "^4.5.6",
|
||||
"@vue/cli-service": "^4.5.12",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"@vue/eslint-config-typescript": "^5.1.0",
|
||||
"@vue/eslint-config-typescript": "^7.0.0",
|
||||
"core-js": "^3.16.4",
|
||||
"emojilib": "^3.0.4",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"node-sass": "^4.14.1",
|
||||
"prettier": "^2.1.2",
|
||||
"sass-loader": "^8.0.0",
|
||||
"ts-node": "^8.10.2",
|
||||
"typescript": "^3.9.7",
|
||||
"vue-template-compiler": "^2.6.12"
|
||||
"eslint-plugin-prettier": "^3.4.1",
|
||||
"eslint-plugin-vue": "^7.17.0",
|
||||
"node-sass": "^5.0.0",
|
||||
"prettier": "^2.3.2",
|
||||
"sass-loader": "^10.1.1",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.4.2",
|
||||
"vue-template-compiler": "^2.6.14"
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -5,18 +5,22 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>n.eko</title>
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<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">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
|
||||
<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">
|
||||
<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>
|
||||
<noscript>
|
||||
<strong>We're sorry but n.eko doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="neko"></div>
|
||||
<p>
|
||||
A self hosted virtual browser (<a href="https://github.com/m1k1o/neko">m1k1o/neko</a>) that runs in docker.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -5,20 +5,20 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<main class="neko-main">
|
||||
<div class="header-container">
|
||||
<div v-if="!hideControls" class="header-container">
|
||||
<neko-header />
|
||||
</div>
|
||||
<div class="video-container">
|
||||
<neko-video ref="video" />
|
||||
<neko-video ref="video" :hideControls="hideControls" @control-attempt="controlAttempt" />
|
||||
</div>
|
||||
<div class="room-container">
|
||||
<div v-if="!hideControls" class="room-container">
|
||||
<neko-members />
|
||||
<div class="room-menu">
|
||||
<div class="settings">
|
||||
<neko-menu />
|
||||
</div>
|
||||
<div class="controls">
|
||||
<neko-controls />
|
||||
<neko-controls :shakeKbd="shakeKbd" />
|
||||
</div>
|
||||
<div class="emotes">
|
||||
<neko-emotes />
|
||||
@ -26,10 +26,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<neko-side v-if="side" />
|
||||
<neko-side v-if="!hideControls && side" />
|
||||
<neko-connect v-if="!connected" />
|
||||
<neko-about v-if="about" />
|
||||
<notifications group="neko" position="top left" :ignoreDuplicates="true" style="top: 50px;pointer-events: none" />
|
||||
<notifications
|
||||
v-if="!hideControls"
|
||||
group="neko"
|
||||
position="top left"
|
||||
style="top: 50px; pointer-events: none"
|
||||
:ignoreDuplicates="true"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@ -109,31 +115,35 @@
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
#neko {
|
||||
&.expanded {
|
||||
.neko-main {
|
||||
transform: translateX(-$side-width);
|
||||
}
|
||||
.neko-menu {
|
||||
transform: translateX(-$side-width);
|
||||
#neko.expanded {
|
||||
.neko-main {
|
||||
transform: translateX(calc(-100% + 65px));
|
||||
|
||||
video {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.neko-menu {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 65px;
|
||||
width: calc(100% - 65px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
#neko {
|
||||
.neko-main {
|
||||
.room-container {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
#neko .neko-main .room-container {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue, Component, Ref } from 'vue-property-decorator'
|
||||
import { Vue, Component, Ref, Watch } from 'vue-property-decorator'
|
||||
|
||||
import Connect from '~/components/connect.vue'
|
||||
import Video from '~/components/video.vue'
|
||||
@ -164,6 +174,25 @@
|
||||
export default class extends Vue {
|
||||
@Ref('video') video!: Video
|
||||
|
||||
shakeKbd = false
|
||||
|
||||
get hideControls() {
|
||||
return !!new URL(location.href).searchParams.get('cast')
|
||||
}
|
||||
|
||||
@Watch('hideControls', { immediate: true })
|
||||
onHideControls() {
|
||||
this.$accessor.video.setMuted(false)
|
||||
this.$accessor.settings.setSound(false)
|
||||
}
|
||||
|
||||
controlAttempt() {
|
||||
if (this.shakeKbd || this.$accessor.remote.hosted) return
|
||||
|
||||
this.shakeKbd = true
|
||||
window.setTimeout(() => (this.shakeKbd = false), 5000)
|
||||
}
|
||||
|
||||
get about() {
|
||||
return this.$accessor.client.about
|
||||
}
|
||||
|
3518
client/src/assets/styles/vendor/_emoji.scss
vendored
3518
client/src/assets/styles/vendor/_emoji.scss
vendored
File diff suppressed because it is too large
Load Diff
@ -143,23 +143,27 @@
|
||||
return this.$accessor.client.about_page
|
||||
}
|
||||
|
||||
async Load() {
|
||||
this.loading = true
|
||||
|
||||
try {
|
||||
const res = await this.$http.get<string>('https://raw.githubusercontent.com/m1k1o/neko/dev/README.md')
|
||||
const res2 = await this.$http.post('https://api.github.com/markdown', {
|
||||
text: res.data,
|
||||
mode: 'gfm',
|
||||
context: 'github/gollum',
|
||||
})
|
||||
this.$accessor.client.setAbout(res2.data)
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
mounted() {
|
||||
if (this.about === '') {
|
||||
this.loading = true
|
||||
this.$http
|
||||
.get<string>('https://raw.githubusercontent.com/nurdism/neko/master/docs/README.md')
|
||||
.then((res) => {
|
||||
return this.$http.post('https://api.github.com/markdown', {
|
||||
text: res.data,
|
||||
mode: 'gfm',
|
||||
context: 'github/gollum',
|
||||
})
|
||||
})
|
||||
.then((res) => {
|
||||
this.$accessor.client.setAbout(res.data)
|
||||
this.loading = false
|
||||
})
|
||||
.catch((err) => console.error(err))
|
||||
this.Load()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,13 +2,16 @@
|
||||
<!--
|
||||
<img :src="`https://ui-avatars.com/api/?name=${seed}&size=${size}`" />
|
||||
-->
|
||||
<div class="avatar" :style="{
|
||||
width: size + 'px',
|
||||
height: size + 'px',
|
||||
lineHeight: size + 'px',
|
||||
fontSize: (size/2) + 'px',
|
||||
backgroundColor: Background(seed),
|
||||
}">
|
||||
<div
|
||||
class="avatar"
|
||||
:style="{
|
||||
width: size + 'px',
|
||||
height: size + 'px',
|
||||
lineHeight: size + 'px',
|
||||
fontSize: size / 2 + 'px',
|
||||
backgroundColor: Background(seed),
|
||||
}"
|
||||
>
|
||||
{{ seed.substring(0, 2).toUpperCase() }}
|
||||
</div>
|
||||
</template>
|
||||
@ -32,21 +35,24 @@
|
||||
name: 'neko-avatar',
|
||||
})
|
||||
export default class extends Vue {
|
||||
@Prop(String) readonly seed: string | undefined;
|
||||
@Prop(Number) readonly size: number | undefined;
|
||||
@Prop(String) readonly seed: string | undefined
|
||||
@Prop(Number) readonly size: number | undefined
|
||||
|
||||
Background(seed: string) {
|
||||
let a = 0, b = 0, c = 0;
|
||||
for(let i = 0; i < seed.length; i++) {
|
||||
a += seed.charCodeAt(i) * 3;
|
||||
b += seed.charCodeAt(i) * 5;
|
||||
c += seed.charCodeAt(i) * 7;
|
||||
let a = 0,
|
||||
b = 0,
|
||||
c = 0
|
||||
|
||||
for (let i = 0; i < seed.length; i++) {
|
||||
a += seed.charCodeAt(i) * 3
|
||||
b += seed.charCodeAt(i) * 5
|
||||
c += seed.charCodeAt(i) * 7
|
||||
}
|
||||
|
||||
let x = Math.floor(128 + (a % 128));
|
||||
let y = Math.floor(128 + (b % 128));
|
||||
let z = Math.floor(128 + (c % 128));
|
||||
return "rgb(" + x + "," + y + "," + z + ")";
|
||||
let x = Math.floor(128 + (a % 128))
|
||||
let y = Math.floor(128 + (b % 128))
|
||||
let z = Math.floor(128 + (c % 128))
|
||||
return 'rgb(' + x + ',' + y + ',' + z + ')'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -2,7 +2,14 @@
|
||||
<div class="chat">
|
||||
<ul class="chat-history" ref="history" @click="onClick">
|
||||
<template v-for="(message, index) in history">
|
||||
<li :key="index" class="message" v-if="message.type === 'text'">
|
||||
<li
|
||||
:key="index"
|
||||
class="message"
|
||||
v-if="message.type === 'text'"
|
||||
:class="{
|
||||
bulk: index > 0 && history[index - 1].id == message.id && history[index - 1].type === 'text',
|
||||
}"
|
||||
>
|
||||
<div class="author" @contextmenu.stop.prevent="onContext($event, { member: member(message.id) })">
|
||||
<neko-avatar class="avatar" :seed="member(message.id).displayname" :size="40" />
|
||||
</div>
|
||||
@ -24,7 +31,7 @@
|
||||
boundariesElement: 'body',
|
||||
}"
|
||||
>
|
||||
<strong v-if="message.id === id">{{ $t('you') }}</strong>
|
||||
<strong v-if="message.id === id && $te('you')">{{ $t('you') }}</strong>
|
||||
<strong v-else>{{ member(message.id).displayname }}</strong>
|
||||
{{ message.content }}
|
||||
</div>
|
||||
@ -94,6 +101,7 @@
|
||||
word-wrap: break-word;
|
||||
|
||||
&.message {
|
||||
padding-top: 15px;
|
||||
font-size: 16px;
|
||||
|
||||
.author {
|
||||
@ -104,7 +112,7 @@
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: $style-primary;
|
||||
margin: 0px 10px 10px 0px;
|
||||
margin-right: 10px;
|
||||
|
||||
.avatar {
|
||||
width: 100%;
|
||||
@ -219,6 +227,19 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.bulk {
|
||||
padding-top: 0px;
|
||||
|
||||
.author {
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.content-head {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.event {
|
||||
|
@ -1,14 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="clipboard"
|
||||
v-if="opened"
|
||||
@click="$event.stopPropagation()"
|
||||
>
|
||||
<textarea
|
||||
ref="textarea"
|
||||
v-model="clipboard"
|
||||
@focus="$event.target.select()"
|
||||
/>
|
||||
<div class="clipboard" v-if="opened" @click="$event.stopPropagation()">
|
||||
<textarea ref="textarea" v-model="clipboard" @focus="$event.target.select()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -23,7 +15,8 @@
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
|
||||
&, textarea {
|
||||
&,
|
||||
textarea {
|
||||
max-width: 320px;
|
||||
width: 100%;
|
||||
max-height: 120px;
|
||||
@ -52,7 +45,7 @@
|
||||
@Ref('textarea') readonly _textarea!: HTMLTextAreaElement
|
||||
|
||||
private opened: boolean = false
|
||||
private typing: any = null
|
||||
private typing?: number
|
||||
|
||||
get clipboard() {
|
||||
return this.$accessor.remote.clipboard
|
||||
@ -63,15 +56,16 @@
|
||||
|
||||
if (this.typing) {
|
||||
clearTimeout(this.typing)
|
||||
this.typing = undefined
|
||||
}
|
||||
|
||||
this.typing = setTimeout(() => this.$accessor.remote.sendClipboard(this.clipboard), 500)
|
||||
this.typing = window.setTimeout(() => this.$accessor.remote.sendClipboard(this.clipboard), 500)
|
||||
}
|
||||
|
||||
open() {
|
||||
this.opened = true
|
||||
document.body.addEventListener('click', this.close)
|
||||
setTimeout(() => this._textarea.focus(), 0)
|
||||
window.setTimeout(() => this._textarea.focus(), 0)
|
||||
}
|
||||
|
||||
close() {
|
||||
|
@ -1,14 +1,15 @@
|
||||
<template>
|
||||
<div class="connect">
|
||||
<div class="window">
|
||||
<div class="logo">
|
||||
<div class="logo" title="About n.eko" @click.stop.prevent="about">
|
||||
<img src="@/assets/images/logo.svg" alt="n.eko" />
|
||||
<span><b>n</b>.eko</span>
|
||||
</div>
|
||||
<form class="message" v-if="!connecting" @submit.stop.prevent="connect">
|
||||
<span>{{ $t('connect.title') }}</span>
|
||||
<span v-if="!autoPassword">{{ $t('connect.login_title') }}</span>
|
||||
<span v-else>{{ $t('connect.invitation_title') }}</span>
|
||||
<input type="text" :placeholder="$t('connect.displayname')" v-model="displayname" />
|
||||
<input type="password" :placeholder="$t('connect.password')" v-model="password" />
|
||||
<input type="password" :placeholder="$t('connect.password')" v-model="password" v-if="!autoPassword" />
|
||||
<button type="submit" @click.stop.prevent="login">
|
||||
{{ $t('connect.connect') }}
|
||||
</button>
|
||||
@ -46,6 +47,7 @@
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
height: 90px;
|
||||
@ -150,12 +152,30 @@
|
||||
|
||||
@Component({ name: 'neko-connect' })
|
||||
export default class extends Vue {
|
||||
private displayname = ''
|
||||
private password = ''
|
||||
private autoPassword: string | null = new URL(location.href).searchParams.get('pwd')
|
||||
|
||||
private displayname: string = ''
|
||||
private password: string = ''
|
||||
|
||||
mounted() {
|
||||
if (this.$accessor.displayname !== '' && this.$accessor.password !== '') {
|
||||
this.$accessor.login({ displayname: this.$accessor.displayname, password: this.$accessor.password })
|
||||
// auto-password fill
|
||||
let password = this.$accessor.password
|
||||
if (this.autoPassword !== null) {
|
||||
this.removeUrlParam('pwd')
|
||||
password = this.autoPassword
|
||||
}
|
||||
|
||||
// auto-user fill
|
||||
let displayname = this.$accessor.displayname
|
||||
const usr = new URL(location.href).searchParams.get('usr')
|
||||
if (usr) {
|
||||
this.removeUrlParam('usr')
|
||||
displayname = this.$accessor.displayname || usr
|
||||
}
|
||||
|
||||
if (displayname !== '' && password !== '') {
|
||||
this.$accessor.login({ displayname, password })
|
||||
this.autoPassword = null
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,8 +183,48 @@
|
||||
return this.$accessor.connecting
|
||||
}
|
||||
|
||||
removeUrlParam(param: string) {
|
||||
let url = document.location.href
|
||||
let urlparts = url.split('?')
|
||||
|
||||
if (urlparts.length >= 2) {
|
||||
let urlBase = urlparts.shift()
|
||||
let queryString = urlparts.join('?')
|
||||
|
||||
let prefix = encodeURIComponent(param) + '='
|
||||
let pars = queryString.split(/[&;]/g)
|
||||
for (let i = pars.length; i-- > 0; ) {
|
||||
if (pars[i].lastIndexOf(prefix, 0) !== -1) {
|
||||
pars.splice(i, 1)
|
||||
}
|
||||
}
|
||||
|
||||
url = urlBase + (pars.length > 0 ? '?' + pars.join('&') : '')
|
||||
window.history.pushState('', document.title, url)
|
||||
}
|
||||
}
|
||||
|
||||
login() {
|
||||
this.$accessor.login({ displayname: this.displayname, password: this.password })
|
||||
let password = this.password
|
||||
if (this.autoPassword !== null) {
|
||||
password = this.autoPassword
|
||||
}
|
||||
|
||||
if (this.displayname == '') {
|
||||
this.$swal({
|
||||
title: this.$t('connect.error') as string,
|
||||
text: this.$t('connect.empty_displayname') as string,
|
||||
icon: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
this.$accessor.login({ displayname: this.displayname, password })
|
||||
this.autoPassword = null
|
||||
}
|
||||
|
||||
about() {
|
||||
this.$accessor.client.toggleAbout()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -39,10 +39,10 @@
|
||||
<template v-if="admin && !child.data.member.admin">
|
||||
<li class="seperator" />
|
||||
<li>
|
||||
<span @click="kick(child.data.member)" style="color: #f04747;">{{ $t('context.kick') }}</span>
|
||||
<span @click="kick(child.data.member)" style="color: #f04747">{{ $t('context.kick') }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span @click="ban(child.data.member)" style="color: #f04747;">{{ $t('context.ban') }}</span>
|
||||
<span @click="ban(child.data.member)" style="color: #f04747">{{ $t('context.ban') }}</span>
|
||||
</li>
|
||||
</template>
|
||||
</template>
|
||||
@ -165,64 +165,64 @@
|
||||
this.context.open(event, data)
|
||||
}
|
||||
|
||||
kick(member: Member) {
|
||||
this.$swal({
|
||||
async kick(member: Member) {
|
||||
const value = await this.$swal({
|
||||
title: this.$t('context.confirm.kick_title', { name: member.displayname }) as string,
|
||||
text: this.$t('context.confirm.kick_text', { name: member.displayname }) as string,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: this.$t('context.confirm.button_yes') as string,
|
||||
cancelButtonText: this.$t('context.confirm.button_cancel') as string,
|
||||
}).then(({ value }) => {
|
||||
if (value) {
|
||||
this.$accessor.user.kick(member)
|
||||
}
|
||||
})
|
||||
|
||||
if (value) {
|
||||
this.$accessor.user.kick(member)
|
||||
}
|
||||
}
|
||||
|
||||
ban(member: Member) {
|
||||
this.$swal({
|
||||
async ban(member: Member) {
|
||||
const value = await this.$swal({
|
||||
title: this.$t('context.confirm.ban_title', { name: member.displayname }) as string,
|
||||
text: this.$t('context.confirm.ban_text', { name: member.displayname }) as string,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: this.$t('context.confirm.button_yes') as string,
|
||||
cancelButtonText: this.$t('context.confirm.button_cancel') as string,
|
||||
}).then(({ value }) => {
|
||||
if (value) {
|
||||
this.$accessor.user.ban(member)
|
||||
}
|
||||
})
|
||||
|
||||
if (value) {
|
||||
this.$accessor.user.ban(member)
|
||||
}
|
||||
}
|
||||
|
||||
mute(member: Member) {
|
||||
this.$swal({
|
||||
async mute(member: Member) {
|
||||
const value = await this.$swal({
|
||||
title: this.$t('context.confirm.mute_title', { name: member.displayname }) as string,
|
||||
text: this.$t('context.confirm.mute_text', { name: member.displayname }) as string,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: this.$t('context.confirm.button_yes') as string,
|
||||
cancelButtonText: this.$t('context.confirm.button_cancel') as string,
|
||||
}).then(({ value }) => {
|
||||
if (value) {
|
||||
this.$accessor.user.mute(member)
|
||||
}
|
||||
})
|
||||
|
||||
if (value) {
|
||||
this.$accessor.user.mute(member)
|
||||
}
|
||||
}
|
||||
|
||||
unmute(member: Member) {
|
||||
this.$swal({
|
||||
async unmute(member: Member) {
|
||||
const value = await this.$swal({
|
||||
title: this.$t('context.confirm.unmute_title', { name: member.displayname }) as string,
|
||||
text: this.$t('context.confirm.unmute_text', { name: member.displayname }) as string,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: this.$t('context.confirm.button_yes') as string,
|
||||
cancelButtonText: this.$t('context.confirm.button_cancel') as string,
|
||||
}).then(({ value }) => {
|
||||
if (value) {
|
||||
this.$accessor.user.unmute(member)
|
||||
}
|
||||
})
|
||||
|
||||
if (value) {
|
||||
this.$accessor.user.unmute(member)
|
||||
}
|
||||
}
|
||||
|
||||
adminRelease(member: Member) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
<li v-if="!isTouch">
|
||||
<i
|
||||
:class="[
|
||||
shakeKbd ? 'shake' : '',
|
||||
hosted && !hosting ? 'disabled' : '',
|
||||
!hosted && !hosting ? 'faded' : '',
|
||||
'fas',
|
||||
@ -53,6 +54,46 @@
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shake {
|
||||
animation: shake 1.25s cubic-bezier(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0% {
|
||||
transform: scale(1) translate(0px, 0) rotate(0);
|
||||
}
|
||||
10% {
|
||||
transform: scale(1.25) translate(-2px, -2px) rotate(-20deg);
|
||||
}
|
||||
20% {
|
||||
transform: scale(1.5) translate(4px, -4px) rotate(20deg);
|
||||
}
|
||||
30% {
|
||||
transform: scale(1.75) translate(-4px, -6px) rotate(-20deg);
|
||||
}
|
||||
40% {
|
||||
transform: scale(2) translate(6px, -8px) rotate(20deg);
|
||||
}
|
||||
50% {
|
||||
transform: scale(2.25) translate(-6px, -10px) rotate(-20deg);
|
||||
}
|
||||
60% {
|
||||
transform: scale(2) translate(6px, -8px) rotate(20deg);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1.75) translate(-4px, -6px) rotate(-20deg);
|
||||
}
|
||||
80% {
|
||||
transform: scale(1.5) translate(4px, -4px) rotate(20deg);
|
||||
}
|
||||
90% {
|
||||
transform: scale(1.25) translate(-2px, -2px) rotate(-20deg);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1) translate(0px, 0) rotate(0);
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -195,10 +236,12 @@
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue, Component } from 'vue-property-decorator'
|
||||
import { Vue, Component, Prop } from 'vue-property-decorator'
|
||||
|
||||
@Component({ name: 'neko-controls' })
|
||||
export default class extends Vue {
|
||||
@Prop(Boolean) readonly shakeKbd = false
|
||||
|
||||
get isTouch() {
|
||||
return (
|
||||
(typeof navigator.maxTouchPoints !== 'undefined' ? navigator.maxTouchPoints < 0 : false) ||
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="header">
|
||||
<div class="neko">
|
||||
<a href="https://github.com/m1k1o/neko" title="Github repository" target="_blank" class="neko">
|
||||
<img src="@/assets/images/logo.svg" alt="n.eko" />
|
||||
<span><b>n</b>.eko</span>
|
||||
</div>
|
||||
</a>
|
||||
<ul class="menu">
|
||||
<li>
|
||||
<i
|
||||
@ -15,8 +15,8 @@
|
||||
? $t('room.unlock')
|
||||
: $t('room.lock')
|
||||
: locked
|
||||
? $t('room.unlocked')
|
||||
: $t('room.locked'),
|
||||
? $t('room.locked')
|
||||
: $t('room.unlocked'),
|
||||
placement: 'bottom',
|
||||
offset: 5,
|
||||
boundariesElement: 'body',
|
||||
@ -25,6 +25,7 @@
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<span v-if="showBadge" class="badge">•</span>
|
||||
<i class="fas fa-bars toggle" @click="toggleMenu" />
|
||||
</li>
|
||||
</ul>
|
||||
@ -45,6 +46,8 @@
|
||||
align-items: center;
|
||||
width: 150px;
|
||||
margin-left: 20px;
|
||||
color: $text-normal;
|
||||
text-decoration: none;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
@ -94,6 +97,40 @@
|
||||
.toggle {
|
||||
background: $background-primary;
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
background: red;
|
||||
font-weight: bold;
|
||||
font-size: 1.25em;
|
||||
border-radius: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
pointer-events: none;
|
||||
|
||||
transform: translate(-50%, -25%) scale(1);
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 1);
|
||||
animation: badger-pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes badger-pulse {
|
||||
0% {
|
||||
transform: translate(-50%, -25%) scale(0.85);
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: translate(-50%, -25%) scale(1);
|
||||
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(-50%, -25%) scale(0.85);
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -112,8 +149,22 @@
|
||||
return this.$accessor.locked
|
||||
}
|
||||
|
||||
get side() {
|
||||
return this.$accessor.client.side
|
||||
}
|
||||
|
||||
get texts() {
|
||||
return this.$accessor.chat.texts
|
||||
}
|
||||
|
||||
get showBadge() {
|
||||
return !this.side && this.readTexts != this.texts
|
||||
}
|
||||
|
||||
readTexts: number = 0
|
||||
toggleMenu() {
|
||||
this.$accessor.client.toggleSide()
|
||||
this.readTexts = this.texts
|
||||
}
|
||||
|
||||
toggleLock() {
|
||||
|
@ -1,21 +1,8 @@
|
||||
import md, { SingleNodeParserRule, HtmlOutputRule, defaultRules, State, Rules } from 'simple-markdown'
|
||||
import { Component, Watch, Vue, Prop } from 'vue-property-decorator'
|
||||
|
||||
const {
|
||||
blockQuote,
|
||||
inlineCode,
|
||||
codeBlock,
|
||||
autolink,
|
||||
newline,
|
||||
escape,
|
||||
strong,
|
||||
text,
|
||||
link,
|
||||
url,
|
||||
em,
|
||||
u,
|
||||
br,
|
||||
} = defaultRules
|
||||
const { blockQuote, inlineCode, codeBlock, autolink, newline, escape, strong, text, link, url, em, u, br } =
|
||||
defaultRules
|
||||
|
||||
type Rule = SingleNodeParserRule & HtmlOutputRule
|
||||
|
||||
|
@ -13,6 +13,13 @@
|
||||
v-if="admin"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<select v-model="$i18n.locale">
|
||||
<option v-for="(lang, i) in langs" :key="`Lang${i}`" :value="lang">
|
||||
{{ lang }}
|
||||
</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
@ -28,10 +35,33 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
appearance: none;
|
||||
background-color: $background-tertiary;
|
||||
border: 1px solid $background-primary;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
height: 24px;
|
||||
vertical-align: text-bottom;
|
||||
display: inline-block;
|
||||
|
||||
option {
|
||||
font-weight: normal;
|
||||
color: $text-normal;
|
||||
background-color: $background-tertiary;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border: 1px solid $background-primary;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Ref, Watch, Vue } from 'vue-property-decorator'
|
||||
import { messages } from '~/locale'
|
||||
|
||||
@Component({ name: 'neko-menu' })
|
||||
export default class extends Vue {
|
||||
@ -39,6 +69,10 @@
|
||||
return this.$accessor.user.admin
|
||||
}
|
||||
|
||||
get langs() {
|
||||
return Object.keys(messages)
|
||||
}
|
||||
|
||||
about() {
|
||||
this.$accessor.client.toggleAbout()
|
||||
}
|
||||
|
@ -44,19 +44,23 @@
|
||||
<span />
|
||||
</label>
|
||||
</li>
|
||||
<template v-if="admin">
|
||||
<li>
|
||||
<span>{{ $t('setting.broadcast_is_active') }}</span>
|
||||
<label class="switch">
|
||||
<input type="checkbox" v-model="broadcast_is_active" />
|
||||
<span />
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<span>{{ $t('setting.broadcast_url') }}</span>
|
||||
<input v-model="broadcast_url" :disabled="broadcast_is_active" class="input">
|
||||
</li>
|
||||
</template>
|
||||
<li class="broadcast" v-if="admin">
|
||||
<div>
|
||||
<span>{{ $t('setting.broadcast_title') }}</span>
|
||||
<button v-if="!broadcast_is_active" @click.stop.prevent="$accessor.settings.broadcastCreate(broadcast_url)">
|
||||
<i class="fas fa-play"></i>
|
||||
</button>
|
||||
<button v-else @click.stop.prevent="$accessor.settings.broadcastDestroy()" class="btn-red">
|
||||
<i class="fas fa-stop"></i>
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
v-model="broadcast_url"
|
||||
:disabled="broadcast_is_active"
|
||||
class="input"
|
||||
placeholder="rtmp://a.rtmp.youtube.com/live2/<stream-key>"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="connected">
|
||||
<button @click.stop.prevent="logout">{{ $t('logout') }}</button>
|
||||
</li>
|
||||
@ -266,6 +270,34 @@
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.broadcast {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
div {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
button {
|
||||
flex-shrink: 1;
|
||||
width: auto !important;
|
||||
margin: 0;
|
||||
padding: 0 10px;
|
||||
|
||||
&.btn-red {
|
||||
background: #a62626;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
text-align: left;
|
||||
width: auto !important;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -276,7 +308,7 @@
|
||||
|
||||
@Component({ name: 'neko-settings' })
|
||||
export default class extends Vue {
|
||||
private broadcast_url: string = '';
|
||||
private broadcast_url: string = ''
|
||||
|
||||
get admin() {
|
||||
return this.$accessor.user.admin
|
||||
@ -338,14 +370,6 @@
|
||||
return this.$accessor.settings.broadcast_is_active
|
||||
}
|
||||
|
||||
set broadcast_is_active(value: boolean) {
|
||||
if (value) {
|
||||
this.$accessor.settings.broadcastCreate(this.broadcast_url)
|
||||
} else {
|
||||
this.$accessor.settings.broadcastDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
get broadcast_url_remote() {
|
||||
return this.$accessor.settings.broadcast_url
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div ref="component" class="video">
|
||||
<div ref="player" class="player">
|
||||
<div ref="container" class="player-container">
|
||||
<video ref="video" />
|
||||
<video ref="video" playsinline />
|
||||
<div class="emotes">
|
||||
<template v-for="(emote, index) in emotes">
|
||||
<neko-emote :id="index" :key="index" />
|
||||
@ -26,7 +26,7 @@
|
||||
</div>
|
||||
<div ref="aspect" class="player-aspect" />
|
||||
</div>
|
||||
<ul v-if="!fullscreen" class="video-menu top">
|
||||
<ul v-if="!fullscreen && !hideControls" class="video-menu top">
|
||||
<li><i @click.stop.prevent="requestFullscreen" class="fas fa-expand"></i></li>
|
||||
<li v-if="admin"><i @click.stop.prevent="onResolution" class="fas fa-desktop"></i></li>
|
||||
<li class="request-control">
|
||||
@ -36,10 +36,13 @@
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="!fullscreen" class="video-menu bottom">
|
||||
<li v-if="hosting && !clipboard_available"><i @click.stop.prevent="onClipboard" class="fas fa-clipboard"></i></li>
|
||||
<ul v-if="!fullscreen && !hideControls" class="video-menu bottom">
|
||||
<li v-if="hosting && (!clipboard_read_available || !clipboard_write_available)">
|
||||
<i @click.stop.prevent="onClipboard" class="fas fa-clipboard"></i>
|
||||
</li>
|
||||
<li>
|
||||
<i
|
||||
v-if="pip_available"
|
||||
@click.stop.prevent="requestPictureInPicture"
|
||||
v-tooltip="{ content: 'Picture-in-Picture', placement: 'left', offset: 5, boundariesElement: 'body' }"
|
||||
class="fas fa-external-link-alt"
|
||||
@ -47,7 +50,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
<neko-resolution ref="resolution" v-if="admin" />
|
||||
<neko-clipboard ref="clipboard" v-if="hosting && !clipboard_available" />
|
||||
<neko-clipboard ref="clipboard" v-if="hosting && (!clipboard_read_available || !clipboard_write_available)" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -180,15 +183,18 @@
|
||||
</style>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Ref, Watch, Vue } from 'vue-property-decorator'
|
||||
import { Component, Ref, Watch, Vue, Prop } from 'vue-property-decorator'
|
||||
import ResizeObserver from 'resize-observer-polyfill'
|
||||
|
||||
import Emote from './emote.vue'
|
||||
import Resolution from './resolution.vue'
|
||||
import Clipboard from './clipboard.vue'
|
||||
|
||||
// @ts-ignore
|
||||
import GuacamoleKeyboard from '~/utils/guacamole-keyboard.ts'
|
||||
|
||||
const WHEEL_LINE_HEIGHT = 19
|
||||
|
||||
@Component({
|
||||
name: 'neko-video',
|
||||
components: {
|
||||
@ -207,10 +213,13 @@
|
||||
@Ref('resolution') readonly _resolution!: any
|
||||
@Ref('clipboard') readonly _clipboard!: any
|
||||
|
||||
@Prop(Boolean) readonly hideControls = false
|
||||
|
||||
private keyboard = GuacamoleKeyboard()
|
||||
private observer = new ResizeObserver(this.onResise.bind(this))
|
||||
private focused = false
|
||||
private fullscreen = false
|
||||
private startsMuted = true
|
||||
|
||||
get admin() {
|
||||
return this.$accessor.user.admin
|
||||
@ -272,8 +281,17 @@
|
||||
return this.$accessor.settings.scroll_invert
|
||||
}
|
||||
|
||||
get clipboard_available() {
|
||||
return 'clipboard' in navigator
|
||||
get pip_available() {
|
||||
//@ts-ignore
|
||||
return typeof document.createElement('video').requestPictureInPicture === 'function'
|
||||
}
|
||||
|
||||
get clipboard_read_available() {
|
||||
return 'clipboard' in navigator && typeof navigator.clipboard.readText === 'function'
|
||||
}
|
||||
|
||||
get clipboard_write_available() {
|
||||
return 'clipboard' in navigator && typeof navigator.clipboard.writeText === 'function'
|
||||
}
|
||||
|
||||
get clipboard() {
|
||||
@ -321,6 +339,7 @@
|
||||
onMutedChanged(muted: boolean) {
|
||||
if (this._video) {
|
||||
this._video.muted = muted
|
||||
this.startsMuted = muted
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,7 +368,7 @@
|
||||
|
||||
@Watch('clipboard')
|
||||
onClipboardChanged(clipboard: string) {
|
||||
if (this.clipboard_available && typeof navigator.clipboard.writeText === 'function') {
|
||||
if (this.clipboard_write_available) {
|
||||
navigator.clipboard.writeText(clipboard).catch(console.error)
|
||||
}
|
||||
}
|
||||
@ -370,7 +389,7 @@
|
||||
this._video.addEventListener('canplaythrough', () => {
|
||||
this.$accessor.video.setPlayable(true)
|
||||
if (this.autoplay) {
|
||||
if (!document.hasFocus() || !this.$accessor.active) {
|
||||
if (this.startsMuted && (!document.hasFocus() || !this.$accessor.active)) {
|
||||
this.$accessor.video.setMuted(true)
|
||||
this._video.muted = true
|
||||
}
|
||||
@ -398,7 +417,7 @@
|
||||
return true
|
||||
}
|
||||
|
||||
this.$client.sendData('keydown', { key })
|
||||
this.$client.sendData('keydown', { key: this.keyMap(key) })
|
||||
return false
|
||||
}
|
||||
this.keyboard.onkeyup = (key: number) => {
|
||||
@ -406,7 +425,7 @@
|
||||
return
|
||||
}
|
||||
|
||||
this.$client.sendData('keyup', { key })
|
||||
this.$client.sendData('keyup', { key: this.keyMap(key) })
|
||||
}
|
||||
this.keyboard.listenTo(this._overlay)
|
||||
}
|
||||
@ -418,19 +437,60 @@
|
||||
/* Guacamole Keyboard does not provide destroy functions */
|
||||
}
|
||||
|
||||
play() {
|
||||
get hasMacOSKbd() {
|
||||
return /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)
|
||||
}
|
||||
|
||||
KeyTable = {
|
||||
XK_ISO_Level3_Shift: 0xfe03, // AltGr
|
||||
XK_Mode_switch: 0xff7e, // Character set switch
|
||||
XK_Control_L: 0xffe3, // Left control
|
||||
XK_Control_R: 0xffe4, // Right control
|
||||
XK_Meta_L: 0xffe7, // Left meta
|
||||
XK_Meta_R: 0xffe8, // Right meta
|
||||
XK_Alt_L: 0xffe9, // Left alt
|
||||
XK_Alt_R: 0xffea, // Right alt
|
||||
XK_Super_L: 0xffeb, // Left super
|
||||
XK_Super_R: 0xffec, // Right super
|
||||
}
|
||||
|
||||
keyMap(key: number): number {
|
||||
// Alt behaves more like AltGraph on macOS, so shuffle the
|
||||
// keys around a bit to make things more sane for the remote
|
||||
// server. This method is used by noVNC, RealVNC and TigerVNC
|
||||
// (and possibly others).
|
||||
if (this.hasMacOSKbd) {
|
||||
switch (key) {
|
||||
case this.KeyTable.XK_Meta_L:
|
||||
key = this.KeyTable.XK_Control_L
|
||||
break
|
||||
case this.KeyTable.XK_Super_L:
|
||||
key = this.KeyTable.XK_Alt_L
|
||||
break
|
||||
case this.KeyTable.XK_Super_R:
|
||||
key = this.KeyTable.XK_Super_L
|
||||
break
|
||||
case this.KeyTable.XK_Alt_L:
|
||||
key = this.KeyTable.XK_Mode_switch
|
||||
break
|
||||
case this.KeyTable.XK_Alt_R:
|
||||
key = this.KeyTable.XK_ISO_Level3_Shift
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
async play() {
|
||||
if (!this._video.paused || !this.playable) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
this._video
|
||||
.play()
|
||||
.then(() => {
|
||||
this.onResise()
|
||||
})
|
||||
.catch((err) => this.$log.error)
|
||||
} catch (err) {
|
||||
await this._video.play()
|
||||
this.onResise()
|
||||
} catch (err: any) {
|
||||
this.$log.error(err)
|
||||
}
|
||||
}
|
||||
@ -463,9 +523,40 @@
|
||||
this.$accessor.remote.toggle()
|
||||
}
|
||||
|
||||
_elementRequestFullscreen(el: HTMLElement) {
|
||||
if (typeof el.requestFullscreen === 'function') {
|
||||
el.requestFullscreen()
|
||||
//@ts-ignore
|
||||
} else if (typeof el.webkitRequestFullscreen === 'function') {
|
||||
//@ts-ignore
|
||||
el.webkitRequestFullscreen()
|
||||
//@ts-ignore
|
||||
} else if (typeof el.webkitEnterFullscreen === 'function') {
|
||||
//@ts-ignore
|
||||
el.webkitEnterFullscreen()
|
||||
//@ts-ignore
|
||||
} else if (typeof el.msRequestFullScreen === 'function') {
|
||||
//@ts-ignore
|
||||
el.msRequestFullScreen()
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
requestFullscreen() {
|
||||
this._player.requestFullscreen()
|
||||
this.onResise()
|
||||
// try to fullscreen player element
|
||||
if (this._elementRequestFullscreen(this._player)) {
|
||||
this.onResise()
|
||||
return
|
||||
}
|
||||
|
||||
// fallback to fullscreen video itself (on mobile devices)
|
||||
if (this._elementRequestFullscreen(this._video)) {
|
||||
this.onResise()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
requestPictureInPicture() {
|
||||
@ -474,21 +565,21 @@
|
||||
this.onResise()
|
||||
}
|
||||
|
||||
onFocus() {
|
||||
async onFocus() {
|
||||
if (!document.hasFocus() || !this.$accessor.active) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.hosting && this.clipboard_available && typeof navigator.clipboard.readText === 'function') {
|
||||
navigator.clipboard
|
||||
.readText()
|
||||
.then((text) => {
|
||||
if (this.clipboard !== text) {
|
||||
this.$accessor.remote.setClipboard(text)
|
||||
this.$accessor.remote.sendClipboard(text)
|
||||
}
|
||||
})
|
||||
.catch(this.$log.error)
|
||||
if (this.hosting && this.clipboard_read_available) {
|
||||
try {
|
||||
const text = await navigator.clipboard.readText()
|
||||
if (this.clipboard !== text) {
|
||||
this.$accessor.remote.setClipboard(text)
|
||||
this.$accessor.remote.sendClipboard(text)
|
||||
}
|
||||
} catch (err: any) {
|
||||
this.$log.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -501,6 +592,7 @@
|
||||
})
|
||||
}
|
||||
|
||||
wheelThrottle = false
|
||||
onWheel(e: WheelEvent) {
|
||||
if (!this.hosting || this.locked) {
|
||||
return
|
||||
@ -510,6 +602,16 @@
|
||||
let x = e.deltaX
|
||||
let y = e.deltaY
|
||||
|
||||
// Pixel units unless it's non-zero.
|
||||
// Note that if deltamode is line or page won't matter since we aren't
|
||||
// sending the mouse wheel delta to the server anyway.
|
||||
// The difference between pixel and line can be important however since
|
||||
// we have a threshold that can be smaller than the line height.
|
||||
if (e.deltaMode !== 0) {
|
||||
x *= WHEEL_LINE_HEIGHT
|
||||
y *= WHEEL_LINE_HEIGHT
|
||||
}
|
||||
|
||||
if (this.scroll_invert) {
|
||||
x = x * -1
|
||||
y = y * -1
|
||||
@ -518,13 +620,25 @@
|
||||
x = Math.min(Math.max(x, -this.scroll), this.scroll)
|
||||
y = Math.min(Math.max(y, -this.scroll), this.scroll)
|
||||
|
||||
this.$client.sendData('wheel', { x, y })
|
||||
if (!this.wheelThrottle) {
|
||||
this.wheelThrottle = true
|
||||
this.$client.sendData('wheel', { x, y })
|
||||
|
||||
window.setTimeout(() => {
|
||||
this.wheelThrottle = false
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
onMouseDown(e: MouseEvent) {
|
||||
if (!this.hosting) {
|
||||
this.$emit('control-attempt', e)
|
||||
}
|
||||
|
||||
if (!this.hosting || this.locked) {
|
||||
return
|
||||
}
|
||||
|
||||
this.onMousePos(e)
|
||||
this.$client.sendData('mousedown', { key: e.button + 1 })
|
||||
}
|
||||
@ -533,6 +647,7 @@
|
||||
if (!this.hosting || this.locked) {
|
||||
return
|
||||
}
|
||||
|
||||
this.onMousePos(e)
|
||||
this.$client.sendData('mouseup', { key: e.button + 1 })
|
||||
}
|
||||
@ -546,11 +661,11 @@
|
||||
}
|
||||
|
||||
onMouseEnter(e: MouseEvent) {
|
||||
if(this.hosting) {
|
||||
if (this.hosting) {
|
||||
this.$accessor.remote.syncKeyboardModifierState({
|
||||
capsLock: e.getModifierState("CapsLock"),
|
||||
numLock: e.getModifierState("NumLock"),
|
||||
scrollLock: e.getModifierState("ScrollLock"),
|
||||
capsLock: e.getModifierState('CapsLock'),
|
||||
numLock: e.getModifierState('NumLock'),
|
||||
scrollLock: e.getModifierState('ScrollLock'),
|
||||
})
|
||||
}
|
||||
|
||||
@ -560,11 +675,11 @@
|
||||
}
|
||||
|
||||
onMouseLeave(e: MouseEvent) {
|
||||
if(this.hosting) {
|
||||
if (this.hosting) {
|
||||
this.$accessor.remote.setKeyboardModifierState({
|
||||
capsLock: e.getModifierState("CapsLock"),
|
||||
numLock: e.getModifierState("NumLock"),
|
||||
scrollLock: e.getModifierState("ScrollLock"),
|
||||
capsLock: e.getModifierState('CapsLock'),
|
||||
numLock: e.getModifierState('NumLock'),
|
||||
scrollLock: e.getModifierState('ScrollLock'),
|
||||
})
|
||||
}
|
||||
|
||||
|
84
client/src/lib.ts
Normal file
84
client/src/lib.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import { accessor as neko } from './store'
|
||||
import { PluginObject } from 'vue'
|
||||
|
||||
// Plugins
|
||||
import Logger from './plugins/log'
|
||||
import Client from './plugins/neko'
|
||||
import Axios from './plugins/axios'
|
||||
import Swal from './plugins/swal'
|
||||
import Anime from './plugins/anime'
|
||||
import { i18n } from './plugins/i18n'
|
||||
|
||||
// Components
|
||||
import Connect from '~/components/connect.vue'
|
||||
import Video from '~/components/video.vue'
|
||||
import Menu from '~/components/menu.vue'
|
||||
import Side from '~/components/side.vue'
|
||||
import Controls from '~/components/controls.vue'
|
||||
import Members from '~/components/members.vue'
|
||||
import Emotes from '~/components/emotes.vue'
|
||||
import About from '~/components/about.vue'
|
||||
import Header from '~/components/header.vue'
|
||||
import Chat from '~/components/chat.vue'
|
||||
import Clipboard from '~/components/clipboard.vue'
|
||||
import Emoji from '~/components/emoji.vue'
|
||||
import Emote from '~/components/emote.vue'
|
||||
import Context from '~/components/context.vue'
|
||||
import Markdown from '~/components/markdown'
|
||||
import Avatar from '~/components/avatar.vue'
|
||||
|
||||
// Vue
|
||||
import Vue from 'vue'
|
||||
import ToolTip from 'v-tooltip'
|
||||
|
||||
Vue.use(ToolTip)
|
||||
|
||||
const exportMixin = {
|
||||
computed: {
|
||||
$accessor() {
|
||||
return neko
|
||||
},
|
||||
$client () {
|
||||
return window.$client
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const plugini18n: PluginObject<undefined> = {
|
||||
install(Vue) {
|
||||
Vue.prototype.i18n = i18n
|
||||
Vue.prototype.$t = i18n.t.bind(i18n)
|
||||
Vue.prototype.$te = i18n.te.bind(i18n)
|
||||
},
|
||||
}
|
||||
|
||||
function extend (component: any) {
|
||||
return component
|
||||
.use(plugini18n)
|
||||
.use(Logger)
|
||||
.use(Axios)
|
||||
.use(Swal)
|
||||
.use(Anime)
|
||||
.use(Client)
|
||||
.extend(exportMixin)
|
||||
}
|
||||
|
||||
export const NekoConnect = extend(Connect)
|
||||
export const NekoVideo = extend(Video)
|
||||
export const NekoMenu = extend(Menu)
|
||||
export const NekoSide = extend(Side)
|
||||
export const NekoControls = extend(Controls)
|
||||
export const NekoMembers = extend(Members)
|
||||
export const NekoEmotes = extend(Emotes)
|
||||
export const NekoAbout = extend(About)
|
||||
export const NekoHeader = extend(Header)
|
||||
export const NekoChat = extend(Chat)
|
||||
export const NekoClipboard = extend(Clipboard)
|
||||
export const NekoEmoji = extend(Emoji)
|
||||
export const NekoEmote = extend(Emote)
|
||||
export const NekoMarkdown = extend(Markdown)
|
||||
export const NekoContext = extend(Context)
|
||||
export const NekoAvatar = extend(Avatar)
|
||||
|
||||
neko.initialise()
|
||||
export default neko
|
@ -1,5 +1,5 @@
|
||||
export const logout = 'logout'
|
||||
export const unsupported = 'this browser does not support webrtc'
|
||||
export const logout = 'log out'
|
||||
export const unsupported = 'this web-browser does not support WebRTC'
|
||||
export const admin_loggedin = 'You are logged in as an admin'
|
||||
export const you = 'You'
|
||||
export const send_a_message = 'Send a message'
|
||||
@ -10,10 +10,13 @@ export const side = {
|
||||
}
|
||||
|
||||
export const connect = {
|
||||
title: 'Please Login',
|
||||
displayname: 'Display Name',
|
||||
login_title: 'Please Log In',
|
||||
invitation_title: 'You have been invited to this room',
|
||||
displayname: 'Enter your display name',
|
||||
password: 'Password',
|
||||
connect: 'Connect',
|
||||
error: 'Login error',
|
||||
empty_displayname: 'Display Name cannot be empty.',
|
||||
}
|
||||
|
||||
export const context = {
|
||||
@ -30,11 +33,11 @@ export const context = {
|
||||
kick_title: 'Kick {name}?',
|
||||
kick_text: 'Are you sure you want to kick {name}?',
|
||||
ban_title: 'Ban {name}?',
|
||||
ban_text: 'Are you sure you want to ban {name}? You will need to restart the server to undo this.',
|
||||
ban_text: 'Do you want to ban {name}? You will need to restart the server to undo this.',
|
||||
mute_title: 'Mute {name}?',
|
||||
mute_text: 'Are you sure you want to mute {name}?',
|
||||
unmute_title: 'Unmute {name}?',
|
||||
unmute_text: 'Are you sure you want to unmute {name}?',
|
||||
unmute_text: 'Do you want to unmute {name}?',
|
||||
button_yes: 'Yes',
|
||||
button_cancel: 'Cancel',
|
||||
},
|
||||
@ -61,29 +64,30 @@ export const setting = {
|
||||
ignore_emotes: 'Ignore Emotes',
|
||||
chat_sound: 'Play Chat Sound',
|
||||
keyboard_layout: 'Keyboard Layout',
|
||||
broadcast_is_active: 'Broadcast Enabled',
|
||||
broadcast_url: 'RTMP url',
|
||||
broadcast_title: 'Live Broadcast',
|
||||
}
|
||||
|
||||
export const connection = {
|
||||
logged_out: 'You have been logged out!',
|
||||
connected: 'Successfully connected',
|
||||
disconnected: 'You have been disconnected',
|
||||
button_confirm: 'Ok',
|
||||
logged_out: 'You have been logged out.',
|
||||
reconnecting: 'Reconnecting...',
|
||||
connected: 'Connected',
|
||||
disconnected: 'Disconnected',
|
||||
kicked: 'You have been removed from this room.',
|
||||
button_confirm: 'OK',
|
||||
}
|
||||
|
||||
export const notifications = {
|
||||
connected: '{name} connected',
|
||||
disconnected: '{name} disconnected',
|
||||
controls_taken: '{name} took the controls',
|
||||
controls_taken_force: 'force took the controls',
|
||||
controls_taken_force: 'took the controls forcibly',
|
||||
controls_taken_steal: 'took the controls from {name}',
|
||||
controls_released: '{name} released the controls',
|
||||
controls_released_force: 'force released the controls',
|
||||
controls_released_force: 'released the controls forcibly',
|
||||
controls_released_steal: 'released the controls from {name}',
|
||||
controls_given: 'gave the controls to {name}',
|
||||
controls_has: '{name} has the controls',
|
||||
controls_has_alt: 'But I let them know you wanted it',
|
||||
controls_has_alt: 'But I let the person know you wanted it',
|
||||
controls_requesting: '{name} is requesting the controls',
|
||||
resolution: 'changed the resolution to {width}x{height}@{rate}',
|
||||
banned: 'banned {name}',
|
||||
|
103
client/src/locale/es-sp.ts
Normal file
103
client/src/locale/es-sp.ts
Normal file
@ -0,0 +1,103 @@
|
||||
export const logout = 'salir'
|
||||
export const unsupported = 'este navegador no soporta webrtc'
|
||||
export const admin_loggedin = 'Registrado como admin'
|
||||
export const you = 'Tú'
|
||||
export const send_a_message = 'Enviar un mensaje'
|
||||
|
||||
export const side = {
|
||||
chat: 'Chat',
|
||||
settings: 'Configuración',
|
||||
}
|
||||
|
||||
export const connect = {
|
||||
login_title: 'Por favor regístrate',
|
||||
invitation_title: 'Te han invitado a esta sala',
|
||||
displayname: 'Introduce tu nombre',
|
||||
password: 'Contraseña',
|
||||
connect: 'Conectar',
|
||||
error: 'Error de login',
|
||||
// TODO
|
||||
//empty_displayname: 'Display Name cannot be empty.',
|
||||
}
|
||||
|
||||
export const context = {
|
||||
ignore: 'Ignorar',
|
||||
unignore: 'No ignorar',
|
||||
mute: 'Silenciar',
|
||||
unmute: 'No silenciar',
|
||||
release: 'Forzar liberar los controles',
|
||||
take: 'Forzar obtener los controles',
|
||||
give: 'Dar los controles',
|
||||
kick: 'Echar',
|
||||
ban: 'Bloquear IP',
|
||||
confirm: {
|
||||
kick_title: 'Echar a {name}?',
|
||||
kick_text: 'Seguro que quiere echar a {name}?',
|
||||
ban_title: 'Bloquear a {name}?',
|
||||
ban_text: 'Seguroq ue quieres bloquear a {name}? Necesitarás reiniciar el servidor para deshacer esta acción.',
|
||||
mute_title: 'Silenciar a {name}?',
|
||||
mute_text: 'Seguro que quieres silenciar a {name}?',
|
||||
unmute_title: 'Dejar de silenciar a {name}?',
|
||||
unmute_text: 'Seguro que quieres dejar de silenciar a {name}?',
|
||||
button_yes: 'Sí',
|
||||
button_cancel: 'Cancelar',
|
||||
},
|
||||
}
|
||||
|
||||
export const controls = {
|
||||
release: 'Controles liberador',
|
||||
request: 'Controles solicitados',
|
||||
lock: 'Controles bloqueados',
|
||||
unlock: 'Controles desbloqueados',
|
||||
}
|
||||
|
||||
export const room = {
|
||||
lock: 'Bloquear sala (para usuarios)',
|
||||
unlock: 'Desbloquear sala (para usuarios)',
|
||||
locked: 'Sala bloqueada (para usuarios)',
|
||||
unlocked: 'Sala desbloqueada (para usuarios)',
|
||||
}
|
||||
|
||||
export const setting = {
|
||||
scroll: 'Sensibilidad del Scroll',
|
||||
scroll_invert: 'Invertir Scroll',
|
||||
autoplay: 'Auto Reproducir Video',
|
||||
ignore_emotes: 'Ignorar Emotes',
|
||||
chat_sound: 'Reproducir Sonidos Chat',
|
||||
keyboard_layout: 'Keyboard Layout',
|
||||
// TODO
|
||||
//broadcast_title: 'Live Broadcast',
|
||||
}
|
||||
|
||||
export const connection = {
|
||||
logged_out: 'Has salido!',
|
||||
// TODO
|
||||
//reconnecting: 'Reconnecting',
|
||||
connected: 'Connectado correctamente',
|
||||
disconnected: 'Has sido desconectado',
|
||||
// TODO
|
||||
//kicked: 'You have been removed from this room.',
|
||||
button_confirm: 'De acuerdo',
|
||||
}
|
||||
|
||||
export const notifications = {
|
||||
connected: '{name} se ha conectado',
|
||||
disconnected: '{name} se ha desconnectado',
|
||||
controls_taken: '{name} tiene los controles',
|
||||
controls_taken_force: 'controles confiscados',
|
||||
controls_taken_steal: 'cogió los controles de {name}',
|
||||
controls_released: '{name} ha liberado los controles',
|
||||
controls_released_force: 'controles liberados',
|
||||
controls_released_steal: 'controles liberados de {name}',
|
||||
controls_given: 'controles asignados a {name}',
|
||||
controls_has: '{name} tiene los controles',
|
||||
controls_has_alt: 'Pero le diré que quieres los controles',
|
||||
controls_requesting: '{name} quiere los controles',
|
||||
resolution: 'resolución cambiada a {width}x{height}@{rate}',
|
||||
banned: '{name} bloqueado',
|
||||
kicked: '{name} expulsado',
|
||||
muted: '{name} silenciado',
|
||||
unmuted: '{name} no silenciado',
|
||||
room_locked: 'bloqueó la sala',
|
||||
room_unlocked: 'desbloqueó la sala',
|
||||
}
|
103
client/src/locale/fr-fr.ts
Normal file
103
client/src/locale/fr-fr.ts
Normal file
@ -0,0 +1,103 @@
|
||||
export const logout = 'Se déconnecter'
|
||||
export const unsupported = 'ce navigateur ne prend pas en charge WebRTC'
|
||||
export const admin_loggedin = "Vous êtes connecté en tant qu'admin"
|
||||
export const you = 'Vous'
|
||||
export const send_a_message = 'Envoyer un message'
|
||||
|
||||
export const side = {
|
||||
chat: 'Chat',
|
||||
settings: 'Paramètres',
|
||||
}
|
||||
|
||||
export const connect = {
|
||||
login_title: 'Veuillez vous connecter',
|
||||
invitation_title: 'Vous avez été invité dans cette salle',
|
||||
displayname: "Entrez votre nom d'utilisateur",
|
||||
password: 'Mot de passe',
|
||||
connect: 'Connexion',
|
||||
error: 'Erreur de connexion',
|
||||
// TODO
|
||||
//empty_displayname: 'Display Name cannot be empty.',
|
||||
}
|
||||
|
||||
export const context = {
|
||||
ignore: 'Ignorer',
|
||||
unignore: 'Ne plus ignorer',
|
||||
mute: 'Mute',
|
||||
unmute: 'Démute',
|
||||
release: 'Forcer le relachement de contrôle',
|
||||
take: 'Forcer la prise de contrôle',
|
||||
give: 'Donner le contrôle',
|
||||
kick: 'Kicker',
|
||||
ban: "Bannir l'IP",
|
||||
confirm: {
|
||||
kick_title: 'Kicker {name}?',
|
||||
kick_text: 'Êtes vous sûr de kick {name}?',
|
||||
ban_title: 'Bannir {name}?',
|
||||
ban_text: 'Voulez-vous bannir {name}? Vous devez relancer le serveur pour annuler le bannissement.',
|
||||
mute_title: 'Muter {name}?',
|
||||
mute_text: 'Êtes-vous sûr de muter {name}?',
|
||||
unmute_title: 'Démute {name}?',
|
||||
unmute_text: 'Voulez-vous démuter {name}?',
|
||||
button_yes: 'Oui',
|
||||
button_cancel: 'Annuler',
|
||||
},
|
||||
}
|
||||
|
||||
export const controls = {
|
||||
release: 'Relacher le contrôle',
|
||||
request: 'Demander le contrôle',
|
||||
lock: 'Vérouiller le contrôle',
|
||||
unlock: 'Débloquer le contrôle',
|
||||
}
|
||||
|
||||
export const room = {
|
||||
lock: 'Vérouiller la salle (pour les utilisateurs)',
|
||||
unlock: 'Dévérouiller la salle (pour les utilisateurs)',
|
||||
locked: 'Salle vérouillée (pour les utilisateurs)',
|
||||
unlocked: 'Salle dévérouillée (pour les utilisateurs)',
|
||||
}
|
||||
|
||||
export const setting = {
|
||||
scroll: 'Sensibilité de défilement (scroll)',
|
||||
scroll_invert: 'Inverser le défilement (scroll)',
|
||||
autoplay: 'Jouer automatiquement la vidéo',
|
||||
ignore_emotes: 'Ignorer les Emotes',
|
||||
chat_sound: 'Jouer le son du tchat',
|
||||
keyboard_layout: 'Langue du clavier',
|
||||
// TODO
|
||||
//broadcast_title: 'Live Broadcast',
|
||||
}
|
||||
|
||||
export const connection = {
|
||||
logged_out: 'Vous avez été déconnecté.',
|
||||
// TODO
|
||||
//reconnecting: 'Reconnecting',
|
||||
connected: 'Connecté',
|
||||
disconnected: 'Déconnecté',
|
||||
// TODO
|
||||
//kicked: 'You have been removed from this room.',
|
||||
button_confirm: 'OK',
|
||||
}
|
||||
|
||||
export const notifications = {
|
||||
connected: '{name} connecté',
|
||||
disconnected: '{name} déconnecté',
|
||||
controls_taken: '{name} a pris le contrôle',
|
||||
controls_taken_force: 'a forcé la prise de contrôle',
|
||||
controls_taken_steal: 'a pris le contrôle de {name}',
|
||||
controls_released: '{name} a relâché le contrôle',
|
||||
controls_released_force: 'a forcé la perte de contrôle',
|
||||
controls_released_steal: 'a forcé la pêrte de contrôle de {name}',
|
||||
controls_given: 'a donné le contrôle à {name}',
|
||||
controls_has: '{name} a le contrôle',
|
||||
controls_has_alt: "Mais j'ai fait savoir que vous le voulez",
|
||||
controls_requesting: '{name} demande le contrôle',
|
||||
resolution: 'a changé la résolution pour du {width}x{height}@{rate}',
|
||||
banned: 'a banni {name}',
|
||||
kicked: 'a kick {name}',
|
||||
muted: 'a mute {name}',
|
||||
unmuted: 'a démute {name}',
|
||||
room_locked: 'a vérouillé la salle',
|
||||
room_unlocked: 'a dévérouillé la salle',
|
||||
}
|
@ -1,5 +1,15 @@
|
||||
import * as en from './en-us'
|
||||
import * as es from './es-sp'
|
||||
import * as sk from './sk-sk'
|
||||
import * as sv from './sv-se'
|
||||
import * as nb from './nb-no'
|
||||
import * as fr from './fr-fr'
|
||||
|
||||
export const messages = {
|
||||
en,
|
||||
es,
|
||||
sk,
|
||||
sv,
|
||||
nb,
|
||||
fr,
|
||||
}
|
||||
|
103
client/src/locale/nb-no.ts
Normal file
103
client/src/locale/nb-no.ts
Normal file
@ -0,0 +1,103 @@
|
||||
export const logout = 'logg ut'
|
||||
export const unsupported = 'Denne nettleseren støtter ikke WebRTC'
|
||||
export const admin_loggedin = 'Du er innlogget som administrator'
|
||||
export const you = 'Deg'
|
||||
export const send_a_message = 'Send en melding'
|
||||
|
||||
export const side = {
|
||||
chat: 'Sludring',
|
||||
settings: 'Innstillinger',
|
||||
}
|
||||
|
||||
export const connect = {
|
||||
login_title: 'Logg inn',
|
||||
invitation_title: 'Du har blitt invitert til dette rommet',
|
||||
displayname: 'Skriv inn ditt visningsnavn',
|
||||
password: 'Passord',
|
||||
connect: 'Koble til',
|
||||
error: 'Innloggingsfeil',
|
||||
// TODO
|
||||
//empty_displayname: 'Display Name cannot be empty.',
|
||||
}
|
||||
|
||||
export const context = {
|
||||
ignore: 'Ignorer',
|
||||
unignore: 'Opphev ignorering',
|
||||
mute: 'Forstum',
|
||||
unmute: 'Opphev forstummelse',
|
||||
release: 'Slipp kontrollen med tvang',
|
||||
take: 'Ta kontrollen med tvang',
|
||||
give: 'Gi vekk kontroll',
|
||||
kick: 'Kast ut',
|
||||
ban: 'Bannlys IP',
|
||||
confirm: {
|
||||
kick_title: 'Kast ut {name}?',
|
||||
kick_text: 'Vil du kaste ut {name}?',
|
||||
ban_title: 'Bannlys {name}?',
|
||||
ban_text: 'Vil du bannlyse {name}? Du vil måtte starte tjeneren på ny for å omgjøre dette.',
|
||||
mute_title: 'Mute {name}?',
|
||||
mute_text: 'Vil du forstumme {name}?',
|
||||
unmute_title: 'Unmute {name}?',
|
||||
unmute_text: 'Vil du oppheve forstummelsen av {name}?',
|
||||
button_yes: 'Ja',
|
||||
button_cancel: 'Avbryt',
|
||||
},
|
||||
}
|
||||
|
||||
export const controls = {
|
||||
release: 'Slipp kontrollen',
|
||||
request: 'Forespør kontroll',
|
||||
lock: 'Lås kontrollen',
|
||||
unlock: 'Lås opp kontrollen',
|
||||
}
|
||||
|
||||
export const room = {
|
||||
lock: 'Lås rommet (for brukere)',
|
||||
unlock: 'Lås opp rommet (for brukere)',
|
||||
locked: 'Rom låst (for brukere)',
|
||||
unlocked: 'Rom opplåst (for brukere)',
|
||||
}
|
||||
|
||||
export const setting = {
|
||||
scroll: 'Rullingssensitivitet',
|
||||
scroll_invert: 'Inverter rulling',
|
||||
autoplay: 'Spill video automatisk',
|
||||
ignore_emotes: 'Ignorer smilefjes',
|
||||
chat_sound: 'Sludringslyd',
|
||||
keyboard_layout: 'Tastaturoppsett',
|
||||
// TODO
|
||||
//broadcast_title: 'Live Broadcast',
|
||||
}
|
||||
|
||||
export const connection = {
|
||||
logged_out: 'Du har blitt utlogget.',
|
||||
// TODO
|
||||
//reconnecting: 'Reconnecting',
|
||||
connected: 'Tilkoblet',
|
||||
disconnected: 'Frakoblet',
|
||||
// TODO
|
||||
//kicked: 'You have been removed from this room.',
|
||||
button_confirm: 'OK',
|
||||
}
|
||||
|
||||
export const notifications = {
|
||||
connected: '{name} koblet til',
|
||||
disconnected: '{name} koblet fra',
|
||||
controls_taken: '{name} tok kontrollen',
|
||||
controls_taken_force: 'tok kontrollen med tvang',
|
||||
controls_taken_steal: 'tok kontrollen fra {name}',
|
||||
controls_released: '{name} ga vekk kontrollen',
|
||||
controls_released_force: 'ga vekk kontrollen med tvang',
|
||||
controls_released_steal: 'ga vekk kontrollen fra {name}',
|
||||
controls_given: 'ga {name} kontrollen',
|
||||
controls_has: '{name} har kontrollen',
|
||||
controls_has_alt: 'Jeg la det komme vedkommende til kjenne at du ønsket den',
|
||||
controls_requesting: '{name} forespør kontrollen',
|
||||
resolution: 'endret oppløsningen til {width}x{height}@{rate}',
|
||||
banned: 'bannlyste {name}',
|
||||
kicked: 'kastet ut {name}',
|
||||
muted: 'forstummet {name}',
|
||||
unmuted: 'opphevet forstummingen av {name}',
|
||||
room_locked: 'låste rommet',
|
||||
room_unlocked: 'låste opp rommet',
|
||||
}
|
100
client/src/locale/sk-sk.ts
Normal file
100
client/src/locale/sk-sk.ts
Normal file
@ -0,0 +1,100 @@
|
||||
export const logout = 'odhlásiť sa'
|
||||
export const unsupported = 'tento prehliadač nepodporuje webrtc'
|
||||
export const admin_loggedin = 'Ste prihlásení/á ako administrátor'
|
||||
// export const you = '' // Incorrect in some translations! Cannot be used!
|
||||
export const send_a_message = 'Odoslať správu'
|
||||
|
||||
export const side = {
|
||||
chat: 'Chat',
|
||||
settings: 'Nastavenia',
|
||||
}
|
||||
|
||||
export const connect = {
|
||||
login_title: 'Prihláste sa',
|
||||
invitation_title: 'Boli ste pozvaný/á do miestnosti',
|
||||
displayname: 'Vaše meno',
|
||||
password: 'Heslo',
|
||||
connect: 'Pripojiť sa',
|
||||
error: 'Chyba pri prihlasovaní',
|
||||
empty_displayname: 'Meno nemôže byť prázdne.',
|
||||
}
|
||||
|
||||
export const context = {
|
||||
ignore: 'Ignorovať',
|
||||
unignore: 'Zrušiť ignorovanie',
|
||||
mute: 'Zakázať chat',
|
||||
unmute: 'Povoliť chat',
|
||||
release: 'Zrušiť ovládanie',
|
||||
take: 'Prevziať ovládanie',
|
||||
give: 'Ponúknuť ovládanie',
|
||||
kick: 'Kick',
|
||||
ban: 'Ban IP',
|
||||
confirm: {
|
||||
kick_title: 'Kick {name}?',
|
||||
kick_text: 'Ste si istý/á, že chcete vykopnúť používateľa {name}?',
|
||||
ban_title: 'Ban {name}?',
|
||||
ban_text:
|
||||
'Ste si istý/á, že chcete zablokovať používateľa {name}? Pre odblokovanie budete musieť reštartovať server.',
|
||||
mute_title: 'Zakázať chat pre používateľa {name}?',
|
||||
mute_text: 'Ste si istý/á, že chcete zakázať chat pre používateľa {name}?',
|
||||
unmute_title: 'Povoliť chat pre používateľa {name}?',
|
||||
unmute_text: 'Ste si istý/á, že chcete povoliť chat pre používateľa {name}?',
|
||||
button_yes: 'Áno',
|
||||
button_cancel: 'Zrušiť',
|
||||
},
|
||||
}
|
||||
|
||||
export const controls = {
|
||||
release: 'Uvoľniť ovládanie',
|
||||
request: 'Požiadať o ovládanie',
|
||||
lock: 'Zamknúť ovládanie',
|
||||
unlock: 'Odomknúť ovládanie',
|
||||
}
|
||||
|
||||
export const room = {
|
||||
lock: 'Zamknúť miestnosť (pre používateľov)',
|
||||
unlock: 'Odomknúť miestnosť (pre používateľov)',
|
||||
locked: 'Miestnosť je zamknutá (pre používateľov)',
|
||||
unlocked: 'Miestnosť odomknutá (pre používateľov)',
|
||||
}
|
||||
|
||||
export const setting = {
|
||||
scroll: 'Citlivosť kolieska myši',
|
||||
scroll_invert: 'Invertovať koliesko myši',
|
||||
autoplay: 'Automatické prehrávanie videa',
|
||||
ignore_emotes: 'Ignorovať smajlíky',
|
||||
chat_sound: 'Prehrávať zvuky chatu',
|
||||
keyboard_layout: 'Rozloženie klávesnice',
|
||||
broadcast_title: 'Živé vysielanie',
|
||||
}
|
||||
|
||||
export const connection = {
|
||||
logged_out: 'Boli ste odhlásený/á',
|
||||
reconnecting: 'Obnova spojenia...',
|
||||
connected: 'Úspešne pripojený/á',
|
||||
disconnected: 'Boli ste odpojený/á',
|
||||
kicked: 'Boli ste odstránený/á z tejto miestnosti.',
|
||||
button_confirm: 'Ok',
|
||||
}
|
||||
|
||||
export const notifications = {
|
||||
connected: '{name} sa pripojil/a',
|
||||
disconnected: '{name} sa odpojil/a',
|
||||
controls_taken: '{name} prevzal/a ovládanie',
|
||||
controls_taken_force: 'ovládanie bolo prevzaté',
|
||||
controls_taken_steal: 'prevzal/a ovládanie od použivateľa {name}',
|
||||
controls_released: '{name} uvoľnil/a ovládanie',
|
||||
controls_released_force: 'ovládanie bolo uvoľnené',
|
||||
controls_released_steal: 'uvoľnil/a ovládanie použivateľa {name}',
|
||||
controls_given: 'ponúkol/a ovládanie používateľovi {name}',
|
||||
controls_has: '{name} má ovládanie',
|
||||
controls_has_alt: 'Ale dám mu vedieť, že si chcel ovládanie',
|
||||
controls_requesting: '{name} by chcel/a ovládanie',
|
||||
resolution: 'zmenené rozlíšenie na {width}x{height}@{rate}',
|
||||
banned: '{name} dostal/a BAN',
|
||||
kicked: '{name} bol/a vykopnutý/a',
|
||||
muted: 'zakázal chat používateľovi {name}',
|
||||
unmuted: 'povolil chat používateľovi {name}',
|
||||
room_locked: 'miestnosť bola zamknutá',
|
||||
room_unlocked: 'miestnosť bola odomknutá',
|
||||
}
|
103
client/src/locale/sv-se.ts
Normal file
103
client/src/locale/sv-se.ts
Normal file
@ -0,0 +1,103 @@
|
||||
export const logout = 'logga ut'
|
||||
export const unsupported = 'denna webbläsare har inte stöd för webrtc'
|
||||
export const admin_loggedin = 'Du är inloggad som en administratör'
|
||||
export const you = 'Du'
|
||||
export const send_a_message = 'Skicka ett meddelande'
|
||||
|
||||
export const side = {
|
||||
chat: 'Chatt',
|
||||
settings: 'Inställningar',
|
||||
}
|
||||
|
||||
export const connect = {
|
||||
login_title: 'Vänligen logga in',
|
||||
invitation_title: 'Du har blivit inbjuden till detta rum',
|
||||
displayname: 'Skriv in ditt namn',
|
||||
password: 'Lösenord',
|
||||
connect: 'Anslut',
|
||||
error: 'Inloggningsfel',
|
||||
// TODO
|
||||
//empty_displayname: 'Display Name cannot be empty.',
|
||||
}
|
||||
|
||||
export const context = {
|
||||
ignore: 'Ignorera',
|
||||
unignore: 'Inte ignorera',
|
||||
mute: 'Tysta',
|
||||
unmute: 'Ta bort tystning',
|
||||
release: 'Tvinga ta bort kontrollen',
|
||||
take: 'Tvinga ta kontrollen',
|
||||
give: 'Ge kontrollen',
|
||||
kick: 'Sparka',
|
||||
ban: 'Bannlys IP',
|
||||
confirm: {
|
||||
kick_title: 'Sparka {name}?',
|
||||
kick_text: 'Är du säker du vill sparka {name}?',
|
||||
ban_title: 'Bannlys {name}?',
|
||||
ban_text: 'Är du säker du vill bannlysa {name}? Du behöver starta om servern för att ta bort den bannlysningen.',
|
||||
mute_title: 'Tysta {name}?',
|
||||
mute_text: 'Är du säker du vill tysta {name}?',
|
||||
unmute_title: 'Ta bort tystningen {name}?',
|
||||
unmute_text: 'Är du säker du vill ta bort tystningen {name}?',
|
||||
button_yes: 'Ja',
|
||||
button_cancel: 'Avbryt',
|
||||
},
|
||||
}
|
||||
|
||||
export const controls = {
|
||||
release: 'Ta kontrollen',
|
||||
request: 'Fråga om kontroll',
|
||||
lock: 'Lås kontrollen',
|
||||
unlock: 'Lås upp kontrollen',
|
||||
}
|
||||
|
||||
export const room = {
|
||||
lock: 'Lås rum (för användare)',
|
||||
unlock: 'Lås upp rummet (för användare)',
|
||||
locked: 'Rum låst (för användare)',
|
||||
unlocked: 'Rum upplåst (för användare)',
|
||||
}
|
||||
|
||||
export const setting = {
|
||||
scroll: 'Scrollkänslighet',
|
||||
scroll_invert: 'Vänd Scrollen',
|
||||
autoplay: 'Automatisk uppspelning av Video',
|
||||
ignore_emotes: 'Ignorera Emotes',
|
||||
chat_sound: 'Spela Chatt Ljud',
|
||||
keyboard_layout: 'Tangentbordslayout',
|
||||
// TODO
|
||||
//broadcast_title: 'Live Broadcast',
|
||||
}
|
||||
|
||||
export const connection = {
|
||||
logged_out: 'Du har blivit utloggad!',
|
||||
// TODO
|
||||
//reconnecting: 'Reconnecting',
|
||||
connected: 'Du har loggats in',
|
||||
disconnected: 'Du har blivit frånkopplad',
|
||||
// TODO
|
||||
//kicked: 'You have been removed from this room.',
|
||||
button_confirm: 'Ok',
|
||||
}
|
||||
|
||||
export const notifications = {
|
||||
connected: '{name} anslöt',
|
||||
disconnected: '{name} kopplade ifrån',
|
||||
controls_taken: '{name} tog kontrollen',
|
||||
controls_taken_force: 'tvinga ta kontrollen',
|
||||
controls_taken_steal: 'tog kontrollen från {name}',
|
||||
controls_released: '{name} lämnade kontrollen',
|
||||
controls_released_force: 'tvingade ta bort kontrollen',
|
||||
controls_released_steal: 'tog bort kontrollen från {name}',
|
||||
controls_given: 'gav kontrollen till {name}',
|
||||
controls_has: '{name} har kontrollen',
|
||||
controls_has_alt: 'Men jag låter dem veta att du vill ha den',
|
||||
controls_requesting: '{name} frågar om kontrollen',
|
||||
resolution: 'ändrade upplösningen till {width}x{height}@{rate}',
|
||||
banned: 'bannlyste {name}',
|
||||
kicked: 'sparkade {name}',
|
||||
muted: 'tystade {name}',
|
||||
unmuted: 'tog bort tystningen på {name}',
|
||||
room_locked: 'låste rummet',
|
||||
room_unlocked: 'låste upp rummet',
|
||||
}
|
@ -2,7 +2,7 @@ import EventEmitter from 'eventemitter3'
|
||||
import { OPCODE } from './data'
|
||||
import { EVENT, WebSocketEvents } from './events'
|
||||
|
||||
import { WebSocketMessages, WebSocketPayloads, SignalProvidePayload } from './messages'
|
||||
import { WebSocketMessages, WebSocketPayloads, SignalProvidePayload, SignalCandidatePayload } from './messages'
|
||||
|
||||
export interface BaseEvents {
|
||||
info: (...message: any[]) => void
|
||||
@ -15,10 +15,11 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||
protected _ws?: WebSocket
|
||||
protected _peer?: RTCPeerConnection
|
||||
protected _channel?: RTCDataChannel
|
||||
protected _timeout?: NodeJS.Timeout
|
||||
protected _timeout?: number
|
||||
protected _displayname?: string
|
||||
protected _state: RTCIceConnectionState = 'disconnected'
|
||||
protected _id = ''
|
||||
protected _candidates: RTCIceCandidate[] = []
|
||||
|
||||
get id() {
|
||||
return this._id
|
||||
@ -51,21 +52,17 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||
return
|
||||
}
|
||||
|
||||
if (displayname === '') {
|
||||
throw new Error('Must add a displayname') // TODO: Better handling
|
||||
}
|
||||
|
||||
this._displayname = displayname
|
||||
this[EVENT.CONNECTING]()
|
||||
|
||||
try {
|
||||
this._ws = new WebSocket(`${url}ws?password=${password}`)
|
||||
this._ws = new WebSocket(`${url}?password=${encodeURIComponent(password)}`)
|
||||
this.emit('debug', `connecting to ${this._ws.url}`)
|
||||
this._ws.onmessage = this.onMessage.bind(this)
|
||||
this._ws.onerror = event => this.onError.bind(this)
|
||||
this._ws.onclose = event => this.onDisconnected.bind(this, new Error('websocket closed'))
|
||||
this._timeout = setTimeout(this.onTimeout.bind(this), 15000)
|
||||
} catch (err) {
|
||||
this._ws.onerror = (event) => this.onError.bind(this)
|
||||
this._ws.onclose = (event) => this.onDisconnected.bind(this, new Error('websocket closed'))
|
||||
this._timeout = window.setTimeout(this.onTimeout.bind(this), 15000)
|
||||
} catch (err: any) {
|
||||
this.onDisconnected(err)
|
||||
}
|
||||
}
|
||||
@ -73,19 +70,46 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||
protected disconnect() {
|
||||
if (this._timeout) {
|
||||
clearTimeout(this._timeout)
|
||||
this._timeout = undefined
|
||||
}
|
||||
|
||||
if (this.socketOpen) {
|
||||
if (this._ws) {
|
||||
// reset all events
|
||||
this._ws.onmessage = () => {}
|
||||
this._ws.onerror = () => {}
|
||||
this._ws.onclose = () => {}
|
||||
|
||||
try {
|
||||
this._ws!.close()
|
||||
this._ws.close()
|
||||
} catch (err) {}
|
||||
|
||||
this._ws = undefined
|
||||
}
|
||||
|
||||
if (this.peerConnected) {
|
||||
if (this._channel) {
|
||||
// reset all events
|
||||
this._channel.onmessage = () => {}
|
||||
this._channel.onerror = () => {}
|
||||
this._channel.onclose = () => {}
|
||||
|
||||
try {
|
||||
this._peer!.close()
|
||||
this._channel.close()
|
||||
} catch (err) {}
|
||||
|
||||
this._channel = undefined
|
||||
}
|
||||
|
||||
if (this._peer) {
|
||||
// reset all events
|
||||
this._peer.onconnectionstatechange = () => {}
|
||||
this._peer.onsignalingstatechange = () => {}
|
||||
this._peer.oniceconnectionstatechange = () => {}
|
||||
this._peer.ontrack = () => {}
|
||||
|
||||
try {
|
||||
this._peer.close()
|
||||
} catch (err) {}
|
||||
|
||||
this._peer = undefined
|
||||
}
|
||||
|
||||
@ -156,7 +180,7 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||
this._ws!.send(JSON.stringify({ event, ...payload }))
|
||||
}
|
||||
|
||||
public createPeer(sdp: string, lite: boolean, servers: string[]) {
|
||||
public async createPeer(sdp: string, lite: boolean, servers: RTCIceServer[]) {
|
||||
this.emit('debug', `creating peer`)
|
||||
if (!this.socketOpen) {
|
||||
this.emit(
|
||||
@ -175,19 +199,19 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||
this._peer = new RTCPeerConnection()
|
||||
if (lite !== true) {
|
||||
this._peer = new RTCPeerConnection({
|
||||
iceServers: [{ urls: servers }],
|
||||
iceServers: servers,
|
||||
})
|
||||
}
|
||||
|
||||
this._peer.onconnectionstatechange = event => {
|
||||
this._peer.onconnectionstatechange = (event) => {
|
||||
this.emit('debug', `peer connection state changed`, this._peer ? this._peer.connectionState : undefined)
|
||||
}
|
||||
|
||||
this._peer.onsignalingstatechange = event => {
|
||||
this._peer.onsignalingstatechange = (event) => {
|
||||
this.emit('debug', `peer signaling state changed`, this._peer ? this._peer.signalingState : undefined)
|
||||
}
|
||||
|
||||
this._peer.oniceconnectionstatechange = event => {
|
||||
this._peer.oniceconnectionstatechange = (event) => {
|
||||
this._state = this._peer!.iceConnectionState
|
||||
|
||||
this.emit('debug', `peer ice connection state changed: ${this._peer!.iceConnectionState}`)
|
||||
@ -196,16 +220,24 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||
case 'checking':
|
||||
if (this._timeout) {
|
||||
clearTimeout(this._timeout)
|
||||
this._timeout = undefined
|
||||
}
|
||||
break
|
||||
case 'connected':
|
||||
this.onConnected()
|
||||
break
|
||||
case 'disconnected':
|
||||
this[EVENT.RECONNECTING]()
|
||||
break
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling#ice_connection_state
|
||||
// We don't watch the disconnected signaling state here as it can indicate temporary issues and may
|
||||
// go back to a connected state after some time. Watching it would close the video call on any temporary
|
||||
// network issue.
|
||||
case 'failed':
|
||||
this.onDisconnected(new Error('peer failed'))
|
||||
break
|
||||
case 'disconnected':
|
||||
this.onDisconnected(new Error('peer disconnected'))
|
||||
case 'closed':
|
||||
this.onDisconnected(new Error('peer closed'))
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -220,19 +252,26 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||
this._channel.onclose = this.onDisconnected.bind(this, new Error('peer data channel closed'))
|
||||
|
||||
this._peer.setRemoteDescription({ type: 'offer', sdp })
|
||||
this._peer
|
||||
.createAnswer()
|
||||
.then(d => {
|
||||
this._peer!.setLocalDescription(d)
|
||||
this._ws!.send(
|
||||
JSON.stringify({
|
||||
event: EVENT.SIGNAL.ANSWER,
|
||||
sdp: d.sdp,
|
||||
displayname: this._displayname,
|
||||
}),
|
||||
)
|
||||
})
|
||||
.catch(err => this.emit('error', err))
|
||||
|
||||
for (const candidate of this._candidates) {
|
||||
this._peer.addIceCandidate(candidate)
|
||||
}
|
||||
this._candidates = []
|
||||
|
||||
try {
|
||||
const d = await this._peer.createAnswer()
|
||||
this._peer!.setLocalDescription(d)
|
||||
|
||||
this._ws!.send(
|
||||
JSON.stringify({
|
||||
event: EVENT.SIGNAL.ANSWER,
|
||||
sdp: d.sdp,
|
||||
displayname: this._displayname,
|
||||
}),
|
||||
)
|
||||
} catch (err: any) {
|
||||
this.emit('error', err)
|
||||
}
|
||||
}
|
||||
|
||||
private onMessage(e: MessageEvent) {
|
||||
@ -247,6 +286,17 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||
return
|
||||
}
|
||||
|
||||
if (event === EVENT.SIGNAL.CANDIDATE) {
|
||||
const { data } = payload as SignalCandidatePayload
|
||||
const candidate: RTCIceCandidate = JSON.parse(data)
|
||||
if (this._peer) {
|
||||
this._peer.addIceCandidate(candidate)
|
||||
} else {
|
||||
this._candidates.push(candidate)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
if (typeof this[event] === 'function') {
|
||||
// @ts-ignore
|
||||
@ -277,6 +327,7 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||
private onConnected() {
|
||||
if (this._timeout) {
|
||||
clearTimeout(this._timeout)
|
||||
this._timeout = undefined
|
||||
}
|
||||
|
||||
if (!this.connected) {
|
||||
@ -292,6 +343,7 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||
this.emit('debug', `connection timeout`)
|
||||
if (this._timeout) {
|
||||
clearTimeout(this._timeout)
|
||||
this._timeout = undefined
|
||||
}
|
||||
this.onDisconnected(new Error('connection timeout'))
|
||||
}
|
||||
@ -306,6 +358,7 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
||||
this.emit('warn', `unhandled websocket event '${event}':`, payload)
|
||||
}
|
||||
|
||||
protected abstract [EVENT.RECONNECTING](): void
|
||||
protected abstract [EVENT.CONNECTING](): void
|
||||
protected abstract [EVENT.CONNECTED](): void
|
||||
protected abstract [EVENT.DISCONNECTED](reason?: Error): void
|
||||
|
@ -1,5 +1,6 @@
|
||||
export const EVENT = {
|
||||
// Internal Events
|
||||
RECONNECTING: 'RECONNECTING',
|
||||
CONNECTING: 'CONNECTING',
|
||||
CONNECTED: 'CONNECTED',
|
||||
DISCONNECTED: 'DISCONNECTED',
|
||||
@ -10,10 +11,12 @@ export const EVENT = {
|
||||
// Websocket Events
|
||||
SYSTEM: {
|
||||
DISCONNECT: 'system/disconnect',
|
||||
ERROR: 'system/error',
|
||||
},
|
||||
SIGNAL: {
|
||||
ANSWER: 'signal/answer',
|
||||
PROVIDE: 'signal/provide',
|
||||
CANDIDATE: 'signal/candidate',
|
||||
},
|
||||
MEMBER: {
|
||||
LIST: 'member/list',
|
||||
@ -39,9 +42,9 @@ export const EVENT = {
|
||||
SET: 'screen/set',
|
||||
},
|
||||
BROADCAST: {
|
||||
STATUS: "broadcast/status",
|
||||
CREATE: "broadcast/create",
|
||||
DESTROY: "broadcast/destroy",
|
||||
STATUS: 'broadcast/status',
|
||||
CREATE: 'broadcast/create',
|
||||
DESTROY: 'broadcast/destroy',
|
||||
},
|
||||
ADMIN: {
|
||||
BAN: 'admin/ban',
|
||||
@ -78,7 +81,7 @@ export type ControlEvents =
|
||||
|
||||
export type SystemEvents = typeof EVENT.SYSTEM.DISCONNECT
|
||||
export type MemberEvents = typeof EVENT.MEMBER.LIST | typeof EVENT.MEMBER.CONNECTED | typeof EVENT.MEMBER.DISCONNECTED
|
||||
export type SignalEvents = typeof EVENT.SIGNAL.ANSWER | typeof EVENT.SIGNAL.PROVIDE
|
||||
export type SignalEvents = typeof EVENT.SIGNAL.ANSWER | typeof EVENT.SIGNAL.PROVIDE | typeof EVENT.SIGNAL.CANDIDATE
|
||||
export type ChatEvents = typeof EVENT.CHAT.MESSAGE | typeof EVENT.CHAT.EMOTE
|
||||
export type ScreenEvents = typeof EVENT.SCREEN.CONFIGURATIONS | typeof EVENT.SCREEN.RESOLUTION | typeof EVENT.SCREEN.SET
|
||||
|
||||
|
@ -6,7 +6,7 @@ import { EVENT } from './events'
|
||||
import { accessor } from '~/store'
|
||||
|
||||
import {
|
||||
DisconnectPayload,
|
||||
SystemMessagePayload,
|
||||
SignalProvidePayload,
|
||||
MemberListPayload,
|
||||
MemberDisconnectPayload,
|
||||
@ -28,10 +28,21 @@ interface NekoEvents extends BaseEvents {}
|
||||
export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
private $vue!: Vue
|
||||
private $accessor!: typeof accessor
|
||||
private url!: string
|
||||
|
||||
init(vue: Vue) {
|
||||
const url =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? `ws://${location.host.split(':')[0]}:${process.env.VUE_APP_SERVER_PORT}/ws`
|
||||
: location.protocol.replace(/^http/, 'ws') + '//' + location.host + location.pathname.replace(/\/$/, '') + '/ws'
|
||||
|
||||
this.initWithURL(vue, url)
|
||||
}
|
||||
|
||||
initWithURL(vue: Vue, url: string) {
|
||||
this.$vue = vue
|
||||
this.$accessor = vue.$accessor
|
||||
this.url = url
|
||||
}
|
||||
|
||||
private cleanup() {
|
||||
@ -43,12 +54,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
}
|
||||
|
||||
login(password: string, displayname: string) {
|
||||
const url =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? `ws://${location.host.split(':')[0]}:${process.env.VUE_APP_SERVER_PORT}/`
|
||||
: `${/https/gi.test(location.protocol) ? 'wss' : 'ws'}://${location.host}/`
|
||||
|
||||
this.connect(url, password, displayname)
|
||||
this.connect(this.url, password, displayname)
|
||||
}
|
||||
|
||||
logout() {
|
||||
@ -64,6 +70,16 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
/////////////////////////////
|
||||
// Internal Events
|
||||
/////////////////////////////
|
||||
protected [EVENT.RECONNECTING]() {
|
||||
this.$vue.$notify({
|
||||
group: 'neko',
|
||||
type: 'warning',
|
||||
title: this.$vue.$t('connection.reconnecting') as string,
|
||||
duration: 5000,
|
||||
speed: 1000,
|
||||
})
|
||||
}
|
||||
|
||||
protected [EVENT.CONNECTING]() {
|
||||
this.$accessor.setConnnecting()
|
||||
}
|
||||
@ -71,7 +87,11 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
protected [EVENT.CONNECTED]() {
|
||||
this.$accessor.user.setMember(this.id)
|
||||
this.$accessor.setConnected(true)
|
||||
this.$accessor.setConnected(true)
|
||||
|
||||
this.$vue.$notify({
|
||||
group: 'neko',
|
||||
clean: true,
|
||||
})
|
||||
|
||||
this.$vue.$notify({
|
||||
group: 'neko',
|
||||
@ -84,6 +104,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
|
||||
protected [EVENT.DISCONNECTED](reason?: Error) {
|
||||
this.cleanup()
|
||||
|
||||
this.$vue.$notify({
|
||||
group: 'neko',
|
||||
type: 'error',
|
||||
@ -109,8 +130,14 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
/////////////////////////////
|
||||
// System Events
|
||||
/////////////////////////////
|
||||
protected [EVENT.SYSTEM.DISCONNECT]({ message }: DisconnectPayload) {
|
||||
protected [EVENT.SYSTEM.DISCONNECT]({ message }: SystemMessagePayload) {
|
||||
if (message == 'kicked') {
|
||||
this.$accessor.logout()
|
||||
message = this.$vue.$t('connection.kicked') as string
|
||||
}
|
||||
|
||||
this.onDisconnected(new Error(message))
|
||||
|
||||
this.$vue.$swal({
|
||||
title: this.$vue.$t('connection.disconnected'),
|
||||
text: message,
|
||||
@ -119,6 +146,15 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
})
|
||||
}
|
||||
|
||||
protected [EVENT.SYSTEM.ERROR]({ title, message }: SystemMessagePayload) {
|
||||
this.$vue.$swal({
|
||||
title,
|
||||
text: message,
|
||||
icon: 'error',
|
||||
confirmButtonText: this.$vue.$t('connection.button_confirm') as string,
|
||||
})
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// Member Events
|
||||
/////////////////////////////
|
||||
@ -177,7 +213,9 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
this.$vue.$notify({
|
||||
group: 'neko',
|
||||
type: 'info',
|
||||
title: this.$vue.$t('notifications.controls_taken', { name: this.$vue.$t('you') }) as string,
|
||||
title: this.$vue.$t('notifications.controls_taken', {
|
||||
name: member.id == this.id && this.$vue.$te('you') ? this.$vue.$t('you') : member.displayname,
|
||||
}) as string,
|
||||
duration: 5000,
|
||||
speed: 1000,
|
||||
})
|
||||
@ -202,7 +240,9 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
this.$vue.$notify({
|
||||
group: 'neko',
|
||||
type: 'info',
|
||||
title: this.$vue.$t('notifications.controls_released', { name: this.$vue.$t('you') }) as string,
|
||||
title: this.$vue.$t('notifications.controls_released', {
|
||||
name: member.id == this.id && this.$vue.$te('you') ? this.$vue.$t('you') : member.displayname,
|
||||
}) as string,
|
||||
duration: 5000,
|
||||
speed: 1000,
|
||||
})
|
||||
@ -259,7 +299,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
this.$accessor.chat.newMessage({
|
||||
id,
|
||||
content: this.$vue.$t('notifications.controls_given', {
|
||||
name: member.id == this.id ? this.$vue.$t('you') : member.displayname,
|
||||
name: member.id == this.id && this.$vue.$te('you') ? this.$vue.$t('you') : member.displayname,
|
||||
}) as string,
|
||||
type: 'event',
|
||||
created: new Date(),
|
||||
@ -350,7 +390,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
this.$accessor.chat.newMessage({
|
||||
id,
|
||||
content: this.$vue.$t('notifications.banned', {
|
||||
name: member.id == this.id ? this.$vue.$t('you') : member.displayname,
|
||||
name: member.id == this.id && this.$vue.$te('you') ? this.$vue.$t('you') : member.displayname,
|
||||
}) as string,
|
||||
type: 'event',
|
||||
created: new Date(),
|
||||
@ -370,7 +410,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
this.$accessor.chat.newMessage({
|
||||
id,
|
||||
content: this.$vue.$t('notifications.kicked', {
|
||||
name: member.id == this.id ? this.$vue.$t('you') : member.displayname,
|
||||
name: member.id == this.id && this.$vue.$te('you') ? this.$vue.$t('you') : member.displayname,
|
||||
}) as string,
|
||||
type: 'event',
|
||||
created: new Date(),
|
||||
@ -392,7 +432,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
this.$accessor.chat.newMessage({
|
||||
id,
|
||||
content: this.$vue.$t('notifications.muted', {
|
||||
name: member.id == this.id ? this.$vue.$t('you') : member.displayname,
|
||||
name: member.id == this.id && this.$vue.$te('you') ? this.$vue.$t('you') : member.displayname,
|
||||
}) as string,
|
||||
type: 'event',
|
||||
created: new Date(),
|
||||
@ -414,7 +454,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
this.$accessor.chat.newMessage({
|
||||
id,
|
||||
content: this.$vue.$t('notifications.unmuted', {
|
||||
name: member.id == this.id ? this.$vue.$t('you') : member.displayname,
|
||||
name: member.id == this.id && this.$vue.$te('you') ? this.$vue.$t('you') : member.displayname,
|
||||
}) as string,
|
||||
type: 'event',
|
||||
created: new Date(),
|
||||
@ -463,7 +503,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
this.$accessor.chat.newMessage({
|
||||
id,
|
||||
content: this.$vue.$t('notifications.controls_taken_steal', {
|
||||
name: member.id == this.id ? this.$vue.$t('you') : member.displayname,
|
||||
name: member.id == this.id && this.$vue.$te('you') ? this.$vue.$t('you') : member.displayname,
|
||||
}) as string,
|
||||
type: 'event',
|
||||
created: new Date(),
|
||||
@ -490,7 +530,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
this.$accessor.chat.newMessage({
|
||||
id,
|
||||
content: this.$vue.$t('notifications.controls_released_steal', {
|
||||
name: member.id == this.id ? this.$vue.$t('you') : member.displayname,
|
||||
name: member.id == this.id && this.$vue.$te('you') ? this.$vue.$t('you') : member.displayname,
|
||||
}) as string,
|
||||
type: 'event',
|
||||
created: new Date(),
|
||||
@ -513,7 +553,7 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
||||
this.$accessor.chat.newMessage({
|
||||
id,
|
||||
content: this.$vue.$t('notifications.controls_given', {
|
||||
name: member.id == this.id ? this.$vue.$t('you') : member.displayname,
|
||||
name: member.id == this.id && this.$vue.$te('you') ? this.$vue.$t('you') : member.displayname,
|
||||
}) as string,
|
||||
type: 'event',
|
||||
created: new Date(),
|
||||
|
@ -15,6 +15,7 @@ export type WebSocketMessages =
|
||||
| WebSocketMessage
|
||||
| SignalProvideMessage
|
||||
| SignalAnswerMessage
|
||||
| SignalCandidateMessage
|
||||
| MemberListMessage
|
||||
| MemberConnectMessage
|
||||
| MemberDisconnectMessage
|
||||
@ -26,6 +27,7 @@ export type WebSocketMessages =
|
||||
export type WebSocketPayloads =
|
||||
| SignalProvidePayload
|
||||
| SignalAnswerPayload
|
||||
| SignalCandidatePayload
|
||||
| MemberListPayload
|
||||
| Member
|
||||
| ControlPayload
|
||||
@ -48,10 +50,12 @@ export interface WebSocketMessage {
|
||||
SYSTEM MESSAGES/PAYLOADS
|
||||
*/
|
||||
// system/disconnect
|
||||
export interface DisconnectMessage extends WebSocketMessage, DisconnectPayload {
|
||||
event: typeof EVENT.SYSTEM.DISCONNECT
|
||||
// system/error
|
||||
export interface SystemMessage extends WebSocketMessage, SystemMessagePayload {
|
||||
event: typeof EVENT.SYSTEM.DISCONNECT | typeof EVENT.SYSTEM.ERROR
|
||||
}
|
||||
export interface DisconnectPayload {
|
||||
export interface SystemMessagePayload {
|
||||
title: string
|
||||
message: string
|
||||
}
|
||||
|
||||
@ -65,7 +69,7 @@ export interface SignalProvideMessage extends WebSocketMessage, SignalProvidePay
|
||||
export interface SignalProvidePayload {
|
||||
id: string
|
||||
lite: boolean
|
||||
ice: string[]
|
||||
ice: RTCIceServer[]
|
||||
sdp: string
|
||||
}
|
||||
|
||||
@ -78,6 +82,14 @@ export interface SignalAnswerPayload {
|
||||
displayname: string
|
||||
}
|
||||
|
||||
// signal/candidate
|
||||
export interface SignalCandidateMessage extends WebSocketMessage, SignalCandidatePayload {
|
||||
event: typeof EVENT.SIGNAL.CANDIDATE
|
||||
}
|
||||
export interface SignalCandidatePayload {
|
||||
data: string
|
||||
}
|
||||
|
||||
/*
|
||||
MEMBER MESSAGES/PAYLOADS
|
||||
*/
|
||||
|
@ -15,9 +15,7 @@ interface Anime {
|
||||
path(
|
||||
path: string | HTMLElement | SVGElement | null,
|
||||
percent?: number,
|
||||
): (
|
||||
prop: string,
|
||||
) => {
|
||||
): (prop: string) => {
|
||||
el: HTMLElement | SVGElement
|
||||
property: string
|
||||
totalLength: number
|
||||
|
@ -25,10 +25,10 @@ class VueSweetalert2 {
|
||||
if (options) {
|
||||
const mixed = Swal.mixin(options)
|
||||
|
||||
return mixed.fire.apply(mixed, args)
|
||||
return mixed.fire(...args)
|
||||
}
|
||||
|
||||
return Swal.fire.apply(Swal, args)
|
||||
return Swal.fire(...args)
|
||||
}
|
||||
|
||||
let methodName: string | number | symbol
|
||||
@ -40,7 +40,7 @@ class VueSweetalert2 {
|
||||
swalFunction[methodName] = ((method) => {
|
||||
return (...args: any[]) => {
|
||||
// @ts-ignore
|
||||
return Swal[method].apply(Swal, args)
|
||||
return Swal[method](...args)
|
||||
}
|
||||
})(methodName)
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ interface Message {
|
||||
export const state = () => ({
|
||||
history: [] as Message[],
|
||||
emotes: {} as Emotes,
|
||||
texts: 0,
|
||||
})
|
||||
|
||||
export const getters = getterTree(state, {
|
||||
@ -31,6 +32,10 @@ export const getters = getterTree(state, {
|
||||
|
||||
export const mutations = mutationTree(state, {
|
||||
addMessage(state, message: Message) {
|
||||
if (message.type == 'text') {
|
||||
state.texts++
|
||||
}
|
||||
|
||||
state.history = state.history.concat([message])
|
||||
},
|
||||
|
||||
@ -52,6 +57,7 @@ export const mutations = mutationTree(state, {
|
||||
reset(state) {
|
||||
state.emotes = {}
|
||||
state.history = []
|
||||
state.texts = 0
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -58,17 +58,17 @@ export const mutations = mutationTree(state, {
|
||||
export const actions = actionTree(
|
||||
{ state, getters, mutations },
|
||||
{
|
||||
initialise() {
|
||||
$http
|
||||
.get<Emojis>('/emoji.json')
|
||||
.then((req) => {
|
||||
for (const group of req.data.groups) {
|
||||
accessor.emoji.addGroup(group)
|
||||
}
|
||||
accessor.emoji.setList(req.data.list)
|
||||
accessor.emoji.setKeywords(req.data.keywords)
|
||||
})
|
||||
.catch(console.error)
|
||||
async initialise() {
|
||||
try {
|
||||
const req = await $http.get<Emojis>('emoji.json')
|
||||
for (const group of req.data.groups) {
|
||||
accessor.emoji.addGroup(group)
|
||||
}
|
||||
accessor.emoji.setList(req.data.list)
|
||||
accessor.emoji.setKeywords(req.data.keywords)
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -3,9 +3,8 @@ import { Member } from '~/neko/types'
|
||||
import { EVENT } from '~/neko/events'
|
||||
import { accessor } from '~/store'
|
||||
|
||||
const keyboardModifierState =
|
||||
(capsLock: boolean, numLock: boolean, scrollLock: boolean) =>
|
||||
Number(capsLock) + 2*Number(numLock) + 4*Number(scrollLock)
|
||||
const keyboardModifierState = (capsLock: boolean, numLock: boolean, scrollLock: boolean) =>
|
||||
Number(capsLock) + 2 * Number(numLock) + 4 * Number(scrollLock)
|
||||
|
||||
export const namespaced = true
|
||||
|
||||
@ -82,7 +81,7 @@ export const actions = actionTree(
|
||||
},
|
||||
|
||||
request({ getters }) {
|
||||
if (!accessor.connected || !getters.hosting) {
|
||||
if (!accessor.connected || getters.hosting) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -90,7 +89,7 @@ export const actions = actionTree(
|
||||
},
|
||||
|
||||
release({ getters }) {
|
||||
if (!accessor.connected || getters.hosting) {
|
||||
if (!accessor.connected || !getters.hosting) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -155,11 +154,11 @@ export const actions = actionTree(
|
||||
|
||||
syncKeyboardModifierState({ state, getters }, { capsLock, numLock, scrollLock }) {
|
||||
if (state.keyboardModifierState === keyboardModifierState(capsLock, numLock, scrollLock)) {
|
||||
return ;
|
||||
return
|
||||
}
|
||||
|
||||
accessor.remote.setKeyboardModifierState({ capsLock, numLock, scrollLock })
|
||||
$client.sendMessage(EVENT.CONTROL.KEYBOARD, { capsLock, numLock, scrollLock })
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -19,9 +19,9 @@ export const state = () => {
|
||||
keyboard_layout: get<string>('keyboard_layout', 'us'),
|
||||
|
||||
keyboard_layouts_list: {} as KeyboardLayouts,
|
||||
|
||||
|
||||
broadcast_is_active: false,
|
||||
broadcast_url: "",
|
||||
broadcast_url: '',
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ export const mutations = mutationTree(state, {
|
||||
state.keyboard_layouts_list = value
|
||||
},
|
||||
setBroadcastStatus(state, { url, isActive }) {
|
||||
state.broadcast_url = url,
|
||||
state.broadcast_url = url
|
||||
state.broadcast_is_active = isActive
|
||||
},
|
||||
})
|
||||
@ -70,14 +70,13 @@ export const mutations = mutationTree(state, {
|
||||
export const actions = actionTree(
|
||||
{ state, getters, mutations },
|
||||
{
|
||||
initialise() {
|
||||
$http
|
||||
.get<KeyboardLayouts>('/keyboard_layouts.json')
|
||||
.then((req) => {
|
||||
accessor.settings.setKeyboardLayoutsList(req.data)
|
||||
console.log(req.data)
|
||||
})
|
||||
.catch(console.error)
|
||||
async initialise() {
|
||||
try {
|
||||
const req = await $http.get<KeyboardLayouts>('keyboard_layouts.json')
|
||||
accessor.settings.setKeyboardLayoutsList(req.data)
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
},
|
||||
|
||||
broadcastStatus({ getters }, { url, isActive }) {
|
||||
|
@ -48,6 +48,11 @@ export const mutations = mutationTree(state, {
|
||||
state.id = id
|
||||
},
|
||||
addMember(state, member: Member) {
|
||||
// remove html tags
|
||||
const tmp = document.createElement('div')
|
||||
tmp.innerHTML = member.displayname
|
||||
member.displayname = tmp.textContent || tmp.innerText || ''
|
||||
|
||||
state.members = {
|
||||
...state.members,
|
||||
[member.id]: {
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable */
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
|
@ -4,41 +4,41 @@ export interface GuacamoleKeyboardInterface {
|
||||
/**
|
||||
* Fired whenever the user presses a key with the element associated
|
||||
* with this Guacamole.Keyboard in focus.
|
||||
*
|
||||
*
|
||||
* @event
|
||||
* @param {Number} keysym The keysym of the key being pressed.
|
||||
* @return {Boolean} true if the key event should be allowed through to the
|
||||
* browser, false otherwise.
|
||||
*/
|
||||
onkeydown?: (keysym: number) => boolean;
|
||||
onkeydown?: (keysym: number) => boolean
|
||||
|
||||
/**
|
||||
* Fired whenever the user releases a key with the element associated
|
||||
* with this Guacamole.Keyboard in focus.
|
||||
*
|
||||
*
|
||||
* @event
|
||||
* @param {Number} keysym The keysym of the key being released.
|
||||
*/
|
||||
onkeyup?: (keysym: number) => void;
|
||||
onkeyup?: (keysym: number) => void
|
||||
|
||||
/**
|
||||
* Marks a key as pressed, firing the keydown event if registered. Key
|
||||
* repeat for the pressed key will start after a delay if that key is
|
||||
* not a modifier. The return value of this function depends on the
|
||||
* return value of the keydown event handler, if any.
|
||||
*
|
||||
*
|
||||
* @param {Number} keysym The keysym of the key to press.
|
||||
* @return {Boolean} true if event should NOT be canceled, false otherwise.
|
||||
*/
|
||||
press: (keysym: number) => boolean;
|
||||
press: (keysym: number) => boolean
|
||||
|
||||
/**
|
||||
* Marks a key as released, firing the keyup event if registered.
|
||||
*
|
||||
*
|
||||
* @param {Number} keysym The keysym of the key to release.
|
||||
*/
|
||||
release: (keysym: number) => void;
|
||||
|
||||
release: (keysym: number) => void
|
||||
|
||||
/**
|
||||
* Presses and releases the keys necessary to type the given string of
|
||||
* text.
|
||||
@ -46,14 +46,14 @@ export interface GuacamoleKeyboardInterface {
|
||||
* @param {String} str
|
||||
* The string to type.
|
||||
*/
|
||||
type: (str: string) => void;
|
||||
type: (str: string) => void
|
||||
|
||||
/**
|
||||
* Resets the state of this keyboard, releasing all keys, and firing keyup
|
||||
* events for each released key.
|
||||
*/
|
||||
reset: () => void;
|
||||
|
||||
reset: () => void
|
||||
|
||||
/**
|
||||
* Attaches event listeners to the given Element, automatically translating
|
||||
* received key, input, and composition events into simple keydown/keyup
|
||||
@ -64,13 +64,13 @@ export interface GuacamoleKeyboardInterface {
|
||||
* The Element to attach event listeners to for the sake of handling
|
||||
* key or input events.
|
||||
*/
|
||||
listenTo: (element: Element | Document) => void;
|
||||
listenTo: (element: Element | Document) => void
|
||||
}
|
||||
|
||||
export default function(element?: Element): GuacamoleKeyboardInterface {
|
||||
var Keyboard = {};
|
||||
export default function (element?: Element): GuacamoleKeyboardInterface {
|
||||
const Keyboard = {}
|
||||
|
||||
GuacamoleKeyboard.bind(Keyboard, element)();
|
||||
GuacamoleKeyboard.bind(Keyboard, element)()
|
||||
|
||||
return Keyboard as GuacamoleKeyboardInterface;
|
||||
return Keyboard as GuacamoleKeyboardInterface
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ export function makeid(length: number) {
|
||||
let result = ''
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||
const charactersLength = characters.length
|
||||
for (var i = 0; i < length; i++) {
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength))
|
||||
}
|
||||
return result
|
||||
|
@ -13,7 +13,7 @@ export function set<T extends string | number | boolean>(key: string, val: T) {
|
||||
}
|
||||
|
||||
export function get<T extends string | number | boolean>(key: string, def: T): T {
|
||||
let store = localStorage.getItem(key)
|
||||
const store = localStorage.getItem(key)
|
||||
if (store) {
|
||||
switch (typeof def) {
|
||||
case 'number':
|
||||
|
@ -2,7 +2,7 @@ import * as fs from 'fs'
|
||||
import { custom } from './emoji_custom'
|
||||
|
||||
const datasource = require('emoji-datasource/emoji.json') as EmojiDatasource[]
|
||||
const emojis = require('emojilib/emojis.json') as { [id: string]: Emoji }
|
||||
const emojis = require('emojilib')
|
||||
|
||||
interface EmojiDatasource {
|
||||
name: string
|
||||
@ -43,14 +43,7 @@ interface EmojiDatasource {
|
||||
obsoleted_by: string
|
||||
}
|
||||
|
||||
interface Emoji {
|
||||
keywords: string[]
|
||||
char: string
|
||||
fitzpatrick_scale: boolean
|
||||
category: string
|
||||
}
|
||||
|
||||
const SHEET_COLUMNS = 57
|
||||
const SHEET_COLUMNS = 58
|
||||
const MULTIPLY = 100 / (SHEET_COLUMNS - 1)
|
||||
|
||||
const css: string[] = []
|
||||
@ -70,16 +63,6 @@ for (const emoji of custom) {
|
||||
for (const source of datasource) {
|
||||
const unified = source.unified.split('-').map(v => v.toLowerCase())
|
||||
|
||||
let emoji: Emoji | null = null
|
||||
let emoji_id: string = ''
|
||||
for (const id of Object.keys(emojis)) {
|
||||
if (unified.includes(emojis[id].char.codePointAt(0)!.toString(16))) {
|
||||
emoji_id = id
|
||||
emoji = emojis[id]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!source.has_img_twitter) {
|
||||
console.log(source.short_name, 'not avalible for set twitter')
|
||||
continue
|
||||
@ -87,10 +70,15 @@ for (const source of datasource) {
|
||||
|
||||
// keywords
|
||||
let words: string[] = []
|
||||
if (!emoji) {
|
||||
for (const id of Object.keys(emojis)) {
|
||||
if (unified.includes(id.codePointAt(0)!.toString(16))) {
|
||||
words = [id, ...emojis[id]]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (words.length == 0) {
|
||||
console.log(source.short_name, 'no keywords')
|
||||
} else {
|
||||
words = [emoji_id, ...emoji.keywords]
|
||||
}
|
||||
|
||||
for (const name of source.short_names) {
|
||||
|
@ -5,12 +5,14 @@ module.exports = {
|
||||
css: {
|
||||
loaderOptions: {
|
||||
sass: {
|
||||
prependData: `
|
||||
additionalData: `
|
||||
@import "@/assets/styles/_variables.scss";
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
publicPath: './',
|
||||
assetsDir: './',
|
||||
configureWebpack: {
|
||||
resolve: {
|
||||
alias: {
|
||||
@ -19,4 +21,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
},
|
||||
devServer: {
|
||||
disableHostCheck: true,
|
||||
}
|
||||
}
|
||||
|
@ -36,16 +36,11 @@ func init() {
|
||||
zerolog.TimeFieldFormat = ""
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
|
||||
if viper.GetBool("debug") {
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
}
|
||||
|
||||
console := zerolog.ConsoleWriter{Out: os.Stdout}
|
||||
|
||||
if !viper.GetBool("logs") {
|
||||
log.Logger = log.Output(console)
|
||||
} else {
|
||||
|
||||
logs := filepath.Join(".", "logs")
|
||||
if runtime.GOOS == "linux" {
|
||||
logs = "/var/log/neko"
|
||||
@ -103,9 +98,14 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
debug := viper.GetBool("debug")
|
||||
if debug {
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
}
|
||||
|
||||
file := viper.ConfigFileUsed()
|
||||
logger := log.With().
|
||||
Bool("debug", viper.GetBool("debug")).
|
||||
Bool("debug", debug).
|
||||
Str("logging", viper.GetString("logs")).
|
||||
Str("config", file).
|
||||
Logger()
|
||||
|
@ -1,35 +1,56 @@
|
||||
module n.eko.moe/neko
|
||||
|
||||
go 1.13
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible // indirect
|
||||
github.com/cpuguy83/go-md2man v1.0.10 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/go-chi/chi v4.1.0+incompatible
|
||||
github.com/golang/protobuf v1.3.5 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/go-chi/chi v4.1.2+incompatible
|
||||
github.com/gopherjs/gopherjs v0.0.0-20210901121439-eee08aaf2717 // indirect
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/kataras/go-events v0.0.2
|
||||
github.com/lucas-clemente/quic-go v0.15.2 // indirect
|
||||
github.com/mitchellh/mapstructure v1.2.2 // indirect
|
||||
github.com/pelletier/go-toml v1.7.0 // indirect
|
||||
github.com/pion/ice v0.7.12 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/pion/ice/v2 v2.1.12 // indirect
|
||||
github.com/pion/interceptor v0.0.18
|
||||
github.com/pion/logging v0.2.2
|
||||
github.com/pion/rtp v1.4.0 // indirect
|
||||
github.com/pion/sdp/v2 v2.3.5 // indirect
|
||||
github.com/pion/turn v1.4.0 // indirect
|
||||
github.com/pion/webrtc/v2 v2.2.4
|
||||
github.com/pion/rtp v1.7.2 // indirect
|
||||
github.com/pion/srtp/v2 v2.0.5 // indirect
|
||||
github.com/pion/webrtc/v3 v3.0.32
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/zerolog v1.18.0
|
||||
github.com/spf13/afero v1.2.2 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/cobra v0.0.7
|
||||
github.com/rs/zerolog v1.25.0
|
||||
github.com/smartystreets/assertions v1.2.0 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/cobra v1.2.1
|
||||
github.com/spf13/viper v1.8.1
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||
golang.org/x/net v0.0.0-20210908191846-a5e095526f91 // indirect
|
||||
golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/pion/datachannel v1.4.21 // indirect
|
||||
github.com/pion/dtls/v2 v2.0.9 // indirect
|
||||
github.com/pion/mdns v0.0.5 // indirect
|
||||
github.com/pion/randutil v0.1.0 // indirect
|
||||
github.com/pion/rtcp v1.2.7 // indirect
|
||||
github.com/pion/sctp v1.7.12 // indirect
|
||||
github.com/pion/sdp/v3 v3.0.4 // indirect
|
||||
github.com/pion/stun v0.3.5 // indirect
|
||||
github.com/pion/transport v0.12.3 // indirect
|
||||
github.com/pion/turn/v2 v2.0.5 // indirect
|
||||
github.com/pion/udp v0.1.1 // indirect
|
||||
github.com/spf13/afero v1.6.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.6.2
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 // indirect
|
||||
golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8 // indirect
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
gopkg.in/ini.v1 v1.55.0 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gopkg.in/ini.v1 v1.63.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
847
server/go.sum
847
server/go.sum
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,8 @@
|
||||
package broadcast
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
@ -9,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
type BroadcastManager struct {
|
||||
mu sync.Mutex
|
||||
logger zerolog.Logger
|
||||
pipeline *gst.Pipeline
|
||||
remote *config.Remote
|
||||
@ -27,9 +30,9 @@ func New(remote *config.Remote, config *config.Broadcast) *BroadcastManager {
|
||||
}
|
||||
}
|
||||
|
||||
func (manager *BroadcastManager) Start() {
|
||||
func (manager *BroadcastManager) Start() error {
|
||||
if !manager.enabled || manager.IsActive() {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
@ -40,18 +43,19 @@ func (manager *BroadcastManager) Start() {
|
||||
manager.url,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
manager.pipeline = nil
|
||||
return err
|
||||
}
|
||||
|
||||
manager.logger.Info().
|
||||
Str("audio_device", manager.remote.Device).
|
||||
Str("video_display", manager.remote.Display).
|
||||
Str("rtmp_pipeline_src", manager.pipeline.Src).
|
||||
Msgf("RTMP pipeline is starting...")
|
||||
|
||||
if err != nil {
|
||||
manager.logger.Panic().Err(err).Msg("unable to create rtmp pipeline")
|
||||
return
|
||||
}
|
||||
|
||||
manager.pipeline.Play()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (manager *BroadcastManager) Stop() {
|
||||
@ -67,13 +71,25 @@ func (manager *BroadcastManager) IsActive() bool {
|
||||
return manager.pipeline != nil
|
||||
}
|
||||
|
||||
func (manager *BroadcastManager) Create(url string) {
|
||||
func (manager *BroadcastManager) Create(url string) error {
|
||||
manager.mu.Lock()
|
||||
defer manager.mu.Unlock()
|
||||
|
||||
manager.url = url
|
||||
manager.enabled = true
|
||||
manager.Start()
|
||||
|
||||
err := manager.Start()
|
||||
if err != nil {
|
||||
manager.enabled = false
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (manager *BroadcastManager) Destroy() {
|
||||
manager.mu.Lock()
|
||||
defer manager.mu.Unlock()
|
||||
|
||||
manager.Stop()
|
||||
manager.enabled = false
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user