Compare commits

...

106 commits

Author SHA1 Message Date
Florian Märkl
a1fd418685 Update Ubuntu for AppImage to 24.04 2025-06-25 16:56:44 +02:00
Florian Märkl
bb5a79f234 Update dependencies in BSDs CI 2025-06-22 11:57:41 +02:00
Florian Märkl
4eb90a7a65 Refresh switch to build again 2025-06-09 12:20:27 +02:00
Florian Märkl
94fcdc3c61 Add reference to chiaki-ng 2024-07-31 15:54:39 +02:00
Florian Märkl
8911a44766 Remove Play Store from README.md 2023-08-20 12:53:52 +02:00
Florian Märkl
89368f63c9 Add script for local macOS distribution 2023-08-20 11:42:20 +02:00
Florian Märkl
d4a0603bf2 Fix switch host_addr regex for more arbitrary strings 2023-08-20 11:17:05 +02:00
Florian Märkl
666238ba9f Bump version to 2.2.0 2023-08-20 11:12:11 +02:00
Florian Märkl
bcdd0dd7fd Update CI images 2023-08-20 09:45:15 +02:00
Florian Märkl
6096de8c13 Do not handle server shutdown as error
When the console is powered off or put into sleep mode during streaming,
the remote will disconnect and report the string "Server shutting down".
This is expected by the user and thus should not be shown as an error
message.
2023-02-08 14:08:47 +01:00
Florian Märkl
582ec7aa54 Allow specifying command in switch podman script 2023-02-08 14:08:17 +01:00
Florian Märkl
e14083c87c Update android target SDK to 33 and dependencies 2023-02-05 19:46:34 +01:00
Johannes Baiter
c2f0932670 gui: Support for DualSense haptics and trigger effects
Haptics with PulseAudio does not seem to be working properly, so using
Pipewire as a backend is recommended (and picked by default, if
available via an SDL hint).
2023-02-01 18:05:59 +01:00
Johannes Baiter
7a490b5aae Fix feedback state position 0x1b when DualSense is connected.
The previous value of `0` caused the PS5 to expect a set of 'trigger status'
values in the 0x19 and 0x1a position, which would have required reading
raw values from the DualSense HID device, since these values are not
reported by the SDL DualSense driver (code that does so can be checked
out from the `trigger-feedback` branch on https://git.sr.ht/~jbaiter/chiaki).

Fortunately this is not neccessary, simply setting the value to `1`
seems to make the PS5 to rely on fallback logic (presumably based on the
L2/R2 value) and games that would otherwise have relied on the trigger
status (Astro's Playroom climbing levels) now work without any problems.
2023-01-29 13:39:30 +01:00
Johannes Baiter
4c8209762c Add support for touchpad and sensor handling via SDL
This should enable support for more controllers besides the DS4 and
DualSense, basically any controller supported by SDL that has at least
one touchpad, an accelerometer and a gyroscope.

Older SDL versions have been tested down to 2.0.9. Versions older than
2.0.14 won't have sensors and touchpad support, though.

Setsu is deprecated and remains in-tree for now, but defaults to being
disabled if SDL2 is found and >= 2.0.14. If Setsu is enabled explicitly,
touchpad and sensors are not handled by SDL.
2022-12-14 20:22:40 +01:00
Florian Märkl
76690a319c Fix testing on AppVeyor and make appveyor-win.sh more portable
test/chiaki-unit.exe failed to load some OpenSSL dlls somehow, which
broke the build. Moving them next to the executable fixes that.
The APPVEYOR_BUILD_FOLDER env var is also not needed anymore now.
2022-12-14 19:27:15 +01:00
Street Pea
36816db7ac Add quit (Ctrl+Q) shortcut to GUI 2022-12-10 15:13:22 +01:00
Street Pea
801f902bea Add transform/scaling modes to GUI
Added zoom and stretch modes to GUI to mirror the transform modes
available on Android. They are reachable through a context menu or
shortcuts (Ctrl+S/Ctrl+Z).
CLI options --stretch and --zoom have been added as well.

Co-authored-by: Florian Märkl <info@florianmaerkl.de>
2022-12-10 15:13:03 +01:00
Johannes Baiter
74d39e6314 lib: Add support for trigger effects and controller haptics
By default, no trigger effects and haptics are requested from the
console, lib users have to explicitly enable them for a session by
setting the new `enable_dualsense` flag on the session's
`ChiakiConnectInfo` struct.

Trigger Effects are simply a new Takion message type `11`  and
include the type of each effect and the effect data (10 bytes) for
each of the triggers. They are exposed as a new Chiaki event type
`CHIAKI_EVENT_TRIGGER_EFFECTS`.

Haptic effects are implemented in the protocol as a separate audio
stream, for which packets are only sent when there are actually
effects being played, i.e. silence is not explicitly encoded.
Audio data is 3kHz little endian 16 bit stereo sent in frames of
10 samples every 100ms. Note that the Takion AV header has the
codec field set to Opus, however this is not true.
Users can provide a new `ChiakiAudioSink` dedicated to haptics
via the new `chiaki_session_set_haptics_sink` API, which behaves
identical to the regular audio sink, except that it has a lower
frequency.
2022-11-01 18:29:05 +01:00
Florian Märkl
40a9dee4ed Fix some EINTR handling 2022-10-23 13:42:12 +02:00
Florian Märkl
6bfbcfc456 Update chiaki-build-switch image in build script 2022-09-25 14:24:43 +02:00
Florian Märkl
e00f5fae9d Update macOS icon in Big Sur style 2022-09-25 10:29:17 +02:00
Florian Märkl
4164255ef9 Update dependencies and fix CI
* nanopb 0.4.6.4 with PB_C99_STATIC_ASSERT to avoid depending on C11
* OpenSSL 1.1.1q on windows
* Switched from docker to podman in CI
* Appdir in appimage build container is now in /build/appdir to fix
  "Invalid cross-device link" errors in linuxdeploy when appdir is in
  mount
* Use python protobuf==3.19.5 in bionic image, which still supports
  python 3.6, and on windows where nanopb otherwise has issues
* Purge nanopb_pb2.py to force regeneration of it for possibly different
  python protobuf versions in subsequent builds
* FreeBSD needs py39 packages now
2022-09-24 15:18:53 +02:00
Florian Märkl
b790fb3fb5 Set PATH to find protoc for nanopb generator 2022-06-02 18:26:42 +02:00
Florian Märkl
b4f051395f Reduce targets in fetched mbedtls and show progress 2022-06-02 17:58:57 +02:00
Florian Märkl
420809b24e Add option to fetch mbedtls with cmake 2022-06-02 17:10:19 +02:00
Florian Märkl
7d820bd4ab Update and fix CI
* appimage: switched to bionic
* android: updated container for nanopb changes
* switch: updated devkitpro and simpler cmake scripts
2022-03-26 18:33:15 +01:00
Florian Märkl
aa3a3b8bbc Update Windows Dependencies in CI 2022-03-26 11:33:50 +01:00
MiniMeOSc
ccecc67d74 Fix stream command not working for second or later host in configuration 2022-03-22 11:38:47 +01:00
Florian Märkl
dcd2e6af4a Update nanopb to fix unaligned pointers on arm64 2021-12-03 11:47:32 +01:00
Florian Märkl
7a01ac0d41 Update BSD CI 2021-12-03 11:46:47 +01:00
Florian Märkl
796a128456 Fix fec.c extension 2021-04-11 18:19:46 +02:00
Florian Märkl
695da18473 Make CLI Wakeup work for PS5 2021-04-11 18:11:35 +02:00
Florian Märkl
7870a28cdd Fix Discovery in CLI 2021-04-11 18:11:35 +02:00
Florian Märkl
a44000ea2b Enable CLI in CI 2021-04-11 18:11:35 +02:00
Florian Märkl
2b4a7426ff Install CLI 2021-04-11 18:11:35 +02:00
Florian Märkl
f50b060795 Fix AppVeyor 2021-04-11 18:11:35 +02:00
Florian Märkl
a049ed43ec
Force-disable Lib Decoders on Android 2021-02-02 10:59:26 +01:00
Florian Märkl
ae3ca1ac7a
Update Flatpak to v2.1.1 2021-01-24 14:02:17 +01:00
Florian Märkl
2257030ade
Version 2.1.1 2021-01-24 10:26:53 +01:00
Florian Märkl
6df937a57c
Remove removed sdl2-static from Alpine 2021-01-24 10:17:49 +01:00
Florian Märkl
078e83ec70
Fix Regist on Switch 2021-01-24 10:11:46 +01:00
Florian Märkl
ac8a3a3a3c
Update Flatpak to v2.1.0 2021-01-15 22:49:56 +01:00
Florian Märkl
fcdc414692
Version 2.1.0 2021-01-15 22:35:50 +01:00
Florian Märkl
505910bc5f
Enable Setsu in AppImage 2021-01-15 22:01:35 +01:00
Florian Märkl
f8b34febbe
Fix FindFFMPEG.cmake for ancient cmake 2021-01-15 22:01:35 +01:00
Florian Märkl
35130b08b7
Refine LR Touch on Android 2021-01-15 19:16:54 +01:00
Florian Märkl
5699c06dd8
Fix RegistActivity on Android 2021-01-15 18:50:49 +01:00
Florian Märkl
cb5870f30d
Add Touch Button Haptics to Android 2021-01-15 18:43:09 +01:00
Florian Märkl
28f017d640
Add Option to disable Motion on Android 2021-01-15 18:23:20 +01:00
Florian Märkl
bae081d5b3
Trigger Touchpad Button from TouchpadView on Android 2021-01-15 18:00:05 +01:00
Florian Märkl
baa034fa3f
Finish Basic Touchpad on Android 2021-01-15 17:00:05 +01:00
Florian Märkl
fa44a3269c
Propagate Touchpad State through TouchControlsFragment on Android 2021-01-15 15:32:37 +01:00
Florian Märkl
5914ceec77
Add TouchpadView to Android 2021-01-15 14:37:48 +01:00
Florian Märkl
402782b4af
Replace Deprecated Android Kotlin Extensions and Others 2021-01-15 14:36:33 +01:00
Florian Märkl
e6af02a35c
Update Android Dependencies 2021-01-15 12:29:56 +01:00
Florian Märkl
367489e230
Extend Touch Areas on Android 2021-01-14 20:50:38 +01:00
Florian Märkl
9a0ef224a0
Add nicer LR Buttons to Android 2021-01-14 20:44:56 +01:00
Florian Märkl
510064c899
Use SurfaceView on Android 2021-01-14 18:56:42 +01:00
Florian Märkl
3a90ef0a65
Extend DPad Touch Area on Android 2021-01-13 20:24:25 +01:00
Florian Märkl
b69bf280f8
Extend Face Button Touch Areas on Android 2021-01-13 19:33:54 +01:00
Florian Märkl
c1a4504470
Add L3 and R3 to Android Touch Controls 2021-01-13 17:14:07 +01:00
Florian Märkl
7c00b13818
Add Rumble Fallback for Pre-O Android 2021-01-13 15:23:03 +01:00
Florian Märkl
3b85e147b6
Add Rumble to Android 2021-01-13 15:00:53 +01:00
Florian Märkl
2906cfdd69
Add Motion to Android 2021-01-12 21:08:33 +01:00
Florian Märkl
2a4b67b58e
Complete ControllerState in JNI 2021-01-12 16:30:06 +01:00
Florian Märkl
bb4e5398b2
Update Switch Metadata and Icons 2021-01-12 13:35:40 +01:00
Florian Märkl
0b6e479e0b
Fix some Double Free and uninitialized Memory in Switch 2021-01-12 12:59:04 +01:00
Florian Märkl
fc58e83e9c
Track Finger IDs on Switch 2021-01-12 12:23:02 +01:00
h0neybadger
1b8fa556f8
Fix switch touchpad resolution 2021-01-12 11:33:49 +01:00
Florian Märkl
12054a91c9
Fix Motion Data on Switch 2021-01-12 11:31:40 +01:00
h0neybadger
acf15480f2 Add switch rumble and motion feedbacks 2021-01-11 20:04:41 +01:00
Florian Märkl
9ab84e6054
Prefer fixed local Port for Discovery 2021-01-10 16:01:37 +01:00
Florian Märkl
a0c3768edb
Fix Idle Controller State on Android 2021-01-10 14:46:24 +01:00
Florian Märkl
7785c310a9
Fix some Uninitialized Memory 2021-01-10 11:04:19 +01:00
Florian Märkl
96cbd5d9b8
Fix Madgwick Filter for Orientation 2021-01-09 10:53:50 +01:00
Florian Märkl
42a3b864d0
Add _USE_MATH_DEFINES for Windows 2021-01-06 22:04:53 +01:00
Florian Märkl
24d73064db
Format Fixes 2021-01-06 21:51:18 +01:00
Florian Märkl
cb827a525a
Add Motion to Feedback 2021-01-06 21:50:13 +01:00
Florian Märkl
170dcd4d65
Connect and read Setsu Motion Device in GUI 2021-01-06 21:47:08 +01:00
Florian Märkl
32e1539c22
Add Orientation Tracker 2021-01-06 21:47:06 +01:00
Florian Märkl
88c03aa744
Finish Motion in Setsu 2021-01-06 15:58:23 +01:00
Florian Märkl
698bce8022
Connect Motion Devices in Setsu 2021-01-06 15:58:23 +01:00
Florian Märkl
20c54b05ad
Print error on Setsu open failure 2021-01-06 15:58:22 +01:00
Florian Märkl
abc9a27208
Add Motion Stub to Setsu 2021-01-06 15:58:22 +01:00
Florian Märkl
0e324a41a0
Fix setting a Feedback State Byte 2021-01-06 15:58:22 +01:00
Roy P
3272f47dc6
Update Flatpak to v2.0.1 2021-01-06 15:56:35 +01:00
h0neybadger
c5246541a9 Fix switch target chiaki.conf format 2021-01-04 23:07:16 +01:00
h0neybadger
0af3ae27d4 Use borealis keyboard for the nintendo switch 2021-01-04 23:07:16 +01:00
Florian Märkl
1ee23e0fa2 Don't mess with the OMX Buffer Size 2021-01-04 12:46:38 +01:00
h0neybadger
b9a9ea497c
Fix switch audio delay with SDL_ClearQueuedAudio 2021-01-04 11:50:36 +01:00
h0neybadger
e531a90d26
Fix switch settings psn id regex 2021-01-04 11:50:36 +01:00
Florian Märkl
7cf370c70d
Show SDL GUID in Controller Name in GUI 2021-01-04 11:16:28 +01:00
h0neybadger
d4dc0ffee1 Fix switch README info and nxlink push script 2021-01-02 13:23:14 +01:00
Alexander Millin
a2955d21fc
Enable hevc_vaapi in FFMPEG Builds 2021-01-01 21:24:51 +01:00
Florian Märkl
da051803f5
Fall back to H264 on Pi 2021-01-01 21:19:52 +01:00
Florian Märkl
49d65ad14a
Minor Style Fixes 2021-01-01 15:31:39 +01:00
Florian Märkl
042e02eb3e
Add Rumble to GUI 2021-01-01 13:56:30 +01:00
Florian Märkl
3c2e9a0418
Use correct Target in GUI CLI Start 2021-01-01 11:52:49 +01:00
Florian Märkl
6c46920adb
Fix some Warnings 2021-01-01 11:09:13 +01:00
Florian Märkl
81984b7d48
Add Rumble to Lib 2020-12-31 23:45:47 +01:00
Florian Märkl
fbb19f94ea
Fix err in streamsession.cpp for pi 2020-12-31 21:24:30 +01:00
Florian Märkl
85d9594ebc
Add DualSense to Setsu 2020-12-30 16:11:06 +01:00
Florian Märkl
89c3175d71
Add fallback if getnameinfo fails 2020-12-30 12:37:28 +01:00
Florian Märkl
9e698dd7c4
Version 2.0.1 2020-12-29 20:12:12 +01:00
Florian Märkl
1ce2dbd5d2
Fix Android Database Migration 2020-12-29 20:11:41 +01:00
196 changed files with 5287 additions and 1977 deletions

View file

@ -5,6 +5,7 @@ image:
branches:
only:
- master
- develop
- /^v\d.*$/
- /^deploy-test(-.*)?$/
@ -36,14 +37,14 @@ for:
install:
- git submodule update --init --recursive
- sudo pip3 install protobuf
- brew install qt opus openssl@1.1 nasm sdl2 protobuf
- HOMEBREW_NO_AUTO_UPDATE=1 brew install qt@5 opus openssl@1.1 nasm sdl2 protobuf
- scripts/build-ffmpeg.sh
build_script:
- export CMAKE_PREFIX_PATH="`pwd`/ffmpeg-prefix;/usr/local/opt/openssl@1.1;/usr/local/opt/qt"
- export CMAKE_PREFIX_PATH="`pwd`/ffmpeg-prefix;/usr/local/opt/openssl@1.1;/usr/local/opt/qt@5"
- scripts/build-common.sh
- cp -a build/gui/chiaki.app Chiaki.app
- /usr/local/opt/qt/bin/macdeployqt Chiaki.app -dmg
- /usr/local/opt/qt@5/bin/macdeployqt Chiaki.app -dmg
artifacts:
- path: Chiaki.dmg

View file

@ -22,7 +22,7 @@ tasks:
sudo docker run \
-v /home/build:/home/build \
-u $(id -u):$(id -g) \
thestr4ng3r/android:f064ea6 \
thestr4ng3r/android:90d826e \
/bin/bash -c "cd /home/build/chiaki/android && ./gradlew assembleRelease bundleRelease"
cp chiaki/android/app/build/outputs/apk/release/app-release*.apk Chiaki.apk
cp chiaki/android/app/build/outputs/bundle/release/app-release*.aab Chiaki.aab

View file

@ -9,38 +9,47 @@ packages:
- ninja
- protoc
- py3-protobuf
- py3-setuptools
- opus-dev
- qt5-qtbase-dev
- qt5-qtsvg-dev
- qt5-qtmultimedia-dev
- ffmpeg-dev
- sdl2-dev
- sdl2-static # this is gone on alpine edge so might be necessary to remove later
- docker
- podman
- fuse
- udev
- argp-standalone
artifacts:
- chiaki.nro
- Chiaki.AppImage
tasks:
- start_docker: |
sudo service docker start
sudo chmod +s /usr/bin/docker # Yes, I know what I am doing
sudo service fuse start # Fuse for AppImages
- setup_podman: |
sudo rc-service udev start
sudo rc-service cgroups start
sudo rc-service fuse start # Fuse for AppImages
echo build:100000:65536 | sudo tee /etc/subuid
echo build:100000:65536 | sudo tee /etc/subgid
# https://www.kernel.org/doc/Documentation/networking/tuntap.txt
# for slirp4netns
sudo mkdir -p /dev/net
sudo mknod /dev/net/tun c 10 200
sudo chmod 0666 /dev/net/tun
- local_build_and_test: |
cd chiaki
cmake -Bbuild -GNinja
cmake -Bbuild -GNinja -DCHIAKI_ENABLE_CLI=ON -DCHIAKI_ENABLE_GUI=ON -DCHIAKI_CLI_ARGP_STANDALONE=ON
ninja -C build
build/test/chiaki-unit
- appimage: |
cd chiaki
scripts/run-docker-build-appimage.sh
scripts/run-podman-build-appimage.sh
cp appimage/Chiaki.AppImage ../Chiaki.AppImage
- switch: |
cd chiaki
scripts/switch/run-docker-build-chiaki.sh
scripts/switch/run-podman-build-chiaki.sh
cp build_switch/switch/chiaki.nro ../chiaki.nro
- bullseye: |
cd chiaki
scripts/run-docker-build-bullseye.sh
scripts/run-podman-build-bullseye.sh

View file

@ -1,5 +1,5 @@
image: freebsd/latest
image: freebsd/14.x
sources:
- https://git.sr.ht/~thestr4ng3r/chiaki
@ -7,7 +7,8 @@ sources:
packages:
- cmake
- protobuf
- py37-protobuf
- py311-setuptools # should not be needed with nanopb >= 0.4.9
- py311-protobuf
- opus
- qt5-core
- qt5-qmake

View file

@ -1,5 +1,5 @@
image: openbsd/6.7
image: openbsd/latest
sources:
- https://git.sr.ht/~thestr4ng3r/chiaki
@ -7,6 +7,7 @@ sources:
packages:
- cmake
- protobuf
- py3-setuptools # should not be needed with nanopb >= 0.4.9
- py3-protobuf
- opus
- qtbase

2
.gitignore vendored
View file

@ -29,3 +29,5 @@ compile_commands.json
chiaki.conf
/appimage
.cache/
/*.app
/*.dmg

2
.gitmodules vendored
View file

@ -15,4 +15,4 @@
url = https://github.com/google/oboe
[submodule "switch/borealis"]
path = switch/borealis
url = https://github.com/natinusala/borealis.git
url = https://git.sr.ht/~thestr4ng3r/borealis

View file

@ -24,6 +24,7 @@ endif()
tri_option(CHIAKI_ENABLE_FFMPEG_DECODER "Enable FFMPEG video decoder" ${CHIAKI_FFMPEG_DEFAULT})
tri_option(CHIAKI_ENABLE_PI_DECODER "Enable Raspberry Pi-specific video decoder (requires libraspberrypi0 and libraspberrypi-doc)" AUTO)
option(CHIAKI_LIB_ENABLE_MBEDTLS "Use mbedtls instead of OpenSSL as part of Chiaki Lib" OFF)
option(CHIAKI_LIB_MBEDTLS_EXTERNAL_PROJECT "Fetch Mbed TLS instead of using system-provided libs" OFF)
option(CHIAKI_LIB_OPENSSL_EXTERNAL_PROJECT "Use OpenSSL as CMake external project" OFF)
option(CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER "Use SDL Gamecontroller for Input" ON)
option(CHIAKI_CLI_ARGP_STANDALONE "Search for standalone argp lib for CLI" OFF)
@ -31,7 +32,7 @@ tri_option(CHIAKI_USE_SYSTEM_JERASURE "Use system-provided jerasure instead of s
tri_option(CHIAKI_USE_SYSTEM_NANOPB "Use system-provided nanopb instead of submodule" AUTO)
set(CHIAKI_VERSION_MAJOR 2)
set(CHIAKI_VERSION_MINOR 0)
set(CHIAKI_VERSION_MINOR 2)
set(CHIAKI_VERSION_PATCH 0)
set(CHIAKI_VERSION ${CHIAKI_VERSION_MAJOR}.${CHIAKI_VERSION_MINOR}.${CHIAKI_VERSION_PATCH})
@ -89,6 +90,20 @@ endif()
if(CHIAKI_LIB_ENABLE_MBEDTLS)
add_definitions(-DCHIAKI_LIB_ENABLE_MBEDTLS)
if(CHIAKI_LIB_MBEDTLS_EXTERNAL_PROJECT)
set(FETCHCONTENT_QUIET CACHE BOOL FALSE)
include(FetchContent)
set(ENABLE_TESTING CACHE INTERNAL OFF)
set(ENABLE_PROGRAMS CACHE INTERNAL OFF)
set(USE_SHARED_MBEDTLS_LIBRARY CACHE INTERNAL OFF)
FetchContent_Declare(
mbedtls
GIT_REPOSITORY https://github.com/Mbed-TLS/mbedtls.git
GIT_TAG 8b3f26a5ac38d4fdccbc5c5366229f3e01dafcc0 # v2.28.0
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(mbedtls)
endif()
endif()
if(CHIAKI_ENABLE_FFMPEG_DECODER)
@ -135,7 +150,15 @@ if(CHIAKI_ENABLE_CLI)
add_subdirectory(cli)
endif()
if(CHIAKI_ENABLE_GUI AND CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER)
find_package(SDL2 MODULE REQUIRED)
endif()
if(CHIAKI_ENABLE_SETSU)
if(CHIAKI_ENABLE_SETSU STREQUAL AUTO AND SDL2_FOUND AND (SDL2_VERSION_MINOR GREATER 0 OR SDL2_VERSION_PATCH GREATER_EQUAL 14))
message(STATUS "SDL version ${SDL2_VERSION} is >= 2.0.14, disabling Setsu")
set(CHIAKI_ENABLE_SETSU OFF)
else()
find_package(Udev QUIET)
find_package(Evdev QUIET)
if(Udev_FOUND AND Evdev_FOUND)
@ -143,14 +166,15 @@ if(CHIAKI_ENABLE_SETSU)
else()
if(NOT CHIAKI_ENABLE_SETSU STREQUAL AUTO)
message(FATAL_ERROR "
CHIAKI_ENABLE_SETSU is set to ON, but its dependencies (udev and evdev) could not be resolved.
Keep in mind that setsu is only supported on Linux!")
CHIAKI_ENABLE_SETSU is set to ON, but its dependencies (udev and evdev) could not be resolved.
Keep in mind that setsu is only supported on Linux!")
endif()
set(CHIAKI_ENABLE_SETSU OFF)
endif()
if(CHIAKI_ENABLE_SETSU)
add_subdirectory(setsu)
endif()
endif()
endif()
if(CHIAKI_ENABLE_SETSU)
@ -160,7 +184,6 @@ else()
endif()
if(CHIAKI_ENABLE_GUI)
#add_subdirectory(setsu)
add_subdirectory(gui)
endif()

View file

@ -8,33 +8,35 @@
[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/c81ogebvsmo43dd3?svg=true)](https://ci.appveyor.com/project/thestr4ng3r/chiaki) [![builds.sr.ht Status](https://builds.sr.ht/~thestr4ng3r/chiaki.svg)](https://builds.sr.ht/~thestr4ng3r/chiaki?)
Chiaki is a Free and Open Source Software Client for PlayStation 4 and PlayStation 5 Remote Play
for Linux, FreeBSD, OpenBSD, Android, macOS, Windows, Nintendo Switch and potentially even more platforms.
for Linux, FreeBSD, OpenBSD, NetBSD, Android, macOS, Windows, Nintendo Switch and potentially even more platforms.
## Project Status and Contributing
As all relevant features are implemented, this project is considered to be finished and in maintenance mode only.
No major updates are planned and contributions are only accepted in special cases such as security issues.
The objective is to keep a stable base and not break existing support for less mainstream platforms such as BSDs.
**For a more active, fast moving and community-oriented project, refer
to [chiaki-ng](https://streetpea.github.io/chiaki-ng/) ("next generation").
If you would like to contribute, this will likely also be the best place to do so.**
![Screenshot](assets/screenshot.png)
## Features
Everything necessary for a full streaming session, including the initial
registration and wakeup of the console, is supported.
The following features however are yet to be implemented:
* Rumble
* Accelerometer/Gyroscope
## Installing
You can either download a pre-built release (easier) or build Chiaki from source.
You can either download a pre-built release or build Chiaki from source.
### Downloading a Release
Builds are provided for Linux, Android, macOS and Windows.
Builds are provided for Linux, Android, macOS, Nintendo Switch and Windows.
You can download them [here](https://git.sr.ht/~thestr4ng3r/chiaki/refs).
* **Linux**: The provided file is an [AppImage](https://appimage.org/). Simply make it executable (`chmod +x <file>.AppImage`) and run it.
* **Android**: Install from [Google Play](https://play.google.com/store/apps/details?id=com.metallic.chiaki), [F-Droid](https://f-droid.org/packages/com.metallic.chiaki/) or download the APK from Sourcehut.
* **Android**: Install from [F-Droid](https://f-droid.org/packages/com.metallic.chiaki/) or download the APK from Sourcehut.
* **macOS**: Drag the application from the `.dmg` into your Applications folder.
* **Windows**: Extract the `.zip` file and execute `chiaki.exe`.
* **Switch**: Follow README specific [instructions](./switch/README.md)
* **Switch**: Download the `.nro` file and copy it into the `switch/` directory on your SD card.
### Building from Source
@ -47,8 +49,8 @@ cmake ..
make
```
For more detailed platform-specific instructions, see [doc/platform-build.md](doc/platform-build.md).
in
For more detailed platform-specific instructions, see [doc/platform-build.md](doc/platform-build.md) or [switch/](./switch/README.md) for Nintendo Switch.
## Usage
If your Console is on your local network, is turned on or in standby mode and does not have Discovery explicitly disabled, Chiaki should find it.
@ -71,13 +73,6 @@ Settings -> Remote Play -> Add Device, or on a PS5: Settings -> System -> Remote
You can now double-click your Console in Chiaki's main window to start Remote Play.
## Joining the Community or Getting Help
There are official groups for Chiaki on Telegram and IRC. They are bridged so you can join whichever you like:
- **Telegram:** https://t.me/chiakitg
- **IRC:** #chiaki on irc.freenode.net
## Acknowledgements
This project has only been made possible because of the following Open Source projects:

View file

@ -1,6 +1,6 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
def rootCMakeLists = "../../CMakeLists.txt"
@ -18,13 +18,12 @@ def chiakiVersion = "$chiakiVersionMajor.$chiakiVersionMinor.$chiakiVersionPatch
println("Determined Chiaki Version: $chiakiVersion")
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
compileSdkVersion 33
defaultConfig {
applicationId "com.metallic.chiaki"
minSdkVersion 21
targetSdkVersion 30
versionCode 8
targetSdkVersion 33
versionCode 12
versionName chiakiVersion
externalNativeBuild {
cmake {
@ -33,11 +32,16 @@ android {
"-DCHIAKI_ENABLE_GUI=OFF",
"-DCHIAKI_ENABLE_SETSU=OFF",
"-DCHIAKI_ENABLE_ANDROID=ON",
"-DCHIAKI_ENABLE_FFMPEG_DECODER=OFF",
"-DCHIAKI_ENABLE_PI_DECODER=OFF",
"-DCHIAKI_LIB_ENABLE_OPUS=OFF",
"-DCHIAKI_LIB_OPENSSL_EXTERNAL_PROJECT=ON"
}
}
}
buildFeatures {
viewBinding true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
@ -47,7 +51,7 @@ android {
}
externalNativeBuild {
cmake {
version "3.10.2+"
version "3.22.1"
path rootCMakeLists
}
}
@ -61,6 +65,7 @@ android {
}
}
Properties properties = new Properties()
def propertiesFile = file("../local.properties")
if (propertiesFile.exists()) {
@ -86,31 +91,26 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
}
}
androidExtensions {
// for @Parcelize
experimental = true
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.preference:preference:1.1.0'
implementation 'com.google.android.material:material:1.1.0-beta02'
implementation 'androidx.appcompat:appcompat:1.6.0'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.preference:preference:1.2.0'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-reactivestreams:2.2.0'
implementation "io.reactivex.rxjava2:rxjava:2.2.12"
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-reactivestreams:2.5.1'
implementation "io.reactivex.rxjava2:rxjava:2.2.20"
implementation "io.reactivex.rxjava2:rxkotlin:2.4.0"
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
def room_version = "2.2.4"
def room_version = "2.5.0"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
implementation "androidx.room:room-rxjava2:$room_version"
implementation "com.squareup.moshi:moshi:1.9.2"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.9.2"
implementation "com.squareup.moshi:moshi:1.14.0"
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.14.0"
}

View file

@ -5,6 +5,8 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" />
<application
android:allowBackup="true"
@ -27,7 +29,8 @@
</provider>
<activity android:name=".main.MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

View file

@ -110,9 +110,9 @@ JNIEXPORT jstring JNICALL JNI_FCN(quitReasonToString)(JNIEnv *env, jobject obj,
return E->NewStringUTF(env, chiaki_quit_reason_string((ChiakiQuitReason)value));
}
JNIEXPORT jboolean JNICALL JNI_FCN(quitReasonIsStopped)(JNIEnv *env, jobject obj, jint value)
JNIEXPORT jboolean JNICALL JNI_FCN(quitReasonIsError)(JNIEnv *env, jobject obj, jint value)
{
return value == CHIAKI_QUIT_REASON_STOPPED;
return chiaki_quit_reason_is_error(value);
}
JNIEXPORT jobject JNICALL JNI_FCN(videoProfilePreset)(JNIEnv *env, jobject obj, jint resolution_preset, jint fps_preset, jobject codec)
@ -133,6 +133,7 @@ typedef struct android_chiaki_session_t
jmethodID java_session_event_connected_meth;
jmethodID java_session_event_login_pin_request_meth;
jmethodID java_session_event_quit_meth;
jmethodID java_session_event_rumble_meth;
jfieldID java_controller_state_buttons;
jfieldID java_controller_state_l2_state;
jfieldID java_controller_state_r2_state;
@ -140,6 +141,20 @@ typedef struct android_chiaki_session_t
jfieldID java_controller_state_left_y;
jfieldID java_controller_state_right_x;
jfieldID java_controller_state_right_y;
jfieldID java_controller_state_touches;
jfieldID java_controller_state_gyro_x;
jfieldID java_controller_state_gyro_y;
jfieldID java_controller_state_gyro_z;
jfieldID java_controller_state_accel_x;
jfieldID java_controller_state_accel_y;
jfieldID java_controller_state_accel_z;
jfieldID java_controller_state_orient_x;
jfieldID java_controller_state_orient_y;
jfieldID java_controller_state_orient_z;
jfieldID java_controller_state_orient_w;
jfieldID java_controller_touch_x;
jfieldID java_controller_touch_y;
jfieldID java_controller_touch_id;
AndroidChiakiVideoDecoder video_decoder;
AndroidChiakiAudioDecoder audio_decoder;
@ -178,6 +193,14 @@ static void android_chiaki_event_cb(ChiakiEvent *event, void *user)
free(reason_str);
break;
}
case CHIAKI_EVENT_RUMBLE:
E->CallVoidMethod(env, session->java_session,
session->java_session_event_rumble_meth,
(jint)event->rumble.left,
(jint)event->rumble.right);
break;
default:
break;
}
(*global_vm)->DetachCurrentThread(global_vm);
@ -296,6 +319,7 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
session->java_session_event_connected_meth = E->GetMethodID(env, session->java_session_class, "eventConnected", "()V");
session->java_session_event_login_pin_request_meth = E->GetMethodID(env, session->java_session_class, "eventLoginPinRequest", "(Z)V");
session->java_session_event_quit_meth = E->GetMethodID(env, session->java_session_class, "eventQuit", "(ILjava/lang/String;)V");
session->java_session_event_rumble_meth = E->GetMethodID(env, session->java_session_class, "eventRumble", "(II)V");
jclass controller_state_class = E->FindClass(env, BASE_PACKAGE"/ControllerState");
session->java_controller_state_buttons = E->GetFieldID(env, controller_state_class, "buttons", "I");
@ -305,6 +329,22 @@ JNIEXPORT void JNICALL JNI_FCN(sessionCreate)(JNIEnv *env, jobject obj, jobject
session->java_controller_state_left_y = E->GetFieldID(env, controller_state_class, "leftY", "S");
session->java_controller_state_right_x = E->GetFieldID(env, controller_state_class, "rightX", "S");
session->java_controller_state_right_y = E->GetFieldID(env, controller_state_class, "rightY", "S");
session->java_controller_state_touches = E->GetFieldID(env, controller_state_class, "touches", "[L"BASE_PACKAGE"/ControllerTouch;");
session->java_controller_state_gyro_x = E->GetFieldID(env, controller_state_class, "gyroX", "F");
session->java_controller_state_gyro_y = E->GetFieldID(env, controller_state_class, "gyroY", "F");
session->java_controller_state_gyro_z = E->GetFieldID(env, controller_state_class, "gyroZ", "F");
session->java_controller_state_accel_x = E->GetFieldID(env, controller_state_class, "accelX", "F");
session->java_controller_state_accel_y = E->GetFieldID(env, controller_state_class, "accelY", "F");
session->java_controller_state_accel_z = E->GetFieldID(env, controller_state_class, "accelZ", "F");
session->java_controller_state_orient_x = E->GetFieldID(env, controller_state_class, "orientX", "F");
session->java_controller_state_orient_y = E->GetFieldID(env, controller_state_class, "orientY", "F");
session->java_controller_state_orient_z = E->GetFieldID(env, controller_state_class, "orientZ", "F");
session->java_controller_state_orient_w = E->GetFieldID(env, controller_state_class, "orientW", "F");
jclass controller_touch_class = E->FindClass(env, BASE_PACKAGE"/ControllerTouch");
session->java_controller_touch_x = E->GetFieldID(env, controller_touch_class, "x", "S");
session->java_controller_touch_y = E->GetFieldID(env, controller_touch_class, "y", "S");
session->java_controller_touch_id = E->GetFieldID(env, controller_touch_class, "id", "B");
chiaki_session_set_event_cb(&session->session, android_chiaki_event_cb, session);
chiaki_session_set_video_sample_cb(&session->session, android_chiaki_video_decoder_video_sample, &session->video_decoder);
@ -373,7 +413,8 @@ JNIEXPORT void JNICALL JNI_FCN(sessionSetSurface)(JNIEnv *env, jobject obj, jlon
JNIEXPORT void JNICALL JNI_FCN(sessionSetControllerState)(JNIEnv *env, jobject obj, jlong ptr, jobject controller_state_java)
{
AndroidChiakiSession *session = (AndroidChiakiSession *)ptr;
ChiakiControllerState controller_state = { 0 };
ChiakiControllerState controller_state;
chiaki_controller_state_set_idle(&controller_state);
controller_state.buttons = (uint32_t)E->GetIntField(env, controller_state_java, session->java_controller_state_buttons);
controller_state.l2_state = (uint8_t)E->GetByteField(env, controller_state_java, session->java_controller_state_l2_state);
controller_state.r2_state = (uint8_t)E->GetByteField(env, controller_state_java, session->java_controller_state_r2_state);
@ -381,6 +422,34 @@ JNIEXPORT void JNICALL JNI_FCN(sessionSetControllerState)(JNIEnv *env, jobject o
controller_state.left_y = (int16_t)E->GetShortField(env, controller_state_java, session->java_controller_state_left_y);
controller_state.right_x = (int16_t)E->GetShortField(env, controller_state_java, session->java_controller_state_right_x);
controller_state.right_y = (int16_t)E->GetShortField(env, controller_state_java, session->java_controller_state_right_y);
jobjectArray touch_array = E->GetObjectField(env, controller_state_java, session->java_controller_state_touches);
size_t touch_array_len = (size_t)E->GetArrayLength(env, touch_array);
for(size_t i = 0; i < CHIAKI_CONTROLLER_TOUCHES_MAX; i++)
{
if(i < touch_array_len)
{
jobject touch = E->GetObjectArrayElement(env, touch_array, i);
controller_state.touches[i].x = (uint16_t)E->GetShortField(env, touch, session->java_controller_touch_x);
controller_state.touches[i].y = (uint16_t)E->GetShortField(env, touch, session->java_controller_touch_y);
controller_state.touches[i].id = (int8_t)E->GetByteField(env, touch, session->java_controller_touch_id);
}
else
{
controller_state.touches[i].x = 0;
controller_state.touches[i].y = 0;
controller_state.touches[i].id = -1;
}
}
controller_state.gyro_x = E->GetFloatField(env, controller_state_java, session->java_controller_state_gyro_x);
controller_state.gyro_y = E->GetFloatField(env, controller_state_java, session->java_controller_state_gyro_y);
controller_state.gyro_z = E->GetFloatField(env, controller_state_java, session->java_controller_state_gyro_z);
controller_state.accel_x = E->GetFloatField(env, controller_state_java, session->java_controller_state_accel_x);
controller_state.accel_y = E->GetFloatField(env, controller_state_java, session->java_controller_state_accel_y);
controller_state.accel_z = E->GetFloatField(env, controller_state_java, session->java_controller_state_accel_z);
controller_state.orient_x = E->GetFloatField(env, controller_state_java, session->java_controller_state_orient_x);
controller_state.orient_y = E->GetFloatField(env, controller_state_java, session->java_controller_state_orient_y);
controller_state.orient_z = E->GetFloatField(env, controller_state_java, session->java_controller_state_orient_z);
controller_state.orient_w = E->GetFloatField(env, controller_state_java, session->java_controller_state_orient_w);
chiaki_session_set_controller_state(&session->session, &controller_state);
}

@ -1 +1 @@
Subproject commit 0ab5b12a5bc3630a3d6c83b20eed2a669ebf7a24
Subproject commit 8740d0fc321a55489dbbf6067298201b7d2e106d

View file

@ -26,13 +26,11 @@ ChiakiErrorCode android_chiaki_video_decoder_init(AndroidChiakiVideoDecoder *dec
return chiaki_mutex_init(&decoder->codec_mutex, false);
}
void android_chiaki_video_decoder_fini(AndroidChiakiVideoDecoder *decoder)
static void kill_decoder(AndroidChiakiVideoDecoder *decoder)
{
if(decoder->codec)
{
chiaki_mutex_lock(&decoder->codec_mutex);
decoder->shutdown_output = true;
ssize_t codec_buf_index = AMediaCodec_dequeueInputBuffer(decoder->codec, -1);
ssize_t codec_buf_index = AMediaCodec_dequeueInputBuffer(decoder->codec, 1000);
if(codec_buf_index >= 0)
{
CHIAKI_LOGI(decoder->log, "Video Decoder sending EOS buffer");
@ -48,7 +46,14 @@ void android_chiaki_video_decoder_fini(AndroidChiakiVideoDecoder *decoder)
chiaki_mutex_unlock(&decoder->codec_mutex);
}
AMediaCodec_delete(decoder->codec);
}
decoder->codec = NULL;
decoder->shutdown_output = false;
}
void android_chiaki_video_decoder_fini(AndroidChiakiVideoDecoder *decoder)
{
if(decoder->codec)
kill_decoder(decoder);
chiaki_mutex_fini(&decoder->codec_mutex);
}
@ -56,6 +61,16 @@ void android_chiaki_video_decoder_set_surface(AndroidChiakiVideoDecoder *decoder
{
chiaki_mutex_lock(&decoder->codec_mutex);
if(!surface)
{
if(decoder->codec)
{
kill_decoder(decoder);
CHIAKI_LOGI(decoder->log, "Decoder shut down after surface was removed");
}
return;
}
if(decoder->codec)
{
#if __ANDROID_API__ >= 23

View file

@ -23,9 +23,11 @@ val MIGRATION_1_2 = object : Migration(1, 2)
{
override fun migrate(database: SupportSQLiteDatabase)
{
database.execSQL("ALTER TABLE registered_host RENAME ps4_mac TO server_mac")
database.execSQL("ALTER TABLE registered_host RENAME ps4_nickname TO server_nickname")
database.execSQL("ALTER TABLE registered_host ADD target INTEGER NOT NULL DEFAULT 1000")
database.execSQL("CREATE TABLE `new_registered_host` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `target` INTEGER NOT NULL, `ap_ssid` TEXT, `ap_bssid` TEXT, `ap_key` TEXT, `ap_name` TEXT, `server_mac` INTEGER NOT NULL, `server_nickname` TEXT, `rp_regist_key` BLOB NOT NULL, `rp_key_type` INTEGER NOT NULL, `rp_key` BLOB NOT NULL)");
database.execSQL("INSERT INTO `new_registered_host` SELECT `id`, `target`, `ap_ssid`, `ap_bssid`, `ap_key`, `ap_name`, `ps4_mac`, `ps4_nickname`, `rp_regist_key`, `rp_key_type`, `rp_key` FROM `registered_host`")
database.execSQL("DROP TABLE registered_host")
database.execSQL("ALTER TABLE new_registered_host RENAME TO registered_host")
}
}

View file

@ -3,7 +3,7 @@
package com.metallic.chiaki.common
import androidx.room.*
import androidx.room.ForeignKey.SET_NULL
import androidx.room.ForeignKey.Companion.SET_NULL
import io.reactivex.Completable
import io.reactivex.Flowable
import io.reactivex.Single

View file

@ -68,11 +68,26 @@ class Preferences(context: Context)
get() = sharedPreferences.getBoolean(onScreenControlsEnabledKey, true)
set(value) { sharedPreferences.edit().putBoolean(onScreenControlsEnabledKey, value).apply() }
val touchpadOnlyEnabledKey get() = resources.getString(R.string.preferences_touchpad_only_key)
val touchpadOnlyEnabledKey get() = resources.getString(R.string.preferences_touchpad_only_enabled_key)
var touchpadOnlyEnabled
get() = sharedPreferences.getBoolean(touchpadOnlyEnabledKey, false)
set(value) { sharedPreferences.edit().putBoolean(touchpadOnlyEnabledKey, value).apply() }
val rumbleEnabledKey get() = resources.getString(R.string.preferences_rumble_enabled_key)
var rumbleEnabled
get() = sharedPreferences.getBoolean(rumbleEnabledKey, true)
set(value) { sharedPreferences.edit().putBoolean(rumbleEnabledKey, value).apply() }
val motionEnabledKey get() = resources.getString(R.string.preferences_motion_enabled_key)
var motionEnabled
get() = sharedPreferences.getBoolean(motionEnabledKey, true)
set(value) { sharedPreferences.edit().putBoolean(motionEnabledKey, value).apply() }
val buttonHapticEnabledKey get() = resources.getString(R.string.preferences_button_haptic_enabled_key)
var buttonHapticEnabled
get() = sharedPreferences.getBoolean(buttonHapticEnabledKey, true)
set(value) { sharedPreferences.edit().putBoolean(buttonHapticEnabledKey, value).apply() }
val logVerboseKey get() = resources.getString(R.string.preferences_log_verbose_key)
var logVerbose
get() = sharedPreferences.getBoolean(logVerboseKey, false)

View file

@ -3,7 +3,7 @@
package com.metallic.chiaki.common
import androidx.room.*
import androidx.room.ColumnInfo.BLOB
import androidx.room.ColumnInfo.Companion.BLOB
import com.metallic.chiaki.lib.RegistHost
import com.metallic.chiaki.lib.Target
import io.reactivex.Completable

View file

@ -25,6 +25,8 @@ import io.reactivex.rxkotlin.addTo
import io.reactivex.schedulers.Schedulers
import okio.Buffer
import okio.Okio
import okio.buffer
import okio.source
import java.io.File
import java.io.IOException
@ -164,7 +166,7 @@ fun importSettingsFromUri(activity: Activity, uri: Uri, disposable: CompositeDis
try
{
val inputStream = activity.contentResolver.openInputStream(uri) ?: throw IOException()
val buffer = Okio.buffer(Okio.source(inputStream))
val buffer = inputStream.source().buffer()
val reader = JsonReader.of(buffer)
val adapter = moshi().serializedSettingsAdapter()

View file

@ -3,8 +3,7 @@ package com.metallic.chiaki.lib
import android.os.Parcelable
import android.util.Log
import android.view.Surface
import kotlinx.android.parcel.IgnoredOnParcel
import kotlinx.android.parcel.Parcelize
import kotlinx.parcelize.Parcelize
import java.lang.Exception
import java.net.InetSocketAddress
import kotlin.math.abs
@ -84,14 +83,14 @@ private class ChiakiNative
}
@JvmStatic external fun errorCodeToString(value: Int): String
@JvmStatic external fun quitReasonToString(value: Int): String
@JvmStatic external fun quitReasonIsStopped(value: Int): Boolean
@JvmStatic external fun quitReasonIsError(value: Int): Boolean
@JvmStatic external fun videoProfilePreset(resolutionPreset: Int, fpsPreset: Int, codec: Codec): ConnectVideoProfile
@JvmStatic external fun sessionCreate(result: CreateResult, connectInfo: ConnectInfo, logFile: String?, logVerbose: Boolean, javaSession: Session)
@JvmStatic external fun sessionFree(ptr: Long)
@JvmStatic external fun sessionStart(ptr: Long): Int
@JvmStatic external fun sessionStop(ptr: Long): Int
@JvmStatic external fun sessionJoin(ptr: Long): Int
@JvmStatic external fun sessionSetSurface(ptr: Long, surface: Surface)
@JvmStatic external fun sessionSetSurface(ptr: Long, surface: Surface?)
@JvmStatic external fun sessionSetControllerState(ptr: Long, controllerState: ControllerState)
@JvmStatic external fun sessionSetLoginPin(ptr: Long, pin: String)
@JvmStatic external fun discoveryServiceCreate(result: CreateResult, options: DiscoveryServiceOptions, javaService: DiscoveryService)
@ -150,6 +149,14 @@ class ChiakiLog(val levelMask: Int, val callback: (level: Int, text: String) ->
private fun maxAbs(a: Short, b: Short) = if(abs(a.toInt()) > abs(b.toInt())) a else b
private val CONTROLLER_TOUCHES_MAX = 2 // must be the same as CHIAKI_CONTROLLER_TOUCHES_MAX
data class ControllerTouch(
var x: UShort = 0U,
var y: UShort = 0U,
var id: Byte = -1 // -1 = up
)
data class ControllerState constructor(
var buttons: UInt = 0U,
var l2State: UByte = 0U,
@ -157,7 +164,19 @@ data class ControllerState constructor(
var leftX: Short = 0,
var leftY: Short = 0,
var rightX: Short = 0,
var rightY: Short = 0
var rightY: Short = 0,
private var touchIdNext: UByte = 0U,
var touches: Array<ControllerTouch> = arrayOf(ControllerTouch(), ControllerTouch()),
var gyroX: Float = 0.0f,
var gyroY: Float = 0.0f,
var gyroZ: Float = 0.0f,
var accelX: Float = 0.0f,
var accelY: Float = 1.0f,
var accelZ: Float = 0.0f,
var orientX: Float = 0.0f,
var orientY: Float = 0.0f,
var orientZ: Float = 0.0f,
var orientW: Float = 1.0f
){
companion object
{
@ -177,6 +196,8 @@ data class ControllerState constructor(
val BUTTON_SHARE = (1 shl 13).toUInt()
val BUTTON_TOUCHPAD = (1 shl 14).toUInt()
val BUTTON_PS = (1 shl 15).toUInt()
val TOUCHPAD_WIDTH: UShort = 1920U
val TOUCHPAD_HEIGHT: UShort = 942U
}
infix fun or(o: ControllerState) = ControllerState(
@ -186,24 +207,116 @@ data class ControllerState constructor(
leftX = maxAbs(leftX, o.leftX),
leftY = maxAbs(leftY, o.leftY),
rightX = maxAbs(rightX, o.rightX),
rightY = maxAbs(rightY, o.rightY)
rightY = maxAbs(rightY, o.rightY),
touches = touches.zip(o.touches) { a, b -> if(a.id >= 0) a else b }.toTypedArray(),
gyroX = gyroX,
gyroY = gyroY,
gyroZ = gyroZ,
accelX = accelX,
accelY = accelY,
accelZ = accelZ,
orientX = orientX,
orientY = orientY,
orientZ = orientZ,
orientW = orientW
)
override fun equals(other: Any?): Boolean
{
if(this === other) return true
if(javaClass != other?.javaClass) return false
other as ControllerState
if(buttons != other.buttons) return false
if(l2State != other.l2State) return false
if(r2State != other.r2State) return false
if(leftX != other.leftX) return false
if(leftY != other.leftY) return false
if(rightX != other.rightX) return false
if(rightY != other.rightY) return false
if(touchIdNext != other.touchIdNext) return false
if(!touches.contentEquals(other.touches)) return false
if(gyroX != other.gyroX) return false
if(gyroY != other.gyroY) return false
if(gyroZ != other.gyroZ) return false
if(accelX != other.accelX) return false
if(accelY != other.accelY) return false
if(accelZ != other.accelZ) return false
if(orientX != other.orientX) return false
if(orientY != other.orientY) return false
if(orientZ != other.orientZ) return false
if(orientW != other.orientW) return false
return true
}
override fun hashCode(): Int
{
var result = buttons.hashCode()
result = 31 * result + l2State.hashCode()
result = 31 * result + r2State.hashCode()
result = 31 * result + leftX
result = 31 * result + leftY
result = 31 * result + rightX
result = 31 * result + rightY
result = 31 * result + touchIdNext.hashCode()
result = 31 * result + touches.contentHashCode()
result = 31 * result + gyroX.hashCode()
result = 31 * result + gyroY.hashCode()
result = 31 * result + gyroZ.hashCode()
result = 31 * result + accelX.hashCode()
result = 31 * result + accelY.hashCode()
result = 31 * result + accelZ.hashCode()
result = 31 * result + orientX.hashCode()
result = 31 * result + orientY.hashCode()
result = 31 * result + orientZ.hashCode()
result = 31 * result + orientW.hashCode()
return result
}
fun startTouch(x: UShort, y: UShort): UByte? =
touches
.find { it.id < 0 }
?.also {
it.id = touchIdNext.toByte()
it.x = x
it.y = y
touchIdNext = ((touchIdNext + 1U) and 0x7fU).toUByte()
}?.id?.toUByte()
fun stopTouch(id: UByte)
{
touches.find {
it.id >= 0 && it.id == id.toByte()
}?.let {
it.id = -1
}
}
fun setTouchPos(id: UByte, x: UShort, y: UShort): Boolean
= touches.find {
it.id >= 0 && it.id == id.toByte()
}?.let {
val r = it.x != x || it.y != y
it.x = x
it.y = y
r
} ?: false
}
class QuitReason(val value: Int)
{
override fun toString() = ChiakiNative.quitReasonToString(value)
/**
* whether the reason is CHIAKI_QUIT_REASON_STOPPED
*/
val isStopped = ChiakiNative.quitReasonIsStopped(value)
val isError = ChiakiNative.quitReasonIsError(value)
}
sealed class Event
object ConnectedEvent: Event()
data class LoginPinRequestEvent(val pinIncorrect: Boolean): Event()
data class QuitEvent(val reason: QuitReason, val reasonString: String?): Event()
data class RumbleEvent(val left: UByte, val right: UByte): Event()
class CreateError(val errorCode: ErrorCode): Exception("Failed to create a native object: $errorCode")
@ -259,7 +372,12 @@ class Session(connectInfo: ConnectInfo, logFile: String?, logVerbose: Boolean)
event(QuitEvent(QuitReason(reasonValue), reasonString))
}
fun setSurface(surface: Surface)
private fun eventRumble(left: Int, right: Int)
{
event(RumbleEvent(left.toUByte(), right.toUByte()))
}
fun setSurface(surface: Surface?)
{
ChiakiNative.sessionSetSurface(nativePtr, surface)
}

View file

@ -3,6 +3,7 @@
package com.metallic.chiaki.main
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
@ -16,8 +17,8 @@ import com.metallic.chiaki.common.DiscoveredDisplayHost
import com.metallic.chiaki.common.DisplayHost
import com.metallic.chiaki.common.ManualDisplayHost
import com.metallic.chiaki.common.ext.inflate
import com.metallic.chiaki.databinding.ItemDisplayHostBinding
import com.metallic.chiaki.lib.DiscoveryHost
import kotlinx.android.synthetic.main.item_display_host.view.*
class DisplayHostDiffCallback(val old: List<DisplayHost>, val new: List<DisplayHost>): DiffUtil.Callback()
{
@ -42,10 +43,10 @@ class DisplayHostRecyclerViewAdapter(
diff.dispatchUpdatesTo(this)
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
class ViewHolder(val binding: ItemDisplayHostBinding): RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
= ViewHolder(parent.inflate(R.layout.item_display_host))
= ViewHolder(ItemDisplayHostBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun getItemCount() = hosts.count()
@ -53,7 +54,7 @@ class DisplayHostRecyclerViewAdapter(
{
val context = holder.itemView.context
val host = hosts[position]
holder.itemView.also {
holder.binding.also {
it.nameTextView.text = host.name
it.hostTextView.text = context.getString(R.string.display_host_host, host.host)
val id = host.id
@ -87,7 +88,7 @@ class DisplayHostRecyclerViewAdapter(
else -> R.drawable.ic_console
}
)
it.setOnClickListener { clickCallback(host) }
it.root.setOnClickListener { clickCallback(host) }
val canWakeup = host.registeredHost != null
val canEditDelete = host is ManualDisplayHost

View file

@ -17,52 +17,54 @@ import com.metallic.chiaki.R
import com.metallic.chiaki.common.*
import com.metallic.chiaki.common.ext.putRevealExtra
import com.metallic.chiaki.common.ext.viewModelFactory
import com.metallic.chiaki.databinding.ActivityMainBinding
import com.metallic.chiaki.lib.ConnectInfo
import com.metallic.chiaki.lib.DiscoveryHost
import com.metallic.chiaki.manualconsole.EditManualConsoleActivity
import com.metallic.chiaki.regist.RegistActivity
import com.metallic.chiaki.settings.SettingsActivity
import com.metallic.chiaki.stream.StreamActivity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity()
{
private lateinit var viewModel: MainViewModel
private lateinit var binding: ActivityMainBinding
private var discoveryMenuItem: MenuItem? = null
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
title = ""
setSupportActionBar(toolbar)
setSupportActionBar(binding.toolbar)
floatingActionButton.setOnClickListener {
expandFloatingActionButton(!floatingActionButton.isExpanded)
binding.floatingActionButton.setOnClickListener {
expandFloatingActionButton(!binding.floatingActionButton.isExpanded)
}
floatingActionButtonDialBackground.setOnClickListener {
binding.floatingActionButtonDialBackground.setOnClickListener {
expandFloatingActionButton(false)
}
addManualButton.setOnClickListener { addManualConsole() }
addManualLabelButton.setOnClickListener { addManualConsole() }
binding.addManualButton.setOnClickListener { addManualConsole() }
binding.addManualLabelButton.setOnClickListener { addManualConsole() }
registerButton.setOnClickListener { showRegistration() }
registerLabelButton.setOnClickListener { showRegistration() }
binding.registerButton.setOnClickListener { showRegistration() }
binding.registerLabelButton.setOnClickListener { showRegistration() }
viewModel = ViewModelProvider(this, viewModelFactory { MainViewModel(getDatabase(this), Preferences(this)) })
.get(MainViewModel::class.java)
val recyclerViewAdapter = DisplayHostRecyclerViewAdapter(this::hostTriggered, this::wakeupHost, this::editHost, this::deleteHost)
hostsRecyclerView.adapter = recyclerViewAdapter
hostsRecyclerView.layoutManager = LinearLayoutManager(this)
binding.hostsRecyclerView.adapter = recyclerViewAdapter
binding.hostsRecyclerView.layoutManager = LinearLayoutManager(this)
viewModel.displayHosts.observe(this, Observer {
val top = hostsRecyclerView.computeVerticalScrollOffset() == 0
val top = binding.hostsRecyclerView.computeVerticalScrollOffset() == 0
recyclerViewAdapter.hosts = it
if(top)
hostsRecyclerView.scrollToPosition(0)
binding.hostsRecyclerView.scrollToPosition(0)
updateEmptyInfo()
})
@ -76,19 +78,19 @@ class MainActivity : AppCompatActivity()
{
if(viewModel.displayHosts.value?.isEmpty() ?: true)
{
emptyInfoLayout.visibility = View.VISIBLE
binding.emptyInfoLayout.visibility = View.VISIBLE
val discoveryActive = viewModel.discoveryActive.value ?: false
emptyInfoImageView.setImageResource(if(discoveryActive) R.drawable.ic_discover_on else R.drawable.ic_discover_off)
emptyInfoTextView.setText(if(discoveryActive) R.string.display_hosts_empty_discovery_on_info else R.string.display_hosts_empty_discovery_off_info)
binding.emptyInfoImageView.setImageResource(if(discoveryActive) R.drawable.ic_discover_on else R.drawable.ic_discover_off)
binding.emptyInfoTextView.setText(if(discoveryActive) R.string.display_hosts_empty_discovery_on_info else R.string.display_hosts_empty_discovery_off_info)
}
else
emptyInfoLayout.visibility = View.GONE
binding.emptyInfoLayout.visibility = View.GONE
}
private fun expandFloatingActionButton(expand: Boolean)
{
floatingActionButton.isExpanded = expand
floatingActionButton.isActivated = floatingActionButton.isExpanded
binding.floatingActionButton.isExpanded = expand
binding.floatingActionButton.isActivated = binding.floatingActionButton.isExpanded
}
override fun onStart()
@ -105,7 +107,7 @@ class MainActivity : AppCompatActivity()
override fun onBackPressed()
{
if(floatingActionButton.isExpanded)
if(binding.floatingActionButton.isExpanded)
{
expandFloatingActionButton(false)
return
@ -151,7 +153,7 @@ class MainActivity : AppCompatActivity()
private fun addManualConsole()
{
Intent(this, EditManualConsoleActivity::class.java).also {
it.putRevealExtra(addManualButton, rootLayout)
it.putRevealExtra(binding.addManualButton, binding.rootLayout)
startActivity(it, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
}
}
@ -159,7 +161,7 @@ class MainActivity : AppCompatActivity()
private fun showRegistration()
{
Intent(this, RegistActivity::class.java).also {
it.putRevealExtra(registerButton, rootLayout)
it.putRevealExtra(binding.registerButton, binding.rootLayout)
startActivity(it, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
}
}

View file

@ -16,10 +16,10 @@ import com.metallic.chiaki.common.RegisteredHost
import com.metallic.chiaki.common.ext.RevealActivity
import com.metallic.chiaki.common.ext.viewModelFactory
import com.metallic.chiaki.common.getDatabase
import com.metallic.chiaki.databinding.ActivityEditManualBinding
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
import kotlinx.android.synthetic.main.activity_edit_manual.*
class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
{
@ -28,18 +28,20 @@ class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
const val EXTRA_MANUAL_HOST_ID = "manual_host_id"
}
override val revealIntent: Intent get() = intent
override val revealRootLayout: View get() = rootLayout
override val revealWindow: Window get() = window
private lateinit var viewModel: EditManualConsoleViewModel
private lateinit var binding: ActivityEditManualBinding
override val revealIntent: Intent get() = intent
override val revealRootLayout: View get() = binding.rootLayout
override val revealWindow: Window get() = window
private val disposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_manual)
binding = ActivityEditManualBinding.inflate(layoutInflater)
setContentView(binding.root)
handleReveal()
viewModel = ViewModelProvider(this, viewModelFactory {
@ -52,17 +54,17 @@ class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
.get(EditManualConsoleViewModel::class.java)
viewModel.existingHost?.observe(this, Observer {
hostEditText.setText(it.host)
binding.hostEditText.setText(it.host)
})
viewModel.selectedRegisteredHost.observe(this, Observer {
registeredHostTextView.setText(titleForRegisteredHost(it))
binding.registeredHostTextView.setText(titleForRegisteredHost(it))
})
viewModel.registeredHosts.observe(this, Observer { hosts ->
registeredHostTextView.setAdapter(ArrayAdapter<String>(this, R.layout.dropdown_menu_popup_item,
binding.registeredHostTextView.setAdapter(ArrayAdapter<String>(this, R.layout.dropdown_menu_popup_item,
hosts.map { titleForRegisteredHost(it) }))
registeredHostTextView.onItemClickListener = object: AdapterView.OnItemClickListener {
binding.registeredHostTextView.onItemClickListener = object: AdapterView.OnItemClickListener {
override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long)
{
if(position >= hosts.size)
@ -73,8 +75,7 @@ class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
}
})
saveButton.setOnClickListener { saveHost() }
binding.saveButton.setOnClickListener { saveHost() }
}
private fun titleForRegisteredHost(registeredHost: RegisteredHost?) =
@ -85,14 +86,14 @@ class EditManualConsoleActivity: AppCompatActivity(), RevealActivity
private fun saveHost()
{
val host = hostEditText.text.toString().trim()
val host = binding.hostEditText.text.toString().trim()
if(host.isEmpty())
{
hostEditText.error = getString(R.string.entered_host_invalid)
binding.hostEditText.error = getString(R.string.entered_host_invalid)
return
}
saveButton.isEnabled = false
binding.saveButton.isEnabled = false
viewModel.saveHost(host)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {

View file

@ -12,9 +12,9 @@ import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.metallic.chiaki.R
import com.metallic.chiaki.common.ext.RevealActivity
import com.metallic.chiaki.databinding.ActivityRegistBinding
import com.metallic.chiaki.lib.RegistInfo
import com.metallic.chiaki.lib.Target
import kotlinx.android.synthetic.main.activity_regist.*
import java.lang.IllegalArgumentException
class RegistActivity: AppCompatActivity(), RevealActivity
@ -30,33 +30,35 @@ class RegistActivity: AppCompatActivity(), RevealActivity
private const val REQUEST_REGIST = 1
}
private lateinit var viewModel: RegistViewModel
private lateinit var binding: ActivityRegistBinding
override val revealWindow: Window get() = window
override val revealIntent: Intent get() = intent
override val revealRootLayout: View get() = rootLayout
private lateinit var viewModel: RegistViewModel
override val revealRootLayout: View get() = binding.rootLayout
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_regist)
binding = ActivityRegistBinding.inflate(layoutInflater)
setContentView(binding.root)
handleReveal()
viewModel = ViewModelProvider(this).get(RegistViewModel::class.java)
hostEditText.setText(intent.getStringExtra(EXTRA_HOST) ?: "255.255.255.255")
broadcastCheckBox.isChecked = intent.getBooleanExtra(EXTRA_BROADCAST, true)
binding.hostEditText.setText(intent.getStringExtra(EXTRA_HOST) ?: "255.255.255.255")
binding.broadcastCheckBox.isChecked = intent.getBooleanExtra(EXTRA_BROADCAST, true)
registButton.setOnClickListener { doRegist() }
binding.registButton.setOnClickListener { doRegist() }
ps4VersionRadioGroup.check(when(viewModel.ps4Version.value ?: RegistViewModel.ConsoleVersion.PS5) {
binding.ps4VersionRadioGroup.check(when(viewModel.ps4Version.value ?: RegistViewModel.ConsoleVersion.PS5) {
RegistViewModel.ConsoleVersion.PS5 -> R.id.ps5RadioButton
RegistViewModel.ConsoleVersion.PS4_GE_8 -> R.id.ps4VersionGE8RadioButton
RegistViewModel.ConsoleVersion.PS4_GE_7 -> R.id.ps4VersionGE7RadioButton
RegistViewModel.ConsoleVersion.PS4_LT_7 -> R.id.ps4VersionLT7RadioButton
})
ps4VersionRadioGroup.setOnCheckedChangeListener { _, checkedId ->
binding.ps4VersionRadioGroup.setOnCheckedChangeListener { _, checkedId ->
viewModel.ps4Version.value = when(checkedId)
{
R.id.ps5RadioButton -> RegistViewModel.ConsoleVersion.PS5
@ -68,14 +70,14 @@ class RegistActivity: AppCompatActivity(), RevealActivity
}
viewModel.ps4Version.observe(this, Observer {
psnAccountIdHelpGroup.visibility = if(it == RegistViewModel.ConsoleVersion.PS4_LT_7) View.GONE else View.VISIBLE
psnIdTextInputLayout.hint = getString(when(it!!)
binding.psnAccountIdHelpGroup.visibility = if(it == RegistViewModel.ConsoleVersion.PS4_LT_7) View.GONE else View.VISIBLE
binding.psnIdTextInputLayout.hint = getString(when(it!!)
{
RegistViewModel.ConsoleVersion.PS4_LT_7 -> R.string.hint_regist_psn_online_id
else -> R.string.hint_regist_psn_account_id
})
pinHelpBeforeTextView.setText(if(it.isPS5) R.string.regist_pin_instructions_ps5_before else R.string.regist_pin_instructions_ps4_before)
pinHelpNavigationTextView.setText(if(it.isPS5) R.string.regist_pin_instructions_ps5_navigation else R.string.regist_pin_instructions_ps4_navigation)
binding.pinHelpBeforeTextView.setText(if(it.isPS5) R.string.regist_pin_instructions_ps5_before else R.string.regist_pin_instructions_ps4_before)
binding.pinHelpNavigationTextView.setText(if(it.isPS5) R.string.regist_pin_instructions_ps5_navigation else R.string.regist_pin_instructions_ps4_navigation)
})
}
@ -83,11 +85,11 @@ class RegistActivity: AppCompatActivity(), RevealActivity
{
val ps4Version = viewModel.ps4Version.value ?: RegistViewModel.ConsoleVersion.PS5
val host = hostEditText.text.toString().trim()
val host = binding.hostEditText.text.toString().trim()
val hostValid = host.isNotEmpty()
val broadcast = broadcastCheckBox.isChecked
val broadcast = binding.broadcastCheckBox.isChecked
val psnId = psnIdEditText.text.toString().trim()
val psnId = binding.psnIdEditText.text.toString().trim()
val psnOnlineId: String? = if(ps4Version == RegistViewModel.ConsoleVersion.PS4_LT_7) psnId else null
val psnAccountId: ByteArray? =
if(ps4Version != RegistViewModel.ConsoleVersion.PS4_LT_7)
@ -101,11 +103,11 @@ class RegistActivity: AppCompatActivity(), RevealActivity
}
val pin = pinEditText.text.toString()
val pin = binding.pinEditText.text.toString()
val pinValid = pin.length == PIN_LENGTH
hostEditText.error = if(!hostValid) getString(R.string.entered_host_invalid) else null
psnIdEditText.error =
binding.hostEditText.error = if(!hostValid) getString(R.string.entered_host_invalid) else null
binding.psnIdEditText.error =
if(!psnIdValid)
getString(when(ps4Version)
{
@ -114,7 +116,7 @@ class RegistActivity: AppCompatActivity(), RevealActivity
})
else
null
pinEditText.error = if(!pinValid) getString(R.string.regist_pin_invalid, PIN_LENGTH) else null
binding.pinEditText.error = if(!pinValid) getString(R.string.regist_pin_invalid, PIN_LENGTH) else null
if(!hostValid || !psnIdValid || !pinValid)
return

View file

@ -16,8 +16,8 @@ import com.metallic.chiaki.R
import com.metallic.chiaki.common.MacAddress
import com.metallic.chiaki.common.ext.viewModelFactory
import com.metallic.chiaki.common.getDatabase
import com.metallic.chiaki.databinding.ActivityRegistExecuteBinding
import com.metallic.chiaki.lib.RegistInfo
import kotlinx.android.synthetic.main.activity_regist_execute.*
import kotlin.math.max
class RegistExecuteActivity: AppCompatActivity()
@ -31,55 +31,57 @@ class RegistExecuteActivity: AppCompatActivity()
}
private lateinit var viewModel: RegistExecuteViewModel
private lateinit var binding: ActivityRegistExecuteBinding
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_regist_execute)
binding = ActivityRegistExecuteBinding.inflate(layoutInflater)
setContentView(binding.root)
viewModel = ViewModelProvider(this, viewModelFactory { RegistExecuteViewModel(getDatabase(this)) })
.get(RegistExecuteViewModel::class.java)
logTextView.setHorizontallyScrolling(true)
logTextView.movementMethod = ScrollingMovementMethod()
binding.logTextView.setHorizontallyScrolling(true)
binding.logTextView.movementMethod = ScrollingMovementMethod()
viewModel.logText.observe(this, Observer {
val textLayout = logTextView.layout ?: return@Observer
val textLayout = binding.logTextView.layout ?: return@Observer
val lineCount = textLayout.lineCount
if(lineCount < 1)
return@Observer
logTextView.text = it
val scrollY = textLayout.getLineBottom(lineCount - 1) - logTextView.height + logTextView.paddingTop + logTextView.paddingBottom
logTextView.scrollTo(0, max(scrollY, 0))
binding.logTextView.text = it
val scrollY = textLayout.getLineBottom(lineCount - 1) - binding.logTextView.height + binding.logTextView.paddingTop + binding.logTextView.paddingBottom
binding.logTextView.scrollTo(0, max(scrollY, 0))
})
viewModel.state.observe(this, Observer {
progressBar.visibility = if(it == RegistExecuteViewModel.State.RUNNING) View.VISIBLE else View.GONE
binding.progressBar.visibility = if(it == RegistExecuteViewModel.State.RUNNING) View.VISIBLE else View.GONE
when(it)
{
RegistExecuteViewModel.State.FAILED ->
{
infoTextView.visibility = View.VISIBLE
infoTextView.setText(R.string.regist_info_failed)
binding.infoTextView.visibility = View.VISIBLE
binding.infoTextView.setText(R.string.regist_info_failed)
setResult(RESULT_FAILED)
}
RegistExecuteViewModel.State.SUCCESSFUL, RegistExecuteViewModel.State.SUCCESSFUL_DUPLICATE ->
{
infoTextView.visibility = View.VISIBLE
infoTextView.setText(R.string.regist_info_success)
binding.infoTextView.visibility = View.VISIBLE
binding.infoTextView.setText(R.string.regist_info_success)
setResult(RESULT_OK)
if(it == RegistExecuteViewModel.State.SUCCESSFUL_DUPLICATE)
showDuplicateDialog()
}
RegistExecuteViewModel.State.STOPPED ->
{
infoTextView.visibility = View.GONE
binding.infoTextView.visibility = View.GONE
setResult(Activity.RESULT_CANCELED)
}
else -> infoTextView.visibility = View.GONE
else -> binding.infoTextView.visibility = View.GONE
}
})
shareLogButton.setOnClickListener {
binding.shareLogButton.setOnClickListener {
val log = viewModel.logText.value ?: ""
Intent(Intent.ACTION_SEND).also {
it.type = "text/plain"

View file

@ -1,19 +1,37 @@
package com.metallic.chiaki.session
import android.util.Log
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
import android.content.Context
import android.hardware.*
import android.view.*
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import com.metallic.chiaki.common.Preferences
import com.metallic.chiaki.lib.ControllerState
class StreamInput(val preferences: Preferences)
class StreamInput(val context: Context, val preferences: Preferences)
{
var controllerStateChangedCallback: ((ControllerState) -> Unit)? = null
val controllerState: ControllerState get()
{
val controllerState = keyControllerState or motionControllerState
val controllerState = sensorControllerState or keyControllerState or motionControllerState
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
@Suppress("DEPRECATION")
when(windowManager.defaultDisplay.rotation)
{
Surface.ROTATION_90 -> {
controllerState.accelX *= -1.0f
controllerState.accelZ *= -1.0f
controllerState.gyroX *= -1.0f
controllerState.gyroZ *= -1.0f
controllerState.orientX *= -1.0f
controllerState.orientZ *= -1.0f
}
else -> {}
}
// prioritize motion controller's l2 and r2 over key
// (some controllers send only key, others both but key earlier than full press)
@ -25,6 +43,7 @@ class StreamInput(val preferences: Preferences)
return controllerState or touchControllerState
}
private val sensorControllerState = ControllerState() // from Motion Sensors
private val keyControllerState = ControllerState() // from KeyEvents
private val motionControllerState = ControllerState() // from MotionEvents
var touchControllerState = ControllerState()
@ -36,6 +55,66 @@ class StreamInput(val preferences: Preferences)
private val swapCrossMoon = preferences.swapCrossMoon
private val sensorEventListener = object: SensorEventListener {
override fun onSensorChanged(event: SensorEvent)
{
when(event.sensor.type)
{
Sensor.TYPE_ACCELEROMETER -> {
sensorControllerState.accelX = event.values[1] / SensorManager.GRAVITY_EARTH
sensorControllerState.accelY = event.values[2] / SensorManager.GRAVITY_EARTH
sensorControllerState.accelZ = event.values[0] / SensorManager.GRAVITY_EARTH
}
Sensor.TYPE_GYROSCOPE -> {
sensorControllerState.gyroX = event.values[1]
sensorControllerState.gyroY = event.values[2]
sensorControllerState.gyroZ = event.values[0]
}
Sensor.TYPE_ROTATION_VECTOR -> {
val q = floatArrayOf(0f, 0f, 0f, 0f)
SensorManager.getQuaternionFromVector(q, event.values)
sensorControllerState.orientX = q[2]
sensorControllerState.orientY = q[3]
sensorControllerState.orientZ = q[1]
sensorControllerState.orientW = q[0]
}
else -> return
}
controllerStateUpdated()
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}
private val motionLifecycleObserver = object: LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume()
{
val samplingPeriodUs = 4000
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
listOfNotNull(
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)
).forEach {
sensorManager.registerListener(sensorEventListener, it, samplingPeriodUs)
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause()
{
val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensorManager.unregisterListener(sensorEventListener)
}
}
fun observe(lifecycleOwner: LifecycleOwner)
{
if(preferences.motionEnabled)
lifecycleOwner.lifecycle.addObserver(motionLifecycleObserver)
}
private fun controllerStateUpdated()
{
controllerStateChangedCallback?.let { it(controllerState) }
@ -98,7 +177,7 @@ class StreamInput(val preferences: Preferences)
{
if(event.source and InputDevice.SOURCE_CLASS_JOYSTICK != InputDevice.SOURCE_CLASS_JOYSTICK)
return false
fun Float.signedAxis() = (this * Short.MAX_VALUE).toShort()
fun Float.signedAxis() = (this * Short.MAX_VALUE).toInt().toShort()
fun Float.unsignedAxis() = (this * UByte.MAX_VALUE.toFloat()).toUInt().toUByte()
motionControllerState.leftX = event.getAxisValue(MotionEvent.AXIS_X).signedAxis()
motionControllerState.leftY = event.getAxisValue(MotionEvent.AXIS_Y).signedAxis()

View file

@ -25,8 +25,11 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
private val _state = MutableLiveData<StreamState>(StreamStateIdle)
val state: LiveData<StreamState> get() = _state
private val _rumbleState = MutableLiveData<RumbleEvent>(RumbleEvent(0U, 0U))
val rumbleState: LiveData<RumbleEvent> get() = _rumbleState
var surfaceTexture: SurfaceTexture? = null
private var surfaceTexture: SurfaceTexture? = null
private var surface: Surface? = null
init
{
@ -59,9 +62,9 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
_state.value = StreamStateConnecting
session.eventCallback = this::eventCallback
session.start()
val surfaceTexture = surfaceTexture
if(surfaceTexture != null)
session.setSurface(Surface(surfaceTexture))
val surface = surface
if(surface != null)
session.setSurface(surface)
this.session = session
}
catch(e: CreateError)
@ -86,9 +89,30 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
event.pinIncorrect
)
)
is RumbleEvent -> _rumbleState.postValue(event)
}
}
fun attachToSurfaceView(surfaceView: SurfaceView)
{
surfaceView.holder.addCallback(object: SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) { }
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int)
{
val surface = holder.surface
this@StreamSession.surface = surface
session?.setSurface(surface)
}
override fun surfaceDestroyed(holder: SurfaceHolder)
{
this@StreamSession.surface = null
session?.setSurface(null)
}
})
}
fun attachToTextureView(textureView: TextureView)
{
textureView.surfaceTextureListener = object: TextureView.SurfaceTextureListener {
@ -97,6 +121,7 @@ class StreamSession(val connectInfo: ConnectInfo, val logManager: LogManager, va
if(surfaceTexture != null)
return
surfaceTexture = surface
this@StreamSession.surface = Surface(surfaceTexture)
session?.setSurface(Surface(surface))
}

View file

@ -9,7 +9,7 @@ import androidx.fragment.app.Fragment
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.metallic.chiaki.R
import kotlinx.android.synthetic.main.activity_settings.*
import com.metallic.chiaki.databinding.ActivitySettingsBinding
interface TitleFragment
{
@ -18,20 +18,23 @@ interface TitleFragment
class SettingsActivity: AppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
{
private lateinit var binding: ActivitySettingsBinding
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root)
title = ""
setSupportActionBar(toolbar)
setSupportActionBar(binding.toolbar)
val rootFragment = SettingsFragment()
replaceFragment(rootFragment, false)
supportFragmentManager.addOnBackStackChangedListener {
val titleFragment = supportFragmentManager.findFragmentById(R.id.settingsFragment) as? TitleFragment ?: return@addOnBackStackChangedListener
titleTextView.text = titleFragment.getTitle(resources)
binding.titleTextView.text = titleFragment.getTitle(resources)
}
titleTextView.text = rootFragment.getTitle(resources)
binding.titleTextView.text = rootFragment.getTitle(resources)
}
override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference) = when(pref.fragment)

View file

@ -16,7 +16,6 @@ import com.metallic.chiaki.common.exportAndShareAllSettings
import com.metallic.chiaki.common.ext.viewModelFactory
import com.metallic.chiaki.common.getDatabase
import com.metallic.chiaki.common.importSettingsFromUri
import com.metallic.chiaki.lib.Codec
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
@ -26,6 +25,9 @@ class DataStore(val preferences: Preferences): PreferenceDataStore()
{
preferences.logVerboseKey -> preferences.logVerbose
preferences.swapCrossMoonKey -> preferences.swapCrossMoon
preferences.rumbleEnabledKey -> preferences.rumbleEnabled
preferences.motionEnabledKey -> preferences.motionEnabled
preferences.buttonHapticEnabledKey -> preferences.buttonHapticEnabled
else -> defValue
}
@ -35,6 +37,9 @@ class DataStore(val preferences: Preferences): PreferenceDataStore()
{
preferences.logVerboseKey -> preferences.logVerbose = value
preferences.swapCrossMoonKey -> preferences.swapCrossMoon = value
preferences.rumbleEnabledKey -> preferences.rumbleEnabled = value
preferences.motionEnabledKey -> preferences.motionEnabled = value
preferences.buttonHapticEnabledKey -> preferences.buttonHapticEnabled = value
}
}

View file

@ -2,13 +2,13 @@
package com.metallic.chiaki.settings
import android.view.View
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.metallic.chiaki.R
import com.metallic.chiaki.common.LogFile
import com.metallic.chiaki.common.ext.inflate
import kotlinx.android.synthetic.main.item_log_file.view.*
import com.metallic.chiaki.databinding.ItemLogFileBinding
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
@ -20,7 +20,7 @@ class SettingsLogsAdapter: RecyclerView.Adapter<SettingsLogsAdapter.ViewHolder>(
private val dateFormat: DateFormat = DateFormat.getDateInstance(DateFormat.SHORT)
private val timeFormat = SimpleDateFormat("HH:mm:ss:SSS", Locale.getDefault())
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
class ViewHolder(val binding: ItemLogFileBinding): RecyclerView.ViewHolder(binding.root)
var logFiles: List<LogFile> = listOf()
set(value)
@ -29,16 +29,16 @@ class SettingsLogsAdapter: RecyclerView.Adapter<SettingsLogsAdapter.ViewHolder>(
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(parent.inflate(R.layout.item_log_file))
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ViewHolder(ItemLogFileBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun getItemCount() = logFiles.size
override fun onBindViewHolder(holder: ViewHolder, position: Int)
{
val view = holder.itemView
val logFile = logFiles[position]
view.nameTextView.text = "${dateFormat.format(logFile.date)} ${timeFormat.format(logFile.date)}"
view.summaryTextView.text = logFile.filename
view.shareButton.setOnClickListener { shareCallback?.let { it(logFile) } }
holder.binding.nameTextView.text = "${dateFormat.format(logFile.date)} ${timeFormat.format(logFile.date)}"
holder.binding.summaryTextView.text = logFile.filename
holder.binding.shareButton.setOnClickListener { shareCallback?.let { it(logFile) } }
}
}

View file

@ -21,29 +21,35 @@ import com.metallic.chiaki.common.LogFile
import com.metallic.chiaki.common.LogManager
import com.metallic.chiaki.common.ext.viewModelFactory
import com.metallic.chiaki.common.fileProviderAuthority
import kotlinx.android.synthetic.main.fragment_settings_logs.*
import com.metallic.chiaki.databinding.FragmentSettingsLogsBinding
class SettingsLogsFragment: AppCompatDialogFragment(), TitleFragment
{
private lateinit var viewModel: SettingsLogsViewModel
private var _binding: FragmentSettingsLogsBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
inflater.inflate(R.layout.fragment_settings_logs, container, false)
FragmentSettingsLogsBinding.inflate(inflater, container, false).let {
_binding = it
it.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
{
val context = context!!
val context = requireContext()
viewModel = ViewModelProvider(this, viewModelFactory { SettingsLogsViewModel(LogManager(context)) })
.get(SettingsLogsViewModel::class.java)
val adapter = SettingsLogsAdapter()
logsRecyclerView.layoutManager = LinearLayoutManager(context)
logsRecyclerView.adapter = adapter
binding.logsRecyclerView.layoutManager = LinearLayoutManager(context)
binding.logsRecyclerView.adapter = adapter
adapter.shareCallback = this::shareLogFile
viewModel.sessionLogs.observe(viewLifecycleOwner, Observer {
adapter.logFiles = it
emptyInfoGroup.visibility = if(it.isEmpty()) View.VISIBLE else View.GONE
binding.emptyInfoGroup.visibility = if(it.isEmpty()) View.VISIBLE else View.GONE
})
val itemTouchSwipeCallback = object : ItemTouchSwipeCallback(context)
@ -55,7 +61,7 @@ class SettingsLogsFragment: AppCompatDialogFragment(), TitleFragment
viewModel.deleteLog(file)
}
}
ItemTouchHelper(itemTouchSwipeCallback).attachToRecyclerView(logsRecyclerView)
ItemTouchHelper(itemTouchSwipeCallback).attachToRecyclerView(binding.logsRecyclerView)
}
override fun getTitle(resources: Resources): String = resources.getString(R.string.preferences_logs_title)

View file

@ -2,17 +2,15 @@
package com.metallic.chiaki.settings
import android.view.View
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.metallic.chiaki.R
import com.metallic.chiaki.common.RegisteredHost
import com.metallic.chiaki.common.ext.inflate
import kotlinx.android.synthetic.main.item_registered_host.view.*
import com.metallic.chiaki.databinding.ItemRegisteredHostBinding
class SettingsRegisteredHostsAdapter: RecyclerView.Adapter<SettingsRegisteredHostsAdapter.ViewHolder>()
{
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
class ViewHolder(val binding: ItemRegisteredHostBinding): RecyclerView.ViewHolder(binding.root)
var hosts: List<RegisteredHost> = listOf()
set(value)
@ -21,15 +19,15 @@ class SettingsRegisteredHostsAdapter: RecyclerView.Adapter<SettingsRegisteredHos
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(parent.inflate(R.layout.item_registered_host))
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
= ViewHolder(ItemRegisteredHostBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun getItemCount() = hosts.size
override fun onBindViewHolder(holder: ViewHolder, position: Int)
{
val view = holder.itemView
val host = hosts[position]
view.nameTextView.text = "${host.serverNickname} (${if(host.target.isPS5) "PS5" else "PS4"})"
view.summaryTextView.text = host.serverMac.toString()
holder.binding.nameTextView.text = "${host.serverNickname} (${if(host.target.isPS5) "PS5" else "PS4"})"
holder.binding.summaryTextView.text = host.serverMac.toString()
}
}

View file

@ -20,25 +20,32 @@ import com.metallic.chiaki.R
import com.metallic.chiaki.common.ext.putRevealExtra
import com.metallic.chiaki.common.ext.viewModelFactory
import com.metallic.chiaki.common.getDatabase
import com.metallic.chiaki.databinding.FragmentSettingsRegisteredHostsBinding
import com.metallic.chiaki.regist.RegistActivity
import kotlinx.android.synthetic.main.fragment_settings_registered_hosts.*
class SettingsRegisteredHostsFragment: AppCompatDialogFragment(), TitleFragment
{
private lateinit var viewModel: SettingsRegisteredHostsViewModel
private var _binding: FragmentSettingsRegisteredHostsBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
inflater.inflate(R.layout.fragment_settings_registered_hosts, container, false)
FragmentSettingsRegisteredHostsBinding.inflate(inflater, container, false).let {
_binding = it
it.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
{
viewModel = ViewModelProvider(this, viewModelFactory { SettingsRegisteredHostsViewModel(getDatabase(context!!)) })
val context = requireContext()
viewModel = ViewModelProvider(this, viewModelFactory { SettingsRegisteredHostsViewModel(getDatabase(context)) })
.get(SettingsRegisteredHostsViewModel::class.java)
val adapter = SettingsRegisteredHostsAdapter()
hostsRecyclerView.layoutManager = LinearLayoutManager(context)
hostsRecyclerView.adapter = adapter
val itemTouchSwipeCallback = object : ItemTouchSwipeCallback(context!!)
binding.hostsRecyclerView.layoutManager = LinearLayoutManager(context)
binding.hostsRecyclerView.adapter = adapter
val itemTouchSwipeCallback = object : ItemTouchSwipeCallback(context)
{
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int)
{
@ -56,15 +63,15 @@ class SettingsRegisteredHostsFragment: AppCompatDialogFragment(), TitleFragment
.show()
}
}
ItemTouchHelper(itemTouchSwipeCallback).attachToRecyclerView(hostsRecyclerView)
ItemTouchHelper(itemTouchSwipeCallback).attachToRecyclerView(binding.hostsRecyclerView)
viewModel.registeredHosts.observe(this, Observer {
adapter.hosts = it
emptyInfoGroup.visibility = if(it.isEmpty()) View.VISIBLE else View.GONE
binding.emptyInfoGroup.visibility = if(it.isEmpty()) View.VISIBLE else View.GONE
})
floatingActionButton.setOnClickListener {
binding.floatingActionButton.setOnClickListener {
Intent(context, RegistActivity::class.java).also {
it.putRevealExtra(floatingActionButton, rootLayout)
it.putRevealExtra(binding.floatingActionButton, binding.rootLayout)
startActivity(it, ActivityOptions.makeSceneTransitionAnimation(activity).toBundle())
}
}

View file

@ -0,0 +1,68 @@
package com.metallic.chiaki.stream
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
// see ExoPlayer's AspectRatioFrameLayout
class AspectRatioFrameLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null): FrameLayout(context, attrs)
{
companion object
{
private const val MAX_ASPECT_RATIO_DEFORMATION_FRACTION = 0.01f
}
var aspectRatio = 0f
set(value)
{
if(field != value)
{
field = value
requestLayout()
}
}
var mode: TransformMode = TransformMode.FIT
set(value)
{
if(field != value)
{
field = value
requestLayout()
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
if(aspectRatio <= 0)
{
// Aspect ratio not set.
return
}
var width = measuredWidth
var height = measuredHeight
val viewAspectRatio = width.toFloat() / height
val aspectDeformation = aspectRatio / viewAspectRatio - 1
if(Math.abs(aspectDeformation) <= MAX_ASPECT_RATIO_DEFORMATION_FRACTION)
return
when(mode)
{
TransformMode.ZOOM ->
if(aspectDeformation > 0)
width = (height * aspectRatio).toInt()
else
height = (width / aspectRatio).toInt()
TransformMode.FIT ->
if(aspectDeformation > 0)
height = (width / aspectRatio).toInt()
else
width = (height * aspectRatio).toInt()
TransformMode.STRETCH -> {}
}
super.onMeasure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
)
}
}

View file

@ -6,12 +6,8 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.app.AlertDialog
import android.graphics.Matrix
import android.os.Bundle
import android.os.Handler
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.TextureView
import android.view.View
import android.os.*
import android.view.*
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isGone
@ -20,15 +16,18 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.*
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.metallic.chiaki.R
import com.metallic.chiaki.common.LogManager
import com.metallic.chiaki.common.Preferences
import com.metallic.chiaki.common.ext.viewModelFactory
import com.metallic.chiaki.databinding.ActivityStreamBinding
import com.metallic.chiaki.lib.ConnectInfo
import com.metallic.chiaki.lib.ConnectVideoProfile
import com.metallic.chiaki.session.*
import com.metallic.chiaki.touchcontrols.TouchpadOnlyFragment
import com.metallic.chiaki.touchcontrols.DefaultTouchControlsFragment
import com.metallic.chiaki.touchcontrols.TouchControlsFragment
import kotlinx.android.synthetic.main.activity_stream.*
import com.metallic.chiaki.touchcontrols.TouchpadOnlyFragment
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
import kotlin.math.min
private sealed class DialogContents
private object StreamQuitDialog: DialogContents()
@ -44,6 +43,8 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
}
private lateinit var viewModel: StreamViewModel
private lateinit var binding: ActivityStreamBinding
private val uiVisibilityHandler = Handler()
override fun onCreate(savedInstanceState: Bundle?)
@ -58,63 +59,75 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
}
viewModel = ViewModelProvider(this, viewModelFactory {
StreamViewModel(Preferences(this), LogManager(this), connectInfo)
StreamViewModel(application, connectInfo)
})[StreamViewModel::class.java]
setContentView(R.layout.activity_stream)
viewModel.input.observe(this)
binding = ActivityStreamBinding.inflate(layoutInflater)
setContentView(binding.root)
window.decorView.setOnSystemUiVisibilityChangeListener(this)
viewModel.onScreenControlsEnabled.observe(this, Observer {
if(onScreenControlsSwitch.isChecked != it)
onScreenControlsSwitch.isChecked = it
if(onScreenControlsSwitch.isChecked)
touchpadOnlySwitch.isChecked = false
if(binding.onScreenControlsSwitch.isChecked != it)
binding.onScreenControlsSwitch.isChecked = it
if(binding.onScreenControlsSwitch.isChecked)
binding.touchpadOnlySwitch.isChecked = false
})
onScreenControlsSwitch.setOnCheckedChangeListener { _, isChecked ->
binding.onScreenControlsSwitch.setOnCheckedChangeListener { _, isChecked ->
viewModel.setOnScreenControlsEnabled(isChecked)
showOverlay()
}
viewModel.touchpadOnlyEnabled.observe(this, Observer {
if(touchpadOnlySwitch.isChecked != it)
touchpadOnlySwitch.isChecked = it
if(touchpadOnlySwitch.isChecked)
onScreenControlsSwitch.isChecked = false
if(binding.touchpadOnlySwitch.isChecked != it)
binding.touchpadOnlySwitch.isChecked = it
if(binding.touchpadOnlySwitch.isChecked)
binding.onScreenControlsSwitch.isChecked = false
})
touchpadOnlySwitch.setOnCheckedChangeListener { _, isChecked ->
binding.touchpadOnlySwitch.setOnCheckedChangeListener { _, isChecked ->
viewModel.setTouchpadOnlyEnabled(isChecked)
showOverlay()
}
displayModeToggle.addOnButtonCheckedListener { _, checkedId, _ ->
// following 'if' is a workaround until selectionRequired for MaterialButtonToggleGroup
// comes out of alpha.
// See https://stackoverflow.com/questions/56164004/required-single-selection-on-materialbuttontogglegroup
if (displayModeToggle.checkedButtonId == -1)
displayModeToggle.check(checkedId)
adjustTextureViewAspect()
binding.displayModeToggle.addOnButtonCheckedListener { _, _, _ ->
adjustStreamViewAspect()
showOverlay()
}
viewModel.session.attachToTextureView(textureView)
//viewModel.session.attachToTextureView(textureView)
viewModel.session.attachToSurfaceView(binding.surfaceView)
viewModel.session.state.observe(this, Observer { this.stateChanged(it) })
textureView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
adjustTextureViewAspect()
adjustStreamViewAspect()
if(Preferences(this).rumbleEnabled)
{
val vibrator = getSystemService(VIBRATOR_SERVICE) as Vibrator
viewModel.session.rumbleState.observe(this, Observer {
val amplitude = min(255, (it.left.toInt() + it.right.toInt()) / 2)
vibrator.cancel()
if(amplitude == 0)
return@Observer
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
vibrator.vibrate(VibrationEffect.createOneShot(1000, amplitude))
else
vibrator.vibrate(1000)
})
}
}
private val controlsDisposable = CompositeDisposable()
override fun onAttachFragment(fragment: Fragment)
{
super.onAttachFragment(fragment)
if(fragment is TouchControlsFragment)
{
fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
fragment.controllerState
.subscribe { viewModel.input.touchControllerState = it }
.addTo(controlsDisposable)
fragment.onScreenControlsEnabled = viewModel.onScreenControlsEnabled
}
if(fragment is TouchpadOnlyFragment)
{
fragment.controllerStateCallback = { viewModel.input.touchControllerState = it }
fragment.touchpadOnlyEnabled = viewModel.touchpadOnlyEnabled
}
}
@ -132,6 +145,12 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
viewModel.session.pause()
}
override fun onDestroy()
{
super.onDestroy()
controlsDisposable.dispose()
}
private fun reconnect()
{
viewModel.session.shutdown()
@ -150,14 +169,14 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
private fun showOverlay()
{
overlay.isVisible = true
overlay.animate()
binding.overlay.isVisible = true
binding.overlay.animate()
.alpha(1.0f)
.setListener(object: AnimatorListenerAdapter()
{
override fun onAnimationEnd(animation: Animator?)
override fun onAnimationEnd(animation: Animator)
{
overlay.alpha = 1.0f
binding.overlay.alpha = 1.0f
}
})
uiVisibilityHandler.removeCallbacks(hideSystemUIRunnable)
@ -166,13 +185,13 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
private fun hideOverlay()
{
overlay.animate()
binding.overlay.animate()
.alpha(0.0f)
.setListener(object: AnimatorListenerAdapter()
{
override fun onAnimationEnd(animation: Animator?)
override fun onAnimationEnd(animation: Animator)
{
overlay.isGone = true
binding.overlay.isGone = true
}
})
}
@ -205,13 +224,15 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
private fun stateChanged(state: StreamState)
{
progressBar.visibility = if(state == StreamStateConnecting) View.VISIBLE else View.GONE
binding.progressBar.visibility = if(state == StreamStateConnecting) View.VISIBLE else View.GONE
when(state)
{
is StreamStateQuit ->
{
if(!state.reason.isStopped && dialogContents != StreamQuitDialog)
if(dialogContents != StreamQuitDialog)
{
if(state.reason.isError)
{
dialog?.dismiss()
val reasonStr = state.reasonString
@ -234,6 +255,9 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
dialogContents = StreamQuitDialog
dialog.show()
}
else
finish()
}
}
is StreamStateCreateError ->
@ -287,77 +311,89 @@ class StreamActivity : AppCompatActivity(), View.OnSystemUiVisibilityChangeListe
dialog.show()
}
}
else -> {}
}
}
private fun adjustTextureViewAspect()
private fun adjustTextureViewAspect(textureView: TextureView)
{
val displayInfo = DisplayInfo(viewModel.session.connectInfo.videoProfile, textureView)
val resolution = displayInfo.computeResolutionFor(displayModeToggle.checkedButtonId)
val trans = TextureViewTransform(viewModel.session.connectInfo.videoProfile, textureView)
val resolution = trans.resolutionFor(TransformMode.fromButton(binding.displayModeToggle.checkedButtonId))
Matrix().also {
textureView.getTransform(it)
it.setScale(resolution.width / displayInfo.viewWidth, resolution.height / displayInfo.viewHeight)
it.postTranslate((displayInfo.viewWidth - resolution.width) * 0.5f, (displayInfo.viewHeight - resolution.height) * 0.5f)
it.setScale(resolution.width / trans.viewWidth, resolution.height / trans.viewHeight)
it.postTranslate((trans.viewWidth - resolution.width) * 0.5f, (trans.viewHeight - resolution.height) * 0.5f)
textureView.setTransform(it)
}
}
private fun adjustSurfaceViewAspect()
{
val videoProfile = viewModel.session.connectInfo.videoProfile
binding.aspectRatioLayout.aspectRatio = videoProfile.width.toFloat() / videoProfile.height.toFloat()
binding.aspectRatioLayout.mode = TransformMode.fromButton(binding.displayModeToggle.checkedButtonId)
}
private fun adjustStreamViewAspect() = adjustSurfaceViewAspect()
override fun dispatchKeyEvent(event: KeyEvent) = viewModel.input.dispatchKeyEvent(event) || super.dispatchKeyEvent(event)
override fun onGenericMotionEvent(event: MotionEvent) = viewModel.input.onGenericMotionEvent(event) || super.onGenericMotionEvent(event)
}
class DisplayInfo constructor(val videoProfile: ConnectVideoProfile, val textureView: TextureView)
enum class TransformMode
{
val contentWidth : Float get() = videoProfile.width.toFloat()
val contentHeight : Float get() = videoProfile.height.toFloat()
FIT,
STRETCH,
ZOOM;
companion object
{
fun fromButton(displayModeButtonId: Int)
= when (displayModeButtonId)
{
R.id.display_mode_stretch_button -> STRETCH
R.id.display_mode_zoom_button -> ZOOM
else -> FIT
}
}
}
class TextureViewTransform(private val videoProfile: ConnectVideoProfile, private val textureView: TextureView)
{
private val contentWidth : Float get() = videoProfile.width.toFloat()
private val contentHeight : Float get() = videoProfile.height.toFloat()
val viewWidth : Float get() = textureView.width.toFloat()
val viewHeight : Float get() = textureView.height.toFloat()
val contentAspect : Float get() = contentHeight / contentWidth
private val contentAspect : Float get() = contentHeight / contentWidth
fun computeResolutionFor(displayModeButtonId: Int) : Resolution
fun resolutionFor(mode: TransformMode): Resolution
= when(mode)
{
when (displayModeButtonId)
{
R.id.display_mode_stretch_button -> return computeStrechedResolution()
R.id.display_mode_zoom_button -> return computeZoomedResolution()
else -> return computeNormalResolution()
}
TransformMode.STRETCH -> strechedResolution
TransformMode.ZOOM -> zoomedResolution
TransformMode.FIT -> normalResolution
}
private fun computeStrechedResolution(): Resolution
{
return Resolution(viewWidth, viewHeight)
}
private val strechedResolution get() = Resolution(viewWidth, viewHeight)
private fun computeZoomedResolution(): Resolution
{
if (viewHeight > viewWidth * contentAspect)
private val zoomedResolution get() =
if(viewHeight > viewWidth * contentAspect)
{
val zoomFactor = viewHeight / contentHeight
return Resolution(contentWidth * zoomFactor, viewHeight)
Resolution(contentWidth * zoomFactor, viewHeight)
}
else
{
val zoomFactor = viewWidth / contentWidth
return Resolution(viewWidth, contentHeight * zoomFactor)
}
Resolution(viewWidth, contentHeight * zoomFactor)
}
private fun computeNormalResolution(): Resolution
{
if (viewHeight > viewWidth * contentAspect)
{
return Resolution(viewWidth, viewWidth * contentAspect)
}
private val normalResolution get() =
if(viewHeight > viewWidth * contentAspect)
Resolution(viewWidth, viewWidth * contentAspect)
else
{
return Resolution(viewHeight / contentAspect, viewHeight)
}
}
Resolution(viewHeight / contentAspect, viewHeight)
}

View file

@ -2,19 +2,22 @@
package com.metallic.chiaki.stream
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import android.app.Application
import android.content.Context
import androidx.lifecycle.*
import com.metallic.chiaki.common.LogManager
import com.metallic.chiaki.session.StreamSession
import com.metallic.chiaki.common.Preferences
import com.metallic.chiaki.lib.*
import com.metallic.chiaki.session.StreamInput
class StreamViewModel(val preferences: Preferences, val logManager: LogManager, val connectInfo: ConnectInfo): ViewModel()
class StreamViewModel(val application: Application, val connectInfo: ConnectInfo): ViewModel()
{
val preferences = Preferences(application)
val logManager = LogManager(application)
private var _session: StreamSession? = null
val input = StreamInput(preferences)
val input = StreamInput(application, preferences)
val session = StreamSession(connectInfo, logManager, preferences.logVerbose, input)
private var _onScreenControlsEnabled = MutableLiveData<Boolean>(preferences.onScreenControlsEnabled)

View file

@ -0,0 +1,29 @@
package com.metallic.chiaki.touchcontrols
import android.content.Context
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import androidx.appcompat.app.AppCompatActivity
import com.metallic.chiaki.common.Preferences
class ButtonHaptics(val context: Context)
{
private val enabled = Preferences(context).buttonHapticEnabled
fun trigger(harder: Boolean = false)
{
if(!enabled)
return
val vibrator = context.getSystemService(AppCompatActivity.VIBRATOR_SERVICE) as Vibrator
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
vibrator.vibrate(
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
VibrationEffect.createPredefined(if(harder) VibrationEffect.EFFECT_CLICK else VibrationEffect.EFFECT_TICK)
else
VibrationEffect.createOneShot(10, if(harder) 200 else 100)
)
else
vibrator.vibrate(10)
}
}

View file

@ -6,14 +6,19 @@ import android.content.Context
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.core.view.children
import com.metallic.chiaki.R
class ButtonView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr)
{
private val haptics = ButtonHaptics(context)
var buttonPressed = false
private set(value)
{
@ -21,6 +26,8 @@ class ButtonView @JvmOverloads constructor(
field = value
if(diff)
{
if(value)
haptics.trigger()
invalidate()
buttonPressedCallback?.let { it(field) }
}
@ -46,16 +53,39 @@ class ButtonView @JvmOverloads constructor(
{
super.onDraw(canvas)
val drawable = if(buttonPressed) drawablePressed else drawableIdle
drawable?.setBounds(0, 0, width, height)
drawable?.setBounds(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom)
drawable?.draw(canvas)
}
/**
* If this button overlaps with others in the same layout,
* let the one whose center is closest to the touch handle it.
*/
private fun bestFittingTouchView(x: Float, y: Float): View
{
val loc = locationOnScreen + Vector(x, y)
return (parent as? ViewGroup)?.children?.filter {
it is ButtonView
}?.filter {
val pos = it.locationOnScreen
loc.x >= pos.x && loc.x < pos.x + it.width && loc.y >= pos.y && loc.y < pos.y + it.height
}?.sortedBy {
(loc - (it.locationOnScreen + Vector(it.width.toFloat(), it.height.toFloat()) * 0.5f)).lengthSq
}?.firstOrNull() ?: this
}
override fun onTouchEvent(event: MotionEvent): Boolean
{
when(event.action)
when(event.actionMasked)
{
MotionEvent.ACTION_DOWN -> buttonPressed = true
MotionEvent.ACTION_UP -> buttonPressed = false
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
if(bestFittingTouchView(event.getX(event.actionIndex), event.getY(event.actionIndex)) != this)
return false
buttonPressed = true
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
buttonPressed = false
}
}
return true
}

View file

@ -16,6 +16,8 @@ class DPadView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr)
{
private val haptics = ButtonHaptics(context)
enum class Direction {
LEFT,
RIGHT,
@ -71,7 +73,7 @@ class DPadView @JvmOverloads constructor(
else
drawable = dpadIdleDrawable
drawable?.setBounds(0, 0, width, height)
drawable?.setBounds(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom)
//drawable?.alpha = 127
drawable?.draw(canvas)
}
@ -113,6 +115,8 @@ class DPadView @JvmOverloads constructor(
if(state != newState)
{
if(newState != null)
haptics.trigger()
state = newState
invalidate()
stateChangeCallback?.let { it(state) }

View file

@ -3,74 +3,97 @@
package com.metallic.chiaki.touchcontrols
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import com.metallic.chiaki.R
import com.metallic.chiaki.databinding.FragmentControlsBinding
import com.metallic.chiaki.lib.ControllerState
import kotlinx.android.synthetic.main.fragment_controls.*
import io.reactivex.Observable
import io.reactivex.rxkotlin.Observables.combineLatest
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.Subject
class TouchControlsFragment : Fragment()
abstract class TouchControlsFragment : Fragment()
{
private var controllerState = ControllerState()
private set(value)
protected var ownControllerState = ControllerState()
set(value)
{
val diff = field != value
field = value
if(diff)
controllerStateCallback?.let { it(value) }
ownControllerStateSubject.onNext(ownControllerState)
}
var controllerStateCallback: ((ControllerState) -> Unit)? = null
var onScreenControlsEnabled: LiveData<Boolean>? = null
protected val ownControllerStateSubject: Subject<ControllerState>
= BehaviorSubject.create<ControllerState>().also { it.onNext(ownControllerState) }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
= inflater.inflate(R.layout.fragment_controls, container, false)
// to delay attaching to the touchpadView until it's available
protected val controllerStateProxy: Subject<Observable<ControllerState>>
= BehaviorSubject.create<Observable<ControllerState>>().also { it.onNext(ownControllerStateSubject) }
val controllerState: Observable<ControllerState> get() =
controllerStateProxy.flatMap { it }
var onScreenControlsEnabled: LiveData<Boolean>? = null
}
class DefaultTouchControlsFragment : TouchControlsFragment()
{
private var _binding: FragmentControlsBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
FragmentControlsBinding.inflate(inflater, container, false).let {
_binding = it
controllerStateProxy.onNext(
combineLatest(ownControllerStateSubject, binding.touchpadView.controllerState) { a, b -> a or b }
)
it.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
{
super.onViewCreated(view, savedInstanceState)
dpadView.stateChangeCallback = this::dpadStateChanged
crossButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_CROSS)
moonButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_MOON)
pyramidButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PYRAMID)
boxButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_BOX)
l1ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_L1)
r1ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_R1)
optionsButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_OPTIONS)
shareButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_SHARE)
psButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PS)
touchpadButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_TOUCHPAD)
binding.dpadView.stateChangeCallback = this::dpadStateChanged
binding.crossButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_CROSS)
binding.moonButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_MOON)
binding.pyramidButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PYRAMID)
binding.boxButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_BOX)
binding.l1ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_L1)
binding.r1ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_R1)
binding.l3ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_L3)
binding.r3ButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_R3)
binding.optionsButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_OPTIONS)
binding.shareButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_SHARE)
binding.psButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_PS)
l2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { l2State = if(it) 255U else 0U } }
r2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { r2State = if(it) 255U else 0U } }
binding.l2ButtonView.buttonPressedCallback = { ownControllerState = ownControllerState.copy().apply { l2State = if(it) 255U else 0U } }
binding.r2ButtonView.buttonPressedCallback = { ownControllerState = ownControllerState.copy().apply { r2State = if(it) 255U else 0U } }
val quantizeStick = { f: Float ->
(Short.MAX_VALUE * f).toShort()
(Short.MAX_VALUE * f).toInt().toShort()
}
leftAnalogStickView.stateChangedCallback = { controllerState = controllerState.copy().apply {
binding.leftAnalogStickView.stateChangedCallback = { ownControllerState = ownControllerState.copy().apply {
leftX = quantizeStick(it.x)
leftY = quantizeStick(it.y)
}}
rightAnalogStickView.stateChangedCallback = { controllerState = controllerState.copy().apply {
binding.rightAnalogStickView.stateChangedCallback = { ownControllerState = ownControllerState.copy().apply {
rightX = quantizeStick(it.x)
rightY = quantizeStick(it.y)
}}
onScreenControlsEnabled?.observe(this, Observer {
onScreenControlsEnabled?.observe(viewLifecycleOwner, Observer {
view.visibility = if(it) View.VISIBLE else View.GONE
})
}
private fun dpadStateChanged(direction: DPadView.Direction?)
{
controllerState = controllerState.copy().apply {
ownControllerState = ownControllerState.copy().apply {
buttons = ((buttons
and ControllerState.BUTTON_DPAD_LEFT.inv()
and ControllerState.BUTTON_DPAD_RIGHT.inv()
@ -92,7 +115,7 @@ class TouchControlsFragment : Fragment()
}
private fun buttonStateChanged(buttonMask: UInt) = { pressed: Boolean ->
controllerState = controllerState.copy().apply {
ownControllerState = ownControllerState.copy().apply {
buttons =
if(pressed)
buttons or buttonMask

View file

@ -26,7 +26,7 @@ class TouchTracker
if(pointerId == null)
{
pointerId = event.getPointerId(event.actionIndex)
currentPosition = Vector(event.x, event.y)
currentPosition = Vector(event.getX(event.actionIndex), event.getY(event.actionIndex))
}
}

View file

@ -6,49 +6,42 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import com.metallic.chiaki.R
import com.metallic.chiaki.lib.ControllerState
import kotlinx.android.synthetic.main.fragment_controls.*
import com.metallic.chiaki.databinding.FragmentTouchpadOnlyBinding
import io.reactivex.rxkotlin.Observables.combineLatest
class TouchpadOnlyFragment : Fragment()
class TouchpadOnlyFragment : TouchControlsFragment()
{
private var controllerState = ControllerState()
private set(value)
{
val diff = field != value
field = value
if(diff)
controllerStateCallback?.let { it(value) }
}
var controllerStateCallback: ((ControllerState) -> Unit)? = null
var touchpadOnlyEnabled: LiveData<Boolean>? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
= inflater.inflate(R.layout.fragment_touchpad_only, container, false)
private var _binding: FragmentTouchpadOnlyBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
FragmentTouchpadOnlyBinding.inflate(inflater, container, false).let {
_binding = it
controllerStateProxy.onNext(
combineLatest(ownControllerStateSubject, binding.touchpadView.controllerState) { a, b -> a or b }
)
it.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
{
super.onViewCreated(view, savedInstanceState)
touchpadButtonView.buttonPressedCallback = buttonStateChanged(ControllerState.BUTTON_TOUCHPAD)
touchpadOnlyEnabled?.observe(this, Observer {
touchpadOnlyEnabled?.observe(viewLifecycleOwner, Observer {
view.visibility = if(it) View.VISIBLE else View.GONE
})
}
private fun buttonStateChanged(buttonMask: UInt) = { pressed: Boolean ->
controllerState = controllerState.copy().apply {
ownControllerState = ownControllerState.copy().apply {
buttons =
if(pressed)
buttons or buttonMask
else
buttons and buttonMask.inv()
}
}
}

View file

@ -0,0 +1,168 @@
// SPDX-License-Identifier: LicenseRef-AGPL-3.0-only-OpenSSL
package com.metallic.chiaki.touchcontrols
import android.content.Context
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.metallic.chiaki.R
import com.metallic.chiaki.lib.ControllerState
import io.reactivex.Observable
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.Subject
import kotlin.math.max
class TouchpadView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr)
{
companion object
{
private const val BUTTON_PRESS_MAX_MOVE_DIST_DP = 32.0f
private const val SHORT_BUTTON_PRESS_DURATION_MS = 200L
private const val BUTTON_HOLD_DELAY_MS = 500L
}
private val haptics = ButtonHaptics(context)
private val drawableIdle: Drawable?
private val drawablePressed: Drawable?
private val state: ControllerState = ControllerState()
inner class Touch(
val stateId: UByte,
private val startX: Float,
private val startY: Float)
{
var lifted = false // will be true but touch still in list when only relevant for short touch
private var maxDist: Float = 0.0f
val moveInsignificant: Boolean get() = maxDist < BUTTON_PRESS_MAX_MOVE_DIST_DP
fun onMove(x: Float, y: Float)
{
val d = (Vector(x, y) - Vector(startX, startY)).length / resources.displayMetrics.density
maxDist = max(d, maxDist)
}
val startButtonHoldRunnable = Runnable {
if(!moveInsignificant || buttonHeld)
return@Runnable
haptics.trigger(true)
state.buttons = state.buttons or ControllerState.BUTTON_TOUCHPAD
buttonHeld = true
triggerStateChanged()
}
}
private val pointerTouches = mutableMapOf<Int, Touch>()
private val stateSubject: Subject<ControllerState>
= BehaviorSubject.create<ControllerState>().also { it.onNext(state) }
val controllerState: Observable<ControllerState> get() = stateSubject
private var shortPressingTouches = listOf<Touch>()
private val shortButtonPressLiftRunnable = Runnable {
state.buttons = state.buttons and ControllerState.BUTTON_TOUCHPAD.inv()
shortPressingTouches.forEach {
state.stopTouch(it.stateId)
}
shortPressingTouches = listOf()
triggerStateChanged()
}
private var buttonHeld = false
init
{
context.theme.obtainStyledAttributes(attrs, R.styleable.TouchpadView, 0, 0).apply {
drawableIdle = getDrawable(R.styleable.TouchpadView_drawableIdle)
drawablePressed = getDrawable(R.styleable.TouchpadView_drawablePressed)
recycle()
}
isClickable = true
}
override fun onDraw(canvas: Canvas)
{
super.onDraw(canvas)
if(pointerTouches.values.find { !it.lifted } == null)
return
val drawable = if(state.buttons and ControllerState.BUTTON_TOUCHPAD != 0U) drawablePressed else drawableIdle
drawable?.setBounds(paddingLeft, paddingTop, width - paddingRight, height - paddingBottom)
drawable?.draw(canvas)
}
private fun touchX(event: MotionEvent, index: Int): UShort =
maxOf(0U.toUShort(), minOf((ControllerState.TOUCHPAD_WIDTH - 1u).toUShort(),
(ControllerState.TOUCHPAD_WIDTH.toFloat() * event.getX(index) / width.toFloat()).toUInt().toUShort()))
private fun touchY(event: MotionEvent, index: Int): UShort =
maxOf(0U.toUShort(), minOf((ControllerState.TOUCHPAD_HEIGHT - 1u).toUShort(),
(ControllerState.TOUCHPAD_HEIGHT.toFloat() * event.getY(index) / height.toFloat()).toUInt().toUShort()))
override fun onTouchEvent(event: MotionEvent): Boolean
{
when(event.actionMasked)
{
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
state.startTouch(touchX(event, event.actionIndex), touchY(event, event.actionIndex))?.let {
haptics.trigger()
val touch = Touch(it, event.getX(event.actionIndex), event.getY(event.actionIndex))
pointerTouches[event.getPointerId(event.actionIndex)] = touch
if(!buttonHeld)
postDelayed(touch.startButtonHoldRunnable, BUTTON_HOLD_DELAY_MS)
triggerStateChanged()
}
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
pointerTouches.remove(event.getPointerId(event.actionIndex))?.let {
removeCallbacks(it.startButtonHoldRunnable)
when
{
buttonHeld ->
{
buttonHeld = false
state.buttons = state.buttons and ControllerState.BUTTON_TOUCHPAD.inv()
state.stopTouch(it.stateId)
}
it.moveInsignificant -> triggerShortButtonPress(it)
else -> state.stopTouch(it.stateId)
}
triggerStateChanged()
}
}
MotionEvent.ACTION_MOVE -> {
val changed = pointerTouches.entries.fold(false) { acc, it ->
val index = event.findPointerIndex(it.key)
if(index < 0)
acc
else
{
it.value.onMove(event.getX(event.actionIndex), event.getY(event.actionIndex))
acc || state.setTouchPos(it.value.stateId, touchX(event, index), touchY(event, index))
}
}
if(changed)
triggerStateChanged()
}
}
return true
}
private fun triggerShortButtonPress(touch: Touch)
{
shortPressingTouches = shortPressingTouches + listOf(touch)
removeCallbacks(shortButtonPressLiftRunnable)
state.buttons = state.buttons or ControllerState.BUTTON_TOUCHPAD
postDelayed(shortButtonPressLiftRunnable, SHORT_BUTTON_PRESS_DURATION_MS)
}
private fun triggerStateChanged()
{
invalidate()
stateSubject.onNext(state)
}
}

View file

@ -2,6 +2,7 @@
package com.metallic.chiaki.touchcontrols
import android.view.View
import kotlin.math.sqrt
data class Vector(val x: Float, val y: Float)
@ -19,3 +20,9 @@ data class Vector(val x: Float, val y: Float)
val length get() = sqrt(lengthSq)
val normalized get() = this / length
}
val View.locationOnScreen: Vector get() {
val v = intArrayOf(0, 0)
this.getLocationOnScreen(v)
return Vector(v[0].toFloat(), v[1].toFloat())
}

View file

@ -1,13 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="128dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="33.86667">
android:viewportHeight="67.73334">
<path
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM21.7852,8.2563l2.3337,0L24.1189,23.6745L31.89,23.6745L31.89,25.8299L21.7852,25.8299ZM40.6771,8.2563l2.3342,0L43.0113,25.8299l-2.3342,0L40.6771,11.5512l-3.1853,3.1853 -1.6474,-1.6474z"
android:pathData="M39.2255,0 L0,39.2255 20.3838,59.6093a27.7366,27.7366 88.4859,0 0,39.2255 0,27.7366 27.7366,88.4859 0,0 0,-39.2255zM25.4129,29.4287l2.7735,0l0,18.1643l9.9818,0l0,2.3342L25.4129,49.9272ZM46.3098,29.4287l2.7735,0l0,18.1643l4.5305,0l0,2.3342l-11.8076,0l0,-2.3342l4.531,0l0,-15.6383l-4.9289,0.9886l0,-2.5259z"
android:strokeLineJoin="round"
android:strokeWidth="23.7399"
android:strokeWidth="5.54109"
android:fillColor="@color/control_primary"
android:strokeColor="#00000000"
android:strokeLineCap="round"/>
</vector>

View file

@ -1,13 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="128dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="33.86667">
android:viewportHeight="67.73334">
<path
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM21.7852,8.2563l2.3337,0L24.1189,23.6745L31.89,23.6745L31.89,25.8299L21.7852,25.8299ZM40.6771,8.2563l2.3342,0L43.0113,25.8299l-2.3342,0L40.6771,11.5512l-3.1853,3.1853 -1.6474,-1.6474z"
android:pathData="M39.2255,0 L0,39.2255 20.3838,59.6093a27.7366,27.7366 88.4859,0 0,39.2255 0,27.7366 27.7366,88.4859 0,0 0,-39.2255zM25.4129,29.4287l2.7735,0l0,18.1643l9.9818,0l0,2.3342L25.4129,49.9272ZM46.3098,29.4287l2.7735,0l0,18.1643l4.5305,0l0,2.3342l-11.8076,0l0,-2.3342l4.531,0l0,-15.6383l-4.9289,0.9886l0,-2.5259z"
android:strokeLineJoin="round"
android:strokeWidth="23.7399"
android:strokeWidth="5.54109"
android:fillColor="@color/control_pressed"
android:strokeColor="#00000000"
android:strokeLineCap="round"/>
</vector>

View file

@ -1,13 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="128dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="33.86667">
android:viewportHeight="67.73334">
<path
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM40.6771,8.0367c1.556,0 2.7872,0.3659 3.6933,1.0981 0.9062,0.7322 1.3591,1.7348 1.3591,3.0071 0,0.4027 -0.0139,0.7136 -0.0413,0.9333 -0.0183,0.2105 -0.0867,0.4989 -0.2057,0.8651 -0.1098,0.357 -0.3021,0.7506 -0.5767,1.1808 -0.2746,0.421 -0.6453,0.9242 -1.1121,1.51l-5.6291,7.0435l7.7845,0L45.9491,25.8299l-10.5441,0l0,-2.1554l6.6999,-8.3886c0.4119,-0.5126 0.7142,-0.9612 0.9064,-1.3457 0.1922,-0.3936 0.3017,-0.6911 0.3292,-0.8925 0.0366,-0.2105 0.0548,-0.5123 0.0548,-0.9059 0,-0.6041 -0.2425,-1.0805 -0.7276,-1.4283 -0.4851,-0.3478 -1.1485,-0.5214 -1.9906,-0.5214 -0.8421,0 -1.506,0.1736 -1.9911,0.5214 -0.4851,0.3478 -0.7276,0.8242 -0.7276,1.4283l-2.3337,-0.371c0,-1.1533 0.4529,-2.064 1.3591,-2.7321 0.9062,-0.6682 2.1373,-1.002 3.6933,-1.002zM21.7852,8.2563l2.3337,0L24.1189,23.6745L31.89,23.6745L31.89,25.8299L21.7852,25.8299Z"
android:pathData="m27.6526,0a27.7366,27.7366 88.4859,0 0,-19.5285 8.1241,27.7366 27.7366,88.4859 0,0 0,39.2255L28.5078,67.7333 67.7333,28.5078 47.3496,8.1241A27.7366,27.7366 88.4859,0 0,27.6526 0ZM34.9017,15.1846c2.1235,0 3.8172,0.5309 5.0803,1.5927 1.2631,1.0618 1.8945,2.4804 1.8945,4.2561 0,0.8421 -0.1602,1.6432 -0.4806,2.403 -0.3112,0.7506 -0.8832,1.6383 -1.7162,2.6634 -0.2288,0.2654 -0.9563,1.034 -2.1828,2.3063 -1.2265,1.2631 -2.9565,3.0342 -5.1899,5.3134l9.6795,0l0,2.3342l-13.0157,0l0,-2.3342c1.0526,-1.0892 2.4851,-2.549 4.2974,-4.3796 1.8215,-1.8398 2.9655,-3.025 3.4323,-3.5559 0.8879,-0.9977 1.5058,-1.84 1.8536,-2.5265 0.357,-0.6956 0.5354,-1.3777 0.5354,-2.0459 0,-1.0892 -0.3846,-1.9769 -1.1534,-2.6634 -0.7597,-0.6865 -1.7526,-1.0299 -2.9791,-1.0299 -0.8695,-0 -1.7898,0.1511 -2.76,0.4532 -0.9611,0.3021 -1.9908,0.7598 -3.0892,1.373L29.1078,16.5437c1.1167,-0.4485 2.1603,-0.7871 3.1306,-1.016 0.9702,-0.2288 1.8579,-0.3431 2.6634,-0.3431zM14.0053,15.5551l2.7735,0l0,18.1643l9.9813,0l0,2.3342L14.0053,36.0536Z"
android:strokeLineJoin="round"
android:strokeWidth="23.7399"
android:strokeWidth="5.54109"
android:fillColor="@color/control_primary"
android:strokeColor="#00000000"
android:strokeLineCap="round"/>
</vector>

View file

@ -1,13 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="128dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="33.86667">
android:viewportHeight="67.73334">
<path
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM40.6771,8.0367c1.556,0 2.7872,0.3659 3.6933,1.0981 0.9062,0.7322 1.3591,1.7348 1.3591,3.0071 0,0.4027 -0.0139,0.7136 -0.0413,0.9333 -0.0183,0.2105 -0.0867,0.4989 -0.2057,0.8651 -0.1098,0.357 -0.3021,0.7506 -0.5767,1.1808 -0.2746,0.421 -0.6453,0.9242 -1.1121,1.51l-5.6291,7.0435l7.7845,0L45.9491,25.8299l-10.5441,0l0,-2.1554l6.6999,-8.3886c0.4119,-0.5126 0.7142,-0.9612 0.9064,-1.3457 0.1922,-0.3936 0.3017,-0.6911 0.3292,-0.8925 0.0366,-0.2105 0.0548,-0.5123 0.0548,-0.9059 0,-0.6041 -0.2425,-1.0805 -0.7276,-1.4283 -0.4851,-0.3478 -1.1485,-0.5214 -1.9906,-0.5214 -0.8421,0 -1.506,0.1736 -1.9911,0.5214 -0.4851,0.3478 -0.7276,0.8242 -0.7276,1.4283l-2.3337,-0.371c0,-1.1533 0.4529,-2.064 1.3591,-2.7321 0.9062,-0.6682 2.1373,-1.002 3.6933,-1.002zM21.7852,8.2563l2.3337,0L24.1189,23.6745L31.89,23.6745L31.89,25.8299L21.7852,25.8299Z"
android:pathData="m27.6526,0a27.7366,27.7366 88.4859,0 0,-19.5285 8.1241,27.7366 27.7366,88.4859 0,0 0,39.2255L28.5078,67.7333 67.7333,28.5078 47.3496,8.1241A27.7366,27.7366 88.4859,0 0,27.6526 0ZM34.9017,15.1846c2.1235,0 3.8172,0.5309 5.0803,1.5927 1.2631,1.0618 1.8945,2.4804 1.8945,4.2561 0,0.8421 -0.1602,1.6432 -0.4806,2.403 -0.3112,0.7506 -0.8832,1.6383 -1.7162,2.6634 -0.2288,0.2654 -0.9563,1.034 -2.1828,2.3063 -1.2265,1.2631 -2.9565,3.0342 -5.1899,5.3134l9.6795,0l0,2.3342l-13.0157,0l0,-2.3342c1.0526,-1.0892 2.4851,-2.549 4.2974,-4.3796 1.8215,-1.8398 2.9655,-3.025 3.4323,-3.5559 0.8879,-0.9977 1.5058,-1.84 1.8536,-2.5265 0.357,-0.6956 0.5354,-1.3777 0.5354,-2.0459 0,-1.0892 -0.3846,-1.9769 -1.1534,-2.6634 -0.7597,-0.6865 -1.7526,-1.0299 -2.9791,-1.0299 -0.8695,-0 -1.7898,0.1511 -2.76,0.4532 -0.9611,0.3021 -1.9908,0.7598 -3.0892,1.373L29.1078,16.5437c1.1167,-0.4485 2.1603,-0.7871 3.1306,-1.016 0.9702,-0.2288 1.8579,-0.3431 2.6634,-0.3431zM14.0053,15.5551l2.7735,0l0,18.1643l9.9813,0l0,2.3342L14.0053,36.0536Z"
android:strokeLineJoin="round"
android:strokeWidth="23.7399"
android:strokeWidth="5.54109"
android:fillColor="@color/control_pressed"
android:strokeColor="#00000000"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="67.73334">
<path
android:pathData="M0,0 L-41.939,102.093 67.7333,67.7333C67.7333,30.3253 37.4081,0 0,0ZM33.6093,31.3252c2.1052,0 3.7711,0.4807 4.9976,1.4418 1.2265,0.9519 1.8397,2.2423 1.8397,3.8716 0,1.135 -0.3253,2.0964 -0.9751,2.8835 -0.6499,0.778 -1.5739,1.318 -2.773,1.6201 1.3272,0.2837 2.3613,0.8739 3.1027,1.771 0.7506,0.897 1.126,2.0047 1.126,3.3228 0,2.0228 -0.6959,3.5878 -2.0872,4.6953 -1.3913,1.1075 -3.368,1.6614 -5.9309,1.6614 -0.8604,0 -1.7486,-0.0871 -2.6639,-0.261 -0.9062,-0.1648 -1.8441,-0.4163 -2.8143,-0.755l0,-2.6774c0.7689,0.4485 1.6106,0.7871 2.5259,1.016 0.9153,0.2288 1.8719,0.3431 2.8696,0.3431 1.7391,0 3.0621,-0.3434 3.9682,-1.0299 0.9153,-0.6865 1.3725,-1.6837 1.3725,-2.9926 0,-1.2082 -0.4252,-2.1514 -1.2764,-2.8288 -0.8421,-0.6865 -2.0187,-1.0294 -3.529,-1.0294l-2.3885,0l0,-2.2794l2.4986,0c1.3638,0 2.4075,-0.2697 3.1306,-0.8098 0.7231,-0.5492 1.0847,-1.3365 1.0847,-2.3616 0,-1.0526 -0.3755,-1.858 -1.126,-2.4164 -0.7414,-0.5675 -1.808,-0.8511 -3.1993,-0.8511 -0.7597,0 -1.5742,0.0823 -2.4438,0.247 -0.8695,0.1648 -1.8261,0.4211 -2.8696,0.7689l0,-2.4717c1.0526,-0.2929 2.037,-0.5124 2.9523,-0.6589 0.9245,-0.1464 1.794,-0.2196 2.6086,-0.2196zM12.3832,31.6957l2.7735,0l0,18.1648l9.9813,0l0,2.3337L12.3832,52.1942Z"
android:strokeLineJoin="round"
android:strokeWidth="11.0688"
android:fillColor="@color/control_primary"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="67.73334">
<path
android:pathData="M0,0 L-41.939,102.093 67.7333,67.7333C67.7333,30.3253 37.4081,0 0,0ZM33.6093,31.3252c2.1052,0 3.7711,0.4807 4.9976,1.4418 1.2265,0.9519 1.8397,2.2423 1.8397,3.8716 0,1.135 -0.3253,2.0964 -0.9751,2.8835 -0.6499,0.778 -1.5739,1.318 -2.773,1.6201 1.3272,0.2837 2.3613,0.8739 3.1027,1.771 0.7506,0.897 1.126,2.0047 1.126,3.3228 0,2.0228 -0.6959,3.5878 -2.0872,4.6953 -1.3913,1.1075 -3.368,1.6614 -5.9309,1.6614 -0.8604,0 -1.7486,-0.0871 -2.6639,-0.261 -0.9062,-0.1648 -1.8441,-0.4163 -2.8143,-0.755l0,-2.6774c0.7689,0.4485 1.6106,0.7871 2.5259,1.016 0.9153,0.2288 1.8719,0.3431 2.8696,0.3431 1.7391,0 3.0621,-0.3434 3.9682,-1.0299 0.9153,-0.6865 1.3725,-1.6837 1.3725,-2.9926 0,-1.2082 -0.4252,-2.1514 -1.2764,-2.8288 -0.8421,-0.6865 -2.0187,-1.0294 -3.529,-1.0294l-2.3885,0l0,-2.2794l2.4986,0c1.3638,0 2.4075,-0.2697 3.1306,-0.8098 0.7231,-0.5492 1.0847,-1.3365 1.0847,-2.3616 0,-1.0526 -0.3755,-1.858 -1.126,-2.4164 -0.7414,-0.5675 -1.808,-0.8511 -3.1993,-0.8511 -0.7597,0 -1.5742,0.0823 -2.4438,0.247 -0.8695,0.1648 -1.8261,0.4211 -2.8696,0.7689l0,-2.4717c1.0526,-0.2929 2.037,-0.5124 2.9523,-0.6589 0.9245,-0.1464 1.794,-0.2196 2.6086,-0.2196zM12.3832,31.6957l2.7735,0l0,18.1648l9.9813,0l0,2.3337L12.3832,52.1942Z"
android:strokeLineJoin="round"
android:strokeWidth="11.0688"
android:fillColor="@color/control_pressed"
android:strokeLineCap="round"/>
</vector>

View file

@ -1,13 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="128dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="33.86667">
android:viewportHeight="67.73334">
<path
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM21.3455,8.2563L27.4417,8.2563c1.5286,0 2.6865,0.352 3.4737,1.0568 0.7963,0.7048 1.1942,1.8814 1.1942,3.529 -0,2.2517 -1.034,3.6703 -3.1027,4.2561L32.7685,25.8299L30.2286,25.8299L26.618,17.4413L23.6797,17.4413L23.6797,25.8299l-2.3342,0zM40.6771,8.2563l2.3342,0L43.0113,25.8299l-2.3342,0L40.6771,11.5512l-3.1853,3.1853 -1.6474,-1.6474zM23.6797,10.4118l0,4.8741l2.3342,0c1.2265,0 2.1601,-0.188 2.8009,-0.5633 0.6407,-0.3753 0.9607,-1.0018 0.9607,-1.8805 0,-0.8695 -0.2014,-1.4923 -0.6041,-1.8676 -0.3936,-0.3753 -0.9699,-0.5628 -1.7296,-0.5628z"
android:pathData="M28.5078,0 L8.1241,20.3838a27.7366,27.7366 88.4859,0 0,0 39.2255,27.7366 27.7366,88.4859 0,0 39.2255,0L67.7333,39.2255ZM12.4178,29.4287l6.2606,0c2.3432,0 4.0913,0.4898 5.2446,1.4692 1.1533,0.9794 1.7301,2.4573 1.7301,4.4344 0,1.2906 -0.3018,2.3615 -0.9059,3.2127 -0.595,0.8512 -1.4645,1.442 -2.6086,1.7715 0.595,0.2014 1.1713,0.6313 1.7296,1.2904 0.5675,0.659 1.1352,1.5654 1.7027,2.7187L28.3853,49.9272L25.4062,49.9272l-2.6226,-5.2586C22.1063,43.2956 21.4472,42.385 20.8065,41.9365c-0.6316,-0.4485 -1.4963,-0.6728 -2.5947,-0.6728L15.1913,41.2636l0,8.6636L12.4178,49.9272ZM37.1864,29.4287l2.7735,0l0,18.1643l4.5305,0l0,2.3342l-11.8075,0l0,-2.3342l4.531,0l0,-15.6383l-4.9289,0.9886l0,-2.5259zM15.1913,31.7076l0,7.2766l3.4871,0c1.3364,0 2.3432,-0.3066 3.0205,-0.9198 0.6865,-0.6224 1.0299,-1.5331 1.0299,-2.7321 0,-1.1991 -0.3434,-2.1006 -1.0299,-2.7047 -0.6773,-0.6133 -1.6841,-0.9198 -3.0205,-0.9198z"
android:strokeLineJoin="round"
android:strokeWidth="23.7399"
android:strokeWidth="5.54109"
android:fillColor="@color/control_primary"
android:strokeColor="#00000000"
android:strokeLineCap="round"/>
</vector>

View file

@ -1,13 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="128dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="33.86667">
android:viewportHeight="67.73334">
<path
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM21.3455,8.2563L27.4417,8.2563c1.5286,0 2.6865,0.352 3.4737,1.0568 0.7963,0.7048 1.1942,1.8814 1.1942,3.529 -0,2.2517 -1.034,3.6703 -3.1027,4.2561L32.7685,25.8299L30.2286,25.8299L26.618,17.4413L23.6797,17.4413L23.6797,25.8299l-2.3342,0zM40.6771,8.2563l2.3342,0L43.0113,25.8299l-2.3342,0L40.6771,11.5512l-3.1853,3.1853 -1.6474,-1.6474zM23.6797,10.4118l0,4.8741l2.3342,0c1.2265,0 2.1601,-0.188 2.8009,-0.5633 0.6407,-0.3753 0.9607,-1.0018 0.9607,-1.8805 0,-0.8695 -0.2014,-1.4923 -0.6041,-1.8676 -0.3936,-0.3753 -0.9699,-0.5628 -1.7296,-0.5628z"
android:pathData="M28.5078,0 L8.1241,20.3838a27.7366,27.7366 88.4859,0 0,0 39.2255,27.7366 27.7366,88.4859 0,0 39.2255,0L67.7333,39.2255ZM12.4178,29.4287l6.2606,0c2.3432,0 4.0913,0.4898 5.2446,1.4692 1.1533,0.9794 1.7301,2.4573 1.7301,4.4344 0,1.2906 -0.3018,2.3615 -0.9059,3.2127 -0.595,0.8512 -1.4645,1.442 -2.6086,1.7715 0.595,0.2014 1.1713,0.6313 1.7296,1.2904 0.5675,0.659 1.1352,1.5654 1.7027,2.7187L28.3853,49.9272L25.4062,49.9272l-2.6226,-5.2586C22.1063,43.2956 21.4472,42.385 20.8065,41.9365c-0.6316,-0.4485 -1.4963,-0.6728 -2.5947,-0.6728L15.1913,41.2636l0,8.6636L12.4178,49.9272ZM37.1864,29.4287l2.7735,0l0,18.1643l4.5305,0l0,2.3342l-11.8075,0l0,-2.3342l4.531,0l0,-15.6383l-4.9289,0.9886l0,-2.5259zM15.1913,31.7076l0,7.2766l3.4871,0c1.3364,0 2.3432,-0.3066 3.0205,-0.9198 0.6865,-0.6224 1.0299,-1.5331 1.0299,-2.7321 0,-1.1991 -0.3434,-2.1006 -1.0299,-2.7047 -0.6773,-0.6133 -1.6841,-0.9198 -3.0205,-0.9198z"
android:strokeLineJoin="round"
android:strokeWidth="23.7399"
android:strokeWidth="5.54109"
android:fillColor="@color/control_pressed"
android:strokeColor="#00000000"
android:strokeLineCap="round"/>
</vector>

View file

@ -1,13 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="128dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="33.86667">
android:viewportHeight="67.73334">
<path
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM40.6771,8.0367c1.556,0 2.7872,0.3659 3.6933,1.0981 0.9062,0.7322 1.3591,1.7348 1.3591,3.0071 0,0.4027 -0.0139,0.7136 -0.0413,0.9333 -0.0183,0.2105 -0.0867,0.4989 -0.2057,0.8651 -0.1098,0.357 -0.3021,0.7506 -0.5767,1.1808 -0.2746,0.421 -0.6453,0.9242 -1.1121,1.51l-5.6291,7.0435l7.7845,0L45.9491,25.8299l-10.5441,0l0,-2.1554l6.6999,-8.3886c0.4119,-0.5126 0.7142,-0.9612 0.9064,-1.3457 0.1922,-0.3936 0.3017,-0.6911 0.3292,-0.8925 0.0366,-0.2105 0.0548,-0.5123 0.0548,-0.9059 0,-0.6041 -0.2425,-1.0805 -0.7276,-1.4283 -0.4851,-0.3478 -1.1485,-0.5214 -1.9906,-0.5214 -0.8421,0 -1.506,0.1736 -1.9911,0.5214 -0.4851,0.3478 -0.7276,0.8242 -0.7276,1.4283l-2.3337,-0.371c0,-1.1533 0.4529,-2.064 1.3591,-2.7321C37.8899,8.3705 39.1211,8.0367 40.6771,8.0367ZM21.3455,8.2563L27.4417,8.2563c1.5286,0 2.6865,0.352 3.4737,1.0568 0.7963,0.7048 1.1942,1.8814 1.1942,3.529 -0,2.2517 -1.034,3.6703 -3.1027,4.2561L32.7685,25.8299L30.2286,25.8299L26.618,17.4413L23.6797,17.4413L23.6797,25.8299l-2.3342,0zM23.6797,10.4118l0,4.8741l2.3342,0c1.2265,0 2.1601,-0.188 2.8009,-0.5633 0.6407,-0.3753 0.9607,-1.0018 0.9607,-1.8805 0,-0.8695 -0.2014,-1.4923 -0.6041,-1.8676 -0.3936,-0.3753 -0.9699,-0.5628 -1.7296,-0.5628z"
android:pathData="M20.3838,8.1241 L0,28.5078 39.2255,67.7333 59.6093,47.3496a27.7366,27.7366 88.4859,0 0,0 -39.2255,27.7366 27.7366,88.4859 0,0 -39.2255,0zM48.9278,15.1846c2.1235,0 3.8167,0.5309 5.0798,1.5927 1.2631,1.0618 1.895,2.4804 1.895,4.2561 0,0.8421 -0.1602,1.6432 -0.4806,2.403 -0.3112,0.7506 -0.8832,1.6383 -1.7162,2.6634 -0.2288,0.2654 -0.9568,1.034 -2.1833,2.3063 -1.2265,1.2631 -2.956,3.0342 -5.1893,5.3134l9.679,0l0,2.3342l-13.0157,0l0,-2.3342c1.0526,-1.0892 2.4851,-2.549 4.2974,-4.3796 1.8215,-1.8398 2.9655,-3.025 3.4323,-3.5559 0.8879,-0.9977 1.5058,-1.84 1.8536,-2.5265 0.357,-0.6956 0.5354,-1.3777 0.5354,-2.0459 0,-1.0892 -0.384,-1.9769 -1.1529,-2.6634 -0.7597,-0.6865 -1.7531,-1.0299 -2.9797,-1.0299 -0.8695,-0 -1.7893,0.1511 -2.7595,0.4532 -0.9611,0.3021 -1.9908,0.7598 -3.0892,1.373L43.1338,16.5437c1.1167,-0.4485 2.1603,-0.7871 3.1306,-1.016 0.9702,-0.2288 1.8579,-0.3431 2.6634,-0.3431zM24.1593,15.5551l6.2611,0c2.3432,0 4.0913,0.4898 5.2446,1.4692 1.1533,0.9794 1.7296,2.4578 1.7296,4.4349 0,1.2906 -0.3018,2.3615 -0.9059,3.2127 -0.595,0.8512 -1.4645,1.4414 -2.6086,1.771 0.595,0.2014 1.1713,0.6313 1.7296,1.2904 0.5675,0.659 1.1352,1.5654 1.7027,2.7187l2.8148,5.6017l-2.9797,0l-2.6221,-5.2586c-0.6773,-1.373 -1.3364,-2.2836 -1.9771,-2.7321 -0.6316,-0.4485 -1.4968,-0.6728 -2.5952,-0.6728l-3.0205,0l0,8.6636L24.1593,36.0536ZM26.9327,17.8346l0,7.2766l3.4876,0c1.3364,0 2.3432,-0.3066 3.0205,-0.9198 0.6865,-0.6224 1.0294,-1.5331 1.0294,-2.7321 0,-1.1991 -0.3429,-2.1006 -1.0294,-2.7047 -0.6773,-0.6133 -1.6841,-0.9198 -3.0205,-0.9198z"
android:strokeLineJoin="round"
android:strokeWidth="23.7399"
android:strokeWidth="5.54109"
android:fillColor="@color/control_primary"
android:strokeColor="#00000000"
android:strokeLineCap="round"/>
</vector>

View file

@ -1,13 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="128dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="33.86667">
android:viewportHeight="67.73334">
<path
android:pathData="M0,0L0,33.8667L67.7333,33.8667L67.7333,0ZM40.6771,8.0367c1.556,0 2.7872,0.3659 3.6933,1.0981 0.9062,0.7322 1.3591,1.7348 1.3591,3.0071 0,0.4027 -0.0139,0.7136 -0.0413,0.9333 -0.0183,0.2105 -0.0867,0.4989 -0.2057,0.8651 -0.1098,0.357 -0.3021,0.7506 -0.5767,1.1808 -0.2746,0.421 -0.6453,0.9242 -1.1121,1.51l-5.6291,7.0435l7.7845,0L45.9491,25.8299l-10.5441,0l0,-2.1554l6.6999,-8.3886c0.4119,-0.5126 0.7142,-0.9612 0.9064,-1.3457 0.1922,-0.3936 0.3017,-0.6911 0.3292,-0.8925 0.0366,-0.2105 0.0548,-0.5123 0.0548,-0.9059 0,-0.6041 -0.2425,-1.0805 -0.7276,-1.4283 -0.4851,-0.3478 -1.1485,-0.5214 -1.9906,-0.5214 -0.8421,0 -1.506,0.1736 -1.9911,0.5214 -0.4851,0.3478 -0.7276,0.8242 -0.7276,1.4283l-2.3337,-0.371c0,-1.1533 0.4529,-2.064 1.3591,-2.7321C37.8899,8.3705 39.1211,8.0367 40.6771,8.0367ZM21.3455,8.2563L27.4417,8.2563c1.5286,0 2.6865,0.352 3.4737,1.0568 0.7963,0.7048 1.1942,1.8814 1.1942,3.529 -0,2.2517 -1.034,3.6703 -3.1027,4.2561L32.7685,25.8299L30.2286,25.8299L26.618,17.4413L23.6797,17.4413L23.6797,25.8299l-2.3342,0zM23.6797,10.4118l0,4.8741l2.3342,0c1.2265,0 2.1601,-0.188 2.8009,-0.5633 0.6407,-0.3753 0.9607,-1.0018 0.9607,-1.8805 0,-0.8695 -0.2014,-1.4923 -0.6041,-1.8676 -0.3936,-0.3753 -0.9699,-0.5628 -1.7296,-0.5628z"
android:pathData="M20.3838,8.1241 L0,28.5078 39.2255,67.7333 59.6093,47.3496a27.7366,27.7366 88.4859,0 0,0 -39.2255,27.7366 27.7366,88.4859 0,0 -39.2255,0zM48.9278,15.1846c2.1235,0 3.8167,0.5309 5.0798,1.5927 1.2631,1.0618 1.895,2.4804 1.895,4.2561 0,0.8421 -0.1602,1.6432 -0.4806,2.403 -0.3112,0.7506 -0.8832,1.6383 -1.7162,2.6634 -0.2288,0.2654 -0.9568,1.034 -2.1833,2.3063 -1.2265,1.2631 -2.956,3.0342 -5.1893,5.3134l9.679,0l0,2.3342l-13.0157,0l0,-2.3342c1.0526,-1.0892 2.4851,-2.549 4.2974,-4.3796 1.8215,-1.8398 2.9655,-3.025 3.4323,-3.5559 0.8879,-0.9977 1.5058,-1.84 1.8536,-2.5265 0.357,-0.6956 0.5354,-1.3777 0.5354,-2.0459 0,-1.0892 -0.384,-1.9769 -1.1529,-2.6634 -0.7597,-0.6865 -1.7531,-1.0299 -2.9797,-1.0299 -0.8695,-0 -1.7893,0.1511 -2.7595,0.4532 -0.9611,0.3021 -1.9908,0.7598 -3.0892,1.373L43.1338,16.5437c1.1167,-0.4485 2.1603,-0.7871 3.1306,-1.016 0.9702,-0.2288 1.8579,-0.3431 2.6634,-0.3431zM24.1593,15.5551l6.2611,0c2.3432,0 4.0913,0.4898 5.2446,1.4692 1.1533,0.9794 1.7296,2.4578 1.7296,4.4349 0,1.2906 -0.3018,2.3615 -0.9059,3.2127 -0.595,0.8512 -1.4645,1.4414 -2.6086,1.771 0.595,0.2014 1.1713,0.6313 1.7296,1.2904 0.5675,0.659 1.1352,1.5654 1.7027,2.7187l2.8148,5.6017l-2.9797,0l-2.6221,-5.2586c-0.6773,-1.373 -1.3364,-2.2836 -1.9771,-2.7321 -0.6316,-0.4485 -1.4968,-0.6728 -2.5952,-0.6728l-3.0205,0l0,8.6636L24.1593,36.0536ZM26.9327,17.8346l0,7.2766l3.4876,0c1.3364,0 2.3432,-0.3066 3.0205,-0.9198 0.6865,-0.6224 1.0294,-1.5331 1.0294,-2.7321 0,-1.1991 -0.3429,-2.1006 -1.0294,-2.7047 -0.6773,-0.6133 -1.6841,-0.9198 -3.0205,-0.9198z"
android:strokeLineJoin="round"
android:strokeWidth="23.7399"
android:strokeWidth="5.54109"
android:fillColor="@color/control_pressed"
android:strokeColor="#00000000"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="67.73334">
<path
android:pathData="M67.7333,0C30.3252,0 0,30.3252 0,67.7333l109.6724,34.3597zM50.2982,31.3252c2.1052,0 3.7711,0.4807 4.9976,1.4418 1.2265,0.9519 1.8402,2.2423 1.8402,3.8716 0,1.135 -0.3253,2.0964 -0.9751,2.8835 -0.6499,0.778 -1.5744,1.318 -2.7735,1.6201 1.3272,0.2837 2.3618,0.8739 3.1032,1.771 0.7506,0.897 1.1255,2.0047 1.1255,3.3228 0,2.0228 -0.6954,3.5878 -2.0867,4.6953 -1.3913,1.1075 -3.3685,1.6614 -5.9314,1.6614 -0.8604,0 -1.7481,-0.0871 -2.6634,-0.261 -0.9062,-0.1648 -1.8441,-0.4163 -2.8143,-0.755l0,-2.6774c0.7689,0.4485 1.6106,0.7871 2.5259,1.016 0.9153,0.2288 1.8719,0.3431 2.8696,0.3431 1.7391,0 3.0616,-0.3434 3.9677,-1.0299 0.9153,-0.6865 1.373,-1.6837 1.373,-2.9926 0,-1.2082 -0.4257,-2.1514 -1.2769,-2.8288 -0.8421,-0.6865 -2.0182,-1.0294 -3.5285,-1.0294l-2.389,0l0,-2.2794l2.4991,0c1.3638,0 2.4069,-0.2697 3.13,-0.8098 0.7231,-0.5492 1.0847,-1.3365 1.0847,-2.3616 0,-1.0526 -0.375,-1.858 -1.1255,-2.4164 -0.7414,-0.5675 -1.808,-0.8511 -3.1993,-0.8511 -0.7597,0 -1.5742,0.0823 -2.4438,0.247 -0.8695,0.1648 -1.8261,0.4211 -2.8696,0.7689l0,-2.4717c1.0526,-0.2929 2.0364,-0.5124 2.9518,-0.6589 0.9245,-0.1464 1.794,-0.2196 2.6086,-0.2196zM25.2005,31.6957L31.4611,31.6957c2.3432,0 4.0913,0.4898 5.2446,1.4692 1.1533,0.9794 1.7301,2.4578 1.7301,4.4349 0,1.2906 -0.3018,2.3615 -0.9059,3.2127 -0.595,0.8512 -1.4645,1.4414 -2.6086,1.771 0.595,0.2014 1.1713,0.6319 1.7296,1.2909 0.5675,0.659 1.1352,1.5649 1.7027,2.7182l2.8143,5.6017l-2.9791,0l-2.6226,-5.2581c-0.6773,-1.373 -1.3364,-2.2841 -1.9771,-2.7326 -0.6316,-0.4485 -1.4963,-0.6723 -2.5947,-0.6723l-3.0205,0l0,8.663L25.2005,52.1942ZM27.974,33.9752l0,7.2766l3.4871,0c1.3364,0 2.3432,-0.3066 3.0205,-0.9198 0.6865,-0.6224 1.0299,-1.5331 1.0299,-2.7321 0,-1.1991 -0.3434,-2.1006 -1.0299,-2.7047 -0.6773,-0.6133 -1.6841,-0.9198 -3.0205,-0.9198z"
android:strokeLineJoin="round"
android:strokeWidth="11.0688"
android:fillColor="@color/control_primary"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="67.73334">
<path
android:pathData="M67.7333,0C30.3252,0 0,30.3252 0,67.7333l109.6724,34.3597zM50.2982,31.3252c2.1052,0 3.7711,0.4807 4.9976,1.4418 1.2265,0.9519 1.8402,2.2423 1.8402,3.8716 0,1.135 -0.3253,2.0964 -0.9751,2.8835 -0.6499,0.778 -1.5744,1.318 -2.7735,1.6201 1.3272,0.2837 2.3618,0.8739 3.1032,1.771 0.7506,0.897 1.1255,2.0047 1.1255,3.3228 0,2.0228 -0.6954,3.5878 -2.0867,4.6953 -1.3913,1.1075 -3.3685,1.6614 -5.9314,1.6614 -0.8604,0 -1.7481,-0.0871 -2.6634,-0.261 -0.9062,-0.1648 -1.8441,-0.4163 -2.8143,-0.755l0,-2.6774c0.7689,0.4485 1.6106,0.7871 2.5259,1.016 0.9153,0.2288 1.8719,0.3431 2.8696,0.3431 1.7391,0 3.0616,-0.3434 3.9677,-1.0299 0.9153,-0.6865 1.373,-1.6837 1.373,-2.9926 0,-1.2082 -0.4257,-2.1514 -1.2769,-2.8288 -0.8421,-0.6865 -2.0182,-1.0294 -3.5285,-1.0294l-2.389,0l0,-2.2794l2.4991,0c1.3638,0 2.4069,-0.2697 3.13,-0.8098 0.7231,-0.5492 1.0847,-1.3365 1.0847,-2.3616 0,-1.0526 -0.375,-1.858 -1.1255,-2.4164 -0.7414,-0.5675 -1.808,-0.8511 -3.1993,-0.8511 -0.7597,0 -1.5742,0.0823 -2.4438,0.247 -0.8695,0.1648 -1.8261,0.4211 -2.8696,0.7689l0,-2.4717c1.0526,-0.2929 2.0364,-0.5124 2.9518,-0.6589 0.9245,-0.1464 1.794,-0.2196 2.6086,-0.2196zM25.2005,31.6957L31.4611,31.6957c2.3432,0 4.0913,0.4898 5.2446,1.4692 1.1533,0.9794 1.7301,2.4578 1.7301,4.4349 0,1.2906 -0.3018,2.3615 -0.9059,3.2127 -0.595,0.8512 -1.4645,1.4414 -2.6086,1.771 0.595,0.2014 1.1713,0.6319 1.7296,1.2909 0.5675,0.659 1.1352,1.5649 1.7027,2.7182l2.8143,5.6017l-2.9791,0l-2.6226,-5.2581c-0.6773,-1.373 -1.3364,-2.2841 -1.9771,-2.7326 -0.6316,-0.4485 -1.4963,-0.6723 -2.5947,-0.6723l-3.0205,0l0,8.663L25.2005,52.1942ZM27.974,33.9752l0,7.2766l3.4871,0c1.3364,0 2.3432,-0.3066 3.0205,-0.9198 0.6865,-0.6224 1.0299,-1.5331 1.0299,-2.7321 0,-1.1991 -0.3434,-2.1006 -1.0299,-2.7047 -0.6773,-0.6133 -1.6841,-0.9198 -3.0205,-0.9198z"
android:strokeLineJoin="round"
android:strokeWidth="11.0688"
android:fillColor="@color/control_pressed"
android:strokeLineCap="round"/>
</vector>

View file

@ -1,12 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="67.73334">
<path
android:pathData="M16.9333,4.2333C7.5523,4.2333 0,10.8416 0,19.05l0,29.6333c0,8.2084 7.5523,14.8167 16.9333,14.8167l33.8667,0c9.3811,0 16.9333,-6.6082 16.9333,-14.8167L67.7333,19.05C67.7333,10.8416 60.1811,4.2333 50.8,4.2333ZM11.8701,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM26.6867,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042A3.7042,3.7042 0,0 1,22.9826 19.05,3.7042 3.7042,0 0,1 26.6867,15.3458ZM41.5034,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM56.3201,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM11.8701,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM26.6867,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM41.5034,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM56.3201,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM11.8701,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM26.6867,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM41.5034,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM56.3201,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042z"
android:strokeLineJoin="round"
android:strokeWidth="22.6959"
android:fillColor="@color/control_primary"
android:strokeLineCap="round"/>
</vector>

View file

@ -1,12 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="67.73333"
android:viewportHeight="67.73334">
<path
android:pathData="M16.9333,4.2333C7.5523,4.2333 0,10.8416 0,19.05l0,29.6333c0,8.2084 7.5523,14.8167 16.9333,14.8167l33.8667,0c9.3811,0 16.9333,-6.6082 16.9333,-14.8167L67.7333,19.05C67.7333,10.8416 60.1811,4.2333 50.8,4.2333ZM11.8701,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM26.6867,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042A3.7042,3.7042 0,0 1,22.9826 19.05,3.7042 3.7042,0 0,1 26.6867,15.3458ZM41.5034,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM56.3201,15.3458a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM11.8701,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM26.6867,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM41.5034,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM56.3201,30.1625a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,135 0,1 -3.7042,3.7042 3.7042,3.7042 135,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM11.8701,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM26.6867,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM41.5034,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042zM56.3201,44.9792a3.7042,3.7042 0,0 1,3.7042 3.7042,3.7042 3.7042,0 0,1 -3.7042,3.7042 3.7042,3.7042 0,0 1,-3.7042 -3.7042,3.7042 3.7042,0 0,1 3.7042,-3.7042z"
android:strokeLineJoin="round"
android:strokeWidth="22.6959"
android:fillColor="@color/control_pressed"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="1920dp"
android:height="942dp"
android:viewportWidth="508"
android:viewportHeight="249.2375">
<path
android:pathData="M32,0L476,0A32,32 0,0 1,508 32L508,217.238A32,32 0,0 1,476 249.238L32,249.238A32,32 0,0 1,-0 217.238L-0,32A32,32 0,0 1,32 0z"
android:strokeLineJoin="round"
android:strokeWidth="1.59637"
android:fillColor="@color/control_primary"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="1920dp"
android:height="942dp"
android:viewportWidth="508"
android:viewportHeight="249.2375">
<path
android:pathData="M32,0L476,0A32,32 0,0 1,508 32L508,217.238A32,32 0,0 1,476 249.238L32,249.238A32,32 0,0 1,-0 217.238L-0,32A32,32 0,0 1,32 0z"
android:strokeLineJoin="round"
android:strokeWidth="1.59637"
android:fillColor="@color/control_pressed"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:attr/textColorPrimary"
android:pathData="M9,11.24V7.5C9,6.12 10.12,5 11.5,5S14,6.12 14,7.5v3.74c1.21,-0.81 2,-2.18 2,-3.74C16,5.01 13.99,3 11.5,3S7,5.01 7,7.5C7,9.06 7.79,10.43 9,11.24zM18.84,15.87l-4.54,-2.26c-0.17,-0.07 -0.35,-0.11 -0.54,-0.11H13v-6C13,6.67 12.33,6 11.5,6S10,6.67 10,7.5v10.74c-3.6,-0.76 -3.54,-0.75 -3.67,-0.75c-0.31,0 -0.59,0.13 -0.79,0.33l-0.79,0.8l4.94,4.94C9.96,23.83 10.34,24 10.75,24h6.79c0.75,0 1.33,-0.55 1.44,-1.28l0.75,-5.27c0.01,-0.07 0.02,-0.14 0.02,-0.2C19.75,16.63 19.37,16.09 18.84,15.87z"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:attr/textColorPrimary"
android:pathData="M16.48,2.52c3.27,1.55 5.61,4.72 5.97,8.48h1.5C23.44,4.84 18.29,0 12,0l-0.66,0.03 3.81,3.81 1.33,-1.32zM10.23,1.75c-0.59,-0.59 -1.54,-0.59 -2.12,0L1.75,8.11c-0.59,0.59 -0.59,1.54 0,2.12l12.02,12.02c0.59,0.59 1.54,0.59 2.12,0l6.36,-6.36c0.59,-0.59 0.59,-1.54 0,-2.12L10.23,1.75zM14.83,21.19L2.81,9.17l6.36,-6.36 12.02,12.02 -6.36,6.36zM7.52,21.48C4.25,19.94 1.91,16.76 1.55,13L0.05,13C0.56,19.16 5.71,24 12,24l0.66,-0.03 -3.81,-3.81 -1.33,1.32z"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:attr/textColorPrimary"
android:pathData="M0,15h2L2,9L0,9v6zM3,17h2L5,7L3,7v10zM22,9v6h2L24,9h-2zM19,17h2L21,7h-2v10zM16.5,3h-9C6.67,3 6,3.67 6,4.5v15c0,0.83 0.67,1.5 1.5,1.5h9c0.83,0 1.5,-0.67 1.5,-1.5v-15c0,-0.83 -0.67,-1.5 -1.5,-1.5zM16,19L8,19L8,5h8v14z"/>
</vector>

View file

@ -1,16 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/mainStreamLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".stream.StreamActivity"
android:keepScreenOn="true">
<TextureView
android:id="@+id/textureView"
<com.metallic.chiaki.stream.AspectRatioFrameLayout
android:id="@+id/aspectRatioLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent"
android:layout_gravity="center">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</com.metallic.chiaki.stream.AspectRatioFrameLayout>
<ProgressBar
android:id="@+id/progressBar"
@ -20,7 +28,7 @@
<fragment
android:id="@+id/controlsFragment"
android:name="com.metallic.chiaki.touchcontrols.TouchControlsFragment"
android:name="com.metallic.chiaki.touchcontrols.DefaultTouchControlsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
@ -34,7 +42,6 @@
android:id="@+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:fitsSystemWindows="true">
<androidx.constraintlayout.widget.ConstraintLayout
@ -76,6 +83,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:singleSelection="true"
app:selectionRequired="true"
app:checkedButton="@id/display_mode_normal_button">
<com.google.android.material.button.MaterialButton

View file

@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:clipChildren="false">
android:clipChildren="false"
tools:ignore="RtlHardcoded,RtlSymmetry">
<com.metallic.chiaki.touchcontrols.ControlsBackgroundView
android:layout_width="match_parent"
@ -45,20 +47,34 @@
app:drawableHandle="@drawable/control_analog_stick_handle"
/>
<com.metallic.chiaki.touchcontrols.TouchpadView
android:id="@+id/touchpadView"
android:layout_width="0dp"
android:layout_height="0dp"
app:drawableIdle="@drawable/control_touchpad"
app:drawablePressed="@drawable/control_touchpad_pressed"
android:layout_marginTop="32dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_max="300dp"
app:layout_constraintDimensionRatio="1920:942"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"/>
<com.metallic.chiaki.touchcontrols.DPadView
android:id="@+id/dpadView"
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_marginLeft="32dp"
android:layout_marginBottom="32dp"
android:layout_width="160dp"
android:layout_height="160dp"
android:padding="16dp"
android:layout_marginLeft="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/faceButtonsLayout"
android:layout_width="144dp"
android:layout_height="144dp"
android:layout_width="@dimen/control_face_button_size_full"
android:layout_height="@dimen/control_face_button_size_full"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
@ -67,9 +83,12 @@
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/crossButtonView"
android:layout_width="@dimen/control_face_button_size"
android:layout_height="@dimen/control_face_button_size"
android:padding="8dp"
android:layout_width="@dimen/control_face_button_size_full"
android:layout_height="@dimen/control_face_button_size_half"
android:paddingLeft="@dimen/control_face_button_padding_to_full"
android:paddingRight="@dimen/control_face_button_padding_to_full"
android:paddingTop="@dimen/control_face_button_padding_to_center"
android:paddingBottom="@dimen/control_face_button_padding_to_outside"
app:drawableIdle="@drawable/control_button_cross"
app:drawablePressed="@drawable/control_button_cross_pressed"
app:layout_constraintLeft_toLeftOf="parent"
@ -78,9 +97,12 @@
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/moonButtonView"
android:layout_width="@dimen/control_face_button_size"
android:layout_height="@dimen/control_face_button_size"
android:padding="8dp"
android:layout_width="@dimen/control_face_button_size_half"
android:layout_height="@dimen/control_face_button_size_full"
android:paddingTop="@dimen/control_face_button_padding_to_full"
android:paddingBottom="@dimen/control_face_button_padding_to_full"
android:paddingLeft="@dimen/control_face_button_padding_to_center"
android:paddingRight="@dimen/control_face_button_padding_to_outside"
app:drawableIdle="@drawable/control_button_moon"
app:drawablePressed="@drawable/control_button_moon_pressed"
app:layout_constraintRight_toRightOf="parent"
@ -89,9 +111,12 @@
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/pyramidButtonView"
android:layout_width="@dimen/control_face_button_size"
android:layout_height="@dimen/control_face_button_size"
android:padding="8dp"
android:layout_width="@dimen/control_face_button_size_full"
android:layout_height="@dimen/control_face_button_size_half"
android:paddingLeft="@dimen/control_face_button_padding_to_full"
android:paddingRight="@dimen/control_face_button_padding_to_full"
android:paddingBottom="@dimen/control_face_button_padding_to_center"
android:paddingTop="@dimen/control_face_button_padding_to_outside"
app:drawableIdle="@drawable/control_button_pyramid"
app:drawablePressed="@drawable/control_button_pyramid_pressed"
app:layout_constraintLeft_toLeftOf="parent"
@ -100,24 +125,45 @@
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/boxButtonView"
android:layout_width="@dimen/control_face_button_size"
android:layout_height="@dimen/control_face_button_size"
android:padding="8dp"
android:layout_width="@dimen/control_face_button_size_half"
android:layout_height="@dimen/control_face_button_size_full"
android:paddingTop="@dimen/control_face_button_padding_to_full"
android:paddingBottom="@dimen/control_face_button_padding_to_full"
android:paddingRight="@dimen/control_face_button_padding_to_center"
android:paddingLeft="@dimen/control_face_button_padding_to_outside"
app:drawableIdle="@drawable/control_button_box"
app:drawablePressed="@drawable/control_button_box_pressed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/l3ButtonView"
android:layout_width="64dp"
android:layout_height="64dp"
app:drawableIdle="@drawable/control_button_l3"
app:drawablePressed="@drawable/control_button_l3_pressed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/r3ButtonView"
android:layout_width="64dp"
android:layout_height="64dp"
app:drawableIdle="@drawable/control_button_r3"
app:drawablePressed="@drawable/control_button_r3_pressed"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/psButtonView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="8dp"
android:layout_marginBottom="8dp"
app:drawableIdle="@drawable/control_button_home"
app:drawablePressed="@drawable/control_button_home_pressed"
app:layout_constraintLeft_toLeftOf="parent"
@ -125,25 +171,14 @@
app:layout_constraintBottom_toBottomOf="parent"/>
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/touchpadButtonView"
android:layout_width="32dp"
android:layout_height="32dp"
android:padding="8dp"
android:layout_marginTop="8dp"
app:drawableIdle="@drawable/control_button_touchpad"
app:drawablePressed="@drawable/control_button_touchpad_pressed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/l2ButtonView"
android:layout_width="64dp"
android:layout_height="32dp"
android:padding="8dp"
android:layout_marginTop="8dp"
android:layout_marginLeft="8dp"
android:layout_width="88dp"
android:layout_height="80dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingRight="8dp"
android:paddingLeft="16dp"
app:drawableIdle="@drawable/control_button_l2"
app:drawablePressed="@drawable/control_button_l2_pressed"
app:layout_constraintLeft_toLeftOf="parent"
@ -151,23 +186,22 @@
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/l1ButtonView"
android:layout_width="64dp"
android:layout_height="32dp"
android:layout_width="80dp"
android:layout_height="80dp"
android:padding="8dp"
android:layout_marginTop="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="32dp"
android:layout_marginLeft="40dp"
app:drawableIdle="@drawable/control_button_l1"
app:drawablePressed="@drawable/control_button_l1_pressed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/l2ButtonView"/>
app:layout_constraintTop_toTopOf="parent"/>
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/shareButtonView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="8dp"
android:layout_marginTop="8dp"
android:layout_marginLeft="8dp"
android:layout_marginLeft="32dp"
app:drawableIdle="@drawable/control_button_share"
app:drawablePressed="@drawable/control_button_share_pressed"
app:layout_constraintLeft_toRightOf="@id/l2ButtonView"
@ -175,11 +209,12 @@
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/r2ButtonView"
android:layout_width="64dp"
android:layout_height="32dp"
android:padding="8dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:layout_width="88dp"
android:layout_height="80dp"
android:paddingTop="8dp"
android:paddingRight="16dp"
android:paddingLeft="8dp"
android:paddingBottom="8dp"
app:drawableIdle="@drawable/control_button_r2"
app:drawablePressed="@drawable/control_button_r2_pressed"
app:layout_constraintRight_toRightOf="parent"
@ -187,23 +222,22 @@
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/r1ButtonView"
android:layout_width="64dp"
android:layout_height="32dp"
android:layout_width="80dp"
android:layout_height="80dp"
android:padding="8dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="32dp"
android:layout_marginRight="40dp"
app:drawableIdle="@drawable/control_button_r1"
app:drawablePressed="@drawable/control_button_r1_pressed"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/r2ButtonView"/>
app:layout_constraintTop_toTopOf="parent"/>
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/optionsButtonView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="8dp"
android:layout_marginTop="8dp"
android:layout_marginRight="8dp"
android:layout_marginRight="32dp"
app:drawableIdle="@drawable/control_button_options"
app:drawablePressed="@drawable/control_button_options_pressed"
app:layout_constraintRight_toLeftOf="@id/r2ButtonView"

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@ -12,16 +13,18 @@
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="90dp" />
<com.metallic.chiaki.touchcontrols.ButtonView
android:id="@+id/touchpadButtonView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="8dp"
android:padding="8dp"
app:drawableIdle="@drawable/control_button_touchpad"
app:drawablePressed="@drawable/control_button_touchpad_pressed"
<com.metallic.chiaki.touchcontrols.TouchpadView
android:id="@+id/touchpadView"
android:layout_width="0dp"
android:layout_height="0dp"
app:drawableIdle="@drawable/control_touchpad"
app:drawablePressed="@drawable/control_touchpad_pressed"
app:layout_constraintDimensionRatio="1920:942"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
android:layout_marginLeft="80dp"
android:layout_marginRight="80dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ButtonView">
<attr name="drawableIdle" format="reference" />
<attr name="drawablePressed" format="integer" />
<attr name="drawablePressed" format="reference" />
<declare-styleable name="ButtonView">
<attr name="drawableIdle" />
<attr name="drawablePressed" />
</declare-styleable>
<declare-styleable name="AnalogStickView">
@ -11,4 +14,9 @@
<attr name="drawableBase" format="reference" />
<attr name="drawableHandle" format="reference" />
</declare-styleable>
<declare-styleable name="TouchpadView">
<attr name="drawableIdle" />
<attr name="drawablePressed" />
</declare-styleable>
</resources>

View file

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="control_face_button_size">48dp</dimen>
<dimen name="control_face_button_size_half">88dp</dimen>
<dimen name="control_face_button_size_full">176dp</dimen>
<dimen name="control_face_button_padding_to_center">24dp</dimen>
<dimen name="control_face_button_padding_to_outside">16dp</dimen>
<dimen name="control_face_button_padding_to_full">64dp</dimen>
<dimen name="control_analog_stick_radius">48dp</dimen>
<dimen name="control_analog_stick_handle_radius">32dp</dimen>
<dimen name="floating_action_button_speed_dial_anim_offset">48dp</dimen>

View file

@ -88,6 +88,12 @@
<string name="preferences_codec_title_h265">H265 (PS5 only)</string>
<string name="preferences_swap_cross_moon_title">Swap Cross/Moon and Box/Pyramid Buttons</string>
<string name="preferences_swap_cross_moon_summary">Swap face buttons if default mapping is incorrect (e.g. for 8BitDo controllers)</string>
<string name="preferences_rumble_enabled_title">Rumble</string>
<string name="preferences_rumble_enabled_summary">Use phone vibration motor for rumble</string>
<string name="preferences_motion_enabled_title">Motion</string>
<string name="preferences_motion_enabled_summary">Use device\'s motion sensors for controller motion</string>
<string name="preferences_button_haptic_enabled_title">Touch Haptics</string>
<string name="preferences_button_haptic_enabled_summary">Use phone vibration motor for short haptic feedback on button touches</string>
<string name="alert_message_delete_registered_host">Are you sure you want to delete the registered console %s with ID %s?</string>
<string name="alert_message_delete_manual_host">Are you sure you want to delete the console entry for %s?</string>
<string name="action_keep">Keep</string>
@ -101,7 +107,10 @@
<!-- Don't localize these -->
<string name="preferences_discovery_enabled_key">discovery_enabled</string>
<string name="preferences_on_screen_controls_enabled_key">on_screen_controls_enabled</string>
<string name="preferences_touchpad_only_key">touchpad_only_enabled</string>
<string name="preferences_touchpad_only_enabled_key">touchpad_only_enabled</string>
<string name="preferences_rumble_enabled_key">rumble_enabled</string>
<string name="preferences_motion_enabled_key">motion_enabled</string>
<string name="preferences_button_haptic_enabled_key">button_haptic_enabled</string>
<string name="preferences_log_verbose_key">log_verbose</string>
<string name="preferences_import_settings_key">import_settings</string>
<string name="preferences_export_settings_key">export_settings</string>

View file

@ -19,6 +19,24 @@
app:summary="@string/preferences_swap_cross_moon_summary"
app:icon="@drawable/ic_gamepad" />
<SwitchPreference
app:key="@string/preferences_rumble_enabled_key"
app:title="@string/preferences_rumble_enabled_title"
app:summary="@string/preferences_rumble_enabled_summary"
app:icon="@drawable/ic_rumble" />
<SwitchPreference
app:key="@string/preferences_motion_enabled_key"
app:title="@string/preferences_motion_enabled_title"
app:summary="@string/preferences_motion_enabled_summary"
app:icon="@drawable/ic_motion" />
<SwitchPreference
app:key="@string/preferences_button_haptic_enabled_key"
app:title="@string/preferences_button_haptic_enabled_title"
app:summary="@string/preferences_button_haptic_enabled_summary"
app:icon="@drawable/ic_button_haptic" />
<SwitchPreference
app:key="@string/preferences_log_verbose_key"
app:title="@string/preferences_log_verbose_title"

View file

@ -1,14 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.61'
ext.kotlin_version = '1.8.0'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'com.android.tools.build:gradle:7.4.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View file

@ -1,6 +1,6 @@
#Wed Mar 11 18:48:31 CET 2020
#Sun Feb 05 16:25:19 CET 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
zipStoreBase=GRADLE_USER_HOME

266
assets/chiaki_macos.svg Normal file
View file

@ -0,0 +1,266 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="1024"
height="1024"
viewBox="0 0 270.93332 270.93334"
version="1.1"
id="svg5"
inkscape:version="1.2.1 (9c6d41e, 2022-07-14)"
sodipodi:docname="chiaki_macos.svg"
xml:space="preserve"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="0.5946522"
inkscape:cx="616.32665"
inkscape:cy="519.63148"
inkscape:window-width="1920"
inkscape:window-height="978"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
showguides="true"><sodipodi:guide
position="26.458333,219.96511"
orientation="-1,0"
id="guide1357"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" /><sodipodi:guide
position="50.653599,244.475"
orientation="0,1"
id="guide1359"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" /><sodipodi:guide
position="244.475,210.52655"
orientation="-1,0"
id="guide1361"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" /><sodipodi:guide
position="67.733332,26.458333"
orientation="0,1"
id="guide1363"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" /><sodipodi:guide
position="135.46666,253.78662"
orientation="-1,0"
id="guide4345"
inkscape:locked="false"
inkscape:label=""
inkscape:color="rgb(0,134,229)" /></sodipodi:namedview><defs
id="defs2"><linearGradient
inkscape:collect="always"
id="linearGradient4465"><stop
style="stop-color:#f1ff7f;stop-opacity:1;"
offset="0"
id="stop4461" /><stop
style="stop-color:#b2d400;stop-opacity:1;"
offset="1"
id="stop4463" /></linearGradient><filter
x="-0.014563107"
y="-0.014563107"
width="1.0291262"
height="1.0412621"
filterUnits="objectBoundingBox"
id="filter-80aygx4sbj-3"><feOffset
dx="0"
dy="5"
in="SourceAlpha"
result="shadowOffsetOuter1"
id="feOffset424" /><feGaussianBlur
stdDeviation="2.5"
in="shadowOffsetOuter1"
result="shadowBlurOuter1"
id="feGaussianBlur426" /><feColorMatrix
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0"
type="matrix"
in="shadowBlurOuter1"
id="feColorMatrix428" /></filter><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath869"><path
d="m -143.73827,56.831818 a 79.375,79.375 0 0 1 102.042535,2e-6 l -51.021268,60.80478 z"
sodipodi:end="5.4105207"
sodipodi:start="4.0142573"
sodipodi:ry="79.375"
sodipodi:rx="79.375"
sodipodi:cy="117.6366"
sodipodi:cx="-92.717003"
sodipodi:type="arc"
id="path871"
style="fill:#008080;fill-opacity:1;stroke:none;stroke-width:11.1206;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:arc-type="slice" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath964"><circle
style="display:inline;fill:#ff9955;fill-opacity:1;stroke:none;stroke-width:8.46667;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="circle966"
cx="-135.46669"
cy="161.49431"
r="135.46667"
inkscape:label="circle"
transform="scale(-1,1)" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath4387"><path
d="m 270.31599,96.939968 c 0,-3.20165 0,-6.403627 -0.0173,-9.605605 -0.0174,-2.697285 -0.0473,-5.393909 -0.12049,-8.089885 -0.15834,-5.876017 -0.50503,-11.802446 -1.55009,-17.613009 -1.05975,-5.894353 -2.79053,-11.380228 -5.51791,-16.735846 -2.68122,-5.26397 -6.18365,-10.080848 -10.3626,-14.256872 -4.17798,-4.176024 -8.99614,-7.676173 -14.26207,-10.355782 -5.36116,-2.728048 -10.85292,-4.458156 -16.75377,-5.517628 -5.81122,-1.043433 -11.73926,-1.38939 -17.61526,-1.54813 -2.69792,-0.07299 -5.39585,-0.103764 -8.09443,-0.120439 -3.2036,-0.01955 -6.40721,-0.01806 -9.61081,-0.01806 L 149.2133,12.945506 h -27.8204 l -36.538684,0.133205 c -3.209827,0 -6.419646,-0.0016 -9.629466,0.01806 -2.704145,0.01657 -5.406981,0.04745 -8.110144,0.120439 -5.889745,0.158737 -11.831203,0.505024 -17.656149,1.549765 -5.908728,1.059145 -11.408663,2.788599 -16.777022,5.514685 -5.277041,2.679934 -10.105685,6.18041 -14.292493,10.35709 -4.186156,4.175697 -7.695131,8.991593 -10.3812753,14.254581 -2.734911,5.35823 -4.4689412,10.84739 -5.5313527,16.74501 -1.0460477,5.808273 -1.3926574,11.732737 -1.55205319,17.606136 -0.0726642,2.696304 -0.10408515,5.392927 -0.12043909,8.089885 -0.0195464,3.202305 -0.18427009,7.179987 -0.18427009,10.381964 v 36.032494 28.11836 l 0.16625869,36.79805 c 0,3.20624 -0.001309,6.41247 0.0180605,9.6187 0.0163323,2.70089 0.0477729,5.40111 0.12078473,8.10101 0.15905935,5.88356 0.50633015,11.81849 1.55336045,17.63691 1.0620848,5.90221 2.7957884,11.39593 5.5284083,16.75842 2.6864707,5.27149 6.1960987,10.09461 10.3825837,14.27651 4.186482,4.18193 9.013815,7.68665 14.289876,10.3702 5.37163,2.73164 10.874184,4.46402 16.786519,5.52481 5.82232,1.04508 11.761815,1.39168 17.649275,1.55042 2.703163,0.073 5.406326,0.10416 8.110465,0.12049 3.20982,0.0198 6.419318,0.0198 9.629138,0.0198 h 36.868929 27.88948 36.79758 c 3.2036,0 6.40721,0 9.61081,-0.0198 2.69858,-0.0174 5.39651,-0.0475 8.09444,-0.12049 5.87828,-0.15908 11.8083,-0.506 17.62211,-1.55173 5.89727,-1.06076 11.38641,-2.79253 16.74462,-5.52253 5.26689,-2.68321 10.08605,-6.18859 14.26436,-10.37117 4.17829,-4.18126 7.68039,-9.00371 10.36162,-14.27421 2.72902,-5.36511 4.45979,-10.86145 5.52024,-16.7676 1.04409,-5.81612 1.39036,-11.74877 1.5491,-17.63003 0.073,-2.70023 0.10416,-5.40045 0.11999,-8.10101 0.0173,-3.20623 0.0173,-6.41246 0.0173,-9.6187 0,0 0,-36.14835 0,-36.79805 v -28.14782 c 0,-0.47982 0,-36.779393 0,-36.779393"
id="path4389"
style="fill:#cfff0d;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.654602"
inkscape:label="clip" /></clipPath><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4465"
id="linearGradient4467"
x1="67.73333"
y1="13.229165"
x2="67.73333"
y2="122.2375"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.0000001,0,0,2.0000001,5.0529028e-6,-2.9497097e-5)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4465"
id="linearGradient1573"
gradientUnits="userSpaceOnUse"
x1="67.73333"
y1="13.229165"
x2="67.73333"
y2="122.2375"
gradientTransform="matrix(1.9999999,0,0,1.9999999,-8.9032287e-4,7.677083e-6)" /></defs><g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"><path
d="m 417,128.31353 c 0,-4.89098 0,-9.78246 -0.0281,-14.67394 -0.0245,-4.12049 -0.072,-8.23997 -0.184,-12.35846 -0.242,-8.976466 -0.77149,-18.029933 -2.36798,-26.906401 C 412.80098,65.370262 410.157,56.989792 405.99053,48.808322 401.89456,40.766852 396.5441,33.408378 390.16015,27.028902 383.7777,20.649425 376.41725,15.302444 368.37281,11.208959 360.18287,7.0414744 351.79343,4.398484 342.779,2.7799899 333.90156,1.1859957 324.84563,0.65749761 315.8692,0.41499849 311.74773,0.3034989 307.62626,0.25649907 303.50379,0.23099916 298.60983,0.20099927 293.71586,0.20349926 288.8219,0.20349926 L 231.99832,0 h -42.49969 l -55.81808,0.20349926 c -4.90347,0 -9.80693,-0.0025 -14.71039,0.0274999 -4.13097,0.0254999 -8.25994,0.0724997 -12.38941,0.18399933 C 97.583315,0.65749761 88.506882,1.1864957 79.608448,2.7824899 70.582015,4.400484 62.180077,7.0424744 53.979137,11.206959 45.917697,15.300944 38.541252,20.648425 32.145299,27.028902 25.750346,33.407878 20.389886,40.764852 16.286417,48.804822 12.108447,56.990293 9.459467,65.375762 7.836479,74.385229 6.2384908,83.258197 5.7089948,92.308664 5.4654965,101.28113 5.3544974,105.40012 5.3064977,109.5196 5.2814979,113.63959 5.2514981,118.53157 5,124.60805 5,129.49953 v 55.0448 42.95484 l 0.2539981,56.2143 c 0,4.89798 -0.002,9.79596 0.0275,14.69394 0.025,4.12599 0.072999,8.25097 0.1844986,12.37546 0.2429983,8.98797 0.7734943,18.05443 2.3729825,26.9429 1.622488,9.01647 4.2709678,17.40894 8.4454378,25.60091 4.103969,8.05297 9.465429,15.42094 15.860882,21.80942 6.395453,6.38848 13.769898,11.74245 21.829839,15.84194 8.205939,4.17298 16.611876,6.81947 25.64381,8.43997 8.894434,1.59649 17.967867,2.12599 26.961802,2.36849 4.12947,0.1115 8.25894,0.159 12.38991,0.184 C 123.87412,412 128.77708,412 133.68055,412 h 56.32258 42.60518 56.21359 c 4.89396,0 9.78793,0 14.68189,-0.0295 4.12247,-0.025 8.24394,-0.0725 12.36541,-0.184 8.97993,-0.243 18.03887,-0.773 26.9203,-2.37049 9.00893,-1.62049 17.39437,-4.26598 25.57981,-8.43647 8.04594,-4.09898 15.40789,-9.45397 21.79084,-15.84344 6.38295,-6.38748 11.73291,-13.75445 15.82888,-21.80592 4.16897,-8.19597 6.81295,-16.59244 8.43294,-25.61491 1.59499,-8.88497 2.12398,-17.94793 2.36648,-26.9324 0.1115,-4.12499 0.159,-8.24997 0.1835,-12.37546 C 417,293.50943 417,288.61145 417,283.71347 c 0,0 0,-55.2218 0,-56.2143 v -42.99984 c 0,-0.733 0,-56.1858 0,-56.1858"
id="use436"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;filter:url(#filter-80aygx4sbj-3)"
transform="matrix(0.52916669,0,0,0.52916669,23.8125,26.458334)" /><path
d="m 244.475,94.357609 c 0,-2.588144 0,-5.176552 -0.014,-7.76496 -0.014,-2.180426 -0.0382,-4.360318 -0.0974,-6.539686 -0.128,-4.750046 -0.40826,-9.540837 -1.25306,-14.237969 -0.85668,-4.764864 -2.2558,-9.199528 -4.46056,-13.52889 -2.16744,-4.255278 -4.99872,-8.149136 -8.3769,-11.524941 -3.37738,-3.375806 -7.27228,-6.20525 -11.52914,-8.371386 -4.33384,-2.205294 -8.77326,-3.603876 -13.54338,-4.46033 -4.69766,-0.843488 -9.48976,-1.123152 -14.23978,-1.251474 -2.18094,-0.059 -4.3619,-0.08388 -6.54336,-0.09736 -2.58972,-0.0158 -5.17946,-0.0146 -7.76918,-0.0146 l -30.06997,-0.10768 h -22.48942 l -29.537067,0.10768 c -2.594752,0 -5.1895,-0.0013 -7.784248,0.0146 -2.185972,0.0134 -4.370884,0.03836 -6.556062,0.09736 -4.761142,0.12832 -9.564087,0.40825 -14.272841,1.252796 -4.776488,0.85619 -9.222514,2.254242 -13.562178,4.45795 -4.265844,2.1664 -8.169214,4.996108 -11.553737,8.372444 -3.383996,3.375541 -6.220574,7.268605 -8.391994,11.523089 -2.210842,4.331478 -3.612594,8.76879 -4.471424,13.536298 -0.845602,4.69528 -1.125794,9.484485 -1.254646,14.232413 -0.05874,2.179634 -0.08414,4.359524 -0.09736,6.539686 -0.0158,2.588674 -0.14896,5.804144 -0.14896,8.392552 v 29.127869 22.73027 l 0.1344,29.74674 c 0,2.59184 -0.0011,5.18368 0.0146,7.77554 0.0132,2.18334 0.03862,4.36614 0.09764,6.54868 0.12858,4.75614 0.409306,9.5538 1.255702,14.25728 0.858566,4.77122 2.260054,9.21222 4.469044,13.54714 2.171684,4.26136 5.00879,8.16026 8.393052,11.54082 3.384259,3.38058 7.286569,6.21372 11.551621,8.38304 4.34231,2.2082 8.790452,3.60862 13.56985,4.46614 4.706638,0.84482 9.507995,1.125 14.267285,1.25332 2.185178,0.059 4.370356,0.0842 6.556326,0.0974 2.594748,0.016 5.189232,0.016 7.783984,0.016 h 29.804033 22.54524 29.74635 c 2.58972,0 5.17944,0 7.76916,-0.016 2.18148,-0.014 4.36242,-0.0384 6.54338,-0.0974 4.75188,-0.1286 9.54556,-0.40904 14.24532,-1.25438 4.76722,-0.8575 9.20452,-2.25742 13.53598,-4.4643 4.25764,-2.16904 8.15334,-5.00272 11.531,-8.38382 3.37764,-3.38004 6.20866,-7.2784 8.3761,-11.53896 2.20608,-4.33704 3.6052,-8.78016 4.46244,-13.55456 0.84402,-4.70162 1.12394,-9.49744 1.25226,-14.25172 0.059,-2.18282 0.0842,-4.36562 0.097,-6.54868 0.014,-2.59186 0.014,-5.1837 0.014,-7.77554 0,0 0,-29.22154 0,-29.74674 v -22.75408 c 0,-0.38788 0,-29.731651 0,-29.731651"
id="use438"
style="display:inline;fill:url(#linearGradient1573);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.529166" /><path
id="path1446"
style="display:none;fill:url(#linearGradient4467);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.529166"
inkscape:label="bg_cut"
d="m 124.0896,26.458306 -29.537222,0.10748 c -2.59475,0 -5.188758,-0.0015 -7.783504,0.0144 -2.18597,0.0134 -4.37153,0.03918 -6.556706,0.09818 -4.761139,0.12832 -9.564283,0.408092 -14.273031,1.252638 -4.776484,0.856188 -9.222305,2.253902 -13.561965,4.457609 -4.26584,2.166398 -8.169296,4.996284 -11.553817,8.372616 -3.383992,3.375538 -6.219802,7.268332 -8.39122,11.522813 -2.210838,4.331472 -3.613248,8.76862 -4.472078,13.536124 -0.8456,4.695277 -1.125852,9.484801 -1.254704,14.232723 -0.05874,2.179632 -0.08394,4.358977 -0.09716,6.539137 -0.0158,2.588672 -0.14882,5.804882 -0.14882,8.393286 V 108.73021 H 244.47502 V 94.35796 h 0.001 c 0,-2.58814 -4.8e-4,-5.177528 -0.014,-7.765934 -0.014,-2.180424 -0.038,-4.359771 -0.0972,-6.539137 -0.128,-4.75004 -0.40784,-9.540762 -1.25264,-14.237891 -0.85668,-4.76486 -2.25596,-9.199532 -4.46072,-13.52889 -2.16744,-4.255275 -4.99856,-8.149077 -8.37674,-11.524879 -3.37738,-3.375802 -7.27216,-6.205448 -11.52902,-8.371582 -4.33384,-2.205293 -8.77324,-3.604257 -13.54336,-4.460711 -4.69766,-0.843486 -9.48993,-1.122248 -14.23996,-1.25057 -2.18094,-0.059 -4.36179,-0.0847 -6.54327,-0.09818 -2.58971,-0.0158 -5.18035,-0.0144 -7.77006,-0.0144 l -30.06949,-0.10748 z m -97.498969,149.491654 0.0032,0.63976 c 0,2.59184 -0.0012,5.18442 0.0144,7.77627 0.0132,2.18333 0.03916,4.36589 0.09818,6.54843 0.12858,4.75613 0.408308,9.55406 1.254704,14.25754 0.858566,4.7712 2.261024,9.21154 4.470012,13.54646 2.171682,4.26134 5.007994,8.16086 8.392252,11.5414 3.384257,3.38058 7.286703,6.21364 11.551751,8.38296 4.342306,2.2082 8.790839,3.60836 13.570231,4.46588 4.706634,0.84482 9.507544,1.12534 14.266831,1.25366 2.185176,0.059 4.370738,0.084 6.556706,0.0972 2.594746,0.016 5.188754,0.016 7.783504,0.016 h 29.804898 22.54437 29.74702 c 2.58972,0 5.17932,5e-4 7.76903,-0.016 2.18148,-0.014 4.36233,-0.0382 6.54327,-0.0972 4.75188,-0.1286 9.54537,-0.40936 14.24513,-1.2547 4.76722,-0.8575 9.20466,-2.25694 13.53612,-4.4638 4.25764,-2.16904 8.15342,-5.0029 11.53108,-8.384 3.37764,-3.38002 6.20828,-7.27878 8.37572,-11.53934 2.20608,-4.33704 3.60554,-8.78034 4.46278,-13.55472 0.84402,-4.70162 1.12432,-9.49706 1.25264,-14.25134 0.059,-2.1828 0.0834,-4.36536 0.0962,-6.54843 0.014,-2.59185 0.014,-5.18443 0.014,-7.77627 v -0.63976 z" /><g
id="g1005"
inkscape:label="main icon"
clip-path="url(#clipPath4387)"
style="display:inline"
transform="matrix(0.80837807,0,0,0.80837807,25.958398,15.993434)"><rect
inkscape:label="bg"
ry="1.0565645"
y="20.8298"
x="-4.0937502e-07"
height="270.93335"
width="270.93335"
id="rect873"
style="display:none;fill:#cfff0d;fill-opacity:1;stroke:none;stroke-width:8.46431;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><g
id="g1033"
inkscape:label="right (original)"
transform="matrix(-1,0,0,1,262.28035,14.266743)"
style="display:none;stroke:none"><path
inkscape:connector-curvature="0"
id="path1019"
d="M -8.6529992,183.6071 H 107.72321 l 19.09046,28.9079 V 100.45235 H -8.6529992 Z"
style="display:inline;fill:#162d50;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:nodetypes="cccccc"
inkscape:label="face" /><rect
style="fill:#ffaaee;fill-opacity:1;stroke:none;stroke-width:0.185662;stroke-miterlimit:4;stroke-dasharray:none"
id="rect1021"
width="15.497028"
height="31.75"
x="92.382744"
y="131.06844"
inkscape:label="eye" /><path
sodipodi:type="star"
style="fill:#162d50;fill-opacity:1;stroke:none;stroke-width:0.5;stroke-miterlimit:4;stroke-dasharray:none"
id="path1023"
sodipodi:sides="3"
sodipodi:cx="75.635689"
sodipodi:cy="87.601158"
sodipodi:r1="33.042126"
sodipodi:r2="16.521063"
sodipodi:arg1="0.52359878"
sodipodi:arg2="1.5707963"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 104.25101,104.12222 -28.615321,0 -28.615321,0 14.30766,-24.781593 14.307661,-24.781595 14.30766,24.781594 z"
inkscape:transform-center-y="-7.0654063"
transform="matrix(0.70280031,0,0,0.85532099,32.495272,20.355967)"
inkscape:label="ear" /><flowRoot
xml:space="preserve"
id="flowRoot1031"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none"
transform="matrix(0.26458333,0,0,0.26458333,-8.6529992,15.861276)"
inkscape:label="flowRoot4545"><flowRegion
id="flowRegion1027"
style="stroke:none"><rect
id="rect1025"
width="104.28571"
height="78.571426"
x="-374.28571"
y="54"
style="stroke:none" /></flowRegion><flowPara
id="flowPara1029" /></flowRoot></g><use
style="display:none"
inkscape:label="left"
transform="matrix(-1,0,0,1,270.93336,-5.3256301e-6)"
height="100%"
width="100%"
id="use4535"
xlink:href="#g4527"
y="0"
x="0" /><g
style="display:inline;stroke:none"
transform="matrix(-1,0,0,1,262.28035,14.266743)"
inkscape:label="face"
id="g4527"><path
style="display:inline;fill:#162d50;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 356.42773,228.50391 -38.0039,80.11132 -21.9375,46.24219 H -619.27414 V 669.14258 H 439.84766 L 512,778.40039 584.15234,669.14258 H 1613.2487 V 354.85742 H 727.51367 l -21.9375,-46.24219 -38.0039,-80.11132 -38.00586,80.11132 -21.9375,46.24219 H 512 416.37109 l -21.9375,-46.24219 z"
id="path4518"
inkscape:connector-curvature="0"
inkscape:label="shape"
transform="matrix(-0.26458333,0,0,0.26458333,262.28035,6.563066)"
sodipodi:nodetypes="ccccccccccccccccccc" /><rect
inkscape:label="eye right"
y="131.18182"
x="92.153267"
height="31.75"
width="15.875"
id="rect1043"
style="display:inline;fill:#ffaaee;fill-opacity:1;stroke:none;stroke-width:0.187912;stroke-miterlimit:4;stroke-dasharray:none" /><rect
style="display:inline;fill:#ffaaee;fill-opacity:1;stroke:none;stroke-width:0.187912;stroke-miterlimit:4;stroke-dasharray:none"
id="rect1047"
width="15.875"
height="31.75"
x="145.59911"
y="131.18182"
inkscape:label="eye left" /></g><g
inkscape:label="dangerous_microwaves"
clip-path="url(#clipPath869)"
style="fill:none"
transform="translate(187.02207,-8.675551)"
id="g844"><circle
inkscape:label="path830"
r="58.208332"
style="fill:none;fill-opacity:1;stroke:#162d50;stroke-width:8.46667;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path830"
cx="-92.71701"
cy="117.6366" /><circle
r="41.241978"
cy="117.6366"
cx="-92.71701"
id="circle840"
style="fill:none;fill-opacity:1;stroke:#162d50;stroke-width:8.46667;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /></g></g></g></svg>

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="1024"
height="1024"
viewBox="0 0 270.93332 270.93334"
version="1.1"
id="svg5"
xml:space="preserve"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs2"><linearGradient
id="linearGradient4465"><stop
style="stop-color:#f6ffbc;stop-opacity:1;"
offset="0"
id="stop4461" /><stop
style="stop-color:#b4d700;stop-opacity:1;"
offset="1"
id="stop4463" /></linearGradient><filter
x="-0.014563107"
y="-0.014563107"
width="1.0291262"
height="1.0412621"
filterUnits="objectBoundingBox"
id="filter-80aygx4sbj-3"><feOffset
dx="0"
dy="5"
in="SourceAlpha"
result="shadowOffsetOuter1"
id="feOffset424" /><feGaussianBlur
stdDeviation="2.5"
in="shadowOffsetOuter1"
result="shadowBlurOuter1"
id="feGaussianBlur426" /><feColorMatrix
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0"
type="matrix"
in="shadowBlurOuter1"
id="feColorMatrix428" /></filter><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath964"><circle
style="display:inline;fill:#ff9955;fill-opacity:1;stroke:none;stroke-width:8.46667;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="circle966"
cx="-135.46669"
cy="161.49431"
r="135.46667"
transform="scale(-1,1)" /></clipPath><linearGradient
xlink:href="#linearGradient4465"
id="linearGradient4467"
x1="67.73333"
y1="13.229165"
x2="67.73333"
y2="122.2375"
gradientUnits="userSpaceOnUse" /><linearGradient
xlink:href="#linearGradient4465-9"
id="linearGradient4467-7"
x1="67.73333"
y1="13.229165"
x2="67.73333"
y2="122.2375"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-7.288516e-4,6.5027792e-5)" /><linearGradient
id="linearGradient4465-9"><stop
style="stop-color:#f1ff7f;stop-opacity:1;"
offset="0"
id="stop4461-3" /><stop
style="stop-color:#b2d400;stop-opacity:1;"
offset="1"
id="stop4463-8" /></linearGradient></defs><g
id="layer1"
transform="matrix(2.0000001,0,0,2.0000001,4.2905177e-6,-3.0059484e-5)"><path
d="m 417,128.31353 c 0,-4.89098 0,-9.78246 -0.0281,-14.67394 -0.0245,-4.12049 -0.072,-8.23997 -0.184,-12.35846 -0.242,-8.976466 -0.77149,-18.029933 -2.36798,-26.906401 C 412.80098,65.370262 410.157,56.989792 405.99053,48.808322 401.89456,40.766852 396.5441,33.408378 390.16015,27.028902 383.7777,20.649425 376.41725,15.302444 368.37281,11.208959 360.18287,7.0414744 351.79343,4.398484 342.779,2.7799899 333.90156,1.1859957 324.84563,0.65749761 315.8692,0.41499849 311.74773,0.3034989 307.62626,0.25649907 303.50379,0.23099916 298.60983,0.20099927 293.71586,0.20349926 288.8219,0.20349926 L 231.99832,0 h -42.49969 l -55.81808,0.20349926 c -4.90347,0 -9.80693,-0.0025 -14.71039,0.0274999 -4.13097,0.0254999 -8.25994,0.0724997 -12.38941,0.18399933 C 97.583315,0.65749761 88.506882,1.1864957 79.608448,2.7824899 70.582015,4.400484 62.180077,7.0424744 53.979137,11.206959 45.917697,15.300944 38.541252,20.648425 32.145299,27.028902 25.750346,33.407878 20.389886,40.764852 16.286417,48.804822 12.108447,56.990293 9.459467,65.375762 7.836479,74.385229 6.2384908,83.258197 5.7089948,92.308664 5.4654965,101.28113 5.3544974,105.40012 5.3064977,109.5196 5.2814979,113.63959 5.2514981,118.53157 5,124.60805 5,129.49953 v 55.0448 42.95484 l 0.2539981,56.2143 c 0,4.89798 -0.002,9.79596 0.0275,14.69394 0.025,4.12599 0.072999,8.25097 0.1844986,12.37546 0.2429983,8.98797 0.7734943,18.05443 2.3729825,26.9429 1.622488,9.01647 4.2709678,17.40894 8.4454378,25.60091 4.103969,8.05297 9.465429,15.42094 15.860882,21.80942 6.395453,6.38848 13.769898,11.74245 21.829839,15.84194 8.205939,4.17298 16.611876,6.81947 25.64381,8.43997 8.894434,1.59649 17.967867,2.12599 26.961802,2.36849 4.12947,0.1115 8.25894,0.159 12.38991,0.184 C 123.87412,412 128.77708,412 133.68055,412 h 56.32258 42.60518 56.21359 c 4.89396,0 9.78793,0 14.68189,-0.0295 4.12247,-0.025 8.24394,-0.0725 12.36541,-0.184 8.97993,-0.243 18.03887,-0.773 26.9203,-2.37049 9.00893,-1.62049 17.39437,-4.26598 25.57981,-8.43647 8.04594,-4.09898 15.40789,-9.45397 21.79084,-15.84344 6.38295,-6.38748 11.73291,-13.75445 15.82888,-21.80592 4.16897,-8.19597 6.81295,-16.59244 8.43294,-25.61491 1.59499,-8.88497 2.12398,-17.94793 2.36648,-26.9324 0.1115,-4.12499 0.159,-8.24997 0.1835,-12.37546 C 417,293.50943 417,288.61145 417,283.71347 c 0,0 0,-55.2218 0,-56.2143 v -42.99984 c 0,-0.733 0,-56.1858 0,-56.1858"
id="use436"
style="display:inline;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;filter:url(#filter-80aygx4sbj-3)"
transform="matrix(0.26458333,0,0,0.26458333,11.906247,13.229181)" /><path
id="path1446-0"
style="display:inline;fill:url(#linearGradient4467-7);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.264583"
d="m 62.044061,13.229232 -14.7686,0.05374 c -1.29738,0 -2.59438,-7.27e-4 -3.89175,0.0072 -1.09299,0.0067 -2.18577,0.01959 -3.27836,0.04909 -2.38057,0.06416 -4.78214,0.204046 -7.13651,0.626319 -2.38824,0.428094 -4.61115,1.126951 -6.78098,2.228804 -2.13292,1.083199 -4.08465,2.498142 -5.77691,4.186308 -1.692,1.687769 -3.1099,3.634166 -4.19561,5.761406 -1.10542,2.165736 -1.80663,4.38431 -2.23604,6.768062 -0.4228,2.347638 -0.56293,4.7424 -0.62735,7.116361 -0.0294,1.089816 -0.042,2.179488 -0.0486,3.269568 -0.008,1.294336 -0.0744,2.902441 -0.0744,4.196643 v 6.872449 H 122.23676 v -7.186125 h 5.2e-4 c 0,-1.29407 -2.4e-4,-2.588764 -0.007,-3.882967 -0.007,-1.090212 -0.019,-2.179885 -0.0486,-3.269568 -0.064,-2.37502 -0.20392,-4.770381 -0.62632,-7.118945 -0.42834,-2.38243 -1.12798,-4.599766 -2.23036,-6.764445 -1.08372,-2.127637 -2.49928,-4.074538 -4.18837,-5.762439 -1.68869,-1.687901 -3.63608,-3.102724 -5.76451,-4.185791 -2.16692,-1.102646 -4.38662,-1.802128 -6.77168,-2.230355 -2.34883,-0.421743 -4.744969,-0.561124 -7.119979,-0.625285 -1.09047,-0.0295 -2.1809,-0.04235 -3.27163,-0.04909 -1.29486,-0.0079 -2.59018,-0.0072 -3.88504,-0.0072 l -15.03474,-0.05374 z m -48.74947,74.745825 0.002,0.319877 c 0,1.295923 -6e-4,2.592212 0.007,3.888135 0.007,1.091667 0.0196,2.182948 0.0491,3.274218 0.0643,2.378065 0.20415,4.777033 0.62735,7.128773 0.42928,2.3856 1.13051,4.60577 2.235,6.77323 1.08584,2.13067 2.504,4.08043 4.19613,5.7707 1.69213,1.69029 3.64335,3.10682 5.77587,4.19148 2.17116,1.1041 4.39542,1.80418 6.78512,2.23294 2.35332,0.42241 4.75377,0.56267 7.13341,0.62683 1.09259,0.0295 2.18537,0.042 3.27836,0.0486 1.29737,0.008 2.59437,0.008 3.89175,0.008 h 14.90244 11.27219 14.87351 c 1.29485,0 2.58965,2.5e-4 3.88451,-0.008 1.09074,-0.007 2.18117,-0.0191 3.27164,-0.0486 2.37594,-0.0643 4.772679,-0.20468 7.122559,-0.62735 2.38361,-0.42875 4.60233,-1.12847 6.76806,-2.2319 2.12882,-1.08452 4.07671,-2.50145 5.76554,-4.192 1.68882,-1.69001 3.10414,-3.63939 4.18786,-5.76967 1.10304,-2.16852 1.80277,-4.39017 2.23139,-6.77736 0.42201,-2.35081 0.56216,-4.748534 0.62632,-7.125673 0.0295,-1.091402 0.0417,-2.182683 0.0481,-3.274218 0.007,-1.295923 0.007,-2.592212 0.007,-3.888135 v -0.319877 z" /><path
id="path4518"
style="display:inline;fill:#162d50;stroke:none;stroke-width:0.106942px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 51.096105,40.852287 -4.064351,8.567436 -2.345593,4.945434 H 13.229682 v 7.69152 11.365198 l 0.06563,14.553117 H 60.017007 L 67.733332,99.65955 75.449657,87.974992 H 122.2375 V 73.421875 62.044791 54.365157 H 90.780505 l -2.345594,-4.945434 -4.064351,-8.567436 -4.064351,8.567436 -2.346111,4.945434 H 67.733332 57.506567 l -2.34611,-4.945434 z" /><rect
y="66.785446"
x="-81.742691"
height="12.833001"
width="6.4165006"
id="rect1043"
style="display:inline;fill:#ffaaee;fill-opacity:1;stroke:none;stroke-width:0.075952;stroke-miterlimit:4;stroke-dasharray:none"
transform="scale(-1,1)" /><rect
style="display:inline;fill:#ffaaee;fill-opacity:1;stroke:none;stroke-width:0.075952;stroke-miterlimit:4;stroke-dasharray:none"
id="rect1047"
width="6.4165006"
height="12.833001"
x="-60.140472"
y="66.785446"
transform="scale(-1,1)" /><path
id="path830"
style="color:#000000;fill:#162d50;-inkscape-stroke:none"
d="m 51.095589,26.798881 c -6.169023,0 -11.826367,2.225125 -16.215031,5.913334 l 2.185913,2.605009 c 3.790164,-3.181711 8.681211,-5.09633 14.029118,-5.09633 5.348221,0 10.239895,1.9148 14.030152,5.096847 l 2.185913,-2.605009 C 62.922884,29.024147 57.264963,26.798881 51.095589,26.798881 Z" /><path
id="circle840"
style="color:#000000;fill:#162d50;-inkscape-stroke:none"
d="m 51.095589,33.656344 c -4.49058,0 -8.610793,1.621713 -11.807548,4.308781 l 2.18643,2.605525 c 2.597877,-2.180752 5.951484,-3.492293 9.621118,-3.492293 3.669777,0 7.024322,1.311388 9.622669,3.492293 l 2.185913,-2.605009 C 59.707311,35.278198 55.58652,33.656344 51.095589,33.656344 Z" /></g></svg>

After

Width:  |  Height:  |  Size: 9.2 KiB

View file

@ -5,37 +5,13 @@
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="l1.svg"
inkscape:version="1.0beta1 (5c3063637d, 2019-10-15)"
id="svg8"
version="1.1"
viewBox="0 0 67.733332 33.866668"
height="128"
viewBox="0 0 67.733332 67.733336"
height="256"
width="256">
<defs
id="defs2" />
<sodipodi:namedview
inkscape:window-maximized="1"
inkscape:window-y="15"
inkscape:window-x="1920"
inkscape:window-height="1048"
inkscape:window-width="1918"
units="px"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="61.042937"
inkscape:cx="87.179174"
inkscape:zoom="2.8"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#000000"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
@ -49,28 +25,12 @@
</rdf:RDF>
</metadata>
<g
style="display:inline"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Ebene 1">
<rect
y="0"
x="0"
height="33.866665"
width="67.73333"
id="rect1406"
style="display:none;fill:#ffffff;stroke:none;stroke-width:6.28119;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:stroke markers fill" />
<text
id="text1404"
y="25.830172"
x="-3.9868231"
style="font-style:normal;font-weight:normal;font-size:28.1184px;line-height:1.25;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;inline-size:75.2671;display:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.70296"
xml:space="preserve" />
id="g2115"
style="display:inline">
<path
inkscape:connector-curvature="0"
transform="scale(0.26458333)"
d="M 0,0 V 128 H 256 V 0 Z m 82.337891,31.205078 h 8.820312 V 89.478516 H 120.5293 V 97.625 H 82.337891 Z m 71.402339,0 h 8.82227 V 97.625 h -8.82227 V 43.658203 l -12.03906,12.039063 -6.22656,-6.226563 z"
style="display:inline;fill:#ffffff;stroke:none;stroke-width:23.7399;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:stroke markers fill"
id="rect1518" />
id="path2031"
style="fill:#ffffff;stroke-width:5.54109;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="M 148.25391,0 0,148.25391 77.041016,225.29492 a 104.83116,104.83117 0 0 0 148.253904,0 104.83116,104.83117 0 0 0 0,-148.253904 z M 96.048828,111.22656 h 10.482422 v 68.65235 h 37.72656 v 8.82226 H 96.048828 Z m 78.980472,0 h 10.48242 v 68.65235 h 17.12305 v 8.82226 h -44.62696 v -8.82226 h 17.125 v -59.10547 l -18.6289,3.73633 v -9.54688 z"
transform="scale(0.26458334)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before After
Before After

View file

@ -5,37 +5,13 @@
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="l2.svg"
inkscape:version="1.0beta1 (5c3063637d, 2019-10-15)"
id="svg8"
version="1.1"
viewBox="0 0 67.733332 33.866668"
height="128"
viewBox="0 0 67.733332 67.733336"
height="256"
width="256">
<defs
id="defs2" />
<sodipodi:namedview
inkscape:window-maximized="1"
inkscape:window-y="15"
inkscape:window-x="1920"
inkscape:window-height="1048"
inkscape:window-width="1918"
units="px"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="66.072659"
inkscape:cx="112.07001"
inkscape:zoom="2.8"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#000000"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
@ -49,28 +25,12 @@
</rdf:RDF>
</metadata>
<g
style="display:inline"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Ebene 1">
<rect
y="0"
x="0"
height="33.866665"
width="67.73333"
id="rect1406"
style="display:none;fill:#ffffff;stroke:none;stroke-width:6.28119;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:stroke markers fill" />
<text
id="text1404"
y="25.830172"
x="-3.9868231"
style="font-style:normal;font-weight:normal;font-size:28.1184px;line-height:1.25;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;inline-size:75.2671;display:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.70296"
xml:space="preserve" />
id="g2135"
style="display:inline">
<path
inkscape:connector-curvature="0"
transform="scale(0.26458333)"
d="M 0,0 V 128 H 256 V 0 Z m 153.74023,30.375 c 5.88107,0 10.53413,1.382832 13.95899,4.150391 3.42485,2.767558 5.13672,6.556599 5.13672,11.365234 0,1.522157 -0.0525,2.697075 -0.15625,3.527344 -0.0692,0.795671 -0.32762,1.88575 -0.77735,3.269531 -0.41513,1.349186 -1.14185,2.83695 -2.17968,4.462891 -1.03784,1.591347 -2.43881,3.492984 -4.20313,5.707031 l -21.27539,26.621094 h 29.42188 V 97.625 h -39.85157 v -8.146484 l 25.32227,-31.705078 c 1.55675,-1.937291 2.6993,-3.632969 3.42578,-5.085938 0.72648,-1.487564 1.14035,-2.611968 1.24414,-3.373047 0.13843,-0.795674 0.20703,-1.936266 0.20703,-3.423828 0,-2.283238 -0.91649,-4.083848 -2.75,-5.398437 -1.83351,-1.314591 -4.34074,-1.970704 -7.52344,-1.970704 -3.18269,0 -5.69188,0.656113 -7.52539,1.970704 -1.8335,1.314589 -2.75,3.115199 -2.75,5.398437 l -8.82031,-1.402344 c 0,-4.358904 1.71187,-7.800773 5.13672,-10.326172 3.42485,-2.525397 8.07792,-3.787109 13.95898,-3.787109 z m -71.402339,0.830078 h 8.820312 V 89.478516 H 120.5293 V 97.625 H 82.337891 Z"
style="fill:#ffffff;stroke:none;stroke-width:23.7399;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:stroke markers fill"
id="rect1462" />
id="path2127"
style="fill:#ffffff;stroke-width:5.54109;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="m 104.51367,0 a 104.83116,104.83117 0 0 0 -73.808592,30.705078 104.83116,104.83117 0 0 0 0,148.253902 L 107.74609,256 256,107.74609 178.95898,30.705078 A 104.83116,104.83117 0 0 0 104.51367,0 Z m 27.39844,57.390625 c 8.02592,0 14.42713,2.00657 19.20117,6.019531 4.77404,4.012961 7.16016,9.374608 7.16016,16.085938 0,3.182692 -0.6056,6.210686 -1.81641,9.082031 -1.17621,2.836747 -3.33823,6.191824 -6.48633,10.066406 -0.86486,1.003241 -3.61434,3.908169 -8.25,8.716799 -4.63566,4.77404 -11.17418,11.468 -19.61523,20.08203 h 36.58398 v 8.82226 h -49.19336 v -8.82226 c 3.97837,-4.11675 9.39248,-9.63383 16.24219,-16.55274 6.8843,-6.95349 11.20834,-11.432968 12.97266,-13.439448 3.35566,-3.770799 5.69127,-6.954243 7.00586,-9.548828 1.34918,-2.629183 2.02343,-5.207025 2.02343,-7.732422 0,-4.116745 -1.45343,-7.471821 -4.35937,-10.066406 -2.87134,-2.594588 -6.62411,-3.892578 -11.25977,-3.892578 -3.28647,-10e-7 -6.76462,0.571271 -10.43164,1.71289 -3.63242,1.141619 -7.52444,2.871623 -11.67578,5.189453 V 62.527344 c 4.22053,-1.695131 8.16502,-2.974982 11.83203,-3.839844 3.66702,-0.864862 7.0221,-1.296875 10.06641,-1.296875 z m -78.978516,1.400391 h 10.482422 v 68.652344 h 37.724604 v 8.82226 H 52.933594 Z"
transform="scale(0.26458334)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

36
assets/controls/l3.svg Normal file
View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8"
version="1.1"
viewBox="0 0 67.733332 67.733336"
height="256"
width="256">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="display:inline"
id="layer1">
<path
id="path1554"
style="fill:#ffffff;stroke-width:11.0688;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="M 0,0 -158.50977,385.86328 256,256 C 256,114.61511 141.38489,0 0,0 Z m 127.02734,118.39453 c 7.95674,0 14.25301,1.8168 18.88868,5.44922 4.63566,3.59783 6.95312,8.47499 6.95312,14.63281 0,4.28972 -1.22934,7.92331 -3.68555,10.89844 -2.4562,2.94053 -5.94857,4.98143 -10.48047,6.12305 5.01622,1.07243 8.92441,3.30309 11.72657,6.69336 2.83674,3.39026 4.25586,7.57698 4.25586,12.55859 0,7.64538 -2.63031,13.56016 -7.88867,17.74609 -5.25838,4.18594 -12.72957,6.2793 -22.41602,6.2793 -3.25188,0 -6.60891,-0.32903 -10.06836,-0.98633 -3.42485,-0.6227 -6.9697,-1.57352 -10.63672,-2.85351 v -10.11914 c 2.90594,1.69512 6.08743,2.97498 9.54688,3.83984 3.45945,0.86486 7.0749,1.29689 10.8457,1.29687 6.57295,10e-6 11.57319,-1.29799 14.99805,-3.89257 3.45945,-2.59459 5.1875,-6.36354 5.1875,-11.31055 0,-4.56647 -1.60693,-8.13141 -4.82422,-10.69141 -3.18269,-2.59458 -7.6298,-3.89062 -13.33789,-3.89062 h -9.02735 v -8.61524 h 9.44336 c 5.15458,0 9.09907,-1.01947 11.83203,-3.06054 2.73297,-2.07567 4.09961,-5.0512 4.09961,-8.92578 0,-3.97837 -1.41911,-7.02255 -4.25586,-9.13282 -2.80215,-2.14486 -6.83343,-3.21679 -12.09179,-3.21679 -2.87135,0 -5.94986,0.31089 -9.23633,0.93359 -3.28648,0.6227 -6.90193,1.59166 -10.8457,2.90625 v -9.3418 c 3.97836,-1.10702 7.69875,-1.93672 11.1582,-2.49023 3.49404,-0.55351 6.78046,-0.83008 9.85937,-0.83008 z m -80.224606,1.40039 h 10.482422 v 68.6543 h 37.72461 v 8.82031 H 46.802734 Z"
transform="scale(0.26458334)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8"
version="1.1"
viewBox="0 0 67.733332 67.733336"
height="256"
width="256">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="display:inline"
id="layer1">
<path
id="path1554"
style="fill:#ffffff;stroke-width:2.92862;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="M 0,0 C 37.408085,0 67.73333,30.325245 67.73333,67.73333 L -41.939116,102.09299 Z" />
<text
id="text1404"
y="25.830172"
x="-3.9868231"
style="font-style:normal;font-weight:normal;font-size:28.1184px;line-height:1.25;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;inline-size:75.2671;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.70296"
xml:space="preserve"
transform="translate(13.609727,26.364208)"><tspan
x="-3.9868231"
y="25.830172"><tspan>L3</tspan></tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

124
assets/controls/lr12.svg Normal file
View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg8"
version="1.1"
viewBox="0 0 67.733332 67.733336"
height="256"
width="256"
sodipodi:docname="lr12.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<sodipodi:namedview
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1916"
inkscape:window-height="1031"
id="namedview1468"
showgrid="false"
inkscape:zoom="0.79980469"
inkscape:cx="101.38147"
inkscape:cy="95.943219"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg8" />
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="g2115"
inkscape:label="l1"
style="display:inline">
<path
id="path2031"
style="fill:#ffffff;stroke-width:1.46608;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="M 39.225383,0 0,39.225383 20.383723,59.609106 a 27.736579,27.73658 0 0 0 39.225787,4.04e-4 27.736579,27.73658 0 0 0 -4.04e-4,-39.225787 z" />
<text
id="text1404-4"
y="25.830172"
x="-3.9868231"
style="font-style:normal;font-weight:normal;font-size:28.1184px;line-height:1.25;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;inline-size:75.2671;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.70296"
xml:space="preserve"
transform="translate(26.639768,24.096855)"><tspan
x="-3.9868231"
y="25.830172"><tspan>L1</tspan></tspan></text>
</g>
<g
id="g2125"
inkscape:label="r1"
style="display:inline">
<path
id="path2117"
style="fill:#ffffff;stroke-width:1.46608;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="M 28.507951,0 67.733334,39.225383 47.349611,59.609106 a 27.736579,27.73658 0 0 1 -39.2257874,4.04e-4 27.736579,27.73658 0 0 1 4.04e-4,-39.225787 z" />
<text
id="text2123"
y="25.830172"
x="-3.9868231"
style="font-style:normal;font-weight:normal;font-size:28.1184px;line-height:1.25;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;inline-size:75.2671;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.70296"
xml:space="preserve"
transform="translate(13.644494,24.096798)"><tspan
x="-3.9868231"
y="25.830172"><tspan>R1</tspan></tspan></text>
</g>
<g
id="g2135"
inkscape:label="l2"
style="display:inline">
<path
id="path2127"
style="fill:#ffffff;stroke-width:1.46608;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="M 28.507951,67.733335 67.733334,28.507952 47.349611,8.1242288 a 27.736579,27.73658 0 0 0 -39.2257874,-4.04e-4 27.736579,27.73658 0 0 0 4.04e-4,39.2257872 z" />
<text
id="text2133"
y="25.830172"
x="-3.9868231"
style="font-style:normal;font-weight:normal;font-size:28.1184px;line-height:1.25;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;inline-size:75.2671;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.70296"
xml:space="preserve"
transform="translate(15.231995,10.223453)"><tspan
x="-3.9868231"
y="25.830172"><tspan>L2</tspan></tspan></text>
</g>
<g
id="g2145"
inkscape:label="r2"
style="display:inline"
transform="matrix(-1,0,0,1,67.733334,0)">
<path
id="path2137"
style="fill:#ffffff;stroke-width:1.46608;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="M 28.507951,67.733335 67.733334,28.507952 47.349611,8.1242288 a 27.736579,27.73658 0 0 0 -39.2257874,-4.04e-4 27.736579,27.73658 0 0 0 4.04e-4,39.2257872 z" />
<text
id="text2143"
y="25.830172"
x="-3.9868231"
style="font-style:normal;font-weight:normal;font-size:28.1184px;line-height:1.25;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;inline-size:75.2671;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.70296"
xml:space="preserve"
transform="matrix(-1,0,0,1,42.347278,10.223453)"><tspan
x="-3.9868231"
y="25.830172"><tspan>R2</tspan></tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -5,37 +5,13 @@
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="r1.svg"
inkscape:version="1.0beta1 (5c3063637d, 2019-10-15)"
id="svg8"
version="1.1"
viewBox="0 0 67.733332 33.866668"
height="128"
viewBox="0 0 67.733332 67.733336"
height="256"
width="256">
<defs
id="defs2" />
<sodipodi:namedview
inkscape:window-maximized="1"
inkscape:window-y="15"
inkscape:window-x="1920"
inkscape:window-height="1048"
inkscape:window-width="1918"
units="px"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="56.825844"
inkscape:cx="96.071037"
inkscape:zoom="2.8"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#000000"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
@ -49,28 +25,12 @@
</rdf:RDF>
</metadata>
<g
style="display:inline"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Ebene 1">
<rect
y="0"
x="0"
height="33.866665"
width="67.73333"
id="rect1406"
style="display:none;fill:#ffffff;stroke:none;stroke-width:6.28119;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:stroke markers fill" />
<text
id="text1404"
y="25.830172"
x="-3.9868231"
style="font-style:normal;font-weight:normal;font-size:28.1184px;line-height:1.25;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;inline-size:75.2671;display:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.70296"
xml:space="preserve" />
id="g2125"
style="display:inline">
<path
inkscape:connector-curvature="0"
transform="scale(0.26458333)"
d="M 0,0 V 128 H 256 V 0 Z M 80.675781,31.205078 H 103.7168 c 5.77728,0 10.15378,1.330365 13.1289,3.994141 3.00972,2.663775 4.51367,7.110882 4.51368,13.33789 -1e-5,8.510244 -3.90821,13.871891 -11.72657,16.085938 L 123.84961,97.625 H 114.25 L 100.60352,65.919922 H 89.498047 V 97.625 h -8.822266 z m 73.064449,0 h 8.82227 V 97.625 h -8.82227 V 43.658203 l -12.03906,12.039063 -6.22656,-6.226563 z m -64.242183,8.146484 v 18.421876 h 8.822265 c 4.635658,0 8.164328,-0.710533 10.585938,-2.128907 2.42161,-1.418373 3.63086,-3.786351 3.63086,-7.107422 0,-3.286475 -0.76105,-5.640217 -2.2832,-7.058593 -1.48757,-1.418374 -3.66577,-2.126954 -6.53711,-2.126954 z"
style="display:inline;fill:#ffffff;stroke:none;stroke-width:23.7399;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:stroke markers fill"
id="rect1556" />
id="path2117"
style="fill:#ffffff;stroke-width:5.54109;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="M 107.74609,0 30.705078,77.041016 a 104.83116,104.83117 0 0 0 0,148.253904 104.83116,104.83117 0 0 0 148.253902,0 L 256,148.25391 Z M 46.933594,111.22656 h 23.662109 c 8.856189,0 15.463362,1.85113 19.822266,5.55274 4.358906,3.70161 6.539062,9.28735 6.539062,16.75976 0,4.87783 -1.140593,8.92529 -3.423828,12.14258 -2.248642,3.21729 -5.535064,5.44991 -9.859375,6.69531 2.248643,0.76108 4.426845,2.38615 6.53711,4.87696 2.144856,2.4908 4.290687,5.91648 6.435546,10.27539 L 107.2832,188.70117 H 96.023438 l -9.91211,-19.875 C 83.551336,163.637 81.060286,160.19513 78.638672,158.5 c -2.387019,-1.69513 -5.655301,-2.54297 -9.806641,-2.54297 H 57.416016 v 32.74414 H 46.933594 Z m 93.613286,0 h 10.48242 v 68.65235 h 17.12304 v 8.82226 h -44.62695 v -8.82226 h 17.125 v -59.10547 l -18.62891,3.73633 v -9.54688 z m -83.130864,8.61328 v 27.50196 h 13.179687 c 5.050794,0 8.856023,-1.15874 11.416016,-3.47657 2.594587,-2.35242 3.892578,-5.79429 3.892578,-10.32617 0,-4.53188 -1.297991,-7.93942 -3.892578,-10.22265 -2.559993,-2.31783 -6.365222,-3.47657 -11.416016,-3.47657 z"
transform="scale(0.26458334)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Before After
Before After

View file

@ -5,37 +5,13 @@
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="r2.svg"
inkscape:version="1.0beta1 (5c3063637d, 2019-10-15)"
id="svg8"
version="1.1"
viewBox="0 0 67.733332 33.866668"
height="128"
viewBox="0 0 67.733332 67.733336"
height="256"
width="256">
<defs
id="defs2" />
<sodipodi:namedview
inkscape:window-maximized="1"
inkscape:window-y="15"
inkscape:window-x="1920"
inkscape:window-height="1048"
inkscape:window-width="1918"
units="px"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="56.825844"
inkscape:cx="96.071037"
inkscape:zoom="2.8"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#000000"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
@ -49,28 +25,13 @@
</rdf:RDF>
</metadata>
<g
id="g2145"
style="display:inline"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Ebene 1">
<rect
y="0"
x="0"
height="33.866665"
width="67.73333"
id="rect1406"
style="display:none;fill:#ffffff;stroke:none;stroke-width:6.28119;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:stroke markers fill" />
<text
id="text1404"
y="25.830172"
x="-3.9868231"
style="font-style:normal;font-weight:normal;font-size:28.1184px;line-height:1.25;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;inline-size:75.2671;display:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.70296"
xml:space="preserve" />
transform="matrix(-1,0,0,1,67.733334,0)">
<path
inkscape:connector-curvature="0"
transform="scale(0.26458333)"
d="M 0,0 V 128 H 256 V 0 Z m 153.74023,30.375 c 5.88107,0 10.53413,1.382832 13.95899,4.150391 3.42485,2.767558 5.13672,6.556599 5.13672,11.365234 0,1.522157 -0.0525,2.697075 -0.15625,3.527344 -0.0692,0.795671 -0.32762,1.88575 -0.77735,3.269531 -0.41513,1.349186 -1.14185,2.83695 -2.17968,4.462891 -1.03784,1.591347 -2.43881,3.492984 -4.20313,5.707031 l -21.27539,26.621094 h 29.42188 V 97.625 h -39.85157 v -8.146484 l 25.32227,-31.705078 c 1.55675,-1.937291 2.6993,-3.632969 3.42578,-5.085938 0.72648,-1.487564 1.14035,-2.611968 1.24414,-3.373047 0.13843,-0.795674 0.20703,-1.936266 0.20703,-3.423828 0,-2.283238 -0.91649,-4.083848 -2.75,-5.398437 -1.83351,-1.314591 -4.34074,-1.970704 -7.52344,-1.970704 -3.18269,0 -5.69188,0.656113 -7.52539,1.970704 -1.8335,1.314589 -2.75,3.115199 -2.75,5.398437 l -8.82031,-1.402344 c 0,-4.358904 1.71187,-7.800773 5.13672,-10.326172 C 143.2061,31.636712 147.85917,30.375 153.74023,30.375 Z M 80.675781,31.205078 H 103.7168 c 5.77728,0 10.15378,1.330365 13.1289,3.994141 3.00972,2.663775 4.51367,7.110882 4.51368,13.33789 -1e-5,8.510244 -3.90821,13.871891 -11.72657,16.085938 L 123.84961,97.625 H 114.25 L 100.60352,65.919922 H 89.498047 V 97.625 h -8.822266 z m 8.822266,8.146484 v 18.421876 h 8.822265 c 4.635658,0 8.164328,-0.710533 10.585938,-2.128907 2.42161,-1.418373 3.63086,-3.786351 3.63086,-7.107422 0,-3.286475 -0.76105,-5.640217 -2.2832,-7.058593 -1.48757,-1.418374 -3.66577,-2.126954 -6.53711,-2.126954 z"
style="display:inline;fill:#ffffff;stroke:none;stroke-width:23.7399;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:stroke markers fill"
id="rect1556" />
id="path2137"
style="fill:#ffffff;stroke-width:5.54109;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="M 77.041016,30.705078 0,107.74609 148.25391,256 225.29492,178.95898 a 104.83116,104.83117 0 0 0 0,-148.253902 104.83116,104.83117 0 0 0 -148.253904,0 z M 184.92383,57.390625 c 8.02592,0 14.42518,2.00657 19.19922,6.019531 4.77403,4.012961 7.16211,9.374608 7.16211,16.085938 0,3.182692 -0.6056,6.210686 -1.81641,9.082031 -1.17621,2.836747 -3.33823,6.191824 -6.48633,10.066406 -0.86486,1.003241 -3.61629,3.908169 -8.25195,8.716799 -4.63566,4.77404 -11.17223,11.468 -19.61328,20.08203 h 36.58203 v 8.82226 h -49.19336 v -8.82226 c 3.97837,-4.11675 9.39248,-9.63383 16.24219,-16.55274 6.8843,-6.95349 11.20833,-11.432968 12.97265,-13.439448 3.35567,-3.770799 5.69127,-6.954243 7.00586,-9.548828 1.34919,-2.629183 2.02344,-5.207025 2.02344,-7.732422 0,-4.116745 -1.45148,-7.471821 -4.35742,-10.066406 -2.87134,-2.594588 -6.62606,-3.892578 -11.26172,-3.892578 -3.28648,-10e-7 -6.76267,0.571271 -10.42969,1.71289 -3.63242,1.141619 -7.52444,2.871623 -11.67578,5.189453 V 62.527344 c 4.22053,-1.695131 8.16502,-2.974982 11.83203,-3.839844 3.66702,-0.864862 7.02209,-1.296875 10.06641,-1.296875 z m -93.613283,1.400391 h 23.664063 c 8.85619,0 15.46335,1.851125 19.82227,5.552734 4.3589,3.701611 6.5371,9.289311 6.5371,16.761719 0,4.877823 -1.14059,8.92529 -3.42382,12.142578 -2.24865,3.217287 -5.53507,5.447957 -9.85938,6.693359 2.24864,0.761074 4.42685,2.386144 6.53711,4.876954 2.14486,2.4908 4.29069,5.91648 6.43555,10.27539 l 10.63867,21.17187 h -11.26172 l -9.91016,-19.875 c -2.55999,-5.18917 -5.05104,-8.63104 -7.47265,-10.32617 -2.38702,-1.69513 -5.65726,-2.54297 -9.8086,-2.54297 h -11.41601 v 32.74414 H 91.310547 Z m 10.482423,8.615234 v 27.501953 h 13.18164 c 5.05079,0 8.85602,-1.158732 11.41601,-3.476562 2.5946,-2.352424 3.89063,-5.794292 3.89063,-10.326172 0,-4.531878 -1.29603,-7.93942 -3.89063,-10.222657 -2.55999,-2.31783 -6.36522,-3.476562 -11.41601,-3.476562 z"
transform="matrix(-0.26458334,0,0,0.26458334,67.733334,0)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before After
Before After

36
assets/controls/r3.svg Normal file
View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8"
version="1.1"
viewBox="0 0 67.733332 67.733336"
height="256"
width="256">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="display:inline"
id="layer1">
<path
id="path1554"
style="fill:#ffffff;stroke-width:11.0688;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="M 256,0 C 114.61511,0 0,114.61511 0,256 l 414.50977,129.86328 z m -65.89648,118.39453 c 7.95673,0 14.25301,1.8168 18.88867,5.44922 4.63566,3.59783 6.95508,8.47499 6.95508,14.63281 0,4.28972 -1.22934,7.92331 -3.68555,10.89844 -2.45621,2.94053 -5.95055,4.98143 -10.48242,6.12305 5.0162,1.07243 8.92636,3.30309 11.72851,6.69336 2.83675,3.39026 4.25391,7.57698 4.25391,12.55859 0,7.64538 -2.62836,13.56016 -7.88672,17.74609 -5.25836,4.18594 -12.73151,6.2793 -22.41797,6.2793 -3.25188,0 -6.60695,-0.32903 -10.06641,-0.98633 -3.42484,-0.6227 -6.9697,-1.57352 -10.63671,-2.85351 v -10.11914 c 2.90592,1.69512 6.08742,2.97498 9.54687,3.83984 3.45945,0.86486 7.07492,1.29689 10.8457,1.29687 6.57297,2e-5 11.57124,-1.29799 14.9961,-3.89257 3.45945,-2.59459 5.18945,-6.36354 5.18945,-11.31055 0,-4.56647 -1.60888,-8.13141 -4.82617,-10.69141 -3.18269,-2.59458 -7.62785,-3.89062 -13.33594,-3.89062 h -9.0293 v -8.61524 h 9.44532 c 5.15458,0 9.09711,-1.01947 11.83008,-3.06054 2.73296,-2.07567 4.0996,-5.0512 4.0996,-8.92578 2e-5,-3.97838 -1.41715,-7.02255 -4.2539,-9.13282 -2.80215,-2.14485 -6.83343,-3.21679 -12.0918,-3.21679 -2.87134,0 -5.94985,0.31089 -9.23633,0.93359 -3.28647,0.6227 -6.90193,1.59166 -10.8457,2.90625 v -9.3418 c 3.97837,-1.10702 7.6968,-1.93672 11.15625,-2.49023 3.49404,-0.55349 6.78046,-0.83008 9.85938,-0.83008 z m -94.857426,1.40039 H 118.9082 c 8.85619,0 15.46336,1.85113 19.82227,5.55274 4.35891,3.70161 6.53906,9.28931 6.53906,16.76172 0,4.87782 -1.14059,8.92529 -3.42383,12.14257 -2.24864,3.21729 -5.53506,5.44796 -9.85937,6.69336 2.24864,0.76108 4.42684,2.3881 6.53711,4.87891 2.14485,2.4908 4.29069,5.91453 6.43554,10.27344 l 10.63672,21.17187 h -11.25976 l -9.91211,-19.87305 c -2.55999,-5.18916 -5.05104,-8.63299 -7.47266,-10.32812 -2.38702,-1.69513 -5.6553,-2.54102 -9.80664,-2.54102 h -11.41601 v 32.74219 H 95.246094 Z m 10.482426,8.61524 v 27.50195 h 13.17968 c 5.0508,0 8.85603,-1.15873 11.41602,-3.47656 2.59459,-2.35243 3.89258,-5.79429 3.89258,-10.32617 0,-4.53189 -1.29799,-7.93942 -3.89258,-10.22266 -2.55999,-2.31783 -6.36522,-3.47656 -11.41602,-3.47656 z"
transform="scale(0.26458333)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8"
version="1.1"
viewBox="0 0 67.733332 67.733336"
height="256"
width="256">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="display:inline"
id="layer1">
<path
id="path1554"
style="fill:#ffffff;stroke-width:2.92862;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="M 67.733333,0 C 30.325248,0 3.2333333e-6,30.325245 3.2333333e-6,67.73333 L 109.67245,102.09299 Z" />
<text
id="text1404"
y="25.830172"
x="-3.9868231"
style="font-style:normal;font-weight:normal;font-size:28.1184px;line-height:1.25;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;inline-size:75.2671;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.70296"
xml:space="preserve"
transform="translate(26.427202,26.364208)"><tspan
x="-3.9868231"
y="25.830172"><tspan>R3</tspan></tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1920"
height="942"
viewBox="0 0 507.99999 249.23751"
version="1.1"
id="svg981"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
sodipodi:docname="touchpad_surface.svg">
<defs
id="defs975" />
<sodipodi:namedview
id="base"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="0.24748737"
inkscape:cx="876.84187"
inkscape:cy="725.61319"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:window-width="1916"
inkscape:window-height="1031"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata978">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#ffffff;stroke-width:1.59637;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
id="rect1544"
width="508"
height="249.2375"
x="-1.7763568e-15"
y="0"
rx="32"
ry="32" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -15,3 +15,4 @@ endif()
add_executable(chiaki-cli src/main.c)
target_link_libraries(chiaki-cli chiaki-cli-lib)
install(TARGETS chiaki-cli)

View file

@ -142,13 +142,19 @@ CHIAKI_EXPORT int chiaki_cli_cmd_discover(ChiakiLog *log, int argc, char *argv[]
return 1;
}
((struct sockaddr_in *)host_addr)->sin_port = htons(CHIAKI_DISCOVERY_PORT_PS4); // TODO: IPv6, PS5, should probably use the service
ChiakiDiscoveryPacket packet;
memset(&packet, 0, sizeof(packet));
packet.cmd = CHIAKI_DISCOVERY_CMD_SRCH;
chiaki_discovery_send(&discovery, &packet, host_addr, host_addr_len);
packet.protocol_version = CHIAKI_DISCOVERY_PROTOCOL_VERSION_PS4;
((struct sockaddr_in *)host_addr)->sin_port = htons(CHIAKI_DISCOVERY_PORT_PS4);
err = chiaki_discovery_send(&discovery, &packet, host_addr, host_addr_len);
if(err != CHIAKI_ERR_SUCCESS)
CHIAKI_LOGE(log, "Failed to send discovery packet for PS4: %s", chiaki_error_string(err));
packet.protocol_version = CHIAKI_DISCOVERY_PROTOCOL_VERSION_PS5;
((struct sockaddr_in *)host_addr)->sin_port = htons(CHIAKI_DISCOVERY_PORT_PS5);
err = chiaki_discovery_send(&discovery, &packet, host_addr, host_addr_len);
if(err != CHIAKI_ERR_SUCCESS)
CHIAKI_LOGE(log, "Failed to send discovery packet for PS5: %s", chiaki_error_string(err));
while(1)
sleep(1); // TODO: wtf

View file

@ -11,10 +11,14 @@ static char doc[] = "Send a PS4 wakeup packet.";
#define ARG_KEY_HOST 'h'
#define ARG_KEY_REGISTKEY 'r'
#define ARG_KEY_PS4 '4'
#define ARG_KEY_PS5 '5'
static struct argp_option options[] = {
{ "host", ARG_KEY_HOST, "Host", 0, "Host to send wakeup packet to", 0 },
{ "registkey", ARG_KEY_REGISTKEY, "RegistKey", 0, "PS4 registration key", 0 },
{ "registkey", ARG_KEY_REGISTKEY, "RegistKey", 0, "Remote Play registration key (plaintext)", 0 },
{ "ps4", ARG_KEY_PS4, NULL, 0, "PlayStation 4", 0 },
{ "ps5", ARG_KEY_PS5, NULL, 0, "PlayStation 5 (default)", 0 },
{ 0 }
};
@ -22,6 +26,7 @@ typedef struct arguments
{
const char *host;
const char *registkey;
bool ps5;
} Arguments;
static int parse_opt(int key, char *arg, struct argp_state *state)
@ -39,6 +44,12 @@ static int parse_opt(int key, char *arg, struct argp_state *state)
case ARGP_KEY_ARG:
argp_usage(state);
break;
case ARG_KEY_PS4:
arguments->ps5 = false;
break;
case ARG_KEY_PS5:
arguments->ps5 = true;
break;
default:
return ARGP_ERR_UNKNOWN;
}
@ -51,6 +62,7 @@ static struct argp argp = { options, parse_opt, 0, doc, 0, 0, 0 };
CHIAKI_EXPORT int chiaki_cli_cmd_wakeup(ChiakiLog *log, int argc, char *argv[])
{
Arguments arguments = { 0 };
arguments.ps5 = true;
error_t argp_r = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &arguments);
if(argp_r != 0)
return 1;
@ -73,5 +85,5 @@ CHIAKI_EXPORT int chiaki_cli_cmd_wakeup(ChiakiLog *log, int argc, char *argv[])
uint64_t credential = (uint64_t)strtoull(arguments.registkey, NULL, 16);
return chiaki_discovery_wakeup(log, NULL, arguments.host, credential, false);
return chiaki_discovery_wakeup(log, NULL, arguments.host, credential, arguments.ps5);
}

View file

@ -49,7 +49,11 @@ function (_ffmpeg_find component headername)
# Try pkg-config first
if(PKG_CONFIG_FOUND)
if(CMAKE_VERSION VERSION_LESS "3.6")
pkg_check_modules(FFMPEG_${component} lib${component})
else()
pkg_check_modules(FFMPEG_${component} lib${component} IMPORTED_TARGET)
endif()
if(FFMPEG_${component}_FOUND)
if((TARGET PkgConfig::FFMPEG_${component}) AND (NOT CMAKE_VERSION VERSION_LESS "3.11.0"))
if(APPLE)
@ -69,6 +73,9 @@ function (_ffmpeg_find component headername)
add_library(FFMPEG::${component} ALIAS PkgConfig::FFMPEG_${component})
else()
add_library("FFMPEG::${component}" INTERFACE IMPORTED)
if(CMAKE_VERSION VERSION_LESS "3.6")
link_directories("${FFMPEG_${component}_LIBRARY_DIRS}")
endif()
set_target_properties("FFMPEG::${component}" PROPERTIES
INTERFACE_LINK_DIRECTORIES "${FFMPEG_${component}_LIBRARY_DIRS}"
INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_${component}_INCLUDE_DIRS}"
@ -224,10 +231,14 @@ foreach (_ffmpeg_component IN LISTS FFMPEG_FIND_COMPONENTS)
list(APPEND _ffmpeg_required_vars
"FFMPEG_${_ffmpeg_component}_LIBRARIES")
else()
if(NOT FFMPEG_${_ffmpeg_component}_INCLUDE_DIRS)
set(FFMPEG_${_ffmpeg_component}_INCLUDE_DIRS
"${FFMPEG_${_ffmpeg_component}_INCLUDE_DIR}")
endif()
if(NOT FFMPEG_${_ffmpeg_component}_LIBRARIES)
set(FFMPEG_${_ffmpeg_component}_LIBRARIES
"${FFMPEG_${_ffmpeg_component}_LIBRARY}")
endif()
list(APPEND FFMPEG_INCLUDE_DIRS
"${FFMPEG_${_ffmpeg_component}_INCLUDE_DIRS}")
list(APPEND FFMPEG_LIBRARIES

View file

@ -1,6 +1,26 @@
find_package(SDL2 NO_MODULE QUIET)
# Adapted from libsdl-org/SDL_ttf: https://github.com/libsdl-org/SDL_ttf/blob/main/cmake/FindPrivateSDL2.cmake#L19-L31
# Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
# Licensed under the zlib license (https://github.com/libsdl-org/SDL_ttf/blob/main/LICENSE.txt)
set(SDL2_VERSION_MAJOR)
set(SDL2_VERSION_MINOR)
set(SDL2_VERSION_PATCH)
set(SDL2_VERSION)
if(SDL2_INCLUDE_DIR)
file(READ "${SDL2_INCLUDE_DIR}/SDL_version.h" _sdl_version_h)
string(REGEX MATCH "#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)" _sdl2_major_re "${_sdl_version_h}")
set(SDL2_VERSION_MAJOR "${CMAKE_MATCH_1}")
string(REGEX MATCH "#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)" _sdl2_minor_re "${_sdl_version_h}")
set(SDL2_VERSION_MINOR "${CMAKE_MATCH_1}")
string(REGEX MATCH "#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)" _sdl2_patch_re "${_sdl_version_h}")
set(SDL2_VERSION_PATCH "${CMAKE_MATCH_1}")
if(_sdl2_major_re AND _sdl2_minor_re AND _sdl2_patch_re)
set(SDL2_VERSION "${SDL2_VERSION_MAJOR}.${SDL2_VERSION_MINOR}.${SDL2_VERSION_PATCH}")
endif()
endif()
if(SDL2_FOUND AND (NOT TARGET SDL2::SDL2))
add_library(SDL2::SDL2 UNKNOWN IMPORTED GLOBAL)
if(NOT SDL2_LIBDIR)

View file

@ -33,8 +33,8 @@ endif()
find_program(MAKE_EXE NAMES gmake make)
ExternalProject_Add(OpenSSL-ExternalProject
URL https://www.openssl.org/source/openssl-1.1.1d.tar.gz
URL_HASH SHA256=1e3a91bc1f9dfce01af26026f856e064eab4c8ee0a8f457b5ae30b40b8b711f2
URL https://www.openssl.org/source/openssl-1.1.1s.tar.gz
URL_HASH SHA256=c5ac01e760ee6ff0dab61d6b2bbd30146724d063eb322180c6f18a6f74e4b6aa
INSTALL_DIR "${OPENSSL_INSTALL_DIR}"
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env ${OPENSSL_BUILD_ENV}
"<SOURCE_DIR>/Configure" "--prefix=<INSTALL_DIR>" no-shared ${OPENSSL_CONFIG_EXTRA_ARGS} "${OPENSSL_OS_COMPILER}"

View file

@ -1,34 +1,15 @@
# Find DEVKITPRO
set(DEVKITPRO "$ENV{DEVKITPRO}" CACHE PATH "Path to DevKitPro")
set(PORTLIBS_PREFIX "$ENV{PORTLIBS_PREFIX}" CACHE PATH "Path to portlibs inside DevKitPro")
if(NOT DEVKITPRO OR NOT PORTLIBS_PREFIX)
message(FATAL_ERROR "Please set DEVKITPRO & PORTLIBS_PREFIX env before calling cmake. https://devkitpro.org/wiki/Getting_Started")
if(NOT DEVKITPRO)
message(FATAL_ERROR "Please set DEVKITPRO env before calling cmake. https://devkitpro.org/wiki/Getting_Started")
endif()
# include devkitpro toolchain
include("${DEVKITPRO}/switch.cmake")
include("${DEVKITPRO}/cmake/Switch.cmake")
set(NSWITCH TRUE)
# Enable gcc -g, to use
# /opt/devkitpro/devkitA64/bin/aarch64-none-elf-addr2line -e build_switch/switch/chiaki -f -p -C -a 0xCCB5C
# set(CMAKE_BUILD_TYPE Debug)
# set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Shared libs not available" )
# FIXME rework this file to use the toolchain only
# https://github.com/diasurgical/devilutionX/pull/764
set(ARCH "-march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -ftls-model=local-exec")
# set(CMAKE_C_FLAGS "-O2 -ffunction-sections ${ARCH}")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")
# workaroud force -fPIE to avoid
# aarch64-none-elf/bin/ld: read-only segment has dynamic relocations
set(CMAKE_EXE_LINKER_FLAGS "-specs=${DEVKITPRO}/libnx/switch.specs ${ARCH} -fPIE -Wl,-Map,Output.map")
# add portlibs to the list of include dir
include_directories("${PORTLIBS_PREFIX}/include")
# troubleshoot
message(STATUS "CMAKE_FIND_ROOT_PATH = ${CMAKE_FIND_ROOT_PATH}")
message(STATUS "PKG_CONFIG_EXECUTABLE = ${PKG_CONFIG_EXECUTABLE}")
@ -79,4 +60,3 @@ function(add_nro_target output_name target title author version icon romfs)
endfunction()
set(CMAKE_USE_SYSTEM_ENVIRONMENT_PATH OFF)
set(CMAKE_PREFIX_PATH "/")

View file

@ -6,9 +6,6 @@ find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Concurrent Multimedia Open
if(APPLE)
find_package(Qt5 REQUIRED COMPONENTS MacExtras)
endif()
if(CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER)
find_package(SDL2 MODULE REQUIRED)
endif()
if(WIN32)
add_definitions(-DWIN32_LEAN_AND_MEAN)
@ -70,12 +67,12 @@ if(CHIAKI_ENABLE_CLI)
endif()
target_link_libraries(chiaki Qt5::Core Qt5::Widgets Qt5::Gui Qt5::Concurrent Qt5::Multimedia Qt5::OpenGL Qt5::Svg)
target_link_libraries(chiaki SDL2::SDL2)
if(APPLE)
target_link_libraries(chiaki Qt5::MacExtras)
target_compile_definitions(chiaki PRIVATE CHIAKI_GUI_ENABLE_QT_MACEXTRAS)
endif()
if(CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER)
target_link_libraries(chiaki SDL2::SDL2)
target_compile_definitions(chiaki PRIVATE CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER)
endif()
if(CHIAKI_ENABLE_SETSU)

Binary file not shown.

View file

@ -3,6 +3,8 @@
#ifndef CHIAKI_AVOPENGLWIDGET_H
#define CHIAKI_AVOPENGLWIDGET_H
#include "transformmode.h"
#include <chiaki/log.h>
#include <QOpenGLWidget>
@ -74,21 +76,24 @@ class AVOpenGLWidget: public QOpenGLWidget
public:
static QSurfaceFormat CreateSurfaceFormat();
explicit AVOpenGLWidget(StreamSession *session, QWidget *parent = nullptr);
explicit AVOpenGLWidget(StreamSession *session, QWidget *parent = nullptr, TransformMode transform_mode = TransformMode::Fit);
~AVOpenGLWidget() override;
void SwapFrames();
AVOpenGLFrame *GetBackgroundFrame() { return &frames[1 - frame_fg]; }
void SetTransformMode(TransformMode mode) { transform_mode = mode; }
TransformMode GetTransformMode() const { return transform_mode; }
protected:
TransformMode transform_mode;
void mouseMoveEvent(QMouseEvent *event) override;
void initializeGL() override;
void paintGL() override;
private slots:
void ResetMouseTimeout();
public slots:
void ResetMouseTimeout();
void HideMouse();
};

View file

@ -12,8 +12,12 @@
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
#include <SDL.h>
#include <chiaki/orientation.h>
#endif
#define PS_TOUCHPAD_MAX_X 1920
#define PS_TOUCHPAD_MAX_Y 1079
class Controller;
class ControllerManager : public QObject
@ -33,7 +37,9 @@ class ControllerManager : public QObject
private slots:
void UpdateAvailableControllers();
void HandleEvents();
void ControllerEvent(int device_id);
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
void ControllerEvent(SDL_Event evt);
#endif
public:
static ControllerManager *GetInstance();
@ -57,12 +63,24 @@ class Controller : public QObject
private:
Controller(int device_id, ControllerManager *manager);
void UpdateState();
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
void UpdateState(SDL_Event event);
bool HandleButtonEvent(SDL_ControllerButtonEvent event);
bool HandleAxisEvent(SDL_ControllerAxisEvent event);
#if SDL_VERSION_ATLEAST(2, 0, 14)
bool HandleSensorEvent(SDL_ControllerSensorEvent event);
bool HandleTouchpadEvent(SDL_ControllerTouchpadEvent event);
#endif
#endif
ControllerManager *manager;
int id;
ChiakiOrientationTracker orientation_tracker;
ChiakiControllerState state;
bool is_dualsense;
#ifdef CHIAKI_GUI_ENABLE_SDL_GAMECONTROLLER
QMap<QPair<Sint64, Sint64>, uint8_t> touch_ids;
SDL_GameController *controller;
#endif
@ -73,9 +91,44 @@ class Controller : public QObject
int GetDeviceID();
QString GetName();
ChiakiControllerState GetState();
void SetRumble(uint8_t left, uint8_t right);
void SetTriggerEffects(uint8_t type_left, const uint8_t *data_left, uint8_t type_right, const uint8_t *data_right);
bool IsDualSense();
signals:
void StateChanged();
};
/* PS5 trigger effect documentation:
https://controllers.fandom.com/wiki/Sony_DualSense#FFB_Trigger_Modes
Taken from SDL2, licensed under the zlib license,
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
https://github.com/libsdl-org/SDL/blob/release-2.24.1/test/testgamecontroller.c#L263-L289
*/
typedef struct
{
Uint8 ucEnableBits1; /* 0 */
Uint8 ucEnableBits2; /* 1 */
Uint8 ucRumbleRight; /* 2 */
Uint8 ucRumbleLeft; /* 3 */
Uint8 ucHeadphoneVolume; /* 4 */
Uint8 ucSpeakerVolume; /* 5 */
Uint8 ucMicrophoneVolume; /* 6 */
Uint8 ucAudioEnableBits; /* 7 */
Uint8 ucMicLightMode; /* 8 */
Uint8 ucAudioMuteBits; /* 9 */
Uint8 rgucRightTriggerEffect[11]; /* 10 */
Uint8 rgucLeftTriggerEffect[11]; /* 21 */
Uint8 rgucUnknown1[6]; /* 32 */
Uint8 ucLedFlags; /* 38 */
Uint8 rgucUnknown2[2]; /* 39 */
Uint8 ucLedAnim; /* 41 */
Uint8 ucLedBrightness; /* 42 */
Uint8 ucPadLights; /* 43 */
Uint8 ucLedRed; /* 44 */
Uint8 ucLedGreen; /* 45 */
Uint8 ucLedBlue; /* 46 */
} DS5EffectsState_t;
#endif // CHIAKI_CONTROLLERMANAGER_H

View file

@ -55,6 +55,7 @@ class MainWindow : public QMainWindow
void UpdateDiscoveryEnabled();
void ShowSettings();
void Quit();
void UpdateDisplayServers();
void UpdateServerWidgets();

View file

@ -63,6 +63,9 @@ class Settings : public QObject
void SetLogVerbose(bool enabled) { settings.setValue("settings/log_verbose", enabled); }
uint32_t GetLogLevelMask();
bool GetDualSenseEnabled() const { return settings.value("settings/dualsense_enabled", false).toBool(); }
void SetDualSenseEnabled(bool enabled) { settings.setValue("settings/dualsense_enabled", enabled); }
ChiakiVideoResolutionPreset GetResolution() const;
void SetResolution(ChiakiVideoResolutionPreset resolution);

View file

@ -20,6 +20,7 @@ class SettingsDialog : public QDialog
QCheckBox *log_verbose_check_box;
QComboBox *disconnect_action_combo_box;
QCheckBox *dualsense_check_box;
QComboBox *resolution_combo_box;
QComboBox *fps_combo_box;
@ -37,6 +38,7 @@ class SettingsDialog : public QDialog
private slots:
void LogVerboseChanged();
void DualSenseChanged();
void DisconnectActionSelected();
void ResolutionSelected();

View file

@ -13,12 +13,14 @@
#if CHIAKI_GUI_ENABLE_SETSU
#include <setsu.h>
#include <chiaki/orientation.h>
#endif
#include "exception.h"
#include "sessionlog.h"
#include "controllermanager.h"
#include "settings.h"
#include "transformmode.h"
#include <QObject>
#include <QImage>
@ -52,9 +54,18 @@ struct StreamSessionConnectInfo
ChiakiConnectVideoProfile video_profile;
unsigned int audio_buffer_size;
bool fullscreen;
TransformMode transform_mode;
bool enable_keyboard;
bool enable_dualsense;
StreamSessionConnectInfo(Settings *settings, ChiakiTarget target, QString host, QByteArray regist_key, QByteArray morning, bool fullscreen);
StreamSessionConnectInfo(
Settings *settings,
ChiakiTarget target,
QString host,
QByteArray regist_key,
QByteArray morning,
bool fullscreen,
TransformMode transform_mode);
};
class StreamSession : public QObject
@ -74,6 +85,9 @@ class StreamSession : public QObject
Setsu *setsu;
QMap<QPair<QString, SetsuTrackingId>, uint8_t> setsu_ids;
ChiakiControllerState setsu_state;
SetsuDevice *setsu_motion_device;
ChiakiOrientationTracker orient_tracker;
bool orient_dirty;
#endif
ChiakiControllerState keyboard_state;
@ -88,17 +102,23 @@ class StreamSession : public QObject
unsigned int audio_buffer_size;
QAudioOutput *audio_output;
QIODevice *audio_io;
SDL_AudioDeviceID haptics_output;
uint8_t *haptics_resampler_buf;
QMap<Qt::Key, int> key_map;
void PushAudioFrame(int16_t *buf, size_t samples_count);
void Event(ChiakiEvent *event);
void PushHapticsFrame(uint8_t *buf, size_t buf_size);
#if CHIAKI_GUI_ENABLE_SETSU
void HandleSetsuEvent(SetsuEvent *event);
#endif
private slots:
void InitAudio(unsigned int channels, unsigned int rate);
void InitHaptics();
void Event(ChiakiEvent *event);
void DisconnectHaptics();
void ConnectHaptics();
public:
explicit StreamSession(const StreamSessionConnectInfo &connect_info, QObject *parent = nullptr);
@ -120,7 +140,7 @@ class StreamSession : public QObject
#endif
void HandleKeyboardEvent(QKeyEvent *event);
void HandleMouseEvent(QMouseEvent *event);
bool HandleMouseEvent(QMouseEvent *event);
signals:
void FfmpegFrameAvailable();

View file

@ -22,10 +22,14 @@ class StreamWindow: public QMainWindow
const StreamSessionConnectInfo connect_info;
StreamSession *session;
QAction *fullscreen_action;
QAction *stretch_action;
QAction *zoom_action;
AVOpenGLWidget *av_widget;
void Init();
void UpdateVideoTransform();
void UpdateTransformModeActions();
protected:
void keyPressEvent(QKeyEvent *event) override;
@ -42,6 +46,9 @@ class StreamWindow: public QMainWindow
void SessionQuit(ChiakiQuitReason reason, const QString &reason_str);
void LoginPINRequested(bool incorrect);
void ToggleFullscreen();
void ToggleStretch();
void ToggleZoom();
void Quit();
};
#endif // CHIAKI_GUI_STREAMWINDOW_H

Some files were not shown because too many files have changed in this diff Show more