Compare commits

...
71 Commits
Author SHA1 Message Date
David Scott 45f2ef7fef Packaging: containerised CI, Flatpak/AppImage, bundled simd, licence notices, and release-please (#53) 2026-09-10 14:34:56 -04:00
David ScottandClaude Opus 5 cd67cb1689 fix: three crashes found while packaging (#54)
* fix: zero the settings structs so --help does not free garbage

`monocoque --help` aborted with `free(): invalid pointer` on Debian forky --
found by installing the released debian-latest .deb on the distro it is built
for. gdb put it in monocoquesettingsfree() called straight from main().

The early-exit paths (`--help`, `--version`, bad parameters) jump to
cleanup_final before any field of Parameters or MonocoqueSettings is
assigned, and cleanup_final calls monocoquesettingsfree() and freeparams(),
which free every pointer in both. malloc leaves those pointing at whatever
was in the heap block. It is undefined behaviour everywhere; forky's glibc is
simply the first to check, which is why the same source looked healthy on
Debian stable and both Fedoras.

calloc in all three entry points -- monocoque.c, monocoque-cli.c (the `monocoque`
binary) and monocoque-gui.c.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(sound): return from sounddev_init so shaker devices survive setup

Only 2 of 24 configured sound devices came up -- "Could not initialize Sound
Device" for the other 22 -- and no shaker fired, on a rig whose PipeWire graph
looked perfect: monocoque streams connected into all six filter chains, every
node running.

sounddev_init() is declared int and fell off its end without returning, so
new_sound_device() read whatever was left in the return register and treated
that as the error code. The PulseAudio streams had already connected by then;
the device objects were simply freed and never fed telemetry again, which is
why the graph looked right and nothing shook.

Undefined behaviour, so it varies by build: the same config drives shakers
from a locally compiled monocoque and not from the packaged one. Now returns
usb_generic_shaker_init()'s own result.

Verified against a live Assetto Corsa session on real hardware: 26 devices
initialised, no failures, shakers working.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(gui): stop gmonocoque segfaulting on a config without an api key

gmonocoque died before drawing a window, in libconfig:

  #0 config_setting_get_member () from libconfig.so.9
  #1 config_setting_lookup ()
  #2 populate_device_list ()

find_default_config() required sim, api and car to all be present on an
entry and skipped the entry if any lookup failed, so it returned -1;
populate_device_list passed that -1 straight to config_setting_get_elem,
got NULL back, and config_setting_lookup dereferenced it.

No real config has all three. conf/monocoque.config -- the example this
project's own README points at -- sets sim and car and never mentions api,
so this crashed for anyone following the documentation. It stayed hidden in
the Flatpak build only until that manifest was given access to the real
~/.config/monocoque; against an empty config there was nothing to look up.

A key that isn't there no longer disqualifies a match, a -1 falls back to
the first entry (which is what a single-entry config means anyway), and both
NULL paths now return with a message instead of dereferencing. Verified by
rebuilding the Flatpak and launching gmonocoque against the real config that
crashed it: the window opens and stays up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 16:26:53 -04:00
David Scott 501d72fb1e Enable multilib for 32-bit Proton bridge support in distrobox install (#52)
* Add distrobox installer/uninstaller for immutable distros

Adds install-distrobox-dev.sh and uninstall-distrobox-dev.sh under
tools/distro/distrobox/, for setting up monocoque + simapi/simd inside
an Arch Linux distrobox container on immutable distros (Bazzite,
Silverblue, etc.) where native package managers aren't available.

The installer enables the multilib repo and installs wine + lib32-glibc
so the 32-bit Proton bridge executables (e.g. acbridge.exe) have the
32-bit dynamic linker they need — without this, bridge processes fail
silently with a bare "No such file or directory".

Fixes two bugs found before submitting:
- install script's chmod/help-text referenced wrapper script names
  without the -dev suffix used by the actual `cat >` heredocs, which
  would abort the script under set -e on the first chmod
- uninstall script targeted the "simracing" container/install dir
  instead of "simracing-dev", which would silently no-op (or worse,
  target the wrong container) for anyone who also has a non-dev
  simracing container

* Fix typo'd URL to simracing-utilities in install script

The repo is actually named cescofry/simracing-utilties (missing "i"),
not simracing-utilities as originally written - the correctly-spelled
URL 404s.

* Fold fixes into the existing install-distrobox.sh instead of a -dev copy

The previous two commits on this branch added new install-distrobox-dev.sh/
uninstall-distrobox-dev.sh files, but tools/distro/distrobox/install-distrobox.sh
and uninstall-distrobox.sh already exist upstream (#41, #47) — the "-dev"
files were a personal working copy (container renamed to simracing-dev)
that should never have been committed as new files.

This removes the -dev files and instead patches the real
install-distrobox.sh directly with the actual fixes:
- Enable the multilib repo and install wine/lib32-glibc, so the 32-bit
  Proton bridge executables (e.g. acbridge.exe) have the 32-bit dynamic
  linker they need — without this they fail silently with a bare
  "No such file or directory".
- Fix the simracing-utilities URL typo (real repo is simracing-utilties).

uninstall-distrobox.sh needed no changes — the container/install-dir
mismatch found earlier was an artifact of the -dev copy's own renaming,
not a bug in the real upstream script.

* Enable multilib + wine/lib32-glibc, fix simracing-utilities URL typo

These are the actual fixes intended by the previous commit's message —
they didn't get staged there because a stale pathspec in the same `git
add` invocation aborted it before reaching this file.
2026-09-02 00:35:30 -04:00
David Scott 340752857d publish test mode simdata to SIMAPI.DAT shared memory (#51)
`monocoque test` used to only drive locally-initialized devices directly,
so external tools that read SIMAPI.DAT the same way a real running sim
populates it (dashboards, telemetry viewers) saw nothing during a test
run. Opens the shared memory segment at the start of tester() and mirrors
each simdata update into it via a new update_devices() helper, which also
replaces the ~20 duplicated inline device-update loops throughout tester()
with a single call site.

On exit the segment is reset to an inactive frame (simon=false,
SIMAPI_STATUS_OFF) rather than left at whatever the last active-play frame
was - otherwise external readers see the test run as a sim that's still
actively driving forever, since nothing writes to SIMAPI.DAT again until
the next test/play run.
2026-09-02 00:34:07 -04:00
Paul Dino Jones 1cd36de5d5 add dependencies required by nappgui to README 2026-08-21 18:27:13 -04:00
Paul Dino Jones 2431113131 update dependencies in debian control file 2026-08-21 18:26:01 -04:00
Paul Dino Jones 722cc4eb4e add curl and glew dependencies 2026-08-21 15:45:43 -04:00
Paul Dino Jones 48baedf202 add glu dependency 2026-08-21 15:26:52 -04:00
Paul Dino Jones fdbae6198b temporarily add libcurl as dependency 2026-08-21 15:19:33 -04:00
Paul Dino Jones 1f81a6bc96 removed unneeded include 2026-08-21 15:14:15 -04:00
Paul Dino Jones 89e33cf196 add include guard 2026-08-21 15:09:34 -04:00
Paul Dino Jones 7924ef474e add copyright, icons, and gmonocoque to debian packages 2026-08-21 14:49:38 -04:00
Paul Dino Jones d8c85bdc95 Add gmonocoque to fedora 43 build spec 2026-08-21 14:49:13 -04:00
Paul Dino Jones 19c5afa329 add copyright file to package build 2026-08-21 13:36:14 -04:00
Paul Dino Jones 27aea767d8 Add nappgui to dependencies list 2026-08-21 13:33:50 -04:00
Paul Dino Jones 062a4e77ce add header to copyright file 2026-08-21 13:13:51 -04:00
Paul Dino Jones 68b273376d adding copyright file for debian packages 2026-08-21 13:11:46 -04:00
Paul Dino Jones 698ed02987 adding desktop file 2026-08-21 12:53:05 -04:00
Paul Dino Jones ecaf41265a move things around in CMake file 2026-08-20 17:11:13 -04:00
Paul Dino Jones dd93098f39 added gui code 2026-08-18 16:39:24 -04:00
Paul Dino Jones 6d4567facf Only add gui subdirectory on gui builds 2026-06-25 19:35:09 -04:00
Paul Dino Jones 7f52462b68 Add mgui when building gui. Compile as PIE. Add nappgui submodule 2026-06-25 16:28:10 -04:00
Paul Dino Jones 945a04ee66 Add mgui when building gui 2026-06-25 16:24:05 -04:00
Paul Dino Jones 4a30a9735e Add targets for library, cli and gui components 2026-06-25 13:27:19 -04:00
Paul Dino Jones 27b7da8c89 disable arch builds for now 2026-06-24 16:16:27 -04:00
Paul Dino Jones 78cc80064a Fix issue with new haptic code on usb devices and consolidate some function calls 2026-06-23 20:07:53 -04:00
Paul Dino Jones 8eb433f01e Refactor haptic code and devices that rely on it, to more easily allow devices to use the haptic attributes 2026-06-23 13:29:49 -04:00
Paul Dino Jones c4fa9e4138 custom nspawn script for arch build 2026-06-22 13:09:50 -04:00
Paul Dino Jones a9f49d91b9 update to use parent arch pkgbuild workflow action 2026-06-22 12:54:14 -04:00
Paul Dino Jones 87818b6a22 specify correct arch build action 2026-06-22 12:44:31 -04:00
Paul Dino Jones 17dcdcde23 fix path to PKGBUILD 2026-06-22 12:34:38 -04:00
Paul Dino Jones 5c0f871415 Update path to PKGBUILD 2026-06-22 12:32:47 -04:00
Paul Dino Jones 44b9dce139 Add arch build to CI 2026-06-22 12:27:52 -04:00
Paul Dino Jones a776085b76 Add arch build to CI 2026-06-22 12:25:49 -04:00
Paul Dino Jones 0d30170de0 Set buffer size to 64 bytes for simnet pedals. Send 0 for frequency and amplitude when no effect is playing 2026-06-11 18:28:21 -04:00
Paul Dino Jones 2aee847d7c update to latest simapi for ac evo 2026-06-09 13:05:59 -04:00
Paul Dino Jones 9712396c6f Add initial support for simnet pedals 2026-06-01 16:00:13 -04:00
Paul Dino Jones 6a36c2825f further fix return codes on serial devices 2026-05-29 18:21:52 -04:00
Paul Dino Jones 2b787cba00 cleanup the way errors are returned for arduino type devices 2026-05-29 14:28:24 -04:00
Paul Dino Jones 3c81f749b8 various fixes for weirdness with serial devices 2026-05-28 16:25:55 -04:00
David Scott 3c1b1948fd create a rootful, privileged distrobox container (#47) 2026-05-27 13:49:30 -04:00
Paul Dino Jones 3b10509ecd add some directives that seem necessary for debian 2026-05-25 23:58:33 -04:00
Paul Dino Jones b734330d38 fix lua linking 2026-05-25 23:36:57 -04:00
Paul Dino Jones eb95db1dda rename lua in headers 2026-05-25 23:13:53 -04:00
Paul Dino Jones 3a803550ea add more lua variables back to cmake 2026-05-25 23:11:04 -04:00
Paul Dino Jones 26e0e87cd7 add macro for lua 5.4 backwards compatibility so we can compile against lua 5.4 or 5.5 2026-05-25 23:03:57 -04:00
Paul Dino Jones 7f17a5307c refer explicitly to lua 5.4 in headers 2026-05-25 20:46:47 -04:00
Paul Dino Jones ae87a2eaa5 add more dependencies to install.sh 2026-05-25 19:54:08 -04:00
Paul Dino Jones 738a181d61 force lua 5.4 in cmake 2026-05-25 19:53:47 -04:00
Paul Dino Jones ba6c624a59 Improve lua dependency detection for linking 2026-05-25 19:17:47 -04:00
Brandan Cotton 822d4096e7 make additional data available to lua scripts (#45) 2026-05-25 08:42:19 -04:00
Paul Dino Jones a3e932e2dc updated to latest simapi for process detection 2026-05-23 12:40:42 -04:00
Paul Dino Jones ad53dd654c build simapi in install script 2026-05-22 17:45:36 -04:00
Paul Dino Jones 522185d19b remove deprectated getpid.h file 2026-04-26 19:38:15 -04:00
Paul Dino Jones 030813cd4f Update to latest simapi 2026-04-26 19:16:29 -04:00
Paul Dino Jones 53467a2a5e Fix cmake compile definitions 2026-04-23 17:35:50 -04:00
Paul Dino Jones b5fa399f79 fix cmake boolean 2026-04-23 17:20:57 -04:00
Paul Dino Jones 4a72f760b6 fix proc2 detection 2026-04-23 16:02:02 -04:00
Paul Dino Jones 89c73a2fd3 fix lua version issue 2026-04-23 15:25:21 -04:00
Paul Dino Jones 6b72e23638 Add procps dependency. Update to latest simapi 2026-04-22 16:35:43 -04:00
Tony Miceli 8c6b8ef7ac Fix multiple serial device init issues (#43) 2026-04-20 10:35:36 -04:00
Francesco a628afa815 Distrobox install (#41) 2026-04-20 10:26:16 -04:00
Simon Zengerling 712b900d68 Feat add moza ks pro wheel (#42) 2026-04-11 16:07:17 -04:00
Paul Dino Jones 79642a7653 Add config entry for Moza R3 2026-04-04 10:02:16 -04:00
Tony Miceli fbe92cc1c9 add configurable fanpower for simwind and new PWM sketch (#37) 2026-04-03 18:03:08 -04:00
Paul Dino Jones 9e32b3f286 disable installing systemd service via cmake 2026-03-20 19:47:31 -04:00
Paul Dino Jones b3b2c6854d move saving of tyre diameter config to when we still know the car and sim info 2026-03-18 15:41:57 -04:00
Paul Dino Jones 29b446e648 Fix issue with monocoque not shutting down cleanly when used in standalone udp mode 2026-03-18 11:48:43 -04:00
Paul Dino Jones 0142ab61a3 Refactor how sim calculate tyre diameter data if necessary 2026-03-18 11:10:35 -04:00
Paul Dino Jones 09a832a38e Use sim provided slip ratio if available for slip based effects 2026-03-17 10:32:27 -04:00
Paul Dino Jones 9f99cb3e12 Fix regression braking udp support 2026-03-17 10:31:32 -04:00
100 changed files with 6308 additions and 809 deletions
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
# https://gitlab.archlinux.org/archlinux/devtools/-/merge_requests/197
args=("$@")
has_keep_unit=0
for arg in "${args[@]}"; do
if [[ "$arg" == "--keep-unit" ]]; then
has_keep_unit=1
fi
done
if ! (( has_keep_unit )); then
args=("--keep-unit" "${args[@]}")
fi
exec /usr/bin/systemd-nspawn "${args[@]}"
+439 -19
View File
@@ -3,79 +3,499 @@ name: Make Packages
on: on:
push: push:
tags: [ "*" ] tags: [ "*" ]
workflow_dispatch: {}
# Called by release-please.yml once it has cut a release. A tag pushed by
# release-please carries the default GITHUB_TOKEN, and GitHub deliberately
# refuses to start a workflow from an event that token created -- so the
# `push: tags` trigger above never fires for a release-please tag. Being
# callable means the release build runs inside release-please's own run
# instead, with no personal access token to store on a public repo.
workflow_call:
inputs:
release_tag:
description: >-
Tag release-please just created. Supplies the package version and
names the release to upload to, both of which would otherwise come
from github.ref_name.
type: string
required: true
jobs: jobs:
build-monocoque-debs: build-monocoque-debs:
# Previously this matrix used `runs-on: ${{ matrix.os }}` with
# debian-latest/debian-stable as runner *labels*, which only resolve to
# self-hosted runners. With none online those two legs sat queued
# indefinitely (observed: four runs stuck 17-60min, only the
# ubuntu-latest leg ever completing) until cancelled by hand. Building
# each distro in a container on ordinary GitHub-hosted runners removes
# the self-hosted dependency entirely.
#
# matrix.os stays the distro *name* because it's load-bearing: it picks
# the per-distro control file under tools/distro/debian/dpkg/<os>/ and
# names the released .deb. matrix.image is what it actually builds in.
strategy: strategy:
fail-fast: false
matrix: matrix:
os: [ubuntu-latest, debian-latest, debian-stable] include:
runs-on: ${{ matrix.os }} - os: ubuntu-latest
image: ubuntu:latest
- os: debian-latest
image: debian:testing
- os: debian-stable
image: debian:stable-slim
runs-on: ubuntu-latest
container:
image: ${{ matrix.image }}
permissions: permissions:
contents: write contents: write
steps: steps:
- uses: actions/checkout@v1 # Must come before actions/checkout: these images are minimal and ship
- name: Checkout submodules # no git, so checkout would fall back to a tarball download and silently
run: git submodule update --init --recursive # skip submodules -- simapi and nappgui_src are both required to build.
- name: Update apt # No sudo either; container jobs already run as root.
run: sudo apt update - name: Install build dependencies
- name: Install Dependencies run: |
run: sudo apt install -y libuv1-dev libargtable2-dev libserialport-dev libconfig-dev libhidapi-dev liblua5.4-dev libxdg-basedir-dev libxml2-dev libpulse-dev apt-get update
apt-get install -y --no-install-recommends \
ca-certificates curl git \
build-essential cmake pkg-config \
libgtk-3-dev libuv1-dev libargtable2-dev libserialport-dev \
libconfig-dev libhidapi-dev liblua5.4-dev libxdg-basedir-dev \
libxml2-dev libpulse-dev libproc2-dev libcurl4-openssl-dev \
libglu1-mesa-dev
- uses: actions/checkout@v4
with:
submodules: recursive
# $GITHUB_WORKSPACE, not ${{ github.workspace }}: in a container job the
# expression still renders the *host* path (/home/runner/work/...) while
# the repo is actually mounted at /__w/... inside the container. Using
# the expression made cmake fail with `The source directory
# "/home/runner/work/monocoque/monocoque" does not exist`. The env var is
# set to the container-side path, so it's correct either way.
- name: Set build dir - name: Set build dir
id: strings id: strings
shell: bash shell: bash
run: | run: |
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" echo "build-output-dir=$GITHUB_WORKSPACE/build" >> "$GITHUB_OUTPUT"
github_sha_hash=${{ github.sha }} github_sha_hash=${{ github.sha }}
echo "github-sha-short=${github_sha_hash:0:7}" >> $GITHUB_OUTPUT echo "github-sha-short=${github_sha_hash:0:7}" >> $GITHUB_OUTPUT
- name: Configure CMake - name: Configure CMake
run: > run: >
cmake -B ${{ steps.strings.outputs.build-output-dir }} cmake -B ${{ steps.strings.outputs.build-output-dir }}
-DBUILD_GUI=on
-DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_COMPILER=g++
-DCMAKE_C_COMPILER=gcc -DCMAKE_C_COMPILER=gcc
-DCMAKE_BUILD_TYPE=Release -DCMAKE_BUILD_TYPE=Release
-S ${{ github.workspace }} -S "$GITHUB_WORKSPACE"
- name: Build - name: Build
run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release
- name: Copy script files around to stop .github from being added to the package then build the package - name: Create directories in package
run: | run: |
mkdir PKG_SOURCE mkdir PKG_SOURCE
mkdir -p PKG_SOURCE/DEBIAN mkdir -p PKG_SOURCE/DEBIAN
mkdir -p PKG_SOURCE/usr/bin/ mkdir -p PKG_SOURCE/usr/bin/
mkdir -p PKG_SOURCE/usr/share/applications
mkdir -p PKG_SOURCE/usr/share/pixmaps
mkdir -p PKG_SOURCE/usr/share/icons/hicolor/scalable/apps
mkdir -p PKG_SOURCE/usr/share/monocoque
- name: Download xpm icon
run: |
curl -sSL https://repo.spacefreak18.xyz/monocoque/icons/monocoque.xpm -o PKG_SOURCE/usr/share/pixmaps/monocoque.xpm
- name: Download svg icon
run: |
curl -sSL https://repo.spacefreak18.xyz/monocoque/icons/monocoque.svg -o PKG_SOURCE/usr/share/icons/hicolor/scalable/apps/monocoque.svg
- name: Determine package version
id: pkgver
shell: bash
env:
RELEASE_TAG: ${{ inputs.release_tag }}
run: |
# The control files carry a hardcoded `Version: 1` that nobody
# maintains, so every .deb ever released claims version 1 regardless
# of its tag. Take the version from the tag instead.
#
# Guarded rather than used blindly: a Debian version must start with a
# digit, so a branch build (workflow_dispatch) or a non-release tag
# like `pkgtest-5` would make dpkg-deb fail outright. Anything that
# doesn't look like a version falls back to 0.0.0.
v=""
# RELEASE_TAG is set only when release-please calls this workflow;
# its tag exists but the run itself is on a branch, so GITHUB_REF_*
# would report master and fall through to 0.0.0.
v="${RELEASE_TAG#v}"
[ -z "$v" ] && [ "$GITHUB_REF_TYPE" = tag ] && v="${GITHUB_REF_NAME#v}"
case "$v" in
[0-9]*) ;;
*) v="0.0.0" ;;
esac
echo "version=$v" >> "$GITHUB_OUTPUT"
echo "packaging as version $v"
- name: Copy script files around to stop .github from being added to the package then build the package
run: |
cp ./tools/distro/debian/dpkg/${{ matrix.os }}/control ./PKG_SOURCE/DEBIAN/control cp ./tools/distro/debian/dpkg/${{ matrix.os }}/control ./PKG_SOURCE/DEBIAN/control
sed -i "s/^Version:.*/Version: ${{ steps.pkgver.outputs.version }}/" ./PKG_SOURCE/DEBIAN/control
cp ./tools/distro/debian/dpkg/copyright ./PKG_SOURCE/DEBIAN/copyright
cp ./build/monocoque ./PKG_SOURCE/usr/bin/monocoque cp ./build/monocoque ./PKG_SOURCE/usr/bin/monocoque
cp ./build/gmonocoque ./PKG_SOURCE/usr/bin/gmonocoque
cp ./tools/monocoque.desktop ./PKG_SOURCE/usr/share/applications/monocoque.desktop
dpkg-deb --build PKG_SOURCE monocoque-${{ matrix.os }}.deb dpkg-deb --build PKG_SOURCE monocoque-${{ matrix.os }}.deb
echo "--- control as packaged ---"
dpkg-deb --field monocoque-${{ matrix.os }}.deb Package Version
- name: Release the Package - name: Release the Package
# Only a tag can publish a release; without this a workflow_dispatch run
# builds everything correctly and then reports failure on a step that
# cannot work, which makes a green build look broken.
if: github.ref_type == 'tag' || inputs.release_tag != ''
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
tag_name: ${{ inputs.release_tag || github.ref_name }}
files: monocoque-${{ matrix.os }}.deb files: monocoque-${{ matrix.os }}.deb
# release-please writes the release body from the changelog it just
# generated. Letting this action generate its own notes as well would
# overwrite them, so it only does so for a hand-pushed tag.
generate_release_notes: ${{ inputs.release_tag == '' }}
build-monocoque-rpms: build-monocoque-rpms:
# Same self-hosted-label problem as the deb jobs above (fedora-43 was a
# runner label with nothing online to claim it); now a container on an
# ordinary GitHub-hosted runner, with fedora-44 added alongside 43.
strategy: strategy:
fail-fast: false
matrix: matrix:
os: [fedora-43] include:
runs-on: ${{ matrix.os }} - os: fedora-43
image: fedora:43
- os: fedora-44
image: fedora:44
runs-on: ubuntu-latest
container:
image: ${{ matrix.image }}
permissions: permissions:
contents: write contents: write
steps: steps:
# The -devel list is the one fedora.spec's own header comment documents,
# plus gtk3/libcurl/mesa-libGLU to match its `Requires:` line -- the spec
# configures with -DBUILD_GUI=on, so the GUI's deps are build deps too.
#
# Retried because this pulls openh264 from Fedora's third-party Cisco
# repo (ciscobinary.openh264.org), which is a single-mirror CDN outside
# Fedora's own infrastructure and has proven flaky: the fedora-43 leg of
# the first run died on `Curl error (7): Could not connect to server`
# for it while fedora-44 downloaded the very same package fine in the
# same run -- i.e. transient, not a dependency or package-name problem.
# Not disabling that repo outright: openh264 is still pulled in even
# with weak deps off, so something here requires it outright (which
# package wasn't traced) and dropping the repo would likely break
# dependency resolution rather than fix anything.
- name: Install build dependencies
run: |
for attempt in 1 2 3; do
dnf install -y --setopt=install_weak_deps=False \
rpm-build cmake gcc gcc-c++ make git curl \
pulseaudio-libs-devel argtable-devel libconfig-devel hidapi-devel \
libserialport-devel lua-devel libuv-devel libxdg-basedir-devel \
libxml2-devel procps-ng-devel gtk3-devel libcurl-devel \
mesa-libGLU-devel && exit 0
echo "dnf install failed (attempt $attempt), retrying in 15s..."
sleep 15
done
echo "dnf install failed after 3 attempts" >&2
exit 1
# submodules: recursive is new here. It didn't matter while %prep cloned
# upstream and ran `git submodule update` itself; now that the staged
# checkout is what gets built, an empty nappgui_src fails configure with
# "does not contain a CMakeLists.txt file". Missed by the local fedora:44
# rehearsal, which mounted a working tree that already had its submodules.
- uses: actions/checkout@v4
with:
submodules: recursive
- name: create rpmbuild dirs - name: create rpmbuild dirs
run: mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} run: mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
- name: get spec file
run: curl -o ~/fedora.spec https://raw.githubusercontent.com/Spacefreak18/monocoque/refs/heads/master/tools/distro/fedora/rpm/fedora.spec # The spec's %prep used to `git clone` upstream master, so the rpm's
# contents tracked master rather than the tag being built -- an rpm built
# from a tag would quietly ship master's code. Staged here and copied by
# %prep instead, so a package's contents are the commit it was built from.
- name: Stage the checked-out tree as the rpm source
run: |
rm -rf ~/rpmbuild/SOURCES/monocoque
cp -r "$GITHUB_WORKSPACE" ~/rpmbuild/SOURCES/monocoque
rm -rf ~/rpmbuild/SOURCES/monocoque/.github
# Uses the spec from the checked-out tree instead of curl'ing it from
# upstream's master branch, so a tag builds the spec that tag contains.
#
# The spec's `Version: 0.0.5` is stale and hand-maintained -- real tags are
# well past it, so released rpms have been installing as monocoque-0.0.5.
# Stamped from the tag here rather than edited in the spec, so the file
# stays a working default for local rpmbuild while CI releases carry the
# real version. Same digit guard as the deb job: rpm versions can't start
# with a non-digit, and scratch tags / branch builds would break it.
#
# NOTE (pre-existing, deliberately not fixed here): the spec's own %prep
# still does a fresh `git clone` of upstream master for the *sources*,
# so the rpm's contents track master rather than the tag being built.
# Worth fixing separately -- it needs the spec parameterised, which is a
# bigger change than getting these jobs running again.
- name: run spec file - name: run spec file
run: rpmbuild -ba ~/fedora.spec shell: bash
env:
RELEASE_TAG: ${{ inputs.release_tag }}
run: |
v=""
# RELEASE_TAG is set only when release-please calls this workflow;
# its tag exists but the run itself is on a branch, so GITHUB_REF_*
# would report master and fall through to 0.0.0.
v="${RELEASE_TAG#v}"
[ -z "$v" ] && [ "$GITHUB_REF_TYPE" = tag ] && v="${GITHUB_REF_NAME#v}"
case "$v" in
[0-9]*) ;;
*) v="0.0.0" ;;
esac
echo "packaging as version $v"
sed -i "s/^Version:.*/Version: $v/" tools/distro/fedora/rpm/fedora.spec
rpmbuild -ba tools/distro/fedora/rpm/fedora.spec
# Glob rather than a hardcoded monocoque-0.0.5-1.x86_64.rpm: the filename
# tracks the spec's Version/Release, which is now stamped per-tag above.
- name: rename file - name: rename file
run: cp ~/rpmbuild/RPMS/x86_64/monocoque-0.0.5-1.x86_64.rpm $GITHUB_WORKSPACE/monocoque-${{ matrix.os }}.rpm run: |
cp ~/rpmbuild/RPMS/x86_64/monocoque-*.rpm "$GITHUB_WORKSPACE/monocoque-${{ matrix.os }}.rpm"
echo "--- rpm identity as built ---"
rpm -qp --qf '%{NAME} %{VERSION}-%{RELEASE}\n' "$GITHUB_WORKSPACE/monocoque-${{ matrix.os }}.rpm"
- name: Release the Package - name: Release the Package
# Only a tag can publish a release; without this a workflow_dispatch run
# builds everything correctly and then reports failure on a step that
# cannot work, which makes a green build look broken.
if: github.ref_type == 'tag' || inputs.release_tag != ''
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
tag_name: ${{ inputs.release_tag || github.ref_name }}
files: monocoque-${{ matrix.os }}.rpm files: monocoque-${{ matrix.os }}.rpm
# release-please writes the release body from the changelog it just
# generated. Letting this action generate its own notes as well would
# overwrite them, so it only does so for a hand-pushed tag.
generate_release_notes: ${{ inputs.release_tag == '' }}
build-monocoque-appimage:
# An AppImage only runs where glibc is at least as new as the one it
# linked against, so it wants the OLDEST base that can build the code --
# building on current Ubuntu silently excludes older distros and defeats
# the point of shipping one.
#
# That floor is set by the compiler, not by choice: simapi's F1 2018
# headers use C23 enum-with-fixed-underlying-type (`enum TrackID :
# uint8_t`), which GCC only implements from 13 onwards. ubuntu-22.04
# ships GCC 11 and fails outright with `expected identifier or '(' before
# ':' token` -- confirmed by a real run, and no -std= value rescues it
# (verified separately: the construct is gated on compiler version, not
# on the standard flag). ubuntu-24.04 / GCC 13 is therefore the oldest
# usable base, giving a glibc 2.39 floor.
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
# librsvg2-common and libgtk-3-bin are for linuxdeploy's gtk plugin, not
# the build itself -- it needs an svg pixbuf loader and
# gtk-update-icon-cache to assemble a self-contained GTK runtime.
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential cmake pkg-config \
libgtk-3-dev libuv1-dev libargtable2-dev libserialport-dev \
libconfig-dev libhidapi-dev liblua5.4-dev libxdg-basedir-dev \
libxml2-dev libpulse-dev libproc2-dev libcurl4-openssl-dev \
libglu1-mesa-dev \
desktop-file-utils file patchelf wget \
librsvg2-common libgtk-3-bin
# -std=gnu2x for the C23 enum syntax above: GCC 13 implements it but
# still defaults to gnu17, unlike GCC 15+ (which the container-based
# deb/rpm legs get, and which accept it without the flag). gnu2x rather
# than c2x so GNU extensions stay on -- simapi also relies on asprintf.
- name: Build
run: |
cmake -B build -DBUILD_GUI=on -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_FLAGS=-std=gnu2x -S .
cmake --build build --config Release
# Uses the install() rules rather than hand-copying binaries the way the
# deb job does -- that's what they were added for.
- name: Populate AppDir
run: |
DESTDIR="$PWD/AppDir" cmake --install build --prefix /usr
# Icon basename must match the desktop file's `Icon=monocoque`.
# tools/monocoque.svg is the square copy shared with the Flatpak
# build; the published 100x50 artwork is rejected by icon tooling.
install -Dm644 tools/monocoque.svg \
AppDir/usr/share/icons/hicolor/scalable/apps/monocoque.svg
# APPIMAGE_EXTRACT_AND_RUN=1 because these tools are themselves
# AppImages and there is no FUSE on the runner to mount them with.
- name: Build AppImage
env:
APPIMAGE_EXTRACT_AND_RUN: 1
RELEASE_TAG: ${{ inputs.release_tag }}
run: |
# An AppImage carries no package metadata, so its filename is the only
# place a version can live -- hence versioning the output name here
# rather than stamping a field as the deb/rpm jobs do. Same digit
# guard, so scratch tags and branch builds still produce a valid name.
v=""
# RELEASE_TAG is set only when release-please calls this workflow;
# its tag exists but the run itself is on a branch, so GITHUB_REF_*
# would report master and fall through to 0.0.0.
v="${RELEASE_TAG#v}"
[ -z "$v" ] && [ "$GITHUB_REF_TYPE" = tag ] && v="${GITHUB_REF_NAME#v}"
case "$v" in
[0-9]*) ;;
*) v="0.0.0" ;;
esac
export OUTPUT="monocoque-${v}-x86_64.AppImage"
echo "packaging as $OUTPUT"
wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-gtk/raw/master/linuxdeploy-plugin-gtk.sh
chmod +x linuxdeploy-x86_64.AppImage linuxdeploy-plugin-gtk.sh
# Deploy dependencies first, without packaging, so there is a populated
# AppDir to take licences from -- an AppImage distributes every library
# it bundles, so their copyright files have to travel with it. A .deb
# needs none of this because its dependencies stay separate packages.
PATH="$PWD:$PATH" ./linuxdeploy-x86_64.AppImage \
--appdir AppDir \
--plugin gtk \
--desktop-file AppDir/usr/share/applications/monocoque.desktop \
--icon-file tools/monocoque.svg
tools/appimage-collect-licenses.sh AppDir
# monocoque itself and its vendored components are built here rather
# than installed from packages, so dpkg cannot account for them.
install -Dm644 LICENSE.rst AppDir/usr/share/doc/monocoque/LICENSE.rst
install -Dm644 tools/distro/debian/dpkg/copyright AppDir/usr/share/doc/monocoque/copyright
install -Dm644 src/monocoque/mgui/nappgui_src/LICENSE AppDir/usr/share/doc/nappgui/LICENSE
# Now package the AppDir, licences included.
PATH="$PWD:$PATH" ./linuxdeploy-x86_64.AppImage \
--appdir AppDir \
--output appimage
ls -la monocoque*.AppImage
echo "--- licence files in the image ---"
find AppDir/usr/share/doc -name copyright | wc -l
- name: Upload the AppImage as a build artifact
uses: actions/upload-artifact@v4
with:
name: monocoque-x86_64.AppImage
path: monocoque*.AppImage
- name: Release the Package
# Only a tag can publish a release; without this a workflow_dispatch run
# builds everything correctly and then reports failure on a step that
# cannot work, which makes a green build look broken.
if: github.ref_type == 'tag' || inputs.release_tag != ''
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ inputs.release_tag || github.ref_name }}
files: monocoque*.AppImage
# release-please writes the release body from the changelog it just
# generated. Letting this action generate its own notes as well would
# overwrite them, so it only does so for a hand-pushed tag.
generate_release_notes: ${{ inputs.release_tag == '' }}
build-monocoque-flatpak:
# Folded in from the standalone flatpak.yml so every artifact lands on
# one release. Kept as continue-on-error because it is the newest and
# least battle-tested leg: a Flatpak failure must never turn a release
# red or block the deb/rpm/AppImage artifacts, which are independent
# jobs and upload regardless.
runs-on: ubuntu-22.04
continue-on-error: true
permissions:
contents: write
steps:
# No submodules: recursive here -- the manifest fetches simapi and
# nappgui_src itself as explicit `type: git` sources (pinned commits,
# reproducible regardless of what's checked out on disk). Confirmed
# via a failed CI run: recursive submodule checkout populates those
# paths with real .git files first, which then collides with
# flatpak-builder's own git source trying to clone into the same spot
# ("cannot overwrite non-directory .git with directory").
- uses: actions/checkout@v4
# ppa:flatpak/stable, not Ubuntu's own package: 22.04's flatpak-builder
# predates freedesktop Sdk 25.08 dropping the legacy appstream-compose
# binary in favour of `appstreamcli compose`, and fails at the finish
# stage without that fallback.
- name: Install flatpak-builder + freedesktop runtime/SDK
run: |
sudo add-apt-repository -y ppa:flatpak/stable
sudo apt-get update
sudo apt-get install -y flatpak flatpak-builder
sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
sudo flatpak install -y --noninteractive flathub org.freedesktop.Platform//25.08 org.freedesktop.Sdk//25.08
- name: Build .flatpak
working-directory: flatpak
run: |
flatpak-builder --force-clean --repo=repo build-dir io.github.spacefreak18.monocoque.yml
flatpak build-bundle repo monocoque.flatpak io.github.spacefreak18.monocoque
- name: Upload .flatpak as a build artifact
uses: actions/upload-artifact@v4
with:
name: monocoque.flatpak
path: flatpak/monocoque.flatpak
- name: Release the Package
if: github.ref_type == 'tag' || inputs.release_tag != ''
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ inputs.release_tag || github.ref_name }}
files: flatpak/monocoque.flatpak
# release-please writes the release body from the changelog it just
# generated. Letting this action generate its own notes as well would
# overwrite them, so it only does so for a hand-pushed tag.
generate_release_notes: ${{ inputs.release_tag == '' }}
#arch-build:
# runs-on: ubuntu-latest
# container:
# image: archlinux
# options: --privileged
# steps:
# - uses: actions/checkout@v3
# - name: Set proper repository url in PKGBUILD
# run: sed -i "s|%URL%|${{ github.server_url }}/${{ github.repository }}|" $GITHUB_WORKSPACE/tools/distro/arch/git/PKGBUILD
# - run: echo "$GITHUB_WORKSPACE/.github/bin" >> "$GITHUB_PATH"
# - name: Build Arch Linux package
# uses: FFY00/build-arch-package@v1
# with:
# BUILD_SCRIPT: extra-x86_64-build
# PKGBUILD: $GITHUB_WORKSPACE/tools/distro/arch/git/PKGBUILD
# OUTDIR: $HOME/arch-packages
# - name: Release the Package
# uses: softprops/action-gh-release@v1
# with:
# files: $HOME/arch-packages/monocoque*.zst
+27
View File
@@ -0,0 +1,27 @@
name: PR Build & Test
on:
pull_request:
push:
branches: [ "master" ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Checkout submodules
run: git submodule update --init --recursive
- name: Update apt
run: sudo apt update
- name: Install dependencies
# Same list as ci.yaml's ubuntu-latest leg, minus the GUI-only deps
# (libgtk-3-dev, libglu1-mesa-dev) and libcurl4-openssl-dev — this
# workflow builds the CLI + test suite only, not the GUI.
run: sudo apt install -y libuv1-dev libargtable2-dev libserialport-dev libconfig-dev libhidapi-dev liblua5.4-dev libxdg-basedir-dev libxml2-dev libpulse-dev libproc2-dev
- name: Configure CMake
run: cmake -B build -DENABLE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug
- name: Build
run: cmake --build build --config Debug
- name: Run tests
run: ctest --test-dir build --output-on-failure
+38
View File
@@ -0,0 +1,38 @@
name: release-please
on:
push:
branches: [ master ]
permissions:
contents: write
pull-requests: write
jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
# Builds and uploads the packages for the release that was just cut.
#
# This calls ci.yaml rather than relying on its `push: tags` trigger,
# because a tag pushed with the default GITHUB_TOKEN does not start a new
# workflow -- GitHub blocks that deliberately, to stop workflows triggering
# each other in a loop. Calling it keeps the whole release inside one run
# and avoids storing a personal access token on a public repository.
build-release:
needs: release-please
if: needs.release-please.outputs.release_created == 'true'
uses: ./.github/workflows/ci.yaml
with:
release_tag: ${{ needs.release-please.outputs.tag_name }}
permissions:
contents: write
+9
View File
@@ -3,3 +3,12 @@
/src/monocoque/simulatorapi/simapi /src/monocoque/simulatorapi/simapi
.vscode .vscode
# flatpak-builder outputs from local builds -- CI regenerates these.
# flatpak/repo in particular is an OSTree store: ~1000 binary objects.
/flatpak/repo/
/flatpak/build-dir/
/flatpak/.flatpak-builder/
# The bundle itself. Missing from this list once already, and a `git add -A`
# duly committed a 6MB build artifact.
*.flatpak
+3
View File
@@ -2,3 +2,6 @@
path = src/monocoque/simulatorapi/simapi path = src/monocoque/simulatorapi/simapi
url = https://github.com/spacefreak18/simapi url = https://github.com/spacefreak18/simapi
# url = git@github.com:spacefreak18/simapi # ssh is better but Microsoft requires an account # url = git@github.com:spacefreak18/simapi # ssh is better but Microsoft requires an account
[submodule "src/monocoque/mgui/nappgui_src"]
path = src/monocoque/mgui/nappgui_src
url = https://github.com/frang75/nappgui_src
+1
View File
@@ -0,0 +1 @@
{ ".": "0.3.6" }
+10
View File
@@ -120,3 +120,13 @@
... ...
obj:*/lib*/lib*.so.* obj:*/lib*/lib*.so.*
} }
{
nappgui_gbind_uninitialized
Memcheck:Cond
fun:_gbind_update_control
}
{
gtk_pango_uninitialized
Memcheck:Cond
fun:_pango_is_Vowel_Dependent
}
+72 -57
View File
@@ -1,7 +1,4 @@
# minimum CMake version required for C++20 support, among other things
cmake_minimum_required(VERSION 3.18) cmake_minimum_required(VERSION 3.18)
# detect if Monocoque is being used as a sub-project of another CMake project # detect if Monocoque is being used as a sub-project of another CMake project
if(NOT DEFINED PROJECT_NAME) if(NOT DEFINED PROJECT_NAME)
set(MONOCOQUE_SUBPROJECT OFF) set(MONOCOQUE_SUBPROJECT OFF)
@@ -13,6 +10,10 @@ endif()
SET_SOURCE_FILES_PROPERTIES( src/monocoque.c PROPERTIES LANGUAGE C) SET_SOURCE_FILES_PROPERTIES( src/monocoque.c PROPERTIES LANGUAGE C)
#set(CMAKE_BUILD_TYPE Debug) #set(CMAKE_BUILD_TYPE Debug)
option(BUILD_GUI "Build GUI support" OFF)
option(BUILD_SHARED "Build monocoque shared library" OFF)
option(ENABLE_TESTS "Build test suite" OFF)
project(monocoque) project(monocoque)
@@ -20,9 +21,25 @@ set(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed -ldl")
set(LIBUSB_INCLUDE_DIR /usr/include/libusb-1.0) set(LIBUSB_INCLUDE_DIR /usr/include/libusb-1.0)
set(LIBXML_INCLUDE_DIR /usr/include/libxml2) set(LIBXML_INCLUDE_DIR /usr/include/libxml2)
FIND_PACKAGE(Lua 5.3 REQUIRED) FIND_PACKAGE(Lua)
set(INCLUDE_DIRS ${LUA_INCLUDE_DIR}) set(INCLUDE_DIRS ${LUA_INCLUDE_DIR})
include_directories(${LUA_INCLUDE_DIR}) include_directories(${LUA_INCLUDE_DIR})
if(Lua_FOUND AND NOT TARGET Lua::Lua)
add_library(Lua::Lua INTERFACE IMPORTED)
set_target_properties(
Lua::Lua
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}"
)
endif()
message("Lua ${Lua_VERSION} found")
if (Lua_VERSION VERSION_GREATER_EQUAL "5.5.0")
add_compile_definitions(USE_LUA_55)
else()
remove_definitions(USE_LUA_55)
endif()
#FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h #FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h
# HINTS $ENV{LIBUSB_ROOT} # HINTS $ENV{LIBUSB_ROOT}
@@ -37,16 +54,46 @@ include_directories(${LUA_INCLUDE_DIR})
set(HIDAPI_WITH_LIBUSB FALSE) # surely will be used only on Linux set(HIDAPI_WITH_LIBUSB FALSE) # surely will be used only on Linux
set(BUILD_SHARED_LIBS TRUE) # HIDAPI as static library on all platforms set(BUILD_SHARED_LIBS TRUE) # HIDAPI as static library on all platforms
add_executable(monocoque src/monocoque/monocoque.c) if(BUILD_SHARED)
add_library(monocoque SHARED src/monocoque/monocoque.c)
else()
add_library(monocoque STATIC src/monocoque/monocoque.c)
endif()
add_executable(monocoque-cli src/monocoque/monocoque-cli.c)
set_target_properties(monocoque-cli PROPERTIES OUTPUT_NAME monocoque)
if(BUILD_GUI)
add_executable(monocoque-gui src/monocoque/monocoque-gui.c)
set_target_properties(monocoque-gui PROPERTIES OUTPUT_NAME gmonocoque)
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBPROCPS libprocps)
pkg_check_modules(LIBPROC2 libproc2)
if (LIBPROCPS_FOUND)
#add_compile_definitions(USE_OLD_PID_VAL=0)
elseif (LIBPROC2_FOUND)
# Some distro packagings (e.g. the freedesktop Flatpak SDK) ship a
# libproc2.pc with `Version: UNKNOWN` rather than a real version
# string. VERSION_GREATER_EQUAL against that always evaluates false,
# silently falling through to the old (pre-4.0.5) PIDS_VAL API and
# failing to compile against an actually-current libproc2 whose real
# header has already moved to the new 3-argument macro. Only take the
# old-API branch when the version string is genuinely parseable and
# actually old.
if (LIBPROC2_VERSION MATCHES "^[0-9]" AND LIBPROC2_VERSION VERSION_LESS "4.0.5")
add_compile_definitions(USE_OLD_PID_VAL=1)
endif()
else()
message(FATAL_ERROR "Either libprocps or libproc2 is required")
endif()
#if(USE_PULSEAUDIO) #if(USE_PULSEAUDIO)
#else() #else()
#endif() #endif()
message("Using pulseaudio backend...") message("Using pulseaudio backend...")
add_compile_definitions(USE_PULSEAUDIO=true) add_compile_definitions(USE_PULSEAUDIO=true)
target_link_libraries(monocoque m hidapi-hidraw pulse serialport xml2 argtable2 config gameloop helper devices slog simulatorapi uv xdg-basedir ${LUA_LIBRARY})
target_include_directories(monocoque PUBLIC config ${LIBXML_INCLUDE_DIR})
add_subdirectory(src/monocoque/gameloop) add_subdirectory(src/monocoque/gameloop)
add_subdirectory(src/monocoque/simulatorapi) add_subdirectory(src/monocoque/simulatorapi)
@@ -54,55 +101,17 @@ add_subdirectory(src/monocoque/helper)
add_subdirectory(src/monocoque/devices) add_subdirectory(src/monocoque/devices)
add_subdirectory(src/monocoque/slog) add_subdirectory(src/monocoque/slog)
#add_executable(listusb tests/testlibusb.c) target_link_libraries(monocoque m hidapi-hidraw pulse serialport xml2 argtable2 config gameloop helper devices slog simulatorapi uv xdg-basedir Lua::Lua proc2)
#target_include_directories(listusb PUBLIC) target_include_directories(monocoque PUBLIC config ${LIBXML_INCLUDE_DIR} ${LUA_INCLUDE_DIR})
#target_link_libraries(listusb portaudio hidapi-hidraw) target_link_libraries(monocoque-cli m hidapi-hidraw pulse serialport xml2 argtable2 config gameloop helper devices slog simulatorapi uv xdg-basedir Lua::Lua proc2)
#add_test(listusb list-usb-devices listusb) target_include_directories(monocoque-cli PUBLIC config ${LIBXML_INCLUDE_DIR} ${LUA_INCLUDE_DIR})
if(BUILD_GUI)
add_subdirectory(src/monocoque/mgui)
target_link_libraries(monocoque-gui m hidapi-hidraw pulse serialport xml2 argtable2 config mgui gameloop helper devices slog simulatorapi uv xdg-basedir Lua::Lua proc2)
target_include_directories(monocoque-gui PUBLIC config ${LIBXML_INCLUDE_DIR} ${LUA_INCLUDE_DIR})
endif()
#add_executable(testrevburner tests/testrevburner.c)
#target_include_directories(testrevburner PUBLIC)
#target_link_libraries(testrevburner hidapi-hidraw)
#add_test(testrevburner testrevburner)
#add_executable(listsound tests/pa_devs.c)
#target_include_directories(listsound PUBLIC)
#target_link_libraries(listsound m portaudio)
#add_test(list-sound-devices listsound)
#
#add_executable(longsine tests/patest_longsine.c)
#target_include_directories(longsine PUBLIC ${LIBXML_INCLUDE_DIR})
#target_link_libraries(longsine m portaudio)
#add_test(longsine longsine)
#
#add_executable(parserevburnerxml tests/revburnerparsetest.c)
#target_include_directories(parserevburnerxml PUBLIC ${LIBXML_INCLUDE_DIR})
#target_link_libraries(parserevburnerxml portaudio xml2)
#add_test(parserevburnerxml parserevburnerxml)
#
add_executable(setmem tests/setmem.c)
target_include_directories(setmem PUBLIC)
target_link_libraries(setmem)
add_test(setmem setmem)
add_executable(getmem tests/getmem.c)
target_include_directories(getmem PUBLIC)
target_link_libraries(getmem)
add_test(getmem getmem)
#
#add_executable(setsimdata tests/setsimdata.c)
#target_include_directories(setsimdata PUBLIC)
#target_link_libraries(setsimdata)
#add_test(setsimdata setsimdata)
#
#add_executable(hidtest tests/hidtest.c)
#target_include_directories(hidtest PUBLIC)
#target_link_libraries(hidtest hidapi-hidraw)
#add_test(hidtest hidtest)
#
add_executable(simlighttest tests/simlighttest.c)
target_include_directories(simlighttest PUBLIC)
target_link_libraries(simlighttest serialport)
add_test(simlighttest simlighttest)
# used for enabling additional compiler options if supported # used for enabling additional compiler options if supported
@@ -136,7 +145,7 @@ else() # GCC/Clang warning option
if(analyze) if(analyze)
message("-- Analyzer is on") message("-- Analyzer is on")
target_compile_options(monocoque PRIVATE -fanalyzer -Wno-analyzer-possible-null-argument -Wno-analyzer-possible-null-dereference) target_compile_options(monocoque PRIVATE -fanalyzer -Wno-analyzer-possible-null-argument -Wno-analyzer-possible-null-dereference -fsanitize=address -g -fno-omit-frame-pointer)
endif() endif()
enable_cxx_compiler_flag_if_supported("-Wall") enable_cxx_compiler_flag_if_supported("-Wall")
@@ -168,6 +177,12 @@ endif()
# unit tests --only enable if requested AND we're not building as a sub-project # unit tests --only enable if requested AND we're not building as a sub-project
if(ENABLE_TESTS AND NOT MONOCOQUE_SUBPROJECT) if(ENABLE_TESTS AND NOT MONOCOQUE_SUBPROJECT)
message(STATUS "[monocoque] Unit Tests Enabled") message(STATUS "[monocoque] Unit Tests Enabled")
add_subdirectory(tests)
enable_testing() enable_testing()
add_subdirectory(tests)
endif() endif()
install(TARGETS monocoque-cli RUNTIME DESTINATION bin)
if(BUILD_GUI)
install(TARGETS monocoque-gui RUNTIME DESTINATION bin)
endif()
install(FILES tools/monocoque.desktop DESTINATION share/applications)
+25 -4
View File
@@ -41,6 +41,20 @@ chmod +x install.sh
./install.sh ./install.sh
``` ```
### Installation on Immutable Distros (Bazzite, Silverblue, etc.)
On immutable distributions where native package managers are unavailable,
monocoque can be installed inside a distrobox container:
```bash
bash tools/distro/distrobox/install-distrobox.sh
```
This creates an Arch Linux container, installs all packages via AUR, and sets up
wrapper scripts (`start-simd`, `start-monocoque`, `test-monocoque`) in `~/.local/bin/`.
To uninstall: `bash tools/distro/distrobox/uninstall-distrobox.sh`
**TUI Manager:** **TUI Manager:**
After installation, use the interactive manager: After installation, use the interactive manager:
```bash ```bash
@@ -55,7 +69,7 @@ for installation instructions***
## Building ## Building
### Dependencies ### Dependencies (static dependencies are linked as to show their respective copyright and licenses)
- libserialport - arduino serial devices - libserialport - arduino serial devices
- hidapi - usb hid devices (hidraw) - hidapi - usb hid devices (hidraw)
- portaudio - sound devices (haptic bass shakers) - portaudio - sound devices (haptic bass shakers)
@@ -66,11 +80,18 @@ for installation instructions***
- libconfig - libconfig
- xdg-basedir - xdg-basedir
- lua - lua
- [slog](https://github.com/kala13x/slog) (static) - libproc2
- [simshmbridge](https://github.com/spacefreak18/simshmbridge) - for sims that need shared memory mapping like AC and Project Cars related. - libcurl4
- libgtk3
- libglu1-mesa
- [simapi](https://github.com/spacefreak18/simapi) - [simapi](https://github.com/spacefreak18/simapi)
- [slog](https://github.com/kala13x/slog) (static)
- [nappgui](https://github.com/frang75/nappgui_src) (static) (for GUI)
(sorta optional)
- [simshmbridge](https://github.com/spacefreak18/simshmbridge) - for sims that need shared memory mapping like AC and Project Cars related.
``` ```
pacman -Syu git cmake pulse-native-provider libxdg-basedir libserialport libconfig libuv argtable hidapi lua pacman -Syu git cmake pulse-native-provider libxdg-basedir libserialport libconfig libuv argtable hidapi lua libproc2
``` ```
This code depends on the shared memory data headers in the simapi [repo](https://github.com/spacefreak18/simapi). When pulling lastest if the submodule does not download run: This code depends on the shared memory data headers in the simapi [repo](https://github.com/spacefreak18/simapi). When pulling lastest if the submodule does not download run:
+2
View File
@@ -220,6 +220,7 @@ configs = (
config = "None"; config = "None";
baud = 115200; baud = 115200;
devpath = "/dev/simdev1"; devpath = "/dev/simdev1";
fanpower = 0.6;
}, },
{ {
@@ -228,6 +229,7 @@ configs = (
config = "None"; config = "None";
baud = 115200; baud = 115200;
devpath = "/dev/simdev1"; devpath = "/dev/simdev1";
fanpower = 0.6;
}, },
{ {
@@ -0,0 +1,9 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=Monocoque
Comment=Device manager for racing sims
Exec=gmonocoque
Icon=io.github.spacefreak18.monocoque
Terminal=false
Categories=Game;Development;
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>io.github.spacefreak18.monocoque</id>
<name>Monocoque</name>
<summary>Device manager for racing sims</summary>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0-only</project_license>
<description>
<p>
Monocoque bridges telemetry from supported racing simulators to
peripheral devices such as haptic shakers, LED strips, shift lights,
and sim wind fans, and exposes a local API for third-party dashboard
and configuration tools.
</p>
</description>
<launchable type="desktop-id">io.github.spacefreak18.monocoque.desktop</launchable>
<provides>
<binary>monocoque</binary>
<binary>gmonocoque</binary>
</provides>
<url type="homepage">https://spacefreak18.github.io/simapi/monocoque.html</url>
<url type="vcs-browser">https://github.com/Spacefreak18/monocoque</url>
<content_rating type="oars-1.1" />
<releases>
<release version="0.3.6" date="2026-08-21" />
</releases>
</component>
@@ -0,0 +1,320 @@
# Flatpak manifest for monocoque -- NOT submitted anywhere (Flathub or
# otherwise) yet. Built 2026-08-30 on the patterns proven working for the
# sibling typiql-tauri/typiql project's own Flatpak pipeline (see this
# repo's .github/workflows/flatpak.yml header for the full list of bugs
# found and fixed here, monocoque-specific beyond what that sibling project
# already covered). VERIFIED LOCALLY end-to-end: built, installed, and ran
# both `monocoque --help` and `gmonocoque` from the resulting bundle. Not
# yet verified against real CI or real hardware (HID/serial/USB device
# access under the sandbox).
#
# org.freedesktop.Platform, not org.gnome.Platform: monocoque's GUI
# (gmonocoque) only needs GTK3, which freedesktop.Platform//25.08 already
# ships -- no need for the heavier GNOME runtime typiql needs for WebKitGTK.
# simd is NOT bundled. It is a separate daemon with its own packaging, and
# monocoque does not need it inside the sandbox to use it: simapi_get_sim()
# checks /dev/shm/SIMAPI.DAT before it ever scans /proc, so a simd running on
# the host -- however it was installed -- publishes telemetry this sandbox
# reads directly. That is what --device=shm below is for, and it is the only
# thing this manifest needs in order to work with simd.
#
# simapi itself is still present, but only as monocoque's own statically
# linked submodule, exactly as upstream's CMakeLists builds it. There is no
# separate simapi module here.
app-id: io.github.spacefreak18.monocoque
runtime: org.freedesktop.Platform
runtime-version: '25.08'
sdk: org.freedesktop.Sdk
command: gmonocoque
finish-args:
# --socket=x11, not fallback-x11: nappgui (the GUI toolkit gmonocoque uses)
# goes to X11 even when a Wayland display is available. fallback-x11 is
# deliberately inactive whenever Wayland works, so under a Wayland session
# DISPLAY stayed empty and gmonocoque died with `cannot open display` --
# confirmed on a real launch, with WAYLAND_DISPLAY=wayland-0 present and the
# socket visible inside the sandbox. Granting real X11 routes it through
# XWayland instead. --socket=wayland is kept so the toolkit can use it
# directly if that ever changes.
- --socket=x11
- --socket=wayland
# Standard companion to X11 access: without it MIT-SHM is unavailable and
# some toolkits fall over.
- --share=ipc
- --socket=pulseaudio
# HID/serial/USB access for wheels, pedals, shifters, and shaker/LED
# devices -- known risk area, Flatpak's sandboxing of raw device nodes is
# not always well-behaved; needs real hardware testing, not just a
# successful build.
- --device=all
# Retained, but not for the reason first written here: the GraphQL API on
# port 9000 is served by typiql's own Rust backend, not by monocoque --
# monocoque has no network code of its own, and the only libcurl in the
# binary is vendored nappgui's HTTP module, which nothing here calls. The
# two programs talk through ~/.config/monocoque and the process table, not
# a socket. This flag can probably be dropped.
- --share=network
# monocoque builds its paths from $HOME directly (e.g.
# "%s/.config/monocoque/diameters.config" in monocoque-cli.c), not from
# XDG_CONFIG_HOME, so it reads and writes the host's real directories.
# Without these grants a real launch failed with
# Failed to read config file '/home/david/.config/monocoque/monocoque.config'
# slog_open_file: Failed to open file: /home/david/.cache/monocoque/...log
# -- the manifest previously granted no filesystem access at all. Sharing
# the real config directory is also what makes a Flatpak monocoque and a
# native one agree, and what lets typiql configure it.
- --filesystem=xdg-config/monocoque:create
- --filesystem=xdg-cache/monocoque:create
# Host /dev/shm, where a host simd publishes telemetry as POSIX shared memory
# (/dev/shm/SIMAPI.DAT, mapped in simapi's simmapper.c). This is the flag
# that makes a Flatpak monocoque work with a simd installed on the host, so
# it matters more now that simd is not bundled, not less.
#
# shm is its own device option and is NOT implied by --device=all above --
# measured, with simd running: `ls /dev/shm` inside this sandbox returned 0
# entries against 18 on the host. The failure it caused is silent rather
# than loud: `monocoque play` started, read its config, connected
# pulseaudio, and then sat at "setting initial app state" forever, never
# logging "Opening universal shared memory api", because simd looked absent.
# The identical run with this flag reached that line one second in.
- --device=shm
modules:
- name: libconfig
buildsystem: cmake-ninja
config-opts:
- -DBUILD_TESTS=OFF
# libconfig 1.7.3's CMakeLists.txt predates CMake 3.5's minimum
# version policy; confirmed via a failed local build.
- -DCMAKE_POLICY_VERSION_MINIMUM=3.5
post-install:
- sh -c 'f=$(ls -1 COPYING COPYING.txt COPYING.LIB LICENSE LICENSE.txt LICENSE.md LICENCE licence.txt 2>/dev/null | head -1); if [ -n "$f" ]; then install -Dm644 "$f" /app/share/licenses/libconfig/$(basename "$f"); else echo "WARNING: no licence file found for libconfig"; fi'
sources:
- type: archive
url: https://github.com/hyperrealm/libconfig/archive/refs/tags/v1.7.3.tar.gz
sha256: 68757e37c567fd026330c8a8449aa5f9cac08a642f213f2687186b903bd7e94e
- name: argtable2
buildsystem: cmake-ninja
config-opts:
# Same CMake-minimum-version issue as libconfig -- this one's
# CMakeLists.txt is even older (2011).
- -DCMAKE_POLICY_VERSION_MINIMUM=3.5
# 2011-era code missing <ctype.h>/<strings.h> includes for
# isspace/toupper/bzero; GCC 14+ made implicit-function-declaration a
# hard error by default where it used to just warn. Confirmed via a
# failed local build.
- -DCMAKE_C_FLAGS=-Wno-error=implicit-function-declaration
# add_library(argtable2 ...) has no explicit STATIC/SHARED, so it
# defaults to CMake's global BUILD_SHARED_LIBS (off by default -> a
# static .a). monocoque's own target_link_libraries() lists argtable2
# before helper (which needs its symbols, e.g. arg_end/arg_parse in
# parameters.c) -- fine for a shared library (all symbols always
# available regardless of link-line position, which is what real
# distro packages of this ship), but a static archive only pulls
# symbols already needed by things linked *before* it, so helper's
# later reference to argtable2's symbols never resolves. Confirmed
# via a failed local build ("undefined reference to arg_end" etc.
# from helper's parameters.c, even though libargtable2.a genuinely
# contained those symbols). Building shared instead matches how this
# dependency normally ships and avoids the link-order sensitivity
# entirely, rather than reordering every target_link_libraries() call
# in CMakeLists.txt.
- -DBUILD_SHARED_LIBS=ON
# This CMake port has no install() rules at all (confirmed: no
# CMakeLists.txt in the tree calls install()), so the default
# cmake-ninja `ninja install` step silently does nothing -- monocoque's
# own build then fails on a missing argtable2.h. build-commands here
# replace the default install step: `ninja` still does the real
# compile, then copy the header and whatever library form actually got
# built (glob rather than a fixed versioned filename, since the exact
# .so version suffix isn't predictable without a build).
build-commands:
- ninja
- install -Dm644 src/argtable2.h /app/include/argtable2.h
- |
for f in src/libargtable2.so* src/libargtable2.a; do
if [ -e "$f" ]; then cp -P "$f" /app/lib/; fi
done
true
post-install:
- sh -c 'f=$(ls -1 COPYING COPYING.txt COPYING.LIB LICENSE LICENSE.txt LICENSE.md LICENCE licence.txt 2>/dev/null | head -1); if [ -n "$f" ]; then install -Dm644 "$f" /app/share/licenses/argtable2/$(basename "$f"); else echo "WARNING: no licence file found for argtable2"; fi'
sources:
- type: archive
url: https://sourceforge.net/projects/argtable/files/argtable/argtable-2.13/argtable2-13.tar.gz/download
sha256: 8f77e8a7ced5301af6e22f47302fdbc3b1ff41f2b83c43c77ae5ca041771ddbf
dest-filename: argtable2-13.tar.gz
- name: lua5.4
# Lua's own Makefile, not CMake/autotools. `posix` (not `linux`) avoids
# a readline dependency only needed for the interactive REPL binary,
# which monocoque doesn't use -- only the library.
buildsystem: simple
build-commands:
- make posix MYCFLAGS=-fPIC
- make install INSTALL_TOP=/app
sources:
- type: archive
url: https://www.lua.org/ftp/lua-5.4.7.tar.gz
sha256: 9fbf5e28ef86c69858f6d3d34eccc32e911c1a28b4120ff3e84aaa70cfbf1e30
- name: libserialport
buildsystem: autotools
# No pre-generated configure script in the git-archive tarball, only
# autogen.sh.
build-commands:
- ./autogen.sh
post-install:
- sh -c 'f=$(ls -1 COPYING COPYING.txt COPYING.LIB LICENSE LICENSE.txt LICENSE.md LICENCE licence.txt 2>/dev/null | head -1); if [ -n "$f" ]; then install -Dm644 "$f" /app/share/licenses/libserialport/$(basename "$f"); else echo "WARNING: no licence file found for libserialport"; fi'
sources:
- type: archive
url: https://github.com/sigrokproject/libserialport/archive/refs/tags/libserialport-0.1.2.tar.gz
sha256: cbb1192a09ff31d34e7efdb17a2f50d9d1974461c0b81c29bb449515d78d8950
- name: libxdg-basedir
buildsystem: autotools
build-commands:
- ./autogen.sh
post-install:
- sh -c 'f=$(ls -1 COPYING COPYING.txt COPYING.LIB LICENSE LICENSE.txt LICENSE.md LICENCE licence.txt 2>/dev/null | head -1); if [ -n "$f" ]; then install -Dm644 "$f" /app/share/licenses/libxdg-basedir/$(basename "$f"); else echo "WARNING: no licence file found for libxdg-basedir"; fi'
sources:
- type: archive
url: https://github.com/devnev/libxdg-basedir/archive/refs/tags/libxdg-basedir-1.2.3.tar.gz
sha256: ff30c60161f7043df4dcc6e7cdea8e064e382aa06c73dcc3d1885c7d2c77451d
- name: hidapi
buildsystem: cmake-ninja
config-opts:
# Same CMake-minimum-version issue as libconfig/argtable2, despite
# this otherwise being a current, actively maintained release.
- -DCMAKE_POLICY_VERSION_MINIMUM=3.5
# monocoque links hidapi-hidraw specifically -- no need for the
# libusb-backed variant, which would pull in an extra dependency.
- -DHIDAPI_WITH_LIBUSB=OFF
- -DHIDAPI_WITH_HIDRAW=ON
post-install:
- sh -c 'f=$(ls -1 COPYING COPYING.txt COPYING.LIB LICENSE LICENSE.txt LICENSE.md LICENCE licence.txt 2>/dev/null | head -1); if [ -n "$f" ]; then install -Dm644 "$f" /app/share/licenses/hidapi/$(basename "$f"); else echo "WARNING: no licence file found for hidapi"; fi'
sources:
- type: archive
url: https://github.com/libusb/hidapi/archive/refs/tags/hidapi-0.14.0.tar.gz
sha256: a5714234abe6e1f53647dd8cba7d69f65f71c558b7896ed218864ffcf405bcbd
- name: glu
buildsystem: meson
sources:
- type: archive
url: https://gitlab.freedesktop.org/mesa/glu/-/archive/glu-9.0.3/glu-glu-9.0.3.tar.gz
sha256: 7e919cbc1b2677b01d65fc28fd36a19d1f3e23d76663020e0f3b82b991475e8b
- name: libuv
buildsystem: cmake-ninja
config-opts:
- -DBUILD_TESTING=OFF
post-install:
- sh -c 'f=$(ls -1 COPYING COPYING.txt COPYING.LIB LICENSE LICENSE.txt LICENSE.md LICENCE licence.txt 2>/dev/null | head -1); if [ -n "$f" ]; then install -Dm644 "$f" /app/share/licenses/libuv/$(basename "$f"); else echo "WARNING: no licence file found for libuv"; fi'
sources:
- type: archive
url: https://github.com/libuv/libuv/archive/refs/tags/v1.49.2.tar.gz
sha256: 388ffcf3370d4cf7c4b3a3205504eea06c4be5f9e80d2ab32d19f8235accc1cf
- name: monocoque
buildsystem: cmake-ninja
config-opts:
- -DBUILD_GUI=on
- -DCMAKE_BUILD_TYPE=Release
# CMakeLists.txt links most of the dependencies built as separate
# flatpak modules above (hidapi-hidraw, serialport, argtable2, config,
# uv, xdg-basedir) by bare library name with no explicit -L, relying on
# the linker's default search path -- fine on a real system where these
# install to /usr/lib, but /app/lib isn't in that default path.
# -DCMAKE_EXE_LINKER_FLAGS=-L/app/lib didn't work: CMake appends
# EXE_LINKER_FLAGS at the *end* of the link command, after the -l
# flags that need it, and ld resolves -l against -L directories seen
# so far, left to right -- confirmed via a failed local build showing
# the exact same missing-library set with that flag present but
# useless. LIBRARY_PATH is a GCC driver environment variable, applied
# to its search regardless of where in the command line things land.
build-options:
env:
LIBRARY_PATH: /app/lib
sources:
# One level up from flatpak/manifest.yml is the repo root -- unlike
# typiql-tauri/typiql's dual-checkout ($GITHUB_WORKSPACE containing
# two separate repos side by side, needing an extra level), monocoque
# is checked out on its own with the standard single-repo layout, so
# this doesn't need the same `../..`/`../../..` juggling that session
# got wrong twice before finding the right depth for that project.
- type: dir
path: ..
# Skip the submodule paths. If the tree being built has them checked
# out, the copy brings along a real `.git` *file* at each path, and the
# git sources below then fail to clone into the same spot: "cannot
# overwrite non-directory .git with directory". Without this the
# manifest only builds from a checkout that deliberately omits
# submodules -- which is why CI's flatpak job checks out without them,
# and why a local build right after `git submodule update --init`
# breaks. Skipping makes it work either way.
skip:
- src/monocoque/simulatorapi/simapi
- src/monocoque/mgui/nappgui_src
# Two git submodules, each pinned to the exact commit .gitmodules/the
# tree tracks -- confirmed via a failed local build that the `dir`
# source above doesn't pull submodule content in (same class of issue
# as typiql-tauri/typiql's own submodules, src/lib/per-form etc.).
- type: git
url: https://github.com/spacefreak18/simapi
commit: 50fd181681862256780b2a0f74ef4bc459ed631a
dest: src/monocoque/simulatorapi/simapi
# nappgui_src is add_subdirectory()'d directly into monocoque's own
# CMake tree (src/monocoque/mgui/CMakeLists.txt), not a separately
# installed library -- so it's a source here, not its own flatpak
# module.
- type: git
url: https://github.com/frang75/nappgui_src
commit: 29c7bd3e0243eb38aa9a7e2eafe6cf74830c4ee8
dest: src/monocoque/mgui/nappgui_src
- type: file
path: io.github.spacefreak18.monocoque.desktop
- type: file
path: io.github.spacefreak18.monocoque.metainfo.xml
# Icon comes from tools/monocoque.svg via the `dir` source above (no
# separate file source needed). That file is a square 100x100 copy of
# the upstream artwork -- the published monocoque.svg the deb job
# downloads is 100x50, which Flatpak's export rejects outright, while
# appstreamcli separately hard-requires any desktop-application to
# have an icon at all (both confirmed via failed real CI runs). It is
# padded via a <g transform="translate(0,25)">, not stretched. Shared
# with the AppImage job, which has the same square-icon need.
post-install:
# This package's own licence, the vendored nappgui's (a source rather
# than a module, so the per-module collection above misses it), and the
# notices file naming every bundled dependency's origin.
- install -Dm644 LICENSE.rst /app/share/licenses/monocoque/LICENSE.rst
# The Debian copyright file is upstream's own account of what is in this
# source tree and under what terms -- slog (MIT), nappgui (MIT), simapi
# (LGPL) -- which a bundle has to carry because it distributes all of
# them, unlike a .deb whose dependencies are separate packages. Shipped
# verbatim rather than restated.
- install -Dm644 tools/distro/debian/dpkg/copyright /app/share/licenses/monocoque/copyright
# lua and glu ship no licence file in their tarballs -- Lua's MIT text
# lives in doc/readme.html, glu's SGI Free B only in source headers -- so
# checked-in copies are installed from here, where the repo is the build
# directory. (Doing it in their own modules fails: those build
# out-of-tree, so their source directory is not the working directory.)
- install -Dm644 packaging/licenses/lua-5.4-LICENSE.txt /app/share/licenses/lua5.4/LICENSE
- install -Dm644 packaging/licenses/glu-LICENSE.txt /app/share/licenses/glu/LICENSE
- install -Dm644 src/monocoque/mgui/nappgui_src/LICENSE /app/share/licenses/nappgui/LICENSE
- sh -c 'python3 tools/generate-third-party-notices.py flatpak/io.github.spacefreak18.monocoque.yml > /tmp/notices.md && install -Dm644 /tmp/notices.md /app/share/licenses/THIRD-PARTY-NOTICES.md'
# Confirmed via a failed local build that flatpak-builder's cmake-ninja
# buildsystem runs the automatic build+install step regardless of
# whether build-commands are also given -- these run in *addition*,
# not instead. So the fix for simlighttest (a stray, always-on scratch
# test executable that failed to link -lserialport, a pre-existing
# CMakeLists.txt bug unrelated to Flatpak) had to be commenting it out
# at the source, not overriding the build here. With that gone, the
# automatic build+install (using this repo's own new install() rules)
# already installs monocoque/gmonocoque correctly; these build-commands
# only need to add the extra files CMake doesn't know about.
build-commands:
- install -Dm644 io.github.spacefreak18.monocoque.desktop /app/share/applications/io.github.spacefreak18.monocoque.desktop
- install -Dm644 io.github.spacefreak18.monocoque.metainfo.xml /app/share/metainfo/io.github.spacefreak18.monocoque.metainfo.xml
- install -Dm644 tools/monocoque.svg /app/share/icons/hicolor/scalable/apps/io.github.spacefreak18.monocoque.svg
+2 -2
View File
@@ -95,7 +95,7 @@ install_dependencies() {
case $DISTRO in case $DISTRO in
arch|manjaro) arch|manjaro)
local deps="yder libuv argtable libserialport libconfig hidapi lua libxdg-basedir mingw-w64-gcc" local deps="yder libuv argtable libserialport libconfig hidapi lua54 libpulse pkgconfig libxdg-basedir mingw-w64-gcc"
# Check if yay is available for AUR # Check if yay is available for AUR
if command -v yay &> /dev/null; then if command -v yay &> /dev/null; then
@@ -223,7 +223,7 @@ build_simapi() {
mkdir -p build mkdir -p build
cd build cd build
cmake .. cmake -DINSTALL_SYSTEMD_SERVICE=off ..
make -j$(nproc) make -j$(nproc)
log_info "Installing simapi library..." log_info "Installing simapi library..."
+40
View File
@@ -0,0 +1,40 @@
SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice including the dates of first publication and
either this permission notice or a reference to
http://oss.sgi.com/projects/FreeB/
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Except as contained in this notice, the name of Silicon Graphics, Inc.
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization from
Silicon Graphics, Inc.
/
Pixel storage modes */
typedef struct {
GLint pack_alignment;
GLint pack_row_length;
GLint pack_skip_rows;
GLint pack_skip_pixels;
GLint pack_lsb_first;
GLint pack_swap_bytes;
GLint pack_skip_images;
GLint pack_image_height;
+19
View File
@@ -0,0 +1,19 @@
Copyright © 1994–2026 Lua.org, PUC-Rio.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+31
View File
@@ -0,0 +1,31 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"packages": {
".": {
"release-type": "simple",
"package-name": "monocoque",
"//": "The 79 existing tags are bare semver (0.3.6), not v0.3.6, and there is only one package here. Both flags keep release-please tagging in that same series instead of starting a parallel one.",
"include-component-in-tag": false,
"include-v-in-tag": false,
"//bump": "While the major version is 0, a breaking change bumps the minor (0.3.6 -> 0.4.0) rather than declaring 1.0.0. Reaching 1.0.0 stays a deliberate act.",
"bump-minor-pre-major": true,
"draft": false,
"changelog-sections": [
{ "type": "feat", "section": "Features" },
{ "type": "fix", "section": "Bug Fixes" },
{ "type": "perf", "section": "Performance Improvements", "hidden": true },
{ "type": "revert", "section": "Reverts", "hidden": true },
{ "type": "docs", "section": "Documentation", "hidden": true },
{ "type": "style", "section": "Styles", "hidden": true },
{ "type": "chore", "section": "Miscellaneous Chores", "hidden": true },
{ "type": "refactor", "section": "Code Refactoring", "hidden": true },
{ "type": "test", "section": "Tests", "hidden": true },
{ "type": "build", "section": "Build System", "hidden": true },
{ "type": "ci", "section": "Continuous Integration", "hidden": true }
]
}
}
}
-1
View File
@@ -1 +0,0 @@
../../monocoque/simulatorapi/simapi/simapi/simdata.h
+166
View File
@@ -0,0 +1,166 @@
# Makefile for Arduino based scketches
#
# Copyright 2020 Valerio Di Giampietro http://va.ler.io v@ler.io
# MIT License - see License.txt file
#
# This Makefile uses the arduino-cli, the Arduino command line interface
# and has been designed and tested to run on Linux, not on Windows.
# Probably it will run on a Mac, but it has not been tested.
#
# Please note that:
#
# 1. each sketch must reside in his own folder with this Makefile
#
# 2. the main make targets are:
# - all compiles and upload
# - compile compiles only
# - upload upload via serial port, compile if the binary file is
# not available
# - ota upload Over The Air, automatically find the device
# IP address using the IOT_NAME (device hostname)
# - clean clean the build directory
# - find find OTA updatable devices on the local subnet
# - requirements it the file "requirements.txt" exists it will
# install the libraries listed in this file
#
# default is "all"
#
# 3. it gets the name of the sketch using the wildcard make command;
# the name is *.ino; this means that you must have ONLY a file
# with .ino extension, otherwise this makefile will break. This
# also means that you can use this Makefile, almost unmodified,
# for any sketch as long as you keep a single .ino file in each
# folder
#
# 4. you can split your project in multiple files, if you wish,
# using a single .ino file and multiple .h files, that you can
# include in the .ino file with an '#include "myfile.h"'
# directive
#
# Optionally some environment variables can be set:
#
# FQBN Fully Qualified Board Name; if not set in the environment
# it will be assigned a value in this makefile
#
# SERIAL_DEV Serial device to upload the sketch; if not set in the
# environment it will be assigned:
# /dev/ttyUSB0 if it exists, or
# /dev/ttyACM0 if it exists, or
# unknown
#
# IOT_NAME Name of the IOT device; if not set in the environment
# it will be assigned a value in this makefile. This is
# very useful for OTA update, the device will be searched
# on the local subnet using this name
#
# OTA_PORT Port used by OTA update; if not set in the environment
# it will be assigned the default value of 8266 in this
# makefile
#
# OTA_PASS Password used for OTA update; if not set in the environment
# it will be assigned the default value of an empty string
#
# V verbose flag; can be 0 (quiet) or 1 (verbose); if not set
# in the environment it will be assigned a default value
# in this makefile
MAKE_DIR := $(PWD)
#
# ----- setup wor Wemos D1 mini -----
#FQBN ?= esp8266:esp8266:d1_mini
#IOT_NAME ?= esp8266-meteo
#OTA_PORT ?= 8266
#OTA_PASS ?=
# ----- setup for Arduino Uno
FQBN ?= arduino:avr:uno
# ----- ---------------------
V ?= 0
VFLAG =
ifeq "$(V)" "1"
VFLAG =-v
endif
ifndef SERIAL_DEV
ifneq (,$(wildcard /dev/ttyUSB0))
SERIAL_DEV = /dev/ttyUSB0
else ifneq (,$(wildcard /dev/ttyACM0))
SERIAL_DEV = /dev/ttyACM0
else
SERIAL_DEV = unknown
endif
endif
BUILD_DIR := $(subst :,.,build/$(FQBN))
SRC := $(wildcard *.ino)
HDRS := $(wildcard *.h)
BIN := $(BUILD_DIR)/$(SRC).bin
ELF := $(BUILD_DIR)/$(SRC).elf
$(info FQBN is [${FQBN}])
$(info IOT_NAME is [${IOT_NAME}])
$(info OTA_PORT is [${OTA_PORT}])
$(info OTA_PASS is [${OTA_PASS}])
$(info V is [${V}])
$(info VFLAG is [${VFLAG}])
$(info MAKE_DIR is [${MAKE_DIR}])
$(info BUILD_DIR is [${BUILD_DIR}])
$(info SRC is [${SRC}])
$(info HDRS is [${HDRS}])
$(info BIN is [${BIN}])
$(info SERIAL_DEV is [${SERIAL_DEV}])
all: $(ELF) upload
.PHONY: all
compile: $(ELF)
.PHONY: compile
$(ELF): $(SRC) $(HDRS)
arduino-cli compile -b $(FQBN) $(VFLAG)
@if which arduino-manifest.pl; \
then echo "---> Generating manifest.txt"; \
arduino-manifest.pl -b $(FQBN) $(SRC) $(HDRS) > manifest.txt; \
else echo "---> If you want to generate manifest.txt, listing used libraries and their versions,"; \
echo "---> please install arduino-manifest, see https://github.com/digiampietro/arduino-manifest"; \
fi
upload:
@if [ ! -c $(SERIAL_DEV) ] ; \
then echo "---> ERROR: Serial Device not available, please set the SERIAL_DEV environment variable" ; \
else echo "---> Uploading sketch\n"; \
arduino-cli upload -b $(FQBN) -p $(SERIAL_DEV) $(VFLAG); \
fi
ota:
@PLAT_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.platform.path' | awk -F= '{print $$2}'` ; \
PY_PATH=`arduino-cli compile -b $(FQBN) --show-properties | grep '^runtime.tools.python3.path' | awk -F= '{print $$2}'` ; \
IOT_IP=`avahi-browse _arduino._tcp --resolve --parsable --terminate|grep -i ';$(IOT_NAME);'|grep ';$(OTA_PORT);'| awk -F\; '{print $$8}'|head -1`; \
BINFILE=$(wildcard $(BUILD_DIR)/$(SRC)*bin); \
echo "PLAT_PATH is [$$PLAT_PATH]" ; \
echo "PY_PATH: is [$$PY_PATH]" ; \
echo "IOT_IP: is [$$IOT_IP]" ; \
echo "BINFILE: is [$$BINFILE]" ; \
if [ "$$IOT_IP" = "" ] ; \
then echo "Unable to find device IP. Check that the IOT_NAME environment variable is correctly set. Use 'make find' to search devices"; \
else echo "---> Uploading Over The Air"; \
$$PY_PATH/python3 $$PLAT_PATH/tools/espota.py -i $$IOT_IP -p $(OTA_PORT) --auth=$(OTA_PASS) -f $$BINFILE ;\
fi
clean:
@echo "---> Cleaning the build directory"
rm -rf build
find:
avahi-browse _arduino._tcp --resolve --parsable --terminate
requirements:
@if [ -e requirements.txt ]; \
then while read -r i ; do echo ; \
echo "---> Installing " '"'$$i'"' ; \
arduino-cli lib install "$$i" ; \
done < requirements.txt ; \
else echo "---> MISSING requirements.txt file"; \
fi
+15
View File
@@ -0,0 +1,15 @@
#ifndef _SIMWINDDATA_H
#define _SIMWINDDATA_H
#include <stdint.h>
#include <stdbool.h>
typedef struct
{
uint8_t velocity;
uint8_t fanpower;
}
SimWindData;
#endif
+56
View File
@@ -0,0 +1,56 @@
#include "simwind.h"
// ============================================================
// Configuration
// ============================================================
#define BAUD_RATE 115200
#define PWM_PIN_1 9 // Fan PWM output pins
#define PWM_PIN_2 10 //
#define PWM_MAX 320 // Max PWM value for 25kHz (16MHz / 25kHz / 2)
#define SPEED_MIN 0 // Min speed (mph) — fans off below this
#define SPEED_MAX 100 // Max speed (mph) — full fan speed at this
#define SPEED_THRESHOLD 5 // Below this speed fans are off
// ============================================================
#define BYTE_SIZE sizeof(SimWindData)
int velocity = 0;
int fanpower = 0;
void setup() {
Serial.begin(BAUD_RATE);
// Configure Timer1 for Phase Correct PWM at 25kHz duty cycle
// Mode 10: Phase Correct PWM, TOP = ICR1
// WGM13:WGM12:WGM11:WGM10 = 1:0:1:0
TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11);
TCCR1B = _BV(WGM13) | _BV(CS10); // <-- WGM13 restored, no prescaler
ICR1 = PWM_MAX; // TOP = 320 → 16MHz / (2 * 320) = 25kHz
pinMode(PWM_PIN_1, OUTPUT);
pinMode(PWM_PIN_2, OUTPUT);
OCR1A = 0;
OCR1B = 0;
}
void loop() {
char buff[BYTE_SIZE];
if (Serial.available() >= BYTE_SIZE) {
if (Serial.readBytes(buff, BYTE_SIZE) != BYTE_SIZE) return;
union { SimWindData data; char bytes[BYTE_SIZE]; } u;
memcpy(u.bytes, buff, BYTE_SIZE);
velocity = u.data.velocity;
fanpower = u.data.fanpower;
}
int pwm = 0;
if (velocity > SPEED_THRESHOLD) {
// Square root scaling for slightly stronger sensation at lower speeds
// fanpower (0-255) scales the overall intensity
float powerScale = (float)fanpower / 255.0;
pwm = (int)(sqrt((float)velocity / SPEED_MAX) * PWM_MAX * powerScale);
pwm = constrain(pwm, 0, PWM_MAX);
}
OCR1A = pwm;
OCR1B = pwm;
}
+6 -2
View File
@@ -13,8 +13,6 @@ set(devices_source_files
tachdevice.c tachdevice.c
wheeldevice.h wheeldevice.h
wheeldevice.c wheeldevice.c
usbhapticdevice.h
usbhapticdevice.c
hapticeffect.h hapticeffect.h
hapticeffect.c hapticeffect.c
sound.h sound.h
@@ -25,6 +23,8 @@ set(devices_source_files
usb/cslelitev3.c usb/cslelitev3.c
usb/simagicp1000.h usb/simagicp1000.h
usb/simagicp1000.c usb/simagicp1000.c
usb/simnetpedals.h
usb/simnetpedals.c
usb/wheels/logitechg29.h usb/wheels/logitechg29.h
usb/wheels/logitechg29.c usb/wheels/logitechg29.c
usb/wheels/cammusc5.h usb/wheels/cammusc5.h
@@ -42,8 +42,12 @@ set(devices_source_files
serial/moza.c serial/moza.c
serial/moza_new.h serial/moza_new.h
serial/moza_new.c serial/moza_new.c
serial/moza_ks_pro_wheel.h
serial/moza_ks_pro_wheel.c
) )
include_directories("." "usb" "sound" "serial") include_directories("." "usb" "sound" "serial")
add_library(devices STATIC ${devices_source_files}) add_library(devices STATIC ${devices_source_files})
set_property(TARGET devices PROPERTY POSITION_INDEPENDENT_CODE ON)
+111 -36
View File
@@ -6,6 +6,7 @@
#include <math.h> #include <math.h>
#include "usbhapticdevice.h" #include "usbhapticdevice.h"
#include "simdevice.h"
#include "../../helper/confighelper.h" #include "../../helper/confighelper.h"
#include "../../simulatorapi/simapi/simapi/simdata.h" #include "../../simulatorapi/simapi/simapi/simdata.h"
#include "../../simulatorapi/simapi/simapi/simmapper.h" #include "../../simulatorapi/simapi/simapi/simmapper.h"
@@ -194,9 +195,66 @@ void getTyreDiameter(SimData* simdata)
} }
} }
int initializeHapticEffect(HapticEffect* h, HapticEffectSettings* hs, MonocoqueSettings* ms)
double slipeffect(SimData* simdata, int effecttype, int tyre, double threshold, int useconfig, int* configcheck, char* configfile)
{ {
switch (hs->effect_type)
{
case (EFFECT_ENGINERPM):
h->effecttype = EFFECT_ENGINERPM;
slogi("Initializing haptic effect for engine vibrations.");
break;
case (EFFECT_GEARSHIFT):
h->effecttype = EFFECT_GEARSHIFT;
slogi("Initializing haptic effect for gear shift vibrations.");
break;
case (EFFECT_TYRESLIP):
h->effecttype = EFFECT_TYRESLIP;
slogi("Initializing haptic effect for tyre slip vibrations.");
break;
case (EFFECT_TYRELOCK):
h->effecttype = EFFECT_TYRELOCK;
slogi("Initializing haptic effect for tyre lock vibrations.");
break;
case (EFFECT_ABSBRAKES):
h->effecttype = EFFECT_ABSBRAKES;
slogi("Initializing haptic effect for abs vibrations.");
break;
case (EFFECT_SUSPENSION):
h->effecttype = EFFECT_SUSPENSION;
slogi("Initializing haptic effect for suspension vibrations.");
break;
}
h->tyre = hs->tyre;
slogi("Haptic effect: %i %i, tyre %i %i", h->effecttype, hs->effect_type, h->tyre, hs->tyre);
h->threshold = hs->threshold;
h->modulationType = hs->modulation;
h->basefrequency = hs->frequency;
h->frequencyMax = hs->frequencyMax;
h->baseamplitude = hs->amplitude;
h->amplitudeMax = hs->amplitudeMax;
h->motorposition = hs->motorposition;
h->duration = hs->duration;
slogt("haptic duration: %f", h->duration);
slogt("haptic base frequency: %i", h->basefrequency);
slogt("haptic base amplitude: %i", h->baseamplitude);
slogt("haptic motorposition: %i", h->motorposition);
h->useconfig = ms->useconfig;
h->configcheck = &ms->configcheck;
h->tyrediameterconfig = ms->tyre_diameter_config;
}
double slipeffect(SimData* simdata, HapticEffect* h, int useconfig, int* configcheck, char* configfile)
{
int effecttype = h->effecttype;
int tyre = h->tyre;
double threshold = h->threshold;
double play = 0; double play = 0;
double wheelslip[4]; double wheelslip[4];
wheelslip[0] = 0; wheelslip[0] = 0;
@@ -204,51 +262,68 @@ double slipeffect(SimData* simdata, int effecttype, int tyre, double threshold,
wheelslip[2] = 0; wheelslip[2] = 0;
wheelslip[3] = 0; wheelslip[3] = 0;
slogt("wheel vibration calculation with wheel config set to %i configchecked %i configfile %s car %s sim %i", useconfig, *configcheck, configfile, simdata->car, simdata->simexe); int sim_slip_ratio =0;
for (int i = 0; i < 4; i++)
switch (effecttype)
{ {
case (EFFECT_TYRESLIP): sim_slip_ratio = abs(simdata->tyreslipratio[i]);
case (EFFECT_TYRELOCK): }
case (EFFECT_ABSBRAKES):
if(sim_slip_ratio == 0)
{
//slogt("wheel vibration calculation with wheel config set to %i configchecked %i configfile %s car %s sim %i", useconfig, *configcheck, configfile, simdata->car, simdata->simexe);
switch (effecttype)
{
case (EFFECT_TYRESLIP):
case (EFFECT_TYRELOCK):
case (EFFECT_ABSBRAKES):
if(hasTyreDiameter(simdata)==true) if(hasTyreDiameter(simdata)==true)
{
double Speedms = kmhtoms * simdata->velocity;
slogt("attempting wheel slip calculation");
if (Speedms > minspeedinms)
{ {
for(int i = 0; i < 4; i++) double Speedms = kmhtoms * simdata->velocity;
slogt("attempting wheel slip calculation");
if (Speedms > minspeedinms)
{ {
wheelslip[i] = (Speedms - simdata->tyrediameter[i] * simdata->tyreRPS[i] / 2) / Speedms; for(int i = 0; i < 4; i++)
{
wheelslip[i] = (Speedms - simdata->tyrediameter[i] * simdata->tyreRPS[i] / 2) / Speedms;
}
} }
} else
else
{
for(int i = 0; i < 4; i++)
{ {
wheelslip[i] = 0; for(int i = 0; i < 4; i++)
{
wheelslip[i] = 0;
}
} }
slogt("wheelslip values are %f %f %f %f", wheelslip[0], wheelslip[1], wheelslip[2], wheelslip[3]);
slogt("velocities (x,y,z) are %f %f %f", simdata->Xvelocity, simdata->Yvelocity, simdata->Zvelocity);
} }
slogt("wheelslip values are %f %f %f %f", wheelslip[0], wheelslip[1], wheelslip[2], wheelslip[3]); break;
slogt("velocities (x,y,z) are %f %f %f", simdata->Xvelocity, simdata->Yvelocity, simdata->Zvelocity); case EFFECT_SUSPENSION:
} break;
break; default:
case EFFECT_SUSPENSION: slogw("Unknown effect type %i", effecttype);
break; }
default: if(simdata->Yvelocity <= 0)
slogd("Unknown effect type"); {
return 0;
}
if(simdata->Zvelocity > 1 || simdata->Zvelocity < -1)
{
return 0;
}
}
else
{
for (int i = 0; i < 4; i++)
{
wheelslip[i] = simdata->tyreslipratio[i];
}
slogt("wheelslip values from sim are %f %f %f %f", wheelslip[0], wheelslip[1], wheelslip[2], wheelslip[3]);
} }
if(simdata->Yvelocity <= 0)
{
return 0;
}
if(simdata->Zvelocity > 1 || simdata->Zvelocity < -1)
{
return 0;
}
switch (effecttype) switch (effecttype)
{ {
+34 -3
View File
@@ -2,21 +2,52 @@
#define _HAPTICEFFECT_H #define _HAPTICEFFECT_H
#include <stdio.h> #include <stdio.h>
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simapi/simapi/simdata.h"
typedef enum
{
HAPTIC_EFFECT_MODULATION_NONE = 0,
HAPTIC_EFFECT_MODULATION_FREQUENCY = 1,
HAPTIC_EFFECT_MODULATION_AMPLIFY = 2,
}
HapticEffectModulationType;
typedef struct
{
uint32_t curr_frequency;
uint32_t curr_amplitude;
double curr_duration;
uint32_t last_gear;
}
CurrentEffectData;
typedef struct typedef struct
{ {
double threshold;
VibrationEffectType effecttype; VibrationEffectType effecttype;
MonocoqueTyreIdentifier tyre; MonocoqueTyreIdentifier tyre;
HapticEffectModulationType modulationType;
uint32_t volume;
double duration;
double threshold;
uint32_t motorposition;
uint32_t basefrequency;
uint32_t frequencyMax;
uint32_t baseamplitude;
uint32_t amplitudeMax;
CurrentEffectData live_effect;
int useconfig; int useconfig;
int* configcheck; int* configcheck;
char* tyrediameterconfig; char* tyrediameterconfig;
} }
HapticEffect; HapticEffect;
double slipeffect(SimData* simdata, int effecttype, int tyre, double threshold, int useconfig, int* configcheck, char* configfile); int initializeHapticEffect(HapticEffect* h, HapticEffectSettings* hs, MonocoqueSettings* ms);
double slipeffect(SimData* simdata, HapticEffect* h, int useconfig, int* configcheck, char* configfile);
bool hasTyreDiameter(SimData* simdata); bool hasTyreDiameter(SimData* simdata);
int loadtyreconfig(SimData* simdata, char* configfile, bool setDiameters); int loadtyreconfig(SimData* simdata, char* configfile, bool setDiameters);
int savetyreconfig(SimData* simdata, char* configfile); int savetyreconfig(SimData* simdata, char* configfile);
+6 -7
View File
@@ -128,21 +128,20 @@ int GetNumberOfLeds(SerialDevice* serialdevice, int* numlights)
int arduino_init(SerialDevice* serialdevice, const char* portdev) int arduino_init(SerialDevice* serialdevice, const char* portdev)
{ {
serialdevice->id = monocoque_serial_open(serialdevice, portdev); int id = monocoque_serial_open(serialdevice, portdev);
return serialdevice->id; if (id < 0) return id;
serialdevice->id = id;
return 0;
} }
int arduino_custom_init(SerialDevice* serialdevice, const char* portdev, const char* luafile, bool uselights) int arduino_custom_init(SerialDevice* serialdevice, const char* portdev, const char* luafile, bool uselights)
{ {
serialdevice->id = monocoque_serial_open(serialdevice, portdev); int error = arduino_init(serialdevice, portdev);
int numlights = 0; int numlights = 0;
monocoque_serial_device serialdev = monocoque_serial_devices[serialdevice->id];
int error = 0;
if(uselights == true) if(uselights == true)
{ {
error = GetNumberOfLeds(serialdevice, &numlights); error = GetNumberOfLeds(serialdevice, &numlights);
@@ -179,7 +178,7 @@ int arduino_custom_init(SerialDevice* serialdevice, const char* portdev, const c
slogi("LUA config setup successful."); slogi("LUA config setup successful.");
return serialdevice->id; return 0;
} }
@@ -64,6 +64,39 @@ int simdata_to_lua(lua_State *L, SimData* simdata) {
} }
lua_setfield(L, -2, "pd"); lua_setfield(L, -2, "pd");
lua_pushnumber(L, simdata->gas);
lua_setfield(L, -2, "gas");
lua_pushnumber(L, simdata->fuel);
lua_setfield(L, -2, "fuel");
lua_pushnumber(L, simdata->turboboost);
lua_setfield(L, -2, "turboboost");
lua_newtable(L);
for(int i = 0; i < 4; i++)
{
lua_pushnumber(L, simdata->tyreRPS[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "tyreRPS");
lua_newtable(L);
for(int i = 0; i < 4; i++)
{
lua_pushnumber(L, simdata->tyrediameter[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "tyrediameter");
lua_newtable(L);
for(int i = 0; i < 4; i++)
{
lua_pushnumber(L, simdata->tyretemp[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "tyretemp");
return 0; // Return the table to Lua return 0; // Return the table to Lua
} }
+8 -3
View File
@@ -1,9 +1,14 @@
#ifndef _ARDUINOLEDLUA_H #ifndef _ARDUINOLEDLUA_H
#define _ARDUINOLEDLUA_H #define _ARDUINOLEDLUA_H
#include "lua.h" //#include <lua.h>
#include "lauxlib.h" #include <lauxlib.h>
#include "lualib.h" #include <lualib.h>
#ifdef USE_LUA_55
/* open all libraries */
#define luaL_openlibs(L) luaL_openselectedlibs(L, ~0, 0)
#endif
#include "../../simulatorapi/simapi/simapi/simdata.h" #include "../../simulatorapi/simapi/simapi/simdata.h"
+4 -2
View File
@@ -84,6 +84,8 @@ int moza_update(SerialDevice* serialdevice, unsigned short rpm, unsigned short m
int moza_init(SerialDevice* serialdevice, const char* portdev) int moza_init(SerialDevice* serialdevice, const char* portdev)
{ {
serialdevice->id = monocoque_serial_open(serialdevice, portdev); int id = monocoque_serial_open(serialdevice, portdev);
return serialdevice->id; if (id < 0) return id;
serialdevice->id = id;
return 0;
} }
@@ -0,0 +1,142 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <hidapi/hidapi.h>
#include "moza.h"
#include "moza_ks_pro_wheel.h"
#include "../serialadapter.h"
#include "../../slog/slog.h"
#define MOZA_TIMEOUT 1000
#define MOZA_MAGIC_VALUE 0x0d
#define MOZA_RPM_MASK_TEMPLATE {0x7e, 0x06, 0x3f, 0x17, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
#define MOZA_RPM_MASK_PAYLOAD_SIZE 11
#define MOZA_RPM_COLOR_PAYLOAD_1 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0xff, 0, 0, 4, 0xff, 0, 0, 0}
#define MOZA_RPM_COLOR_PAYLOAD_2 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 5, 0xff, 0, 0, 6, 0xff, 0, 0, 7, 0xff, 0, 0, 8, 0xff, 0, 0, 9, 0xff, 0, 0, 0}
#define MOZA_RPM_COLOR_PAYLOAD_3 {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 10, 0xff, 0x7f, 0, 11, 0xff, 0x7f, 0, 12, 0xff, 0x7f, 0, 13, 0, 0, 0xff, 14, 0, 0, 0xff, 0}
#define MOZA_BTN_COLOR_PAYLOAD_1 {0x7e, 0x16, 0x3f, 0x17, 0x19, 1, 0, 0xff, 0, 0, 1, 0xff, 0, 0, 2, 0xff, 0, 0, 3, 0xff, 0, 0, 4, 0xff, 0, 0, 0}
#define MOZA_BTN_COLOR_PAYLOAD_2 {0x7e, 0x16, 0x3f, 0x17, 0x19, 1, 5, 0xff, 0, 0, 6, 0xff, 0, 0, 7, 0xff, 0, 0, 8, 0xff, 0, 0, 9, 0xff, 0, 0, 0}
#define MOZA_COLOR_PAYLOAD_SIZE 27
#define MOZA_FLAG_COLOR_YELLOW_LEFT {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 0, 0xff, 0xaa, 0, 1, 0xff, 0xaa, 0, 2, 0xff, 0xaa, 0, 0, 0xff, 0xaa, 0, 1, 0xff, 0xaa, 0, 0}
#define MOZA_FLAG_COLOR_YELLOW_RIGHT {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 15, 0xff, 0xaa, 0, 16, 0xff, 0xaa, 0, 17, 0xff, 0xaa, 0, 15, 0xff, 0xaa, 0, 16, 0xff, 0xaa, 0, 0}
#define MOZA_FLAG_COLOR_OFF_LEFT {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}
#define MOZA_FLAG_COLOR_OFF_RIGHT {0x7e, 0x16, 0x3f, 0x17, 0x19, 0, 15, 0, 0, 0, 16, 0, 0, 0, 17, 0, 0, 0, 15, 0, 0, 0, 16, 0, 0, 0, 0}
#define BIT(nr) (1UL << (nr))
int moza_ks_pro_wheel_update(SerialDevice* serialdevice, SimData* simData)
{
static uint8_t last_flag = 0xff;
unsigned char bytes[] = MOZA_RPM_MASK_TEMPLATE;
int size = MOZA_RPM_MASK_PAYLOAD_SIZE;
float perctflt = ((float)simData->rpms/(float)simData->maxrpm)*100;
int perct = round(perctflt);
if (perct >= 98 && (simData->mtick >> 7) & 1 == 1) perct = 0;
if (perct >= 75)
bytes[6] |= BIT(3);
if (perct >= 77)
bytes[6] |= BIT(4);
if (perct >= 79)
bytes[6] |= BIT(5);
if (perct >= 81)
bytes[6] |= BIT(6);
if (perct >= 83)
bytes[6] |= BIT(7);
if (perct >= 85)
bytes[7] |= BIT(0);
if (perct >= 87)
bytes[7] |= BIT(1);
if (perct >= 89)
bytes[7] |= BIT(2);
if (perct >= 91)
bytes[7] |= BIT(3);
if (perct >= 93)
bytes[7] |= BIT(4);
if (perct >= 95)
bytes[7] |= BIT(5);
if (perct >= 97)
bytes[7] |= BIT(6);
if (simData->playerflag == SIMAPI_FLAG_YELLOW) {
bytes[6] |= BIT(0) | BIT(1) | BIT(2); // left 3 flag LEDs (indices 0, 1, 2)
bytes[7] |= BIT(7); // right flag LED (index 15)
bytes[8] |= BIT(0) | BIT(1); // right flag LEDs (indices 16, 17)
}
bytes[10] = moza_checksum(bytes, size);
int result = 1;
if (serialdevice->port)
{
if (simData->playerflag != last_flag) {
last_flag = simData->playerflag;
if (simData->playerflag == SIMAPI_FLAG_YELLOW) {
unsigned char fl[] = MOZA_FLAG_COLOR_YELLOW_LEFT;
unsigned char fr[] = MOZA_FLAG_COLOR_YELLOW_RIGHT;
fl[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(fl, MOZA_COLOR_PAYLOAD_SIZE);
fr[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(fr, MOZA_COLOR_PAYLOAD_SIZE);
monocoque_serial_write(serialdevice->id, fl, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, fr, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
} else {
unsigned char fl[] = MOZA_FLAG_COLOR_OFF_LEFT;
unsigned char fr[] = MOZA_FLAG_COLOR_OFF_RIGHT;
fl[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(fl, MOZA_COLOR_PAYLOAD_SIZE);
fr[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(fr, MOZA_COLOR_PAYLOAD_SIZE);
monocoque_serial_write(serialdevice->id, fl, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, fr, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
}
}
slogd("copying %i bytes to moza device", MOZA_RPM_MASK_PAYLOAD_SIZE);
slogt("writing bytes %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x from rpm %i maxrpm %i", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], simData->rpms, simData->maxrpm);
result = monocoque_serial_write(serialdevice->id, bytes, size, MOZA_TIMEOUT);
}
return result;
}
int moza_ks_pro_wheel_init(SerialDevice* serialdevice, const char* portdev)
{
serialdevice->id = monocoque_serial_open(serialdevice, portdev);
if (serialdevice->id == -1) return serialdevice->id;
unsigned char p1[] = MOZA_RPM_COLOR_PAYLOAD_1;
unsigned char p2[] = MOZA_RPM_COLOR_PAYLOAD_2;
unsigned char p3[] = MOZA_RPM_COLOR_PAYLOAD_3;
unsigned char p4[] = MOZA_BTN_COLOR_PAYLOAD_1;
unsigned char p5[] = MOZA_BTN_COLOR_PAYLOAD_2;
p1[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p1, MOZA_COLOR_PAYLOAD_SIZE);
p2[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p2, MOZA_COLOR_PAYLOAD_SIZE);
p3[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p3, MOZA_COLOR_PAYLOAD_SIZE);
p4[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p4, MOZA_COLOR_PAYLOAD_SIZE);
p5[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p5, MOZA_COLOR_PAYLOAD_SIZE);
monocoque_serial_write(serialdevice->id, p1, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, p2, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, p3, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, p4, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, p5, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
unsigned char p6[] = MOZA_FLAG_COLOR_OFF_RIGHT;
p6[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p6, MOZA_COLOR_PAYLOAD_SIZE);
monocoque_serial_write(serialdevice->id, p6, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
return serialdevice->id;
}
@@ -0,0 +1,10 @@
#ifndef _MOZA_KS_PRO_WHEEL_H
#define _MOZA_KS_PRO_WHEEL_H
#include "../serialdevice.h"
#include "../simdevice.h"
int moza_ks_pro_wheel_update(SerialDevice* serialdevice, SimData* simData);
int moza_ks_pro_wheel_init(SerialDevice* serialdevice, const char* portdev);
#endif
+9 -7
View File
@@ -74,8 +74,9 @@ int moza_new_update(SerialDevice* serialdevice, SimData* simData)
int moza_new_init(SerialDevice* serialdevice, const char* portdev) int moza_new_init(SerialDevice* serialdevice, const char* portdev)
{ {
serialdevice->id = monocoque_serial_open(serialdevice, portdev); int id = monocoque_serial_open(serialdevice, portdev);
if (serialdevice->id == -1) return serialdevice->id; if (id < 0) return id;
serialdevice->id = id;
unsigned char p1[] = MOZA_RPM_COLOR_PAYLOAD_1; unsigned char p1[] = MOZA_RPM_COLOR_PAYLOAD_1;
unsigned char p2[] = MOZA_RPM_COLOR_PAYLOAD_2; unsigned char p2[] = MOZA_RPM_COLOR_PAYLOAD_2;
@@ -87,10 +88,11 @@ int moza_new_init(SerialDevice* serialdevice, const char* portdev)
p3[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p3, MOZA_COLOR_PAYLOAD_SIZE); p3[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p3, MOZA_COLOR_PAYLOAD_SIZE);
p4[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p4, MOZA_COLOR_PAYLOAD_SIZE); p4[MOZA_COLOR_PAYLOAD_SIZE-1] = moza_checksum(p4, MOZA_COLOR_PAYLOAD_SIZE);
monocoque_serial_write(serialdevice->id, p1, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT); unsigned char* payloads[] = {p1, p2, p3, p4};
monocoque_serial_write(serialdevice->id, p2, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT); for (int i = 0; i < 4; i++) {
monocoque_serial_write(serialdevice->id, p3, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT); int result = monocoque_serial_write(serialdevice->id, payloads[i], MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT);
monocoque_serial_write(serialdevice->id, p4, MOZA_COLOR_PAYLOAD_SIZE, MOZA_TIMEOUT); if (result < 0) return result;
}
return serialdevice->id; return 0;
} }
+32 -38
View File
@@ -27,10 +27,13 @@ int check(enum sp_return result)
error_message = sp_last_error_message(); error_message = sp_last_error_message();
sloge("error: serial write failed: %s", error_message); sloge("error: serial write failed: %s", error_message);
sp_free_error_message(error_message); sp_free_error_message(error_message);
return result;
case SP_ERR_SUPP: case SP_ERR_SUPP:
printf("Error: Not supported.\n"); printf("Error: Not supported.\n");
return result;
case SP_ERR_MEM: case SP_ERR_MEM:
printf("Error: Couldn't allocate memory.\n"); printf("Error: Couldn't allocate memory.\n");
return result;
case SP_OK: case SP_OK:
default: default:
return result; return result;
@@ -114,23 +117,30 @@ int monocoque_input_wait(uint8_t serialdevicenum)
return sp_input_waiting(monocoque_serial_dev.port); return sp_input_waiting(monocoque_serial_dev.port);
} }
int monocoque_serial_write(uint8_t serialdevicenum, void* data, size_t size, int timeout) // Helper function to get and validate device
static monocoque_serial_device* monocoque_get_serial_device(uint8_t serialdevicenum)
{ {
monocoque_serial_device* dev = &monocoque_serial_devices[serialdevicenum];
slogt("serial device id %i", serialdevicenum); slogt("serial device id %i", serialdevicenum);
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevicenum]; slogt("port name: %s, busy %i, open %i, openfail %i", dev->portname, dev->busy, dev->open, dev->openfail);
slogt("port name: %s, busy %i, open %i, openfail %i", monocoque_serial_dev.portname, monocoque_serial_dev.busy, monocoque_serial_dev.open, monocoque_serial_dev.busy); if(dev->port == NULL)
if(monocoque_serial_dev.port == NULL)
{ {
sloge("port is null"); sloge("port is null");
} }
return dev;
}
int monocoque_serial_write(uint8_t serialdevicenum, void* data, size_t size, int timeout)
{
monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
int result = -1; int result = -1;
if(monocoque_serial_dev.busy == false && monocoque_serial_dev.open == true) if(dev->busy == false && dev->open == true)
{ {
monocoque_serial_dev.busy = true; dev->busy = true;
result = sp_blocking_write(monocoque_serial_dev.port, data, size, timeout); result = sp_blocking_write(dev->port, data, size, timeout);
} }
else else
{ {
@@ -138,67 +148,51 @@ int monocoque_serial_write(uint8_t serialdevicenum, void* data, size_t size, int
result = -1; result = -1;
} }
slogt("write result is %i", result); slogt("write result is %i", result);
monocoque_serial_dev.busy = false; dev->busy = false;
return result; return result;
} }
int monocoque_serial_write_block(uint8_t serialdevicenum, void* data, size_t size, int timeout) int monocoque_serial_write_block(uint8_t serialdevicenum, void* data, size_t size, int timeout)
{ {
slogt("serial device id %i", serialdevicenum); monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevicenum];
slogt("port name: %s, busy %i, open %i, openfail %i", monocoque_serial_dev.portname, monocoque_serial_dev.busy, monocoque_serial_dev.open, monocoque_serial_dev.busy);
if(monocoque_serial_dev.port == NULL)
{
sloge("port is null");
}
int result = -1; int result = -1;
if(monocoque_serial_dev.open == true) if(dev->open == true)
{ {
while(monocoque_serial_dev.busy == true) while(dev->busy == true)
{ {
slogt("hopefully this doesn't happen long"); slogt("hopefully this doesn't happen long");
continue; continue;
} }
monocoque_serial_dev.busy = true; dev->busy = true;
result = sp_blocking_write(monocoque_serial_dev.port, data, size, timeout); result = sp_blocking_write(dev->port, data, size, timeout);
slogi("actually performed write"); slogi("actually performed write");
} }
monocoque_serial_dev.busy = false; dev->busy = false;
return result; return result;
} }
int monocoque_serial_read_block(uint8_t serialdevicenum, void* data, size_t size, int timeout) int monocoque_serial_read_block(uint8_t serialdevicenum, void* data, size_t size, int timeout)
{ {
slogt("serial device id %i", serialdevicenum); monocoque_serial_device* dev = monocoque_get_serial_device(serialdevicenum);
monocoque_serial_device monocoque_serial_dev = monocoque_serial_devices[serialdevicenum];
slogt("port name: %s, busy %i, open %i, openfail %i", monocoque_serial_dev.portname, monocoque_serial_dev.busy, monocoque_serial_dev.open, monocoque_serial_dev.busy);
if(monocoque_serial_dev.port == NULL)
{
sloge("port is null");
}
int result = -1; int result = -1;
if(monocoque_serial_dev.open == true) if(dev->open == true)
{ {
while(monocoque_serial_dev.busy == true) while(dev->busy == true)
{ {
slogt("hopefully this doesn't happen long"); slogt("hopefully this doesn't happen long");
continue; continue;
} }
monocoque_serial_dev.busy = true; dev->busy = true;
result = sp_blocking_read(monocoque_serial_dev.port, data, size, timeout); result = sp_blocking_read(dev->port, data, size, timeout);
slogi("actually performed read"); slogi("actually performed read");
} }
monocoque_serial_dev.busy = false; dev->busy = false;
return result; return result;
} }
+46 -25
View File
@@ -11,6 +11,7 @@
#include "serial/arduino.h" #include "serial/arduino.h"
#include "serial/moza.h" #include "serial/moza.h"
#include "serial/moza_new.h" #include "serial/moza_new.h"
#include "serial/moza_ks_pro_wheel.h"
#include "../helper/parameters.h" #include "../helper/parameters.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simapi/simapi/simdata.h"
#include "../slog/slog.h" #include "../slog/slog.h"
@@ -26,6 +27,10 @@ int serial_wheel_update(SimDevice* this, SimData* simdata)
moza_new_update(serialdevice, simdata); moza_new_update(serialdevice, simdata);
break; break;
case SERIALDEV__MOZA_KS_PRO_WHEEL:
moza_ks_pro_wheel_update(serialdevice, simdata);
break;
case SIMDEVSUBTYPE_MOZAR5: case SIMDEVSUBTYPE_MOZAR5:
default: default:
moza_update(serialdevice, simdata->rpms, simdata->maxrpm); moza_update(serialdevice, simdata->rpms, simdata->maxrpm);
@@ -56,7 +61,6 @@ int arduino_custom_updater(SimDevice* this, SimData* simdata)
int arduino_customled_updater(SimDevice* this, SimData* simdata) int arduino_customled_updater(SimDevice* this, SimData* simdata)
{ {
SerialDevice* serialdevice = (void *) this->derived; SerialDevice* serialdevice = (void *) this->derived;
arduino_customled_update(serialdevice, simdata); arduino_customled_update(serialdevice, simdata);
return 0; return 0;
@@ -118,8 +122,9 @@ int arduino_simwind_update(SimDevice* this, SimData* simdata)
serialdevice->u.simwinddata.velocity = ceil(simdata->velocity*KPHTOMPH); serialdevice->u.simwinddata.velocity = ceil(simdata->velocity*KPHTOMPH);
slogt("Updating arduino device speed to %i", serialdevice->u.simwinddata.velocity); slogt("Updating arduino device speed to %i", serialdevice->u.simwinddata.velocity);
// this can be added to the config, all config dependent can be added to init
serialdevice->u.simwinddata.fanpower = (int)0.6 * 255; serialdevice->u.simwinddata.fanpower = (int)(serialdevice->fanpower * 255);
slogt("Sending fanpower: %i (from config: %f)", serialdevice->u.simwinddata.fanpower, serialdevice->fanpower);
size_t size = sizeof(SimWindData); size_t size = sizeof(SimWindData);
arduino_update(serialdevice, &serialdevice->u.simwinddata, size); arduino_update(serialdevice, &serialdevice->u.simwinddata, size);
@@ -134,8 +139,12 @@ int arduino_simhaptic_update(SimDevice* this, SimData* simdata)
int result = 1; int result = 1;
slogt("arduino haptic device updating"); slogt("arduino haptic device updating");
serialdevice->u.simhapticdata.motor1 = 0;
serialdevice->u.simhapticdata.motor2 = 0;
serialdevice->u.simhapticdata.motor3 = 0;
serialdevice->u.simhapticdata.motor4 = 0;
double play = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig); double play = slipeffect(simdata, &this->hapticeffect, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
double rplay = play; double rplay = play;
play = play * serialdevice->ampfactor; play = play * serialdevice->ampfactor;
@@ -143,25 +152,27 @@ int arduino_simhaptic_update(SimDevice* this, SimData* simdata)
{ {
play = 1.0; play = 1.0;
} }
int effectspeed = ceil(255 * play); uint8_t effectspeed = ceil(255 * play);
int motor = serialdevice->motorsposition; uint8_t motor = this->hapticeffect.motorposition;
if (play != serialdevice->state) if (play != serialdevice->hapticstate)
{ {
// motor 1
if (motor == 0 || motor == 4 || motor == 7 || motor == 8 || motor == 10 || motor == 11 || motor == 13 || motor == 14) if (motor == 0 || motor == 4 || motor == 7 || motor == 8 || motor == 10 || motor == 11 || motor == 13 || motor == 14)
{ {
serialdevice->u.simhapticdata.effect1 = effectspeed; serialdevice->u.simhapticdata.effect1 = effectspeed;
serialdevice->u.simhapticdata.motor1 = 1; serialdevice->u.simhapticdata.motor1 = 1;
slogt("Updating arduino haptic device with effect type %i speed motor speed %i on motor %i from original effect %f with ampfactor %f", this->hapticeffect.effecttype, serialdevice->u.simhapticdata.effect3, serialdevice->motorsposition, rplay, serialdevice->ampfactor); slogt("Updating arduino haptic device with effect type %i speed motor speed %i on motor %i from original effect %f with ampfactor %f", this->hapticeffect.effecttype, serialdevice->u.simhapticdata.effect1, 1, rplay, serialdevice->ampfactor);
} }
// motor 3
if (motor == 2 || motor == 6 || motor == 8 || motor == 9 || motor == 10 || motor == 11 || motor == 12 || motor == 14) if (motor == 2 || motor == 6 || motor == 8 || motor == 9 || motor == 10 || motor == 11 || motor == 12 || motor == 14)
{ {
serialdevice->u.simhapticdata.effect3 = effectspeed; serialdevice->u.simhapticdata.effect3 = effectspeed;
serialdevice->u.simhapticdata.motor3 = 1; serialdevice->u.simhapticdata.motor3 = 1;
slogt("Updating arduino haptic device with effect type %i speed motor speed %i on motor %i from original effect %f with ampfactor %f", this->hapticeffect.effecttype, serialdevice->u.simhapticdata.effect3, serialdevice->motorsposition, rplay, serialdevice->ampfactor); slogt("Updating arduino haptic device with effect type %i speed motor speed %i on motor %i from original effect %f with ampfactor %f", this->hapticeffect.effecttype, serialdevice->u.simhapticdata.effect3, 3, rplay, serialdevice->ampfactor);
} }
serialdevice->state = play; serialdevice->hapticstate = play;
} }
size_t size = sizeof(SimHapticData); size_t size = sizeof(SimHapticData);
@@ -185,7 +196,7 @@ int serialdev_free(SimDevice* this)
serialdevice->u.simhapticdata.motor3 = 1; serialdevice->u.simhapticdata.motor3 = 1;
serialdevice->u.simhapticdata.effect4 = 0; serialdevice->u.simhapticdata.effect4 = 0;
serialdevice->u.simhapticdata.motor4 = 1; serialdevice->u.simhapticdata.motor4 = 1;
serialdevice->state = 0; serialdevice->hapticstate = 0;
size_t size = sizeof(SimHapticData); size_t size = sizeof(SimHapticData);
monocoque_serial_write_block(serialdevice->id, &serialdevice->u.simhapticdata, size, 9000); monocoque_serial_write_block(serialdevice->id, &serialdevice->u.simhapticdata, size, 9000);
@@ -221,7 +232,7 @@ int serial_wheel_free(SimDevice* this)
int serialdev_init(SerialDevice* serialdevice, DeviceSettings* ds, SimInfo* siminfo) int serialdev_init(SerialDevice* serialdevice, DeviceSettings* ds, SimInfo* siminfo)
{ {
slogi("initializing serial device on port %s to %i...", ds->serialdevsettings.portdev, ds->serialdevsettings.baud); slogi("initializing serial device on port %s to %i...", ds->dev, ds->serialdevsettings.baud);
int error = 0; int error = 0;
@@ -234,14 +245,17 @@ int serialdev_init(SerialDevice* serialdevice, DeviceSettings* ds, SimInfo* simi
// the wheel stuff assumed it was a usb // the wheel stuff assumed it was a usb
//error = wheeldev_init(&serialdevice->u.wheeldevice, ds); //error = wheeldev_init(&serialdevice->u.wheeldevice, ds);
// maybe this call a more generic serial wheel init first // maybe this call a more generic serial wheel init first
error = moza_init(serialdevice, ds->serialdevsettings.portdev); error = moza_init(serialdevice, ds->dev);
break; break;
case SERIALDEV__MOZA_NEW: case SERIALDEV__MOZA_NEW:
error = moza_new_init(serialdevice, ds->serialdevsettings.portdev); error = moza_new_init(serialdevice, ds->dev);
break;
case SERIALDEV__MOZA_KS_PRO_WHEEL:
error = moza_ks_pro_wheel_init(serialdevice, ds->dev);
break; break;
case ARDUINODEV__SIMLED__CUSTOM: case ARDUINODEV__SIMLED__CUSTOM:
serialdevice->m.device_specific_config_file = strdup(ds->specific_config_file); serialdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
error = arduino_custom_init(serialdevice, ds->serialdevsettings.portdev, serialdevice->m.device_specific_config_file, true); error = arduino_custom_init(serialdevice, ds->dev, serialdevice->m.device_specific_config_file, true);
if(error < 0) if(error < 0)
{ {
free(serialdevice->m.device_specific_config_file); free(serialdevice->m.device_specific_config_file);
@@ -249,17 +263,17 @@ int serialdev_init(SerialDevice* serialdevice, DeviceSettings* ds, SimInfo* simi
break; break;
case ARDUINODEV__CUSTOM: case ARDUINODEV__CUSTOM:
serialdevice->m.device_specific_config_file = strdup(ds->specific_config_file); serialdevice->m.device_specific_config_file = strdup(ds->specific_config_file);
error = arduino_custom_init(serialdevice, ds->serialdevsettings.portdev, serialdevice->m.device_specific_config_file, false); error = arduino_custom_init(serialdevice, ds->dev, serialdevice->m.device_specific_config_file, false);
if(error < 0) if(error < 0)
{ {
free(serialdevice->m.device_specific_config_file); free(serialdevice->m.device_specific_config_file);
} }
break; break;
case ARDUINODEV__SIMLED: case ARDUINODEV__SIMLED:
error = arduino_custom_init(serialdevice, ds->serialdevsettings.portdev, NULL, false); error = arduino_custom_init(serialdevice, ds->dev, NULL, false);
break; break;
default: default:
error = arduino_init(serialdevice, ds->serialdevsettings.portdev); error = arduino_init(serialdevice, ds->dev);
break; break;
} }
@@ -318,7 +332,8 @@ SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimIn
case (SIMDEVTYPE_SIMWIND): case (SIMDEVTYPE_SIMWIND):
this->devicetype = ARDUINODEV__SIMWIND; this->devicetype = ARDUINODEV__SIMWIND;
this->m.vtable = &arduino_simwind_vtable; this->m.vtable = &arduino_simwind_vtable;
slogi("Initializing arduino devices for sim wind."); this->fanpower = ds->serialdevsettings.fanpower;
slogi("Initializing arduino devices for sim wind with fanpower: %f", this->fanpower);
break; break;
case (SIMDEVTYPE_ARDUINOCUSTOM): case (SIMDEVTYPE_ARDUINOCUSTOM):
this->devicetype = ARDUINODEV__CUSTOM; this->devicetype = ARDUINODEV__CUSTOM;
@@ -340,8 +355,9 @@ SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimIn
this->u.simhapticdata.effect2 = 0; this->u.simhapticdata.effect2 = 0;
this->u.simhapticdata.effect3 = 0; this->u.simhapticdata.effect3 = 0;
this->u.simhapticdata.effect4 = 0; this->u.simhapticdata.effect4 = 0;
this->state = 0; this->hapticstate = 0;
this->ampfactor = ds->serialdevsettings.ampfactor; this->ampfactor = ds->serialdevsettings.ampfactor;
this->fanpower = ds->serialdevsettings.fanpower;
slogi("Initializing arduino device for haptic effects."); slogi("Initializing arduino device for haptic effects.");
break; break;
case (SIMDEVTYPE_SERIALWHEEL): case (SIMDEVTYPE_SERIALWHEEL):
@@ -354,6 +370,11 @@ SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimIn
this->devicetype = SERIALDEV__MOZA_NEW; this->devicetype = SERIALDEV__MOZA_NEW;
this->m.vtable = &serialwheel_vtable; this->m.vtable = &serialwheel_vtable;
break; break;
case SIMDEVSUBTYPE_MOZA_KS_PRO_WHEEL:
slogi("Initializing new firmware Moza serial wheel device.");
this->devicetype = SERIALDEV__MOZA_KS_PRO_WHEEL;
this->m.vtable = &serialwheel_vtable;
break;
case SIMDEVSUBTYPE_MOZAR5: case SIMDEVSUBTYPE_MOZAR5:
default: default:
//move this stuff to wheeldevice or it's own serial wheel device module //move this stuff to wheeldevice or it's own serial wheel device module
@@ -366,10 +387,10 @@ SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimIn
if(this->devicetype == ARDUINODEV__HAPTIC && error == 0) if(this->devicetype == ARDUINODEV__HAPTIC && error == 0)
{ {
this->m.hapticeffect.threshold = ds->threshold; this->m.hapticeffect.threshold = ds->hapticsettings.threshold;
this->m.hapticeffect.effecttype = ds->effect_type; this->m.hapticeffect.effecttype = ds->hapticsettings.effect_type;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type); slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->hapticsettings.effect_type);
this->m.hapticeffect.tyre = ds->tyre; this->m.hapticeffect.tyre = ds->hapticsettings.tyre;
this->m.hapticeffect.useconfig = ms->useconfig; this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck; this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config; this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
@@ -383,7 +404,7 @@ SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimIn
if (error != 0) if (error != 0)
{ {
slogw("Did not initialize usb device due to error code %i", error); slogw("Did not initialize serial device due to error code %i", error);
free(this); free(this);
return NULL; return NULL;
} }
+2 -1
View File
@@ -13,7 +13,8 @@ typedef enum
ARDUINODEV__SIMLED = 4, ARDUINODEV__SIMLED = 4,
ARDUINODEV__SIMLED__CUSTOM = 5, ARDUINODEV__SIMLED__CUSTOM = 5,
ARDUINODEV__CUSTOM = 6, ARDUINODEV__CUSTOM = 6,
SERIALDEV__MOZA_NEW = 7 SERIALDEV__MOZA_NEW = 7,
SERIALDEV__MOZA_KS_PRO_WHEEL = 8
} }
SerialDeviceType; SerialDeviceType;
+12 -9
View File
@@ -4,7 +4,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <hidapi/hidapi.h> #include <hidapi/hidapi.h>
#include "lua.h" #include <lua.h>
#include "usbdevice.h" #include "usbdevice.h"
#include "sounddevice.h" #include "sounddevice.h"
@@ -66,7 +66,8 @@ typedef struct
int endled; int endled;
int baudrate; int baudrate;
double ampfactor; double ampfactor;
double state; double fanpower;
double hapticstate;
union union
{ {
SimWindData simwinddata; SimWindData simwinddata;
@@ -87,10 +88,10 @@ SerialDevice* new_serial_device(DeviceSettings* ds, MonocoqueSettings* ms, SimIn
/********* USB HID Devices *****/ /********* USB HID Devices *****/
typedef enum typedef enum
{ {
USBDEV_UNKNOWN = 0, USBDEV_UNKNOWN = 0,
USBDEV_TACHOMETER = 1, USBDEV_TACHOMETER = 1,
USBDEV_GENERICHAPTIC = 2, USBDEV_GENERICHAPTIC = 2,
USBDEV_WHEEL = 3 USBDEV_WHEEL_OR_PEDALS = 3
} }
USBType; USBType;
@@ -99,12 +100,14 @@ typedef struct
SimDevice m; SimDevice m;
int id; int id;
hid_device* handle; hid_device* handle;
FILE* filehandle;
char* dev;
double hapticstate;
USBType type; USBType type;
union union
{ {
TachDevice tachdevice; TachDevice tachdevice;
WheelDevice wheeldevice; WheelDevice wheeldevice;
USBGenericHapticDevice hapticdevice;
} u; } u;
} }
USBDevice; USBDevice;
@@ -121,7 +124,6 @@ typedef struct
SimDevice m; SimDevice m;
int id; int id;
int configcheck; int configcheck;
SoundEffectModulationType modulationType;
SoundData sounddata; SoundData sounddata;
//#ifdef USE_PULSEAUDIO //#ifdef USE_PULSEAUDIO
pa_stream *stream; pa_stream *stream;
@@ -153,7 +155,8 @@ int simdevfree(SimDevice* this);
/****** USB SubTypes ****************/ /****** USB SubTypes ****************/
int wheeldev_update(USBDevice* usbdevice, SimData* simdata); int wheelhapticdev_update(SimDevice* this, SimData* simdata);
int wheeldev_update(SimDevice* this, SimData* simdata);
int wheeldev_init(USBDevice* usbdevice, DeviceSettings* ds); int wheeldev_init(USBDevice* usbdevice, DeviceSettings* ds);
int wheeldev_free(USBDevice* usbdevice); int wheeldev_free(USBDevice* usbdevice);
+49 -91
View File
@@ -15,13 +15,15 @@
#include "../helper/parameters.h" #include "../helper/parameters.h"
#include "../slog/slog.h" #include "../slog/slog.h"
int gear_sound_set(SoundDevice* sounddevice, SimData* simdata) int gear_sound_set(SimDevice* this, SimData* simdata)
{ {
SoundDevice* sounddevice = (void *) this->derived;
if (sounddevice->sounddata.last_gear != simdata->gear && simdata->gear > 1) if (sounddevice->sounddata.last_gear != simdata->gear && simdata->gear > 1)
{ {
//sounddevice->sounddata.gear_sound_data = 3.14; //sounddevice->sounddata.gear_sound_data = 3.14;
sounddevice->sounddata.curr_frequency = sounddevice->sounddata.frequency; sounddevice->sounddata.curr_frequency = this->hapticeffect.basefrequency;
sounddevice->sounddata.curr_amplitude = sounddevice->sounddata.amplitude; sounddevice->sounddata.curr_amplitude = this->hapticeffect.baseamplitude;
sounddevice->sounddata.curr_duration = 0; sounddevice->sounddata.curr_duration = 0;
} }
sounddevice->sounddata.last_gear = simdata->gear; sounddevice->sounddata.last_gear = simdata->gear;
@@ -30,27 +32,29 @@ int gear_sound_set(SoundDevice* sounddevice, SimData* simdata)
} }
double modulate(SoundDevice* sounddevice, double raw_effect, SoundEffectModulationType modulation) double modulate(SimDevice* this, double raw_effect, HapticEffectModulationType modulation)
{ {
SoundDevice* sounddevice = (void *) this->derived;
double modulated_effect = raw_effect; double modulated_effect = raw_effect;
switch (modulation) switch (modulation)
{ {
case SOUND_EFFECT_MODULATION_FREQUENCY: case HAPTIC_EFFECT_MODULATION_FREQUENCY:
modulated_effect = ((sounddevice->sounddata.frequencyMax - sounddevice->sounddata.frequency) * raw_effect) + sounddevice->sounddata.frequency; modulated_effect = ((this->hapticeffect.frequencyMax - this->hapticeffect.basefrequency) * raw_effect) + this->hapticeffect.basefrequency;
sounddevice->sounddata.curr_frequency = modulated_effect; sounddevice->sounddata.curr_frequency = modulated_effect;
sounddevice->sounddata.curr_amplitude = sounddevice->sounddata.amplitude; sounddevice->sounddata.curr_amplitude = this->hapticeffect.baseamplitude;
slogt("set curr frequency to %f from raw effect %f and base frequency %i", sounddevice->sounddata.curr_frequency, raw_effect, sounddevice->sounddata.frequency); slogt("set curr frequency to %f from raw effect %f and base frequency %i", sounddevice->sounddata.curr_frequency, raw_effect, this->hapticeffect.basefrequency);
break; break;
case SOUND_EFFECT_MODULATION_AMPLIFY: case HAPTIC_EFFECT_MODULATION_AMPLIFY:
modulated_effect = ((sounddevice->sounddata.amplitudeMax - sounddevice->sounddata.amplitude) * raw_effect) + sounddevice->sounddata.amplitude; modulated_effect = ((this->hapticeffect.amplitudeMax - this->hapticeffect.baseamplitude) * raw_effect) + this->hapticeffect.baseamplitude;
sounddevice->sounddata.curr_amplitude = modulated_effect; sounddevice->sounddata.curr_amplitude = modulated_effect;
sounddevice->sounddata.curr_frequency = sounddevice->sounddata.frequency; sounddevice->sounddata.curr_frequency = this->hapticeffect.basefrequency;
slogt("set curr amplitude to %i from raw effect %f and base amplitude %i", sounddevice->sounddata.curr_amplitude, raw_effect, sounddevice->sounddata.amplitude); slogt("set curr amplitude to %i from raw effect %f and base amplitude %i", sounddevice->sounddata.curr_amplitude, raw_effect, this->hapticeffect.baseamplitude);
break; break;
case SOUND_EFFECT_MODULATION_NONE: case HAPTIC_EFFECT_MODULATION_NONE:
default: default:
sounddevice->sounddata.curr_frequency = sounddevice->sounddata.frequency; sounddevice->sounddata.curr_frequency = this->hapticeffect.basefrequency;
sounddevice->sounddata.curr_amplitude = sounddevice->sounddata.amplitude; sounddevice->sounddata.curr_amplitude = this->hapticeffect.baseamplitude;
break; break;
} }
@@ -64,19 +68,19 @@ int sounddev_engine_update(SimDevice* this, SimData* simdata)
double effect = ((double)simdata->rpms/60)/((double)simdata->maxrpm/60); double effect = ((double)simdata->rpms/60)/((double)simdata->maxrpm/60);
slogt("Set base effect of %f from rpms of %i", effect, simdata->rpms); slogt("Set base effect of %f from rpms of %i", effect, simdata->rpms);
modulate(sounddevice, effect, sounddevice->modulationType); modulate(this, effect, this->hapticeffect.modulationType);
} }
int sounddev_tyreslip_update(SimDevice* this, SimData* simdata) int sounddev_tyreslip_update(SimDevice* this, SimData* simdata)
{ {
SoundDevice* sounddevice = (void *) this->derived; SoundDevice* sounddevice = (void *) this->derived;
double effect = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig); double effect = slipeffect(simdata, &this->hapticeffect, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
slogt("Updating sound device frequency from original tyre slip effect %f", effect); slogt("Updating sound device frequency from original %i tyre slip effect %f", this->hapticeffect.effecttype, effect);
if (effect > 0) if (effect > 0)
{ {
modulate(sounddevice, effect, sounddevice->modulationType); modulate(this, effect, this->hapticeffect.modulationType);
} }
else else
{ {
@@ -91,12 +95,12 @@ int sounddev_tyrelock_update(SimDevice* this, SimData* simdata)
{ {
SoundDevice* sounddevice = (void *) this->derived; SoundDevice* sounddevice = (void *) this->derived;
double play = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig); double play = slipeffect(simdata, &this->hapticeffect, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
slogt("Updating sound device frequency from original tyre lock effect %f", play); slogt("Updating sound device frequency from original tyre lock effect %f", play);
if (play > 0) if (play > 0)
{ {
modulate(sounddevice, play, sounddevice->modulationType); modulate(this, play, this->hapticeffect.modulationType);
} }
else else
{ {
@@ -109,12 +113,12 @@ int sounddev_tyrelock_update(SimDevice* this, SimData* simdata)
int sounddev_absbrakes_update(SimDevice* this, SimData* simdata) int sounddev_absbrakes_update(SimDevice* this, SimData* simdata)
{ {
SoundDevice* sounddevice = (void *) this->derived; SoundDevice* sounddevice = (void *) this->derived;
double play = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig); double play = slipeffect(simdata, &this->hapticeffect, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
slogt("Updating sound device frequency from original abs effect %f", play); slogt("Updating sound device frequency from original abs effect %f", play);
if (play > 0) if (play > 0)
{ {
modulate(sounddevice, play, sounddevice->modulationType); modulate(this, play, this->hapticeffect.modulationType);
} }
else else
{ {
@@ -128,13 +132,13 @@ int sounddev_suspension_update(SimDevice* this, SimData* simdata)
{ {
SoundDevice* sounddevice = (void *) this->derived; SoundDevice* sounddevice = (void *) this->derived;
double effect = slipeffect(simdata, this->hapticeffect.effecttype, this->hapticeffect.tyre, this->hapticeffect.threshold, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig); double effect = slipeffect(simdata, &this->hapticeffect, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
effect = effect * 10; effect = effect * 10;
slogt("Updating sound device output from original suspension travel effect %f", effect); slogt("Updating sound device output from original suspension travel effect %f", effect);
if (effect > 0) if (effect > 0)
{ {
effect = modulate(sounddevice, effect, sounddevice->modulationType); effect = modulate(this, effect, this->hapticeffect.modulationType);
} }
else else
{ {
@@ -147,9 +151,7 @@ int sounddev_suspension_update(SimDevice* this, SimData* simdata)
int sounddev_gearshift_update(SimDevice* this, SimData* simdata) int sounddev_gearshift_update(SimDevice* this, SimData* simdata)
{ {
SoundDevice* sounddevice = (void *) this->derived; gear_sound_set(this, simdata);
gear_sound_set(sounddevice, simdata);
} }
@@ -163,28 +165,17 @@ int sounddev_free(SimDevice* this)
return 0; return 0;
} }
int sounddev_init(SoundDevice* sounddevice, const char* devname, MonocoqueTyreIdentifier tyre, SoundDeviceSettings sds) int sounddev_init(SoundDevice* sounddevice, const char* devname, SoundDeviceSettings sds)
{ {
slogi("initializing standalone sound device..."); slogi("initializing standalone sound device...");
slogi("volume is: %i", sds.volume); slogi("volume is: %i", sds.volume);
slogi("Modulation type: %i", sds.modulation);
slogi("frequency is: %i", sds.frequency);
slogi("frequency Max is: %i", sds.frequencyMax);
slogi("amplitude is: %i", sds.amplitude);
slogi("amplitude Max is: %i", sds.amplitudeMax);
slogi("pan is: %i", sds.pan); slogi("pan is: %i", sds.pan);
slogi("channels is: %i", sds.channels); slogi("channels is: %i", sds.channels);
slogi("duration is: %f", sds.duration);
slogi("noise is: %i", sds.noise); slogi("noise is: %i", sds.noise);
sounddevice->modulationType = sds.modulation;
sounddevice->sounddata.frequency = sds.frequency;
sounddevice->sounddata.frequencyMax = sds.frequencyMax;
sounddevice->sounddata.amplitude = sds.amplitude;
sounddevice->sounddata.amplitudeMax = sds.amplitudeMax;
sounddevice->sounddata.noise = (double)sds.noise; sounddevice->sounddata.noise = (double)sds.noise;
sounddevice->sounddata.curr_duration = 0; sounddevice->sounddata.curr_duration = 0;
@@ -198,7 +189,7 @@ int sounddev_init(SoundDevice* sounddevice, const char* devname, MonocoqueTyreId
switch (sounddevice->m.hapticeffect.effecttype) { switch (sounddevice->m.hapticeffect.effecttype) {
case (EFFECT_GEARSHIFT): case (EFFECT_GEARSHIFT):
sounddevice->sounddata.last_gear = 0; sounddevice->sounddata.last_gear = 0;
sounddevice->sounddata.duration = sds.duration; sounddevice->sounddata.duration = 0.125;
streamname = "Gear"; streamname = "Gear";
break; break;
case (EFFECT_TYRESLIP): case (EFFECT_TYRESLIP):
@@ -220,8 +211,16 @@ int sounddev_init(SoundDevice* sounddevice, const char* devname, MonocoqueTyreId
} }
usb_generic_shaker_init(sounddevice, mainloop, context, devname, sds.volume, sds.pan, sds.channels, streamname); // Returned, not discarded: this function is declared int and used to fall
//usb_generic_shaker_init(sounddevice); // off its end, so new_sound_device() read whatever happened to be in the
// return register and treated most devices as failures -- "Could not
// initialize Sound Device" for 22 of 24 configured shakers, while their
// PulseAudio streams had in fact connected. The devices were freed and
// never fed telemetry, so the graph looked correct in qpwgraph and nothing
// shook. Being undefined behaviour it varied by build, which is why the
// same config worked against a locally compiled monocoque and not the
// packaged one.
return usb_generic_shaker_init(sounddevice, mainloop, context, devname, sds.volume, sds.pan, sds.channels, streamname);
} }
static const vtable engine_sound_simdevice_vtable = { &sounddev_engine_update, &sounddev_free }; static const vtable engine_sound_simdevice_vtable = { &sounddev_engine_update, &sounddev_free };
@@ -240,7 +239,7 @@ SoundDevice* new_sound_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo
this->m.derived = this; this->m.derived = this;
int error = 0; int error = 0;
switch (ds->effect_type) switch (ds->hapticsettings.effect_type)
{ {
case (EFFECT_TYRESLIP): case (EFFECT_TYRESLIP):
case (EFFECT_TYRELOCK): case (EFFECT_TYRELOCK):
@@ -258,73 +257,32 @@ SoundDevice* new_sound_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo
if(error == 0) if(error == 0)
{ {
slogt("Attempting to configure sound device with subtype: %i", ds->effect_type); initializeHapticEffect(&this->m.hapticeffect, &ds->hapticsettings, ms);
switch (ds->effect_type) slogt("Attempting to configure sound device with subtype: %i", ds->hapticsettings.effect_type);
switch (ds->hapticsettings.effect_type)
{ {
case (EFFECT_ENGINERPM): case (EFFECT_ENGINERPM):
this->m.hapticeffect.effecttype = EFFECT_ENGINERPM;
this->m.vtable = &engine_sound_simdevice_vtable; this->m.vtable = &engine_sound_simdevice_vtable;
slogi("Initializing sound device for engine vibrations."); slogi("Initializing sound device for engine vibrations.");
break; break;
case (EFFECT_GEARSHIFT): case (EFFECT_GEARSHIFT):
this->m.hapticeffect.effecttype = EFFECT_GEARSHIFT;
this->m.vtable = &gear_sound_simdevice_vtable; this->m.vtable = &gear_sound_simdevice_vtable;
slogi("Initializing sound device for gear shift vibrations."); slogi("Initializing sound device for gear shift vibrations.");
break; break;
case (EFFECT_TYRESLIP): case (EFFECT_TYRESLIP):
this->m.hapticeffect.effecttype = EFFECT_TYRESLIP;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &tyreslip_sound_simdevice_vtable; this->m.vtable = &tyreslip_sound_simdevice_vtable;
slogi("Initializing sound device for tyre slip vibrations."); slogi("Initializing sound device for tyre slip vibrations.");
break; break;
case (EFFECT_TYRELOCK): case (EFFECT_TYRELOCK):
this->m.hapticeffect.effecttype = EFFECT_TYRELOCK;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &tyrelock_sound_simdevice_vtable; this->m.vtable = &tyrelock_sound_simdevice_vtable;
slogi("Initializing sound device for tyre slip vibrations."); slogi("Initializing sound device for tyre lock vibrations.");
break; break;
case (EFFECT_ABSBRAKES): case (EFFECT_ABSBRAKES):
this->m.hapticeffect.effecttype = EFFECT_ABSBRAKES;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i", this->m.hapticeffect.effecttype, ds->effect_type);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &absbrakes_sound_simdevice_vtable; this->m.vtable = &absbrakes_sound_simdevice_vtable;
slogi("Initializing sound device for abs vibrations."); slogi("Initializing sound device for abs vibrations.");
break; break;
case (EFFECT_SUSPENSION): case (EFFECT_SUSPENSION):
this->m.hapticeffect.effecttype = EFFECT_SUSPENSION;
this->m.hapticeffect.threshold = ds->threshold;
this->m.hapticeffect.threshold = ds->threshold;
slogt("Haptic effect: %i %i on tyre %i", this->m.hapticeffect.effecttype, ds->effect_type, ds->tyre);
this->m.hapticeffect.tyre = ds->tyre;
this->m.hapticeffect.useconfig = ms->useconfig;
this->m.hapticeffect.configcheck = &ms->configcheck;
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config;
this->m.vtable = &suspension_sound_simdevice_vtable; this->m.vtable = &suspension_sound_simdevice_vtable;
slogi("Initializing sound device for suspension vibrations."); slogi("Initializing sound device for suspension vibrations.");
break; break;
@@ -333,8 +291,8 @@ SoundDevice* new_sound_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo
if(error == 0) if(error == 0)
{ {
slogt("Attempting to use sound device %s", ds->sounddevsettings.dev); slogt("Attempting to use sound device %s", ds->dev);
error = sounddev_init(this, ds->sounddevsettings.dev, ds->tyre, ds->sounddevsettings); error = sounddev_init(this, ds->dev, ds->sounddevsettings);
} }
if (error != 0) if (error != 0)
-12
View File
@@ -8,23 +8,11 @@
//#endif //#endif
typedef enum
{
SOUND_EFFECT_MODULATION_NONE = 0,
SOUND_EFFECT_MODULATION_FREQUENCY = 1,
SOUND_EFFECT_MODULATION_AMPLIFY = 2,
}
SoundEffectModulationType;
#define MAX_TABLE_SIZE (6000) #define MAX_TABLE_SIZE (6000)
typedef struct typedef struct
{ {
uint8_t last_gear; uint8_t last_gear;
int volume; int volume;
uint32_t frequency;
uint32_t frequencyMax;
uint32_t amplitude;
uint32_t amplitudeMax;
double duration; double duration;
double curr_frequency; double curr_frequency;
uint32_t curr_amplitude; uint32_t curr_amplitude;
+12 -13
View File
@@ -3,7 +3,7 @@
#include <string.h> #include <string.h>
#include <glob.h> #include <glob.h>
#include "usbhapticdevice.h" #include "cslelitev3.h"
#include "../../helper/confighelper.h" #include "../../helper/confighelper.h"
#include "../slog/slog.h" #include "../slog/slog.h"
@@ -11,7 +11,7 @@
const char* SYSFSRUMBLEPATH = "/sys/module/hid_fanatec/drivers/hid:f*/0003:0EB7:183B.*/rumble"; const char* SYSFSRUMBLEPATH = "/sys/module/hid_fanatec/drivers/hid:f*/0003:0EB7:183B.*/rumble";
int cslelitev3_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play) int cslelitev3_update(USBDevice* usbdevice, int effecttype, int play)
{ {
int res = 0; int res = 0;
@@ -33,26 +33,25 @@ int cslelitev3_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, i
} }
} }
fprintf(usbdevice->filehandle, "%i\n", value);
fprintf(usbhapticdevice->filehandle, "%i\n", value); fflush(usbdevice->filehandle);
fflush(usbhapticdevice->filehandle);
return res; return res;
} }
int cslelitev3_free(USBGenericHapticDevice* usbhapticdevice) int cslelitev3_free(USBDevice* usbdevice)
{ {
int res = 0; int res = 0;
free(usbhapticdevice->dev); free(usbdevice->dev);
fflush(usbhapticdevice->filehandle); fflush(usbdevice->filehandle);
fclose(usbhapticdevice->filehandle); fclose(usbdevice->filehandle);
return res; return res;
} }
int cslelitev3_init(USBGenericHapticDevice* usbhapticdevice) int cslelitev3_init(USBDevice* usbdevice)
{ {
slogi("initializing CSL Elite V3 Pedals..."); slogi("initializing CSL Elite V3 Pedals...");
@@ -75,7 +74,7 @@ int cslelitev3_init(USBGenericHapticDevice* usbhapticdevice)
{ {
if (i == 0) if (i == 0)
{ {
usbhapticdevice->dev = strdup(globlist.gl_pathv[i]); usbdevice->dev = strdup(globlist.gl_pathv[i]);
} }
i++; i++;
} }
@@ -91,9 +90,9 @@ int cslelitev3_init(USBGenericHapticDevice* usbhapticdevice)
return res; return res;
} }
usbhapticdevice->filehandle = fopen(usbhapticdevice->dev, "w"); usbdevice->filehandle = fopen(usbdevice->dev, "w");
if (!usbhapticdevice->filehandle) if (!usbdevice->filehandle)
{ {
sloge("Could not open pedal device..."); sloge("Could not open pedal device...");
return res; return res;
+6 -3
View File
@@ -1,8 +1,11 @@
#ifndef _CSLELITEV3_H #ifndef _CSLELITEV3_H
#define _CSLELITEV3_H #define _CSLELITEV3_H
int cslelitev3_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play); #include "../wheeldevice.h"
int cslelitev3_init(USBGenericHapticDevice* usbhapticdevice); #include "../simdevice.h"
int cslelitev3_free(USBGenericHapticDevice* usbhapticdevice);
int cslelitev3_update(USBDevice* usbdevice, int effecttype, int play);
int cslelitev3_init(USBDevice* usbdevice);
int cslelitev3_free(USBDevice* usbdevice);
#endif #endif
+17 -17
View File
@@ -3,7 +3,7 @@
#include <string.h> #include <string.h>
#include <hidapi/hidapi.h> #include <hidapi/hidapi.h>
#include "usbhapticdevice.h" #include "simagicp1000.h"
#include "../../helper/confighelper.h" #include "../../helper/confighelper.h"
#include "../slog/slog.h" #include "../slog/slog.h"
@@ -14,12 +14,12 @@
const size_t SIMAGIC_P1000_BUFSIZE = 49; const size_t SIMAGIC_P1000_BUFSIZE = 49;
const char* SIMAGICP1000DEVSTRING = "SIMAGIC P1000 Pedals"; const char* SIMAGICP1000DEVSTRING = "SIMAGIC P1000 Pedals";
int senddevreport(USBGenericHapticDevice* usbhapticdevice, unsigned char* buf, size_t bufsize) int senddevreport(USBDevice* usbdevice, unsigned char* buf, size_t bufsize)
{ {
int res = 0; int res = 0;
if (usbhapticdevice->handle) if (usbdevice->handle)
{ {
res = hid_send_feature_report(usbhapticdevice->handle, buf, SIMAGIC_P1000_BUFSIZE); res = hid_send_feature_report(usbdevice->handle, buf, SIMAGIC_P1000_BUFSIZE);
slogd("sent %i bytes to %s", bufsize, SIMAGICP1000DEVSTRING); slogd("sent %i bytes to %s", bufsize, SIMAGICP1000DEVSTRING);
slogt("sent bytes %02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); slogt("sent bytes %02x %02x %02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
} }
@@ -30,7 +30,7 @@ int senddevreport(USBGenericHapticDevice* usbhapticdevice, unsigned char* buf, s
return res != bufsize; return res != bufsize;
} }
int simagicp1000_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play) int simagicp1000_update(USBDevice* usbdevice, int effecttype, int play)
{ {
int res = 0; int res = 0;
@@ -54,17 +54,17 @@ int simagicp1000_update(USBGenericHapticDevice* usbhapticdevice, int effecttype,
{ {
case (EFFECT_TYRESLIP): case (EFFECT_TYRESLIP):
bytes[2] = 0x02; bytes[2] = 0x02;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE); senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
break; break;
case (EFFECT_TYRELOCK): case (EFFECT_TYRELOCK):
bytes[2] = 0x01; bytes[2] = 0x01;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE); senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
break; break;
case (EFFECT_ABSBRAKES): case (EFFECT_ABSBRAKES):
bytes[2] = 0x02; bytes[2] = 0x02;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE); senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
bytes[2] = 0x01; bytes[2] = 0x01;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE); senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
break; break;
} }
} }
@@ -72,19 +72,19 @@ int simagicp1000_update(USBGenericHapticDevice* usbhapticdevice, int effecttype,
{ {
bytes[0] = 241; bytes[0] = 241;
bytes[2] = 0x01; bytes[2] = 0x01;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE); senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
bytes[2] = 0x02; bytes[2] = 0x02;
senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE); senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
} }
return res; return res;
} }
int simagicp1000_free(USBGenericHapticDevice* usbhapticdevice) int simagicp1000_free(USBDevice* usbdevice)
{ {
int res = 0; int res = 0;
hid_close(usbhapticdevice->handle); hid_close(usbdevice->handle);
res = hid_exit(); res = hid_exit();
return res; return res;
@@ -92,7 +92,7 @@ int simagicp1000_free(USBGenericHapticDevice* usbhapticdevice)
return res; return res;
} }
int simagicp1000_init(USBGenericHapticDevice* usbhapticdevice) int simagicp1000_init(USBDevice* usbdevice)
{ {
slogi("initializing %s...", SIMAGICP1000DEVSTRING); slogi("initializing %s...", SIMAGICP1000DEVSTRING);
@@ -100,9 +100,9 @@ int simagicp1000_init(USBGenericHapticDevice* usbhapticdevice)
res = hid_init(); res = hid_init();
usbhapticdevice->handle = hid_open(SIMAGIC_VENDOR_ID, SIMAGIC_PRODUCT_ID_P1000, NULL); usbdevice->handle = hid_open(SIMAGIC_VENDOR_ID, SIMAGIC_PRODUCT_ID_P1000, NULL);
if (!usbhapticdevice->handle) if (!usbdevice->handle)
{ {
sloge("Could not find attached %s", SIMAGICP1000DEVSTRING); sloge("Could not find attached %s", SIMAGICP1000DEVSTRING);
res = hid_exit(); res = hid_exit();
@@ -123,7 +123,7 @@ int simagicp1000_init(USBGenericHapticDevice* usbhapticdevice)
bytes[5] = 0x00; bytes[5] = 0x00;
bytes[6] = 0x01; bytes[6] = 0x01;
bytes[7] = 0x02; bytes[7] = 0x02;
res = senddevreport(usbhapticdevice, bytes, SIMAGIC_P1000_BUFSIZE); res = senddevreport(usbdevice, bytes, SIMAGIC_P1000_BUFSIZE);
slogd("Initialization returned %i", res); slogd("Initialization returned %i", res);
if(res != 0) if(res != 0)
{ {
+6 -3
View File
@@ -1,8 +1,11 @@
#ifndef _SIMAGICP1000_H #ifndef _SIMAGICP1000_H
#define _SIMAGICP1000_H #define _SIMAGICP1000_H
int simagicp1000_update(USBGenericHapticDevice* usbhapticdevice, int effecttype, int play); #include "../wheeldevice.h"
int simagicp1000_init(USBGenericHapticDevice* usbhapticdevice); #include "../simdevice.h"
int simagicp1000_free(USBGenericHapticDevice* usbhapticdevice);
int simagicp1000_update(USBDevice* usbdevice, int effecttype, int play);
int simagicp1000_init(USBDevice* usbdevice);
int simagicp1000_free(USBDevice* usbdevice);
#endif #endif
+90
View File
@@ -0,0 +1,90 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glob.h>
#include "simnetpedals.h"
#include "../../helper/confighelper.h"
#include "../slog/slog.h"
#define SIMNETPEDALS_BUFSIZE 64
//Byte[0] : 0x01 (fixed) = Report ID
//Byte[1] : 0x00 (fixed)
//Byte[2] : motor number (0x01, 0x2 or 0x4)
//Byte[3]: motor 1 frequency in hz
//Byte[4] : motor 1 amplitude (0 to 100%)
//Byte[5] : motor 2 frequency in hz
//Byte[6]: motor 2 amplitude (0 to 100%)
//Byte[7]: motor 3 frequency in hz
//Byte[8]: motor 3 amplitude (0 to 100%)
int simnetpedals_update(USBDevice* usbdevice, int effecttype, int play, int motorposition, int frequency, int amplitude)
{
int res = 0;
unsigned char bytes[SIMNETPEDALS_BUFSIZE];
for (int x = 0; x < SIMNETPEDALS_BUFSIZE; x++)
{
bytes[x] = 0x00;
}
bytes[0] = 0x01;
bytes[1] = 0x00;
bytes[2] = motorposition;
uint8_t motormultiple = motorposition - 1;
if(motorposition == 0x04)
{
motormultiple = 2;
}
if(play > 0)
{
bytes[3 + (motormultiple * 2)] = frequency;
bytes[4 + (motormultiple * 2)] = amplitude;
}
if (usbdevice->handle)
{
slogt("writing bytes x%02xx%02xx%02xx%02xx%02xx%02xx%02xx%02xx%02x", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8]);
res = hid_write(usbdevice->handle, bytes, SIMNETPEDALS_BUFSIZE);
}
else
{
slogd("no handle");
}
return res;
}
int simnetpedals_free(USBDevice* usbdevice)
{
int res = 0;
hid_close(usbdevice->handle);
res = hid_exit();
return res;
}
int simnetpedals_init(USBDevice* usbdevice)
{
slogi("initializing simnet pedals...");
int res = 0;
res = hid_init();
usbdevice->handle = hid_open(0xcafe, 0xa301, NULL);
if (!usbdevice->handle)
{
sloge("Could not find attached Simnet Pedals");
res = hid_exit();
return 1;
}
slogd("Found Simnet Pedals handle %i...", usbdevice->handle);
return res;
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef _SIMNETPEDALS_H
#define _SIMNETPEDALS_H
#include "../wheeldevice.h"
#include "../simdevice.h"
int simnetpedals_update(USBDevice* usbdevice, int effecttype, int play, int motorposition, int frequency, int amplitude);
int simnetpedals_init(USBDevice* usbdevice);
int simnetpedals_free(USBDevice* usbdevice);
#endif
@@ -2,7 +2,6 @@
#define _CAMMUSC12_H #define _CAMMUSC12_H
#include "../../wheeldevice.h" #include "../../wheeldevice.h"
#include "../../simdevice.h" #include "../../simdevice.h"
int cammusc12_update(USBDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity); int cammusc12_update(USBDevice* wheeldevice, int maxrpm, int rpm, int gear, int velocity);
+27 -29
View File
@@ -19,11 +19,9 @@ int usbdev_update(SimDevice* this, SimData* simdata)
case USBDEV_TACHOMETER : case USBDEV_TACHOMETER :
tachdev_update(usbdevice, simdata); tachdev_update(usbdevice, simdata);
break; break;
case USBDEV_WHEEL : case USBDEV_WHEEL_OR_PEDALS :
wheeldev_update(usbdevice, simdata); // this should be directed via vtable to wheeldevice.c
break; //wheeldev_update(usbdevice, simdata);
case USBDEV_GENERICHAPTIC :
usbhapticdev_update(&usbdevice->u.hapticdevice, simdata, this->hapticeffect.tyre, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
break; break;
} }
@@ -41,12 +39,9 @@ int usbdev_free(SimDevice* this)
case USBDEV_TACHOMETER : case USBDEV_TACHOMETER :
tachdev_free(usbdevice); tachdev_free(usbdevice);
break; break;
case USBDEV_WHEEL : case USBDEV_WHEEL_OR_PEDALS :
wheeldev_free(usbdevice); wheeldev_free(usbdevice);
break; break;
case USBDEV_GENERICHAPTIC :
usbhapticdev_free(&usbdevice->u.hapticdevice);
break;
} }
free(usbdevice); free(usbdevice);
@@ -66,53 +61,56 @@ int usbdev_init(USBDevice* usbdevice, DeviceSettings* ds, SimInfo* siminfo)
case USBDEV_TACHOMETER : case USBDEV_TACHOMETER :
error = tachdev_init(usbdevice, ds); error = tachdev_init(usbdevice, ds);
break; break;
case USBDEV_WHEEL : case USBDEV_WHEEL_OR_PEDALS :
error = wheeldev_init(usbdevice, ds); error = wheeldev_init(usbdevice, ds);
break; break;
case USBDEV_GENERICHAPTIC :
error = usbhapticdev_init(&usbdevice->u.hapticdevice, ds, siminfo);
break;
} }
return error; return error;
} }
static const vtable usb_simdevice_vtable = { &usbdev_update, &usbdev_free }; static const vtable usb_simdevice_vtable = { &usbdev_update, &usbdev_free };
static const vtable usb_wheeldevice_vtable = { &wheeldev_update, &usbdev_free };
static const vtable usb_wheelhaptic_vtable = { &wheelhapticdev_update, &usbdev_free };
USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) { USBDevice* new_usb_device(DeviceSettings* ds, MonocoqueSettings* ms, SimInfo* siminfo) {
USBDevice* this = (USBDevice*) malloc(sizeof(USBDevice)); USBDevice* this = (USBDevice*) malloc(sizeof(USBDevice));
int error = 0;
this->m.update = &update; this->m.update = &update;
this->m.free = &simdevfree; this->m.free = &simdevfree;
this->m.derived = this; this->m.derived = this;
this->m.vtable = &usb_simdevice_vtable; this->m.vtable = &usb_simdevice_vtable;
// TODO: turn this into a switch when we get more devices // TODO: turn this into a switch when we get more devices
this->type = USBDEV_TACHOMETER; this->type = USBDEV_TACHOMETER;
if(ds->dev_subtype == SIMDEVTYPE_USBWHEEL) if(ds->dev_subtype == SIMDEVTYPE_USBWHEEL)
{ {
this->type = USBDEV_WHEEL; this->type = USBDEV_WHEEL_OR_PEDALS;
this->m.vtable = &usb_wheeldevice_vtable;
} }
if(ds->has_haptic_effects == true)
// really generic haptic isn't and shouldn't be it's own type
// it's an attribute that is added to a device via composition
// same if that haptic device is a serial device
if (ds->dev_subtype == SIMDEVTYPE_USBHAPTIC)
{ {
this->m.hapticeffect.threshold = ds->threshold; if(siminfo->SimSupportsHapticEffects == false)
this->m.hapticeffect.effecttype = ds->effect_type; {
this->m.hapticeffect.tyre = ds->tyre; // if the user added haptic effects to the config and the sim does not support it, but still wishes to use features
this->m.hapticeffect.useconfig = ms->useconfig; // just remove the haptic effect from the config?
this->m.hapticeffect.configcheck = &ms->configcheck; slogi("This sim does not support haptic effects");
this->m.hapticeffect.tyrediameterconfig = ms->tyre_diameter_config; error = MONOCOQUE_ERROR_UNSUPPORTED_SIM_FEATURE;
this->type = USBDEV_GENERICHAPTIC; }
else
{
int error = 0;
this->type = USBDEV_WHEEL_OR_PEDALS;
initializeHapticEffect(&this->m.hapticeffect, &ds->hapticsettings, ms);
this->m.vtable = &usb_wheelhaptic_vtable;
}
} }
int error = usbdev_init(this, ds, siminfo); error = usbdev_init(this, ds, siminfo);
if (error != 0) if (error != 0)
{ {
+20 -24
View File
@@ -7,6 +7,7 @@
#include "hapticeffect.h" #include "hapticeffect.h"
#include "usb/cslelitev3.h" #include "usb/cslelitev3.h"
#include "usb/simagicp1000.h" #include "usb/simagicp1000.h"
#include "usb/simnetpedals.h"
#include "../../helper/confighelper.h" #include "../../helper/confighelper.h"
#include "../../simulatorapi/simapi/simapi/simdata.h" #include "../../simulatorapi/simapi/simapi/simdata.h"
@@ -16,7 +17,7 @@
int usbhapticdev_update(USBGenericHapticDevice* usbhapticdevice, SimData* simdata, int tyre, int useconfig, int* configcheck, char* configfile) int usbhapticdev_update(USBGenericHapticDevice* usbhapticdevice, SimData* simdata, int tyre, int useconfig, int* configcheck, char* configfile)
{ {
double play = slipeffect(simdata, usbhapticdevice->effecttype, tyre, usbhapticdevice->threshold, useconfig, configcheck, configfile); double play = slipeffect(simdata, this->hapticeffect.effecttype, tyre, this->hapticeffect.threshold, useconfig, configcheck, configfile);
if (play != usbhapticdevice->state) if (play != usbhapticdevice->state)
{ {
@@ -27,10 +28,13 @@ int usbhapticdev_update(USBGenericHapticDevice* usbhapticdevice, SimData* simdat
switch ( usbhapticdevice->type ) switch ( usbhapticdevice->type )
{ {
case USBHAPTIC_CSLELITEV3PEDALS: case USBHAPTIC_CSLELITEV3PEDALS:
cslelitev3_update(usbhapticdevice, usbhapticdevice->effecttype, rplay); cslelitev3_update(usbhapticdevice, this->hapticeffect.effecttype, rplay);
break; break;
case USBHAPTIC_SIMAGICP1000PEDALS: case USBHAPTIC_SIMAGICP1000PEDALS:
simagicp1000_update(usbhapticdevice, usbhapticdevice->effecttype, rplay); simagicp1000_update(usbhapticdevice, this->hapticeffect.effecttype, rplay);
break;
case USBHAPTIC_SIMNETPEDALS:
simnetpedals_update(usbhapticdevice, this->hapticeffect.effecttype, rplay);
break; break;
} }
} }
@@ -39,10 +43,13 @@ int usbhapticdev_update(USBGenericHapticDevice* usbhapticdevice, SimData* simdat
switch ( usbhapticdevice->type ) switch ( usbhapticdevice->type )
{ {
case USBHAPTIC_CSLELITEV3PEDALS: case USBHAPTIC_CSLELITEV3PEDALS:
cslelitev3_update(usbhapticdevice, usbhapticdevice->effecttype, rplay); cslelitev3_update(usbhapticdevice, this->hapticeffect.effecttype, rplay);
break; break;
case USBHAPTIC_SIMAGICP1000PEDALS: case USBHAPTIC_SIMAGICP1000PEDALS:
simagicp1000_update(usbhapticdevice, usbhapticdevice->effecttype, rplay); simagicp1000_update(usbhapticdevice, this->hapticeffect.effecttype, rplay);
break;
case USBHAPTIC_SIMNETPEDALS:
simnetpedals_update(usbhapticdevice, this->hapticeffect.effecttype, rplay);
break; break;
} }
} }
@@ -62,6 +69,9 @@ int usbhapticdev_free(USBGenericHapticDevice* usbhapticdevice)
case USBHAPTIC_SIMAGICP1000PEDALS: case USBHAPTIC_SIMAGICP1000PEDALS:
simagicp1000_free(usbhapticdevice); simagicp1000_free(usbhapticdevice);
break; break;
case USBHAPTIC_SIMNETPEDALS:
simnetpedals_free(usbhapticdevice);
break;
} }
return 0; return 0;
@@ -76,25 +86,7 @@ int usbhapticdev_init(USBGenericHapticDevice* usbhapticdevice, DeviceSettings* d
} }
int error = 0; int error = 0;
usbhapticdevice->state = 0; initializeHapticEffect(&this->m.hapticeffect, &ds->hapticsettings, ms);
usbhapticdevice->value0 = ds->usbdevsettings.value0;
usbhapticdevice->value1 = ds->usbdevsettings.value1;
usbhapticdevice->state = usbhapticdevice->value0;
usbhapticdevice->effecttype = ds->effect_type;
usbhapticdevice->threshold = ds->threshold;
if(ds->effect_type == EFFECT_TYRESLIP)
{
usbhapticdevice->effecttype = EFFECT_TYRESLIP;
}
if(ds->effect_type == EFFECT_TYRELOCK)
{
usbhapticdevice->effecttype = EFFECT_TYRELOCK;
}
if(ds->effect_type == EFFECT_ABSBRAKES)
{
usbhapticdevice->effecttype = EFFECT_ABSBRAKES;
}
slogi("initializing standalone usb haptic device..."); slogi("initializing standalone usb haptic device...");
// detection of usb device model // detection of usb device model
@@ -107,6 +99,10 @@ int usbhapticdev_init(USBGenericHapticDevice* usbhapticdevice, DeviceSettings* d
usbhapticdevice->type = USBHAPTIC_CSLELITEV3PEDALS; usbhapticdevice->type = USBHAPTIC_CSLELITEV3PEDALS;
error = cslelitev3_init(usbhapticdevice); error = cslelitev3_init(usbhapticdevice);
break; break;
case (SIMDEVSUBTYPE_SIMNETPEDALS):
usbhapticdevice->type = USBHAPTIC_SIMNETPEDALS;
error = simnetpedals_init(usbhapticdevice);
break;
default: default:
slogw("Possibly unknown device"); slogw("Possibly unknown device");
} }
+2 -7
View File
@@ -10,19 +10,14 @@ typedef enum
{ {
USBHAPTIC_UNKNOWN = 0, USBHAPTIC_UNKNOWN = 0,
USBHAPTIC_CSLELITEV3PEDALS = 1, USBHAPTIC_CSLELITEV3PEDALS = 1,
USBHAPTIC_SIMAGICP1000PEDALS = 2 USBHAPTIC_SIMAGICP1000PEDALS = 2,
USBHAPTIC_SIMNETPEDALS = 3
} }
HapticType; HapticType;
typedef struct typedef struct
{ {
int id; int id;
HapticType type;
double state;
double threshold;
int value0;
int value1;
VibrationEffectType effecttype;
FILE* filehandle; FILE* filehandle;
hid_device* handle; hid_device* handle;
char* dev; char* dev;
+80 -7
View File
@@ -2,20 +2,73 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "usb/wheels/logitechg29.h" #include "usb/wheels/logitechg29.h"
#include "usb/wheels/cammusc5.h" #include "usb/wheels/cammusc5.h"
#include "usb/wheels/cammusc12.h" #include "usb/wheels/cammusc12.h"
#include "usb/wheels/gtneo.h" #include "usb/wheels/gtneo.h"
#include "serial/moza.h" #include "serial/moza.h"
#include "usb/cslelitev3.h"
#include "usb/simagicp1000.h"
#include "usb/simnetpedals.h"
#include "simdevice.h" #include "simdevice.h"
#include "../helper/confighelper.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simapi/simapi/simdata.h"
#include "../slog/slog.h" #include "../slog/slog.h"
int wheeldev_update(USBDevice* usbdevice, SimData* simdata) int wheelhapticdev_update(SimDevice* this, SimData* simdata)
{ {
USBDevice* usbdevice = (void *) this->derived;
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice; WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
double play = slipeffect(simdata, &this->hapticeffect, this->hapticeffect.useconfig, this->hapticeffect.configcheck, this->hapticeffect.tyrediameterconfig);
if (play != usbdevice->hapticstate)
{
int rplay = 0;
if(play > 0)
{
rplay = 1;
switch ( wheeldevice->type )
{
case WHEELDEV_CSLELITEV3PEDALS:
cslelitev3_update(usbdevice, this->hapticeffect.effecttype, rplay);
break;
case WHEELDEV_SIMAGICP1000PEDALS:
simagicp1000_update(usbdevice, this->hapticeffect.effecttype, rplay);
break;
case WHEELDEV_SIMNETPEDALS:
simnetpedals_update(usbdevice, this->hapticeffect.effecttype, rplay, this->hapticeffect.motorposition, this->hapticeffect.basefrequency, this->hapticeffect.baseamplitude);
break;
}
}
else
{
switch ( wheeldevice->type )
{
case WHEELDEV_CSLELITEV3PEDALS:
cslelitev3_update(usbdevice, this->hapticeffect.effecttype, rplay);
break;
case WHEELDEV_SIMAGICP1000PEDALS:
simagicp1000_update(usbdevice, this->hapticeffect.effecttype, rplay);
break;
case WHEELDEV_SIMNETPEDALS:
simnetpedals_update(usbdevice, this->hapticeffect.effecttype, rplay, this->hapticeffect.motorposition, 0, 0);
break;
}
}
usbdevice->hapticstate = play;
}
return 0;
}
int wheeldev_update(SimDevice* this, SimData* simdata)
{
USBDevice* usbdevice = (void *) this->derived;
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
switch ( wheeldevice->type ) switch ( wheeldevice->type )
{ {
case WHEELDEV_UNKNOWN : case WHEELDEV_UNKNOWN :
@@ -72,6 +125,15 @@ int wheeldev_free(USBDevice* usbdevice)
free(usbdevice->m.device_specific_config_file); free(usbdevice->m.device_specific_config_file);
} }
break; break;
case WHEELDEV_CSLELITEV3PEDALS:
cslelitev3_free(usbdevice);
break;
case WHEELDEV_SIMAGICP1000PEDALS:
simagicp1000_free(usbdevice);
break;
case WHEELDEV_SIMNETPEDALS:
simnetpedals_free(usbdevice);
break;
} }
return 0; return 0;
@@ -79,8 +141,9 @@ int wheeldev_free(USBDevice* usbdevice)
int wheeldev_init(USBDevice* usbdevice, DeviceSettings* ds) int wheeldev_init(USBDevice* usbdevice, DeviceSettings* ds)
{ {
slogi("initializing wheel device..."); slogi("initializing wheel or pedals device...");
int error = 0; int error = 0;
usbdevice->hapticstate = 0;
// detection of wheel model // detection of wheel model
WheelDevice* wheeldevice = &usbdevice->u.wheeldevice; WheelDevice* wheeldevice = &usbdevice->u.wheeldevice;
switch (ds->dev_subsubtype) { switch (ds->dev_subsubtype) {
@@ -138,11 +201,21 @@ int wheeldev_init(USBDevice* usbdevice, DeviceSettings* ds)
error = -1; error = -1;
} }
break; break;
default: case (SIMDEVSUBTYPE_SIMAGICP1000PEDALS):
wheeldevice->type = WHEELDEV_UNKNOWN; wheeldevice->type = WHEELDEV_SIMAGICP1000PEDALS;
slogw("Unknown cammus wheel detected, trying C5"); error = simagicp1000_init(usbdevice);
error = cammusc5_init(usbdevice);
break; break;
case (SIMDEVSUBTYPE_CSLELITEV3PEDALS):
slogi("Attempting to initialize CSL Elite V3 Pedals");
wheeldevice->type = WHEELDEV_CSLELITEV3PEDALS;
error = cslelitev3_init(usbdevice);
break;
case (SIMDEVSUBTYPE_SIMNETPEDALS):
wheeldevice->type = WHEELDEV_SIMNETPEDALS;
error = simnetpedals_init(usbdevice);
break;
default:
slogw("Possibly unknown wheel device");
} }
+9 -6
View File
@@ -8,12 +8,15 @@
typedef enum typedef enum
{ {
WHEELDEV_UNKNOWN = 0, WHEELDEV_UNKNOWN = 0,
WHEELDEV_CAMMUSC5 = 1, WHEELDEV_CAMMUSC5 = 1,
WHEELDEV_CAMMUSC12 = 2, WHEELDEV_CAMMUSC12 = 2,
WHEELDEV_MOZAR5 = 3, WHEELDEV_MOZAR5 = 3,
WHEELDEV_SIMAGICGTNEO = 4, WHEELDEV_SIMAGICGTNEO = 4,
WHEELDEV_LOGITECHG29 = 5 WHEELDEV_LOGITECHG29 = 5,
WHEELDEV_CSLELITEV3PEDALS = 6,
WHEELDEV_SIMAGICP1000PEDALS = 7,
WHEELDEV_SIMNETPEDALS = 8
} }
WheelType; WheelType;
+2
View File
@@ -10,3 +10,5 @@ set(LIBXML_INCLUDE_DIR /usr/include/libxml2)
include_directories("." ${LIBXML_INCLUDE_DIR}) include_directories("." ${LIBXML_INCLUDE_DIR})
add_library(gameloop STATIC ${gameloop_source_files}) add_library(gameloop STATIC ${gameloop_source_files})
set_property(TARGET gameloop PROPERTY POSITION_INDEPENDENT_CODE ON)
+449 -191
View File
@@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h>
#include <poll.h> #include <poll.h>
#include <termios.h> #include <termios.h>
#include <signal.h> #include <signal.h>
@@ -16,6 +17,7 @@
#include "../devices/hapticeffect.h" #include "../devices/hapticeffect.h"
#include "../simulatorapi/simapi/simapi/simdata.h" #include "../simulatorapi/simapi/simapi/simdata.h"
#include "../simulatorapi/simapi/simapi/simmapper.h" #include "../simulatorapi/simapi/simapi/simmapper.h"
#include "../simulatorapi/simapi/simapi/simmap.h"
#include "../slog/slog.h" #include "../slog/slog.h"
#define DEFAULT_UPDATE_RATE 240.0 #define DEFAULT_UPDATE_RATE 240.0
@@ -23,20 +25,33 @@
bool go = false; bool go = false;
bool go2 = false; bool go2 = false;
struct sigaction act; struct sigaction act;
static pthread_t loop_thread;
static uv_loop_t *loop = NULL;
SimData* simdata;
SimMap* simmap;
loop_data* baton;
device_loop_data* test_baton;
SimDevice* test_simdevice;
SimInfo* test_siminfo;
uv_idle_t idler; uv_idle_t idler;
uv_timer_t datachecktimer; uv_timer_t datachecktimer;
uv_timer_t showstatstimer; uv_timer_t showstatstimer;
uv_timer_t datamaptimer; uv_timer_t datamaptimer;
uv_timer_t tyrediametertimer;
uv_timer_t testdevicetimer;
uv_udp_t recv_socket; uv_udp_t recv_socket;
bool doui = false; bool doui = false;
int appstate = 0;
void shmdatamapcallback(uv_timer_t* handle); void shmdatamapcallback(uv_timer_t* handle);
void showstatscallback(uv_timer_t* handle); void showstatscallback(uv_timer_t* handle);
void datacheckcallback(uv_timer_t* handle); void datacheckcallback(uv_timer_t* handle);
void tyrediametercheckcallback(uv_timer_t* handle);
void startdatalogger(MonocoqueSettings* ms, loop_data* l); void startdatalogger(MonocoqueSettings* ms, loop_data* l);
static void close_walk_cb(uv_handle_t* handle, void* arg) static void close_walk_cb(uv_handle_t* handle, void* arg)
@@ -227,9 +242,51 @@ void devicetimercallback(uv_timer_t* handle)
device->update(device, simdata); device->update(device, simdata);
} }
void tyrediametercheckcallback(uv_timer_t* handle)
{
void* b = uv_handle_get_data((uv_handle_t*) handle);
device_loop_data* f = (device_loop_data*) b;
SimData* simdata = f->simdata;
if(simdata->car != NULL || simdata->car[0] != 0)
{
slogi("car is %s", simdata->car);
// check for saved tyre diameter in config file
// if not saved version exists get tyre diameter and save it
// use config check variable to track if the config check has been performed
// avoid many opens of the same file
int error = 0;
if(hasTyreDiameter(simdata)==false && f->ms->configcheck == 0)
{
slogi("attempting load of tyre diameter config");
error = loadtyreconfig(simdata, f->ms->tyre_diameter_config, true);
f->ms->configcheck = 1;
}
if(hasTyreDiameter(simdata)==false)
{
slogt("could not find tyre diameter in config file, attempting to calculate new");
getTyreDiameter(simdata);
// if this successfully calculates data the diameters will be saved to the file after the
// sim stops actively mapping data
}
}
if(hasTyreDiameter(simdata)==true)
{
int a = loadtyreconfig(simdata, f->ms->tyre_diameter_config, false);
if(a < 0)
{
slogi("saving new tyre diameter config for car %s");
savetyreconfig(simdata, f->ms->tyre_diameter_config);
}
uv_timer_stop(handle);
}
}
void looprun(MonocoqueSettings* ms, loop_data* f, SimData* simdata) void looprun(MonocoqueSettings* ms, loop_data* f, SimData* simdata)
{ {
if (doui == true) if (doui == true)
{ {
slogi("looking for ui config %s pass 1 simapi %i", ms->config_str, f->siminfo.simulatorapi); slogi("looking for ui config %s pass 1 simapi %i", ms->config_str, f->siminfo.simulatorapi);
@@ -271,6 +328,7 @@ void looprun(MonocoqueSettings* ms, loop_data* f, SimData* simdata)
SimDevice* devices = f->simdevices; SimDevice* devices = f->simdevices;
f->device_timers = (uv_timer_t*) (malloc(uv_handle_size(UV_TIMER) * numdevices)); f->device_timers = (uv_timer_t*) (malloc(uv_handle_size(UV_TIMER) * numdevices));
f->device_batons = (device_loop_data*) (malloc(sizeof(device_loop_data) * numdevices)); f->device_batons = (device_loop_data*) (malloc(sizeof(device_loop_data) * numdevices));
f->started_tyre_calc_thread = false;
for (int x = 0; x < numdevices; x++) for (int x = 0; x < numdevices; x++)
{ {
@@ -285,54 +343,33 @@ void looprun(MonocoqueSettings* ms, loop_data* f, SimData* simdata)
int interval = 1000/devices[x].fps; int interval = 1000/devices[x].fps;
uv_timer_start(dt, devicetimercallback, 0, interval); uv_timer_start(dt, devicetimercallback, 0, interval);
slogi("starting device type %i at id at %i fps: %i (%i ms ticks)", devices[x].type, x, devices[x].fps, interval); slogi("starting device type %i at id at %i fps: %i (%i ms ticks)", devices[x].type, x, devices[x].fps, interval);
}
else SimInfo siminfo = f->siminfo;
{ if(f->started_tyre_calc_thread == false)
slogw("skipped id %i", x); {
if(devices[x].hapticeffect.effecttype == EFFECT_TYRELOCK || devices[x].hapticeffect.effecttype == EFFECT_TYRESLIP || devices[x].hapticeffect.effecttype == EFFECT_ABSBRAKES)
{
if(siminfo.SimCalculatesTyreDiameter == false && siminfo.SimSupportsHapticEffects == true && siminfo.SimCalculatesSlipRatio == false && f->ms->useconfig == 1 && f->ms->tyre_diameter_config != NULL)
{
slogi("Starting thread to calculate tyre diameters and save to config file");
f->started_tyre_calc_thread = true;
f->tyrebaton = (device_loop_data*) malloc(sizeof(device_loop_data));
f->tyrebaton->simdata = simdata;
f->tyrebaton->ms = f->ms;
uv_handle_set_data((uv_handle_t*) &tyrediametertimer, (void*) f->tyrebaton);
uv_timer_start(&tyrediametertimer, tyrediametercheckcallback, 0, 1000);
}
}
}
} }
} }
uv_timer_start(&showstatstimer, showstatscallback, 0, 100); //uv_timer_start(&showstatstimer, showstatscallback, 0, 100);
doui = false; doui = false;
} }
else
{
//showstats(simdata);
//SimDevice* devices = f->simdevices;
//int numdevices = f->numdevices;
//for (int x = 0; x < numdevices; x++)
//{
// if (devices[x].initialized == true && devices[x].type == 2)
// {
// devices[x].update(&devices[x], simdata);
// }
//}
if(f->ms->useconfig == 1 && f->ms->tyre_diameter_config != NULL)
{
if(simdata->car != NULL || simdata->car[0] != 0)
{
// check for saved tyre diameter in config file
// if not saved version exists get tyre diameter and save it
// use config check variable to track if the config check has been performed
// avoid many opens of the same file
int error = 0;
if(hasTyreDiameter(simdata)==false && f->ms->configcheck == 0)
{
slogi("attempting load of tyre diameter config");
error = loadtyreconfig(simdata, f->ms->tyre_diameter_config, true);
f->ms->configcheck = 1;
}
if(hasTyreDiameter(simdata)==false)
{
slogt("could not find tyre diameter in config file, attempting to calculate new");
getTyreDiameter(simdata);
}
}
}
}
} }
void showstatscallback(uv_timer_t* handle) void showstatscallback(uv_timer_t* handle)
@@ -366,14 +403,10 @@ void releaseloop(loop_data* f, SimData* simdata, SimMap* simmap)
slogi("releasing devices, please wait"); slogi("releasing devices, please wait");
//attempt tyre diameter saving //attempt tyre diameter saving
if(hasTyreDiameter(simdata) == true) uv_timer_stop(&tyrediametertimer);
if(f->started_tyre_calc_thread == true)
{ {
int a = loadtyreconfig(simdata, f->ms->tyre_diameter_config, false); free(f->tyrebaton);
if(a < 0)
{
slogi("saving new tyre diameter config");
savetyreconfig(simdata, f->ms->tyre_diameter_config);
}
} }
f->uion = false; f->uion = false;
@@ -498,7 +531,7 @@ static void on_udp_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* rcvbuf,
int startudp(int port) int startudp(int port)
{ {
uv_udp_init(uv_default_loop(), &recv_socket);
struct sockaddr_in recv_addr; struct sockaddr_in recv_addr;
uv_ip4_addr("0.0.0.0", port, &recv_addr); uv_ip4_addr("0.0.0.0", port, &recv_addr);
int err = uv_udp_bind(&recv_socket, (const struct sockaddr *) &recv_addr, UV_UDP_REUSEADDR); int err = uv_udp_bind(&recv_socket, (const struct sockaddr *) &recv_addr, UV_UDP_REUSEADDR);
@@ -546,13 +579,12 @@ void datacheckcallback(uv_timer_t* handle)
simdata->tyrediameter[2] = -1; simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1; simdata->tyrediameter[3] = -1;
if(f->use_udp == true) if(f->use_udp == true || f->siminfo.SimUsesUDP == true)
{ {
slogt("starting udp receive loop"); slogt("starting udp receive loop");
udpstart(f->ms, f, simdata, simmap); udpstart(f->ms, f, simdata, simmap);
uv_udp_recv_start(&recv_socket, on_alloc, on_udp_recv); uv_udp_recv_start(&recv_socket, on_alloc, on_udp_recv);
slogt("udp receive loop started"); slogt("udp receive loop started");
uv_timer_stop(handle);
} }
else else
{ {
@@ -560,7 +592,6 @@ void datacheckcallback(uv_timer_t* handle)
slogd("starting telemetry mapping at %i fps (%i ms ticks)", f->ms->fps, interval); slogd("starting telemetry mapping at %i fps (%i ms ticks)", f->ms->fps, interval);
uv_timer_start(&datamaptimer, shmdatamapcallback, 2000, interval); uv_timer_start(&datamaptimer, shmdatamapcallback, 2000, interval);
} }
//uv_timer_start(&datachecktimer, datacheckcallback, 0, 3000);
} }
if(appstate == 2) if(appstate == 2)
{ {
@@ -611,11 +642,240 @@ void cb(uv_poll_t* handle, int status, int events)
void* monocoque_mainloop_start(void* arg)
{
MonocoqueSettings* ms = arg;
simdata = malloc(sizeof(SimData));
simmap = simapi_simmap_create();
slogd("setting initial app state");
appstate = 1;
//struct pollfd mypoll = { STDIN_FILENO, POLLIN|POLLPRI };
//uv_poll_t* poll = (uv_poll_t*) malloc(uv_handle_size(UV_POLL));
baton = (loop_data*) malloc(sizeof(loop_data));
baton->siminfo.mapapi = -1;
baton->simmap = simmap;
baton->simdata = simdata;
baton->ms = ms;
baton->uion = false;
baton->releasing = false;
baton->use_udp = false;
baton->req.data = (void*) baton;
simapi_set_log_info(simapilib_loginfo);
simapi_set_log_debug(simapilib_logdebug);
simapi_set_log_trace(simapilib_logtrace);
//if (0 != uv_poll_init(uv_default_loop(), poll, 0))
//{
// return NULL;
//};
uv_udp_init(loop, &recv_socket);
uv_timer_init(loop, &datachecktimer);
uv_timer_init(loop, &datamaptimer);
uv_timer_init(loop, &tyrediametertimer);
uv_handle_set_data((uv_handle_t*) &datachecktimer, (void*) baton);
uv_handle_set_data((uv_handle_t*) &datamaptimer, (void*) baton);
//uv_handle_set_data((uv_handle_t*) &showstatstimer, (void*) baton);
uv_handle_set_data((uv_handle_t*) &recv_socket, (void*) baton);
//}
//uv_handle_set_data((uv_handle_t*) poll, (void*) baton);
//if (0 != uv_poll_start(poll, UV_READABLE, cb))
//{
// return NULL;
//};
uv_timer_start(&datachecktimer, datacheckcallback, 1000, 1000);
//fprintf(stdout, "Searching for sim data... Press q to quit...\n");
uv_run(loop, UV_RUN_DEFAULT);
return NULL;
}
SimData* get_test_simdata()
{
if(test_baton == NULL)
{
return NULL;
}
return test_baton->simdata;
}
void* monocoque_testloop_start(void* arg)
{
test_loop_args* args = arg;
simmap = simapi_simmap_create();
simapi_set_log_info(simapilib_loginfo);
simapi_set_log_debug(simapilib_logdebug);
simapi_set_log_trace(simapilib_logtrace);
DeviceSettings ds;
//int numdevices = getsingledevice(args->ms->config_str, args->confignum, args->devicenum, args->ms, &ds);
test_simdevice = malloc(1 * sizeof(SimDevice));
test_siminfo = malloc(sizeof(SimInfo));
simapi_set_faux_siminfo(test_siminfo);
int initdevices = devinit(test_simdevice, test_siminfo, 1, args->ds, args->ms);
//settingsfree(ds);
test_baton->siminfo.mapapi = -1;
test_baton->simdevice = test_simdevice;
test_baton->ms = args->ms;
test_baton->req.data = (void*) test_baton;
uv_timer_init(loop, &testdevicetimer);
uv_handle_set_data((uv_handle_t*) &testdevicetimer, (void*) test_baton);
uv_timer_start(&testdevicetimer, devicetimercallback, 1000, 16);
uv_run(loop, UV_RUN_DEFAULT);
return NULL;
}
int monocoque_testloop_stop()
{
uv_timer_stop(&testdevicetimer);
uv_stop(loop);
pthread_join(loop_thread, NULL);
uv_run(loop, UV_RUN_NOWAIT);
uv_walk(loop, close_walk_cb, NULL);
uv_loop_close(loop);
uv_library_shutdown();
slogi("All threads stopped...");
test_simdevice->free(test_simdevice);
free(loop);
free(test_baton);
//free(simdata);
free(simmap);
return 0;
}
int monocoque_mainloop_stop(MonocoqueSettings* ms)
{
uv_udp_recv_stop(&recv_socket);
uv_timer_stop(&datamaptimer);
uv_timer_stop(&datachecktimer);
uv_stop(loop);
pthread_join(loop_thread, NULL);
uv_run(loop, UV_RUN_NOWAIT);
uv_walk(loop, close_walk_cb, NULL);
uv_loop_close(loop);
uv_library_shutdown();
slogi("All threads stopped...");
free(loop);
free(baton);
free(simdata);
free(simmap);
appstate = 0;
return 0;
}
int start_loop(MonocoqueSettings* ms)
{
loop = malloc(sizeof(uv_loop_t));
if (loop == NULL)
{
return -1;
}
if (uv_loop_init(loop) != 0)
{
free(loop);
loop = NULL;
return -1;
}
return pthread_create(&loop_thread, NULL, monocoque_mainloop_start, ms);
}
int start_test(test_loop_args* test_data)
{
test_baton = (device_loop_data*) malloc(sizeof(device_loop_data));
test_baton->simdata = test_data->simdata;
loop = malloc(sizeof(uv_loop_t));
if (loop == NULL)
{
return -1;
}
if (uv_loop_init(loop) != 0)
{
free(loop);
loop = NULL;
return -1;
}
return pthread_create(&loop_thread, NULL, monocoque_testloop_start, test_data);
}
const char* get_simexe_name(void)
{
if(appstate <= 0)
{
return "None Detected";
}
if(baton == NULL)
{
return "None Detected";
}
if(baton->siminfo.simulatorexe <= 0)
{
return "None Detected";
}
return simapi_gametofullstr(baton->siminfo.simulatorexe);
}
const char* get_simd_onoff(void)
{
if(appstate <= 0)
{
return "Not Detected";
}
if(baton == NULL)
{
return "Not Detected";
}
if(baton->siminfo.mapapi == 0)
{
return "Running";
}
return "Not Detected";
}
int monocoque_mainloop(MonocoqueSettings* ms) int monocoque_mainloop(MonocoqueSettings* ms)
{ {
SimData* simdata = malloc(sizeof(SimData)); simdata = malloc(sizeof(SimData));
SimMap* simmap = simapi_simmap_create(); simmap = simapi_simmap_create();
struct termios newsettings, canonicalmode; struct termios newsettings, canonicalmode;
tcgetattr(0, &canonicalmode); tcgetattr(0, &canonicalmode);
@@ -629,7 +889,7 @@ int monocoque_mainloop(MonocoqueSettings* ms)
uv_poll_t* poll = (uv_poll_t*) malloc(uv_handle_size(UV_POLL)); uv_poll_t* poll = (uv_poll_t*) malloc(uv_handle_size(UV_POLL));
loop_data* baton = (loop_data*) malloc(sizeof(loop_data)); baton = (loop_data*) malloc(sizeof(loop_data));
baton->simmap = simmap; baton->simmap = simmap;
baton->simdata = simdata; baton->simdata = simdata;
baton->ms = ms; baton->ms = ms;
@@ -646,9 +906,12 @@ int monocoque_mainloop(MonocoqueSettings* ms)
{ {
return 1; return 1;
}; };
uv_udp_init(uv_default_loop(), &recv_socket);
uv_timer_init(uv_default_loop(), &datachecktimer); uv_timer_init(uv_default_loop(), &datachecktimer);
uv_timer_init(uv_default_loop(), &showstatstimer); uv_timer_init(uv_default_loop(), &showstatstimer);
uv_timer_init(uv_default_loop(), &datamaptimer); uv_timer_init(uv_default_loop(), &datamaptimer);
uv_timer_init(uv_default_loop(), &tyrediametertimer);
slogd("setting initial app state"); slogd("setting initial app state");
appstate = 1; appstate = 1;
@@ -680,15 +943,102 @@ int monocoque_mainloop(MonocoqueSettings* ms)
free(baton); free(baton);
free(simdata); free(simdata);
free(simmap);
return 0; return 0;
} }
void set_basic_simdata(SimData* simdata)
{
simdata->car[0] = 'C';
simdata->car[1] = 'A';
simdata->car[2] = 'R';
simdata->car[3] = '\0';
simdata->gear = SIMAPI_GEAR_NEUTRAL;
simdata->gearc[0] = 0x4e;
simdata->gearc[1] = 0;
simdata->velocity = 160;
simdata->rpms = 7000;
simdata->maxrpm = 8000;
simdata->abs = 0;
simdata->tyrediameter[0] = -1;
simdata->tyrediameter[1] = -1;
simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1;
simdata->tyreslipratio[0] = 0;
simdata->tyreslipratio[1] = 0;
simdata->tyreslipratio[2] = 0;
simdata->tyreslipratio[3] = 0;
simdata->Xvelocity = 0;
simdata->Yvelocity = 100;
simdata->Zvelocity = 0;
}
void set_wheel_spin_simdata(SimData* simdata)
{
simdata->velocity = 15;
simdata->tyreRPS[0] = 50;
simdata->tyreRPS[1] = 50;
simdata->tyreRPS[2] = 50;
simdata->tyreRPS[3] = 50;
simdata->tyrediameter[0] = 0.638636385206394;
simdata->tyrediameter[1] = 0.633384434597093;
simdata->tyrediameter[2] = 0.710475735564615;
simdata->tyrediameter[3] = 0.710475735564615;
}
void set_wheel_lock_simdata(SimData* simdata)
{
simdata->velocity = 150;
simdata->tyreRPS[0] = 25;
simdata->tyreRPS[1] = 25;
simdata->tyreRPS[2] = 25;
simdata->tyreRPS[3] = 25;
simdata->tyrediameter[0] = 0.638636385206394;
simdata->tyrediameter[1] = 0.633384434597093;
simdata->tyrediameter[2] = 0.710475735564615;
simdata->tyrediameter[3] = 0.710475735564615;
}
// Drives every initialized device with the current test simdata, then
// mirrors that same simdata into the SIMAPI.DAT shared memory segment (if
// available) so external tools - dashboards, telemetry viewers, anything
// that reads SIMAPI.DAT the same way a real running sim would populate it -
// can follow monocoque's own test sequence instead of seeing nothing.
static void update_devices(SimDevice* devices, int numdevices, SimData* simdata, SimMap* testsimmap)
{
for (int x = 0; x < numdevices; x++)
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
if (testsimmap != NULL && testsimmap->addr != NULL)
{
simdata->mtick++;
memcpy(testsimmap->addr, simdata, sizeof(SimData));
}
}
int tester(SimDevice* devices, int numdevices) int tester(SimDevice* devices, int numdevices)
{ {
slogi("preparing test with %i devices...", numdevices); slogi("preparing test with %i devices...", numdevices);
SimData* simdata = malloc(sizeof(SimData)); SimData* simdata = malloc(sizeof(SimData));
memset(simdata, 0, sizeof(SimData));
simdata->simon = true;
simdata->simstatus = SIMAPI_STATUS_ACTIVEPLAY;
SimMap* testsimmap = malloc(sizeof(SimMap));
memset(testsimmap, 0, sizeof(SimMap));
int shmerr = simapi_universalmap_open(testsimmap, simdata);
if (shmerr != SIMAPI_ERROR_NONE)
{
slog_warn("Could not open shared telemetry memory for test mode (error %i) - test sequence will still drive local devices, but external tools won't see it", shmerr);
free(testsimmap);
testsimmap = NULL;
}
struct termios newsettings, canonicalmode; struct termios newsettings, canonicalmode;
tcgetattr(0, &canonicalmode); tcgetattr(0, &canonicalmode);
@@ -715,6 +1065,10 @@ int tester(SimDevice* devices, int numdevices)
simdata->tyrediameter[1] = -1; simdata->tyrediameter[1] = -1;
simdata->tyrediameter[2] = -1; simdata->tyrediameter[2] = -1;
simdata->tyrediameter[3] = -1; simdata->tyrediameter[3] = -1;
simdata->tyreslipratio[0] = 0;
simdata->tyreslipratio[1] = 0;
simdata->tyreslipratio[2] = 0;
simdata->tyreslipratio[3] = 0;
simdata->Xvelocity = 0; simdata->Xvelocity = 0;
simdata->Yvelocity = 100; simdata->Yvelocity = 100;
simdata->Zvelocity = 0; simdata->Zvelocity = 0;
@@ -726,38 +1080,20 @@ int tester(SimDevice* devices, int numdevices)
for (int r = 0; r < 8000; r += 3) for (int r = 0; r < 8000; r += 3)
{ {
simdata->rpms = 1000 + r; simdata->rpms = 1000 + r;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
usleep(1000); usleep(1000);
} }
for (int r = 0; r < 8000; r += 3) for (int r = 0; r < 8000; r += 3)
{ {
simdata->rpms = 9000 - r; simdata->rpms = 9000 - r;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
usleep(1000); usleep(1000);
} }
fprintf(stdout, "Setting rpms to 1000\n"); fprintf(stdout, "Setting rpms to 1000\n");
simdata->rpms = 1000; simdata->rpms = 1000;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Green Flag!\n"); fprintf(stdout, "Green Flag!\n");
@@ -766,24 +1102,12 @@ int tester(SimDevice* devices, int numdevices)
fprintf(stdout, "Shifting into first gear\n"); fprintf(stdout, "Shifting into first gear\n");
simdata->gear = SIMAPI_GEAR_FIRST; simdata->gear = SIMAPI_GEAR_FIRST;
simdata->gearc[0] = 0x31; simdata->gearc[0] = 0x31;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Setting speed to 100\n"); fprintf(stdout, "Setting speed to 100\n");
simdata->velocity = 100; simdata->velocity = 100;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "testing wheel spin\n"); fprintf(stdout, "testing wheel spin\n");
@@ -796,13 +1120,7 @@ int tester(SimDevice* devices, int numdevices)
simdata->tyrediameter[1] = 0.633384434597093; simdata->tyrediameter[1] = 0.633384434597093;
simdata->tyrediameter[2] = 0.710475735564615; simdata->tyrediameter[2] = 0.710475735564615;
simdata->tyrediameter[3] = 0.710475735564615; simdata->tyrediameter[3] = 0.710475735564615;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Testing wheel Lock\n"); fprintf(stdout, "Testing wheel Lock\n");
@@ -811,13 +1129,7 @@ int tester(SimDevice* devices, int numdevices)
simdata->tyreRPS[2] = 25; simdata->tyreRPS[2] = 25;
simdata->tyreRPS[3] = 25; simdata->tyreRPS[3] = 25;
simdata->velocity = 150; simdata->velocity = 150;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Shifting into second gear\n"); fprintf(stdout, "Shifting into second gear\n");
@@ -832,13 +1144,7 @@ int tester(SimDevice* devices, int numdevices)
simdata->abs = 0; simdata->abs = 0;
simdata->gear = SIMAPI_GEAR_SECOND; simdata->gear = SIMAPI_GEAR_SECOND;
simdata->gearc[0] = 0x32; simdata->gearc[0] = 0x32;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Testing abs brake lock Lock\n"); fprintf(stdout, "Testing abs brake lock Lock\n");
@@ -851,13 +1157,7 @@ int tester(SimDevice* devices, int numdevices)
simdata->tyrediameter[2] = 0.710475735564615; simdata->tyrediameter[2] = 0.710475735564615;
simdata->tyrediameter[3] = 0.710475735564615; simdata->tyrediameter[3] = 0.710475735564615;
simdata->abs = .11; simdata->abs = .11;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Setting speed to 200\n"); fprintf(stdout, "Setting speed to 200\n");
@@ -871,13 +1171,7 @@ int tester(SimDevice* devices, int numdevices)
simdata->tyrediameter[3] = -1; simdata->tyrediameter[3] = -1;
simdata->abs = 0; simdata->abs = 0;
simdata->velocity = 200; simdata->velocity = 200;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Yellow Flag!\n"); fprintf(stdout, "Yellow Flag!\n");
@@ -886,24 +1180,12 @@ int tester(SimDevice* devices, int numdevices)
fprintf(stdout, "Shifting into third gear\n"); fprintf(stdout, "Shifting into third gear\n");
simdata->gear = SIMAPI_GEAR_THIRD; simdata->gear = SIMAPI_GEAR_THIRD;
simdata->gearc[0] = 0x33; simdata->gearc[0] = 0x33;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Setting rpms to 2000\n"); fprintf(stdout, "Setting rpms to 2000\n");
simdata->rpms = 2000; simdata->rpms = 2000;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Blue Flag!\n"); fprintf(stdout, "Blue Flag!\n");
@@ -911,47 +1193,23 @@ int tester(SimDevice* devices, int numdevices)
fprintf(stdout, "Setting rpms to 4000\n"); fprintf(stdout, "Setting rpms to 4000\n");
simdata->rpms = 4000; simdata->rpms = 4000;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Shifting into fourth gear\n"); fprintf(stdout, "Shifting into fourth gear\n");
simdata->gear = SIMAPI_GEAR_FOURTH; simdata->gear = SIMAPI_GEAR_FOURTH;
simdata->gearc[0] = 0x34; simdata->gearc[0] = 0x34;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Setting speed to 300\n"); fprintf(stdout, "Setting speed to 300\n");
simdata->velocity = 300; simdata->velocity = 300;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Setting rpms to 7000\n"); fprintf(stdout, "Setting rpms to 7000\n");
simdata->rpms = 7000; simdata->rpms = 7000;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(3); sleep(3);
fprintf(stdout, "Red Flag!\n"); fprintf(stdout, "Red Flag!\n");
@@ -959,22 +1217,10 @@ int tester(SimDevice* devices, int numdevices)
fprintf(stdout, "Setting rpms to 8000\n"); fprintf(stdout, "Setting rpms to 8000\n");
simdata->rpms = 8000; simdata->rpms = 8000;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
for(int x = 0; x < 100; x++) for(int x = 0; x < 100; x++)
{ {
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
} }
sleep(3); sleep(3);
@@ -982,18 +1228,30 @@ int tester(SimDevice* devices, int numdevices)
simdata->rpms = 100; simdata->rpms = 100;
simdata->gear = SIMAPI_GEAR_NEUTRAL; simdata->gear = SIMAPI_GEAR_NEUTRAL;
simdata->gearc[0] = 0x4e; simdata->gearc[0] = 0x4e;
for (int x = 0; x < numdevices; x++) update_devices(devices, numdevices, simdata, testsimmap);
{
if (devices[x].initialized == true)
{
devices[x].update(&devices[x], simdata);
}
}
sleep(1); sleep(1);
fflush(stdout); fflush(stdout);
tcsetattr(0, TCSANOW, &canonicalmode); tcsetattr(0, TCSANOW, &canonicalmode);
// Not shm_unlink()'d - the segment itself stays behind, the same way a
// real sim's shared memory file stays around after that sim exits - but
// its *contents* are reset to an inactive frame first. Leaving the last
// active-play frame in place made every external reader (e.g.
// typiql-tauri) see this test run as a sim that's still actively
// driving forever, since nothing else ever writes to SIMAPI.DAT again
// until the next `monocoque test`/`monocoque play`.
if (testsimmap != NULL)
{
simdata->simon = false;
simdata->simstatus = SIMAPI_STATUS_OFF;
update_devices(devices, numdevices, simdata, testsimmap);
munmap(testsimmap->addr, sizeof(SimData));
close(testsimmap->fd);
free(testsimmap);
}
free(simdata); free(simdata);
return 0; return 0;
+17
View File
@@ -1,7 +1,24 @@
#include "../devices/simdevice.h" #include "../devices/simdevice.h"
#include "../helper/parameters.h" #include "../helper/parameters.h"
#include "loopdata.h"
extern int appstate;
int tester(SimDevice* devices, int numdevices); int tester(SimDevice* devices, int numdevices);
int looper(SimDevice* devices, int numdevices, Parameters* p); int looper(SimDevice* devices, int numdevices, Parameters* p);
int monocoque_mainloop(MonocoqueSettings* ms); int monocoque_mainloop(MonocoqueSettings* ms);
int start_loop(MonocoqueSettings* ms);
int start_test(test_loop_args* test_loop_data);
int monocoque_mainloop_stop(MonocoqueSettings* ms);
int monocoque_testloop_stop();
const char* get_simd_onoff(void);
const char* get_simexe_name(void);
SimData* get_test_simdata();
void set_basic_simdata(SimData* simdata);
void set_wheel_spin_simdata(SimData* simdata);
void set_wheel_lock_simdata(SimData* simdata);
+15
View File
@@ -10,9 +10,11 @@
typedef struct device_loop_data typedef struct device_loop_data
{ {
// these are all pointers to pointers and freed independently, usually in releaseloop
uv_work_t req; uv_work_t req;
SimData* simdata; SimData* simdata;
SimDevice* simdevice; SimDevice* simdevice;
MonocoqueSettings* ms;
SimInfo siminfo; SimInfo siminfo;
} device_loop_data; } device_loop_data;
@@ -22,16 +24,29 @@ typedef struct loop_data
bool use_udp; bool use_udp;
bool uion; bool uion;
bool releasing; bool releasing;
bool started_tyre_calc_thread;
int numdevices; int numdevices;
SimInfo siminfo; SimInfo siminfo;
// monocoque settings is a pointer from monocoque.c and freed there
MonocoqueSettings* ms; MonocoqueSettings* ms;
// these are freed at the end of the main gameloop
SimData* simdata; SimData* simdata;
SimMap* simmap; SimMap* simmap;
// these all get malloced in looprun and freed in releaseloop
SimDevice* simdevices; SimDevice* simdevices;
uv_timer_t* device_timers; uv_timer_t* device_timers;
device_loop_data* device_batons; device_loop_data* device_batons;
device_loop_data* tyrebaton;
} loop_data; } loop_data;
typedef struct test_loop_args
{
MonocoqueSettings* ms;
DeviceSettings* ds;
SimData* simdata;
int devicenum;
int confignum;
} test_loop_args;
#endif #endif
+2
View File
@@ -11,3 +11,5 @@ set(LIBXML_INCLUDE_DIR /usr/include/libxml2)
include_directories("." ${LIBXML_INCLUDE_DIR}) include_directories("." ${LIBXML_INCLUDE_DIR})
add_library(helper STATIC ${helper_source_files}) add_library(helper STATIC ${helper_source_files})
set_property(TARGET helper PROPERTY POSITION_INDEPENDENT_CODE ON)
+692 -132
View File
@@ -37,35 +37,35 @@ int strtoeffecttype(const char* effect, DeviceSettings* ds)
if (strcicmp(effect, "Engine") == 0) if (strcicmp(effect, "Engine") == 0)
{ {
ds->is_valid = true; ds->is_valid = true;
ds->effect_type = EFFECT_ENGINERPM; ds->hapticsettings.effect_type = EFFECT_ENGINERPM;
} }
if (strcicmp(effect, "Gear") == 0) if (strcicmp(effect, "Gear") == 0)
{ {
ds->is_valid = true; ds->is_valid = true;
ds->effect_type = EFFECT_GEARSHIFT; ds->hapticsettings.effect_type = EFFECT_GEARSHIFT;
} }
if (strcicmp(effect, "Suspension") == 0) if (strcicmp(effect, "Suspension") == 0)
{ {
ds->is_valid = true; ds->is_valid = true;
ds->effect_type = EFFECT_SUSPENSION; ds->hapticsettings.effect_type = EFFECT_SUSPENSION;
} }
if (strcicmp(effect, "ABS") == 0) if (strcicmp(effect, "ABS") == 0)
{ {
ds->is_valid = true; ds->is_valid = true;
slogt("found abas effect set"); slogt("found abas effect set");
ds->effect_type = EFFECT_ABSBRAKES; ds->hapticsettings.effect_type = EFFECT_ABSBRAKES;
} }
if ((strcicmp(effect, "SLIP") == 0) || (strcicmp(effect, "TYRESLIP") == 0) || (strcicmp(effect, "TIRESLIP") == 0)) if ((strcicmp(effect, "SLIP") == 0) || (strcicmp(effect, "TYRESLIP") == 0) || (strcicmp(effect, "TIRESLIP") == 0))
{ {
ds->is_valid = true; ds->is_valid = true;
slogt("found tyreslip effect set"); slogt("found tyreslip effect set");
ds->effect_type = EFFECT_TYRESLIP; ds->hapticsettings.effect_type = EFFECT_TYRESLIP;
} }
if ((strcicmp(effect, "LOCK") == 0) || (strcicmp(effect, "TYRELOCK") == 0) || (strcicmp(effect, "TIRELOCK") == 0)) if ((strcicmp(effect, "LOCK") == 0) || (strcicmp(effect, "TYRELOCK") == 0) || (strcicmp(effect, "TIRELOCK") == 0))
{ {
ds->is_valid = true; ds->is_valid = true;
slogt("found tyreslock effect set"); slogt("found tyreslock effect set");
ds->effect_type = EFFECT_TYRELOCK; ds->hapticsettings.effect_type = EFFECT_TYRELOCK;
} }
if (ds->is_valid == false) if (ds->is_valid == false)
@@ -113,11 +113,26 @@ int strtodevsubsubtype(const char* device_subsubtype, DeviceSettings* ds)
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZAR5; ds->dev_subsubtype = SIMDEVSUBTYPE_MOZAR5;
devfound = true; devfound = true;
} }
if (strcicmp(device_subsubtype, "MozaR3") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZAR5;
devfound = true;
}
if (strcicmp(device_subsubtype, "MozaKSProWheel") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_MOZA_KS_PRO_WHEEL;
devfound = true;
}
if (strcicmp(device_subsubtype, "CSLELITEV3PEDALS") == 0) if (strcicmp(device_subsubtype, "CSLELITEV3PEDALS") == 0)
{ {
ds->dev_subsubtype = SIMDEVSUBTYPE_CSLELITEV3PEDALS; ds->dev_subsubtype = SIMDEVSUBTYPE_CSLELITEV3PEDALS;
devfound = true; devfound = true;
} }
if (strcicmp(device_subsubtype, "SIMNETPEDALS") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_SIMNETPEDALS;
devfound = true;
}
if (strcicmp(device_subsubtype, "SIMAGICP1000PEDALS") == 0) if (strcicmp(device_subsubtype, "SIMAGICP1000PEDALS") == 0)
{ {
ds->dev_subsubtype = SIMDEVSUBTYPE_SIMAGICP1000PEDALS; ds->dev_subsubtype = SIMDEVSUBTYPE_SIMAGICP1000PEDALS;
@@ -128,6 +143,11 @@ int strtodevsubsubtype(const char* device_subsubtype, DeviceSettings* ds)
ds->dev_subsubtype = SIMDEVSUBTYPE_SIMAGICGTNEO; ds->dev_subsubtype = SIMDEVSUBTYPE_SIMAGICGTNEO;
devfound = true; devfound = true;
} }
if (strcicmp(device_subsubtype, "REVBURNER") == 0)
{
ds->dev_subsubtype = SIMDEVSUBTYPE_REVBURNERTACHOMETER;
devfound = true;
}
if(devfound == false) if(devfound == false)
{ {
@@ -576,31 +596,31 @@ int gettyre(config_setting_t* device_settings, DeviceSettings* ds) {
const char* temp; const char* temp;
int found = config_setting_lookup_string(device_settings, "tyre", &temp); int found = config_setting_lookup_string(device_settings, "tyre", &temp);
ds->tyre = ALLFOUR; ds->hapticsettings.tyre = ALLFOUR;
if (strcicmp(temp, "FRONTS") == 0) if (strcicmp(temp, "FRONTS") == 0)
{ {
ds->tyre = FRONTS; ds->hapticsettings.tyre = FRONTS;
} }
if (strcicmp(temp, "REARS") == 0) if (strcicmp(temp, "REARS") == 0)
{ {
ds->tyre = REARS; ds->hapticsettings.tyre = REARS;
} }
if (strcicmp(temp, "FRONTLEFT") == 0) if (strcicmp(temp, "FRONTLEFT") == 0)
{ {
ds->tyre = FRONTLEFT; ds->hapticsettings.tyre = FRONTLEFT;
} }
if (strcicmp(temp, "FRONTRIGHT") == 0) if (strcicmp(temp, "FRONTRIGHT") == 0)
{ {
ds->tyre = FRONTRIGHT; ds->hapticsettings.tyre = FRONTRIGHT;
} }
if (strcicmp(temp, "REARLEFT") == 0) if (strcicmp(temp, "REARLEFT") == 0)
{ {
ds->tyre = REARLEFT; ds->hapticsettings.tyre = REARLEFT;
} }
if (strcicmp(temp, "REARRIGHT") == 0) if (strcicmp(temp, "REARRIGHT") == 0)
{ {
ds->tyre = REARRIGHT; ds->hapticsettings.tyre = REARRIGHT;
} }
} }
@@ -664,6 +684,25 @@ int configcheck(const char* config_file_str, int confignum, int* devices)
return 0; return 0;
//return cfg; //return cfg;
} }
static int config_get_device(const config_setting_t *entry, DeviceSettings *ds)
{
const char *value = NULL;
if (config_setting_lookup_string(entry, "devid", &value))
{
ds->dev = strdup(value);
return ds->dev != NULL;
}
if (config_setting_lookup_string(entry, "devpath", &value))
{
ds->dev = strdup(value);
return ds->dev != NULL;
}
ds->dev = NULL;
return 0;
}
int devsetup(const char* device_type, const char* device_subtype, const char* config_file, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings) int devsetup(const char* device_type, const char* device_subtype, const char* config_file, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings)
{ {
@@ -690,6 +729,7 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
ds->fps = 60; ds->fps = 60;
config_setting_lookup_int(device_settings, "fps", &ds->fps); config_setting_lookup_int(device_settings, "fps", &ds->fps);
config_get_device(device_settings, ds);
if (ds->dev_subtype == SIMDEVTYPE_TACHOMETER) if (ds->dev_subtype == SIMDEVTYPE_TACHOMETER)
{ {
@@ -710,115 +750,7 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
} }
} }
if (ds->dev_subtype == SIMDEVTYPE_USBHAPTIC || ds->dev_subtype == SIMDEVTYPE_USBWHEEL) if (ds->dev_type == SIMDEV_USB)
{
// logic for different devices
int b = 0;
const char* temp;
int found = config_setting_lookup_string(device_settings, "subtype", &temp);
if(temp != NULL && found > 0)
{
b = strtodevsubsubtype(temp, ds);
}
}
if (ds->dev_subtype == SIMDEVTYPE_USBHAPTIC || ds->dev_type == SIMDEV_SOUND || ds->dev_subtype == SIMDEVTYPE_SERIALHAPTIC)
{
slogt("analysing haptic effect settings");
const char* effect;
config_setting_lookup_string(device_settings, "effect", &effect);
strtoeffecttype(effect, ds);
if (ds->effect_type == EFFECT_TYRESLIP || ds->effect_type == EFFECT_TYRELOCK || ds->effect_type == EFFECT_ABSBRAKES || ds->effect_type == EFFECT_SUSPENSION )
{
gettyre(device_settings, ds);
ds->threshold = 0;
int found = config_setting_lookup_float(device_settings, "threshold", &ds->threshold);
}
if (ds->dev_type == SIMDEV_SOUND)
{
slogi("reading configured sound device settings");
ds->sounddevsettings.frequency = 0;
ds->sounddevsettings.frequencyMax = 0;
ds->sounddevsettings.amplitude = 50;
ds->sounddevsettings.amplitudeMax = 50;
ds->sounddevsettings.volume = 0;
ds->sounddevsettings.pan = 0;
ds->sounddevsettings.channels = 1;
ds->sounddevsettings.duration = 2.0;
ds->sounddevsettings.noise = 0;
if (ds->effect_type == EFFECT_GEARSHIFT)
{
ds->sounddevsettings.duration = .125;
}
if (device_settings != NULL)
{
config_setting_lookup_int(device_settings, "volume", &ds->sounddevsettings.volume);
config_setting_lookup_int(device_settings, "frequency", &ds->sounddevsettings.frequency);
config_setting_lookup_int(device_settings, "frequencyMax", &ds->sounddevsettings.frequencyMax);
config_setting_lookup_int(device_settings, "amplitude", &ds->sounddevsettings.amplitude);
config_setting_lookup_int(device_settings, "amplitudeMax", &ds->sounddevsettings.amplitudeMax);
config_setting_lookup_int(device_settings, "pan", &ds->sounddevsettings.pan);
config_setting_lookup_int(device_settings, "channels", &ds->sounddevsettings.channels);
config_setting_lookup_float(device_settings, "duration", &ds->sounddevsettings.duration);
config_setting_lookup_int(device_settings, "noise", &ds->sounddevsettings.noise);
const char* temp = NULL;
int found = 0;
found = config_setting_lookup_string(device_settings, "devid", &temp);
if (found == CONFIG_FALSE)
{
ds->sounddevsettings.dev = NULL;
}
else
{
if(temp != NULL)
{
ds->sounddevsettings.dev = strdup(temp);
}
}
found = config_setting_lookup_string(device_settings, "modulation", &temp);
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_NONE;
if (found == 0)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_NONE;
slogd("Effect modulation not found, set to none");
}
else
{
if(strcicmp(temp, "FREQUENCY") == 0)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_FREQUENCY;
if(ds->sounddevsettings.frequencyMax == 0 || ds->sounddevsettings.frequencyMax < ds->sounddevsettings.frequency)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_NONE;
slogw("Falling back to no frequency modulation since frequencyMax is either not set or set below target frequency");
}
else
{
slogi("Effect modulation found, set to FREQUENCY");
}
}
else if(strcicmp(temp, "AMPLIFY") == 0)
{
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_AMPLIFY;
slogi("Effect modulation found, set to AMPLIFY");
}
else
{
slogw("%s is not a valid modulation type, falling back to no effect modulation");
ds->sounddevsettings.modulation = SOUND_EFFECT_MODULATION_NONE;
}
}
}
}
}
if (ds->dev_type == SIMDEV_SERIAL)
{ {
if (device_settings != NULL) if (device_settings != NULL)
{ {
@@ -828,9 +760,19 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
{ {
strtodevsubsubtype(temp, ds); strtodevsubsubtype(temp, ds);
} }
}
}
config_setting_lookup_string(device_settings, "devpath", &temp); if (ds->dev_type == SIMDEV_SERIAL)
ds->serialdevsettings.portdev = strdup(temp); {
if (device_settings != NULL)
{
const char* temp = NULL;
int found = config_setting_lookup_string(device_settings, "subtype", &temp);
if(temp != NULL && found > 0)
{
strtodevsubsubtype(temp, ds);
}
int motorposition = 8; int motorposition = 8;
config_setting_lookup_int(device_settings, "motors", &motorposition); config_setting_lookup_int(device_settings, "motors", &motorposition);
@@ -861,7 +803,126 @@ int devsetup(const char* device_type, const char* device_subtype, const char* co
found = config_setting_lookup_float(device_settings, "ampfactor", &ampfactor); found = config_setting_lookup_float(device_settings, "ampfactor", &ampfactor);
ds->serialdevsettings.ampfactor = ampfactor; ds->serialdevsettings.ampfactor = ampfactor;
slogt("set port baud rate to %i, ampfactor %f", baud, ampfactor); double fanpower = 0.6;
config_setting_lookup_float(device_settings, "fanpower", &fanpower);
ds->serialdevsettings.fanpower = fanpower;
slogt("set port baud rate to %i, ampfactor %f, fanpower %f", baud, ampfactor, fanpower);
}
}
ds->has_haptic_effects = false;
if (ds->dev_subtype == SIMDEVTYPE_USBHAPTIC || ds->dev_subtype == SIMDEVTYPE_USBWHEEL || ds->dev_type == SIMDEV_SOUND || ds->dev_subtype == SIMDEVTYPE_SERIALHAPTIC)
{
slogt("analysing haptic effect settings");
ds->has_haptic_effects = true;
const char* effect;
config_setting_lookup_string(device_settings, "effect", &effect);
strtoeffecttype(effect, ds);
if (ds->hapticsettings.effect_type == EFFECT_TYRESLIP || ds->hapticsettings.effect_type == EFFECT_TYRELOCK || ds->hapticsettings.effect_type == EFFECT_ABSBRAKES || ds->hapticsettings.effect_type == EFFECT_SUSPENSION )
{
gettyre(device_settings, ds);
ds->hapticsettings.threshold = 0;
int found = config_setting_lookup_float(device_settings, "threshold", &ds->hapticsettings.threshold);
}
slogi("reading configured haptic effect settings");
ds->hapticsettings.frequency = 0;
ds->hapticsettings.frequencyMax = 0;
ds->hapticsettings.amplitude = 50;
ds->hapticsettings.amplitudeMax = 50;
if (ds->hapticsettings.effect_type == EFFECT_GEARSHIFT)
{
ds->hapticsettings.duration = .125;
}
if (device_settings != NULL)
{
config_setting_lookup_int(device_settings, "frequency", &ds->hapticsettings.frequency);
config_setting_lookup_int(device_settings, "frequencyMax", &ds->hapticsettings.frequencyMax);
config_setting_lookup_int(device_settings, "amplitude", &ds->hapticsettings.amplitude);
config_setting_lookup_float(device_settings, "duration", &ds->hapticsettings.duration);
config_setting_lookup_int(device_settings, "amplitudeMax", &ds->hapticsettings.amplitudeMax);
const char* temp = NULL;
int found = 0;
found = config_setting_lookup_string(device_settings, "modulation", &temp);
ds->hapticsettings.modulation = EFFECT_MODULATION_NONE;
if (found == 0)
{
ds->hapticsettings.modulation = EFFECT_MODULATION_NONE;
slogd("Effect modulation not found, set to none");
}
else
{
if(strcicmp(temp, "FREQUENCY") == 0)
{
ds->hapticsettings.modulation = EFFECT_MODULATION_FREQUENCY;
if(ds->hapticsettings.frequencyMax == 0 || ds->hapticsettings.frequencyMax < ds->hapticsettings.frequency)
{
ds->hapticsettings.modulation = EFFECT_MODULATION_NONE;
slogw("Falling back to no frequency modulation since frequencyMax is either not set or set below target frequency");
}
else
{
slogi("Effect modulation found, set to FREQUENCY");
}
}
else if(strcicmp(temp, "AMPLIFY") == 0)
{
ds->hapticsettings.modulation = EFFECT_MODULATION_AMPLIFY;
slogi("Effect modulation found, set to AMPLIFY");
}
else
{
slogw("%s is not a valid modulation type, falling back to no effect modulation");
ds->hapticsettings.modulation = EFFECT_MODULATION_NONE;
}
}
ds->hapticsettings.motorposition = 0;
int motorposition = 1;
config_setting_lookup_int(device_settings, "motors", &motorposition);
ds->hapticsettings.motorposition = motorposition;
}
if (ds->dev_type == SIMDEV_SOUND)
{
slogi("reading configured sound device settings");
ds->sounddevsettings.volume = 0;
ds->sounddevsettings.pan = 0;
ds->sounddevsettings.channels = 1;
ds->sounddevsettings.noise = 0;
if (ds->hapticsettings.effect_type == EFFECT_GEARSHIFT)
{
ds->hapticsettings.duration = .125;
}
if (device_settings != NULL)
{
config_setting_lookup_int(device_settings, "volume", &ds->sounddevsettings.volume);
config_setting_lookup_int(device_settings, "pan", &ds->sounddevsettings.pan);
config_setting_lookup_int(device_settings, "channels", &ds->sounddevsettings.channels);
config_setting_lookup_int(device_settings, "noise", &ds->sounddevsettings.noise);
const char* temp = NULL;
int found = 0;
found = config_setting_lookup_string(device_settings, "devid", &temp);
if (found == CONFIG_FALSE)
{
ds->dev = NULL;
}
else
{
if(temp != NULL)
{
ds->dev = strdup(temp);
}
}
}
} }
@@ -938,21 +999,516 @@ int uiloadconfig(const char* config_file_str, int confignum, int configureddevic
return numdevices; return numdevices;
} }
int settingsfree(DeviceSettings ds) int getsingledevice(const char* config_file_str, int confignum, int devicenum, MonocoqueSettings* ms, DeviceSettings* ds)
{ {
if (ds.dev_type == SIMDEV_SERIAL) int numdevices = 0;
config_t cfg;
config_init(&cfg);
if (!config_read_file(&cfg, config_file_str))
{ {
if (ds.serialdevsettings.portdev != NULL) fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
}
else
{
slogi("Parsing config file");
config_setting_t* config = NULL;
config = config_lookup(&cfg, "configs");
config_setting_t* selectedconfig = config_setting_get_elem(config, confignum);
config_setting_t* config_devices = NULL;
config_devices = config_setting_lookup(selectedconfig, "devices");
int num_devices = config_setting_length(config_devices);
int i = 0;
int error = MONOCOQUE_ERROR_NONE;
while (i<num_devices)
{ {
free(ds.serialdevsettings.portdev); if(i!=devicenum)
{
i++;
continue;
}
error = MONOCOQUE_ERROR_NONE;
config_setting_t* config_device = config_setting_get_elem(config_devices, i);
const char* device_type = NULL;
const char* device_subtype = NULL;
const char* device_config_file = NULL;
int found = 0;
config_setting_lookup_string(config_device, "device", &device_type);
config_setting_lookup_string(config_device, "type", &device_subtype);
found = config_setting_lookup_string(config_device, "config", &device_config_file);
slogt("device type: %s", device_type);
slogt("device sub type: %s", device_subtype);
if(found == CONFIG_FALSE)
{
device_config_file = NULL;
}
else
{
slogt("device config file: %s", device_config_file);
}
if (error == MONOCOQUE_ERROR_NONE)
{
error = devsetup(device_type, device_subtype, device_config_file, ms, ds, config_device);
}
if (error == MONOCOQUE_ERROR_NONE)
{
numdevices++;
}
i++;
} }
} }
if (ds.dev_type == SIMDEV_SOUND)
config_destroy(&cfg);
return numdevices;
}
static const char *haptic_effect_type_to_string(VibrationEffectType effect)
{
switch (effect)
{ {
if (ds.sounddevsettings.dev != NULL) case EFFECT_ENGINERPM:
return "EngineRPM";
case EFFECT_GEARSHIFT:
return "GearShift";
case EFFECT_ABSBRAKES:
return "ABS";
case EFFECT_TYRESLIP:
return "TyreSlip";
case EFFECT_TYRELOCK:
return "TyreLock";
case EFFECT_SUSPENSION:
return "Suspension";
default:
return NULL;
}
}
static const char *tyre_identifier_to_string(MonocoqueTyreIdentifier tyre)
{
switch (tyre)
{
case FRONTLEFT:
return "FrontLeft";
case FRONTRIGHT:
return "FrontRight";
case REARLEFT:
return "RearLeft";
case REARRIGHT:
return "RearRight";
case FRONTS:
return "Front";
case REARS:
return "Rear";
case ALLFOUR:
return "ALL";
default:
return NULL;
}
}
static const char *modulation_type_to_string(EffectModulationType modulation)
{
switch (modulation)
{
case EFFECT_MODULATION_NONE:
return "none";
case EFFECT_MODULATION_FREQUENCY:
return "frequency";
case EFFECT_MODULATION_AMPLIFY:
return "amplify";
default:
return NULL;
}
}
static int set_string(config_setting_t *parent, const char *name, const char *value)
{
config_setting_t *setting;
if (value == NULL)
return 1;
setting = config_setting_lookup(parent, name);
if (setting == NULL)
setting = config_setting_add(parent, name, CONFIG_TYPE_STRING);
if (setting == NULL)
return 0;
return config_setting_set_string(setting, value);
}
static int set_int(config_setting_t *parent, const char *name, int value)
{
config_setting_t *setting;
setting = config_setting_lookup(parent, name);
if (setting == NULL)
setting = config_setting_add(parent, name, CONFIG_TYPE_INT);
if (setting == NULL)
return 0;
return config_setting_set_int(setting, value);
}
int set_float(config_setting_t *parent, const char *name, double value)
{
config_setting_t *setting;
setting = config_setting_lookup(parent, name);
if (setting == NULL)
setting = config_setting_add(parent, name, CONFIG_TYPE_FLOAT);
if (setting == NULL)
return 0;
return config_setting_set_float(setting, value);
}
int delete_device_config(config_t *cfg, const char *configfile, int confignum, int devicenum)
{
config_setting_t *configs;
config_setting_t *config;
config_setting_t *devices;
configs = config_lookup(cfg, "configs");
if (configs == NULL)
return -1;
config = config_setting_get_elem(configs, confignum);
if (config == NULL)
return -1;
devices = config_setting_lookup(config, "devices");
if (devices == NULL)
return -1;
if (devicenum < 0 || devicenum >= config_setting_length(devices))
return -1;
if (!config_setting_remove_elem(devices, devicenum))
return -1;
if (!config_write_file(cfg, configfile))
return -1;
return 0;
}
static const char *device_type_to_string(DeviceType device_type)
{
switch (device_type)
{
case SIMDEV_USB:
return "USB";
case SIMDEV_SOUND:
return "Sound";
case SIMDEV_SERIAL:
return "Serial";
case SIMDEV_UNKNOWN:
return "Unknown";
default:
return "Unknown";
}
}
static const char *device_subtype_to_string(DeviceSubType device_subtype)
{
switch (device_subtype)
{
case SIMDEVTYPE_SOUNDHAPTIC:
return "SoundHaptic";
case SIMDEVTYPE_TACHOMETER:
return "Tachometer";
case SIMDEVTYPE_USBHAPTIC:
return "UsbHaptic";
case SIMDEVTYPE_USBWHEEL:
return "UsbWheel";
case SIMDEVTYPE_SHIFTLIGHTS:
return "ShiftLights";
case SIMDEVTYPE_SIMWIND:
return "SimWind";
case SIMDEVTYPE_SERIALHAPTIC:
return "SerialHaptic";
case SIMDEVTYPE_SERIALWHEEL:
return "Wheel";
case SIMDEVTYPE_SIMLED:
return "Simleds";
case SIMDEVTYPE_ARDUINOCUSTOM:
return "ArduinoCustom";
case SIMDEVTYPE_UNKNOWN:
default:
return "Unknown";
}
}
static const char *haptic_effect_to_string(VibrationEffectType effect)
{
switch (effect)
{
case EFFECT_ENGINERPM:
return "Engine";
case EFFECT_GEARSHIFT:
return "Gear";
case EFFECT_ABSBRAKES:
return "ABS";
case EFFECT_TYRESLIP:
return "TyreSlip";
case EFFECT_TYRELOCK:
return "TyreLock";
case EFFECT_SUSPENSION:
return "Suspension";
default:
return "Unknown";
}
}
static const char *modulation_to_string(EffectModulationType modulation)
{
switch (modulation)
{
case EFFECT_MODULATION_NONE:
return "None";
case EFFECT_MODULATION_FREQUENCY:
return "Frequency";
case EFFECT_MODULATION_AMPLIFY:
return "Amplitude";
default:
return "Unknown";
}
}
static const char *tyre_to_string(MonocoqueTyreIdentifier tyre)
{
switch (tyre)
{
case FRONTLEFT:
return "FrontLeft";
case FRONTRIGHT:
return "FrontRight";
case REARLEFT:
return "RearLeft";
case REARRIGHT:
return "RearRight";
case FRONTS:
return "Fronts";
case REARS:
return "Rears";
case ALLFOUR:
return "All";
default:
return "Unknown";
}
}
int save_device_config(config_t *cfg, const char* configfile, int confignum, int devicenum, const DeviceSettings *ds)
{
config_setting_t *configs;
config_setting_t *config_entry;
config_setting_t *devices;
config_setting_t *device_entry;
if (cfg == NULL || ds == NULL)
return 0;
slogd("Saving device with device num %i and confignum %i", devicenum, confignum);
configs = config_lookup(cfg, "configs");
if (configs == NULL)
return 0;
config_entry = config_setting_get_elem(configs, confignum);
if (config_entry == NULL)
return 0;
devices = config_setting_lookup(config_entry, "devices");
if (devices == NULL)
return 0;
device_entry = config_setting_get_elem(devices, devicenum);
if (device_entry == NULL)
{
device_entry = config_setting_add(devices, NULL, CONFIG_TYPE_GROUP);
if (device_entry == NULL)
return 0;
if (config_setting_index(device_entry) != devicenum)
return 0;
}
if (device_entry == NULL)
return 0;
set_string(device_entry, "device", device_type_to_string(ds->dev_type));
set_int(device_entry, "fps", ds->fps);
if (ds->specific_config_file != NULL)
{
set_string(device_entry, "config", ds->specific_config_file);
}
if (ds->dev != NULL)
{
set_string(device_entry, "devid", ds->dev);
}
switch (ds->dev_type)
{
case SIMDEV_SERIAL:
{ {
free(ds.sounddevsettings.dev); set_string(device_entry, "type", device_subtype_to_string(ds->dev_type + SerialDevicesOffset));
const SerialDeviceSettings *ss = &ds->serialdevsettings;
set_int(device_entry, "baud", ss->baud);
set_float(device_entry, "ampfactor", ss->ampfactor);
set_float(device_entry, "fanpower", ss->fanpower);
break;
} }
case SIMDEV_SOUND:
{
set_string(device_entry, "type", device_subtype_to_string(ds->dev_type));
const SoundDeviceSettings *ss = &ds->sounddevsettings;
set_int(device_entry, "volume", ss->volume);
set_int(device_entry, "pan", ss->pan);
set_int(device_entry, "channels", ss->channels);
set_int(device_entry, "noise", ss->noise);
break;
}
case SIMDEV_USB:
{
set_string(device_entry, "type", device_subtype_to_string(ds->dev_type + USBDevicesOffset));
const USBDeviceSettings *us = &ds->usbdevsettings;
break;
}
default:
break;
}
if(ds->has_haptic_effects == true)
{
slogt("Saving haptic effect settings");
const HapticEffectSettings *hs = &ds->hapticsettings;
set_int(device_entry, "frequency", hs->frequency);
set_int(device_entry, "amplitude", hs->amplitude);
set_int(device_entry, "frequencyMax", hs->frequencyMax);
set_int(device_entry, "amplitudeMax", hs->amplitudeMax);
set_float(device_entry, "threshold", hs->threshold);
set_float(device_entry, "duration", hs->duration);
set_string(device_entry, "effect", haptic_effect_to_string(hs->effect_type));
set_string(device_entry, "tyre", tyre_to_string(hs->tyre));
set_string(device_entry, "modulation", modulation_to_string(hs->modulation));
}
if(ds->has_led_effects == true)
{
slogt("Saving led effect settings");
const SerialDeviceSettings *ss = &ds->serialdevsettings;
set_int(device_entry, "numleds", ss->numleds);
set_int(device_entry, "startled", ss->startled);
set_int(device_entry, "endled", ss->endled);
}
if(!config_write_file(cfg, configfile))
{
return 1;
}
return 0;
}
config_t *open_monocoque_config(const char *filename)
{
config_t *cfg;
if (filename == NULL)
return NULL;
cfg = malloc(sizeof(*cfg));
if (cfg == NULL)
return NULL;
config_init(cfg);
if (!config_read_file(cfg, filename))
{
fprintf(stderr,
"Failed to read config file '%s' (line %d): %s\n",
filename,
config_error_line(cfg),
config_error_text(cfg));
config_destroy(cfg);
free(cfg);
return NULL;
}
return cfg;
}
void close_monocoque_config(config_t* cfg)
{
if (cfg == NULL)
return;
config_destroy(cfg);
}
int settingsfree(DeviceSettings ds)
{
if (ds.dev != NULL)
{
free(ds.dev);
} }
if(ds.has_config && ds.specific_config_file != NULL) if(ds.has_config && ds.specific_config_file != NULL)
@@ -968,17 +1524,21 @@ int monocoquesettingsfree(MonocoqueSettings* ms)
if(ms->tyre_diameter_config != NULL) if(ms->tyre_diameter_config != NULL)
{ {
free(ms->tyre_diameter_config); free(ms->tyre_diameter_config);
ms->tyre_diameter_config = NULL;
} }
if(ms->config_str != NULL) if(ms->config_str != NULL)
{ {
free(ms->config_str); free(ms->config_str);
ms->config_str = NULL;
} }
if(ms->log_filename_str != NULL) if(ms->log_filename_str != NULL)
{ {
free(ms->log_filename_str); free(ms->log_filename_str);
ms->log_filename_str = NULL;
} }
if(ms->log_dirname_str != NULL) if(ms->log_dirname_str != NULL)
{ {
free(ms->log_dirname_str); free(ms->log_dirname_str);
ms->log_dirname_str = NULL;
} }
} }
+73 -41
View File
@@ -13,28 +13,32 @@
typedef enum typedef enum
{ {
SIMDEV_UNKNOWN = 0, SIMDEV_USB = 0,
SIMDEV_USB = 1, SIMDEV_SOUND = 1,
SIMDEV_SOUND = 2, SIMDEV_SERIAL = 2,
SIMDEV_SERIAL = 3 SIMDEV_UNKNOWN = 3,
} }
DeviceType; DeviceType;
typedef enum typedef enum
{ {
SIMDEVTYPE_UNKNOWN = 0, SIMDEVTYPE_UNKNOWN = 0,
SIMDEVTYPE_TACHOMETER = 1, SIMDEVTYPE_SOUNDHAPTIC = 1,
SIMDEVTYPE_USBHAPTIC = 2, SIMDEVTYPE_TACHOMETER = 2,
SIMDEVTYPE_SHIFTLIGHTS = 3, SIMDEVTYPE_USBHAPTIC = 3,
SIMDEVTYPE_SIMWIND = 4, SIMDEVTYPE_USBWHEEL = 4,
SIMDEVTYPE_SERIALHAPTIC = 5, SIMDEVTYPE_SHIFTLIGHTS = 5,
SIMDEVTYPE_USBWHEEL = 6, SIMDEVTYPE_SIMWIND = 6,
SIMDEVTYPE_SERIALWHEEL = 7, SIMDEVTYPE_SERIALHAPTIC = 7,
SIMDEVTYPE_SIMLED = 8, SIMDEVTYPE_SERIALWHEEL = 8,
SIMDEVTYPE_ARDUINOCUSTOM = 9 SIMDEVTYPE_SIMLED = 9,
SIMDEVTYPE_ARDUINOCUSTOM = 10,
} }
DeviceSubType; DeviceSubType;
#define SerialDevicesOffset 5
#define USBDevicesOffset 2
typedef enum typedef enum
{ {
SIMDEVSUBTYPE_UNKNOWN = 0, SIMDEVSUBTYPE_UNKNOWN = 0,
@@ -45,7 +49,10 @@ typedef enum
SIMDEVSUBTYPE_SIMAGICP1000PEDALS = 5, SIMDEVSUBTYPE_SIMAGICP1000PEDALS = 5,
SIMDEVSUBTYPE_SIMAGICGTNEO = 6, SIMDEVSUBTYPE_SIMAGICGTNEO = 6,
SIMDEVSUBTYPE_MOZA_NEW = 7, SIMDEVSUBTYPE_MOZA_NEW = 7,
SIMDEVSUBTYPE_LOGITECH_G29 = 8 SIMDEVSUBTYPE_LOGITECH_G29 = 8,
SIMDEVSUBTYPE_MOZA_KS_PRO_WHEEL = 9,
SIMDEVSUBTYPE_SIMNETPEDALS = 10,
SIMDEVSUBTYPE_REVBURNERTACHOMETER = 11
} }
DeviceSubSubType; DeviceSubSubType;
@@ -72,6 +79,14 @@ typedef enum
} }
VibrationEffectType; VibrationEffectType;
typedef enum
{
EFFECT_MODULATION_NONE = 0,
EFFECT_MODULATION_FREQUENCY = 1,
EFFECT_MODULATION_AMPLIFY = 2,
}
EffectModulationType;
typedef enum typedef enum
{ {
MOTOR_1 = 0, MOTOR_1 = 0,
@@ -145,6 +160,7 @@ typedef struct
char* config_str; char* config_str;
char* log_filename_str; char* log_filename_str;
char* log_dirname_str; char* log_dirname_str;
config_t* cfg;
} }
MonocoqueSettings; MonocoqueSettings;
@@ -160,40 +176,46 @@ TachometerSettings;
typedef struct typedef struct
{ {
char* portdev;
MotorPosition motorsposition; MotorPosition motorsposition;
int numlights; uint32_t numlights;
int numleds; uint32_t numleds;
int startled; uint32_t startled;
int endled; uint32_t endled;
float ampfactor; float ampfactor;
int baud; float fanpower;
// baud is the only serial thing
uint32_t baud;
} }
SerialDeviceSettings; SerialDeviceSettings;
typedef struct typedef struct
{ {
uint32_t frequency;
uint32_t volume; uint32_t volume;
int lowbound_frequency; uint32_t pan;
int upperbound_frequency; uint32_t channels;
uint32_t frequencyMax; uint32_t noise;
uint32_t amplitudeMax;
uint32_t amplitude;
int pan;
int channels;
double duration;
char* dev;
SoundEffectModulationType modulation;
int noise;
} }
SoundDeviceSettings; SoundDeviceSettings;
typedef struct typedef struct
{ {
int value0; uint32_t frequency;
int value1; uint32_t amplitude;
uint32_t frequencyMax;
uint32_t amplitudeMax;
double threshold;
double duration;
MotorPosition motorposition;
VibrationEffectType effect_type;
MonocoqueTyreIdentifier tyre;
EffectModulationType modulation;
}
HapticEffectSettings;
typedef struct
{
char* dev; char* dev;
} }
USBDeviceSettings; USBDeviceSettings;
@@ -201,18 +223,19 @@ USBDeviceSettings;
typedef struct typedef struct
{ {
bool is_valid; bool is_valid;
int fps;
DeviceType dev_type; DeviceType dev_type;
DeviceSubType dev_subtype; DeviceSubType dev_subtype;
DeviceSubSubType dev_subsubtype; DeviceSubSubType dev_subsubtype;
// to get really fancy move the effect information to it's own structure that would be a member of bool has_haptic_effects;
// any device settings member structure that can carry an effect bool has_led_effects;
VibrationEffectType effect_type;
MonocoqueTyreIdentifier tyre; char* dev;
double threshold;
bool has_config; bool has_config;
char* specific_config_file; char* specific_config_file;
uint32_t fps;
// union? // union?
HapticEffectSettings hapticsettings;
TachometerSettings tachsettings; TachometerSettings tachsettings;
SerialDeviceSettings serialdevsettings; SerialDeviceSettings serialdevsettings;
SoundDeviceSettings sounddevsettings; SoundDeviceSettings sounddevsettings;
@@ -222,6 +245,8 @@ DeviceSettings;
int strtogame(const char* game, MonocoqueSettings* ms); int strtogame(const char* game, MonocoqueSettings* ms);
int strtodevsubsubtype(const char* device_subsubtype, DeviceSettings* ds);
int devsetup(const char* device_type, const char* device_subtype, const char* config_files, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings); int devsetup(const char* device_type, const char* device_subtype, const char* config_files, MonocoqueSettings* ms, DeviceSettings* ds, config_setting_t* device_settings);
int settingsfree(DeviceSettings ds); int settingsfree(DeviceSettings ds);
@@ -234,9 +259,16 @@ int getconfigtouse2(const char* config_file_str, char* car, int sim);
int getconfigtouse1(const char* config_file_str, char* car, int sim); int getconfigtouse1(const char* config_file_str, char* car, int sim);
int getconfigtouse(const char* config_file_str, char* car, int sim); int getconfigtouse(const char* config_file_str, char* car, int sim);
config_t *open_monocoque_config(const char *filename);
void close_monocoque_config(config_t *cfg);
int delete_device_config(config_t *cfg, const char *configfile, int confignum, int devicenum);
int save_device_config(config_t *cfg, const char* configfile, int confignum, int devicenum, const DeviceSettings *ds);
int configcheck(const char* config_file_str, int confignum, int* devices); int configcheck(const char* config_file_str, int confignum, int* devices);
int uiloadconfig(const char* config_file_str, int confignum, int configureddevices, MonocoqueSettings* ms, DeviceSettings* ds); int uiloadconfig(const char* config_file_str, int confignum, int configureddevices, MonocoqueSettings* ms, DeviceSettings* ds);
int getsingledevice(const char* config_file_str, int confignum, int devicenum, MonocoqueSettings* ms, DeviceSettings* ds);
int getNumberOfConfigs(const char* config_file_str); int getNumberOfConfigs(const char* config_file_str);
#endif #endif
+38
View File
@@ -0,0 +1,38 @@
set(mgui_source_files
addeditaction.c
addeditaction.h
testaction.c
testaction.h
uiconfighelper.c
uiconfighelper.h
mainwindow.h
)
set(NAPPGUI_DEMO NO CACHE BOOL "" FORCE)
set(NAPPGUI_WEB False CACHE BOOL "Build NAppGUI WebView support.")
set(_saved_build_type ${CMAKE_BUILD_TYPE})
set(CMAKE_BUILD_TYPE Release)
add_subdirectory(nappgui_src)
set(CMAKE_BUILD_TYPE ${_saved_build_type})
add_library(mgui STATIC ${mgui_source_files} ${NAppGUI_SOURCE_DIR}/src)
set_property(TARGET mgui PROPERTY POSITION_INDEPENDENT_CODE ON)
add_dependencies(mgui gui osapp osgui draw2d geom2d core osbs sewer encode)
target_include_directories(mgui PUBLIC ${LIBXML_INCLUDE_DIR} ${NAppGUI_SOURCE_DIR}/src ${GTK3_INCLUDE_DIRS})
set_target_properties(mgui PROPERTIES LINKER_LANGUAGE C)
set(NAPPGUI_LIB_DIR ${CMAKE_BINARY_DIR}/Release/lib)
target_link_libraries(mgui PUBLIC
#gui osapp osgui draw2d geom2d core osbs sewer osapp encode
${NAPPGUI_LIB_DIR}/libgui.a
${NAPPGUI_LIB_DIR}/libosapp.a
${NAPPGUI_LIB_DIR}/libosgui.a
${NAPPGUI_LIB_DIR}/libdraw2d.a
${NAPPGUI_LIB_DIR}/libgeom2d.a
${NAPPGUI_LIB_DIR}/libcore.a
${NAPPGUI_LIB_DIR}/libosbs.a
${NAPPGUI_LIB_DIR}/libsewer.a
${NAPPGUI_LIB_DIR}/libosapp.a
${NAPPGUI_LIB_DIR}/libencode.a
${GTK3_LIBRARIES})
+604
View File
@@ -0,0 +1,604 @@
#include <string.h>
#include <nappgui.h>
#include <gui/guiall.h>
#include "addeditaction.h"
#include "testaction.h"
#include "mainwindow.h"
#include "uiconfighelper.h"
#include "../helper/confighelper.h"
#include "../gameloop/gameloop.h"
#include "../gameloop/loopdata.h"
#include "../slog/slog.h"
test_loop_args* test_loop_data;
/*---------------------------------------------------------------------------*/
static Layout *i_modal_layout(AddDeviceData *data, App* app, MonocoqueSettings* ms, enum device_action dev_action, int confignum, int devicenum);
/*---------------------------------------------------------------------------*/
static AddDeviceData *i_modal_data(Window *parent)
{
AddDeviceData *data = heap_new0(AddDeviceData);
data->parent = parent;
data->type = UINT32_MAX;
return data;
}
/*---------------------------------------------------------------------------*/
static void i_destroy_modal_data(AddDeviceData **data)
{
AddDeviceData* d = *data;
DeviceSettings ds = *d->ds;
settingsfree(ds);
free(d->ds);
heap_delete(data, AddDeviceData);
}
/*---------------------------------------------------------------------------*/
static void i_OnTestClose(App *app, Event *e)
{
}
static void update_device_data_from_controls(AddDeviceData* data)
{
data->ds->dev_type = combo_get_selected(data->combo_select_device_class);
data->ds->dev_subtype = combo_get_selected(data->combo_select_device_subtype);
uint32_t device_selected = combo_get_selected(data->combo_select_device);
data->ds->dev = strdup(combo_get_text(data->combo_select_device, device_selected));
if(data->ds->dev_type == SIMDEV_SERIAL)
{
data->ds->dev_subtype = combo_get_selected(data->combo_select_device_subtype) + SerialDevicesOffset;
}
if(data->ds->dev_type == SIMDEV_USB)
{
data->ds->dev_subtype = combo_get_selected(data->combo_select_device_subtype) + USBDevicesOffset;
}
data->ds->hapticsettings.tyre = combo_get_selected(data->combo_select_tyre);
data->ds->hapticsettings.effect_type = combo_get_selected(data->combo_select_effect);
data->ds->hapticsettings.modulation = combo_get_selected(data->combo_select_modulation);
}
static void i_OnTestDevice(AddDeviceData *data, Event *e)
{
update_device_data_from_controls(data);
Panel *panel = NULL;
data->apppointer->window3 = window_create(ekWINDOW_STDRES);
SimData* t_simdata = malloc(sizeof(SimData));
set_basic_simdata(t_simdata);
set_wheel_spin_simdata(t_simdata);
if(data->ds->has_haptic_effects == true)
{
if(data->ds->hapticsettings.effect_type == EFFECT_TYRELOCK)
{
set_wheel_lock_simdata(t_simdata);
}
}
test_loop_data = malloc(sizeof(test_loop_args));
test_loop_data->ms = data->ms;
test_loop_data->confignum = data->confignum;
test_loop_data->devicenum = data->devicenum;
test_loop_data->ds = data->ds;
test_loop_data->simdata = t_simdata;
start_test(test_loop_data);
panel = test_window(data->apppointer->window3, data->apppointer);
window_panel(data->apppointer->window3, panel);
window_origin(data->apppointer->window3, v2df(800, 200));
window_title(data->apppointer->window3, "monocoque");
window_OnClose(data->apppointer->window3, listener(data->apppointer, i_OnTestClose, App));
window_modal(data->apppointer->window3, data->apppointer->window);
window_destroy(&data->apppointer->window3);
free(t_simdata);
free(test_loop_data);
}
static void addHapticDeviceLayout(AddDeviceData* data)
{
struct _add_device_t* subwindow = data;
subwindow->haptic_effect_sublayout = layout_create(8, 3);
subwindow->label_select_effect = label_create();
subwindow->combo_select_effect = combo_create();
subwindow->label_select_modulation = label_create();
subwindow->combo_select_modulation = combo_create();
subwindow->label_select_tyre = label_create();
subwindow->combo_select_tyre = combo_create();
subwindow->label_motorposition = label_create();
subwindow->edit_motorposition = edit_create();
subwindow->label_frequency = label_create();
subwindow->edit_frequency = edit_create();
subwindow->label_amplitude = label_create();
subwindow->edit_amplitude = edit_create();
subwindow->label_frequencyMax = label_create();
subwindow->edit_frequencyMax = edit_create();
subwindow->label_amplitudeMax = label_create();
subwindow->edit_amplitudeMax = edit_create();
subwindow->label_threshold = label_create();
subwindow->edit_threshold = edit_create();
subwindow->label_duration = label_create();
subwindow->edit_duration = edit_create();
label_text(subwindow->label_select_effect, "Effect:");
label_text(subwindow->label_select_modulation, "Modulation:");
label_text(subwindow->label_select_tyre, "Tyre:");
label_text(subwindow->label_motorposition, "Motor Position:");
label_text(subwindow->label_frequency, "Frequency:");
label_text(subwindow->label_amplitude, "Amplitude:");
label_text(subwindow->label_frequencyMax, "Max Frequency:");
label_text(subwindow->label_amplitudeMax, "Max Amplitude:");
label_text(subwindow->label_threshold, "Threshold:");
label_text(subwindow->label_duration, "Duration:");
layout_label(subwindow->haptic_effect_sublayout, subwindow->label_select_effect, 0, 0);
layout_combo(subwindow->haptic_effect_sublayout, subwindow->combo_select_effect, 1, 0);
layout_label(subwindow->haptic_effect_sublayout, subwindow->label_select_modulation, 2, 0);
layout_combo(subwindow->haptic_effect_sublayout, subwindow->combo_select_modulation, 3, 0);
layout_label(subwindow->haptic_effect_sublayout, subwindow->label_select_tyre, 4, 0);
layout_combo(subwindow->haptic_effect_sublayout, subwindow->combo_select_tyre, 5, 0);
layout_label(subwindow->haptic_effect_sublayout, subwindow->label_motorposition, 6, 0);
layout_edit(subwindow->haptic_effect_sublayout, subwindow->edit_motorposition, 7, 0);
combo_add_elem(subwindow->combo_select_effect, "Engine", NULL);
combo_add_elem(subwindow->combo_select_effect, "Gear", NULL);
combo_add_elem(subwindow->combo_select_effect, "ABS", NULL);
combo_add_elem(subwindow->combo_select_effect, "TyreSlip", NULL);
combo_add_elem(subwindow->combo_select_effect, "TyreLock", NULL);
combo_add_elem(subwindow->combo_select_effect, "Suspension", NULL);
combo_add_elem(subwindow->combo_select_modulation, "None", NULL);
combo_add_elem(subwindow->combo_select_modulation, "Frequency", NULL);
combo_add_elem(subwindow->combo_select_modulation, "Amplitude", NULL);
combo_add_elem(subwindow->combo_select_tyre, "FrontLeft", NULL);
combo_add_elem(subwindow->combo_select_tyre, "FrontRight", NULL);
combo_add_elem(subwindow->combo_select_tyre, "RearLeft", NULL);
combo_add_elem(subwindow->combo_select_tyre, "RearRight", NULL);
combo_add_elem(subwindow->combo_select_tyre, "Fronts", NULL);
combo_add_elem(subwindow->combo_select_tyre, "Rears", NULL);
combo_add_elem(subwindow->combo_select_tyre, "All", NULL);
layout_label(subwindow->haptic_effect_sublayout, subwindow->label_frequency, 0, 1);
layout_edit(subwindow->haptic_effect_sublayout, subwindow->edit_frequency, 1, 1);
layout_label(subwindow->haptic_effect_sublayout, subwindow->label_amplitude, 2, 1);
layout_edit(subwindow->haptic_effect_sublayout, subwindow->edit_amplitude, 3, 1);
layout_label(subwindow->haptic_effect_sublayout, subwindow->label_frequencyMax, 6, 1);
layout_edit(subwindow->haptic_effect_sublayout, subwindow->edit_frequencyMax, 7, 1);
layout_label(subwindow->haptic_effect_sublayout, subwindow->label_amplitudeMax, 0, 2);
layout_edit(subwindow->haptic_effect_sublayout, subwindow->edit_amplitudeMax, 1, 2);
layout_label(subwindow->haptic_effect_sublayout, subwindow->label_threshold, 2, 2);
layout_edit(subwindow->haptic_effect_sublayout, subwindow->edit_threshold, 3, 2);
layout_label(subwindow->haptic_effect_sublayout, subwindow->label_duration, 4, 2);
layout_edit(subwindow->haptic_effect_sublayout, subwindow->edit_duration, 5, 2);
cell_dbind(layout_cell(subwindow->haptic_effect_sublayout, 1, 1), HapticEffectSettings, uint32_t, frequency);
cell_dbind(layout_cell(subwindow->haptic_effect_sublayout, 3, 1), HapticEffectSettings, uint32_t, amplitude);
cell_dbind(layout_cell(subwindow->haptic_effect_sublayout, 7, 1), HapticEffectSettings, uint32_t, frequencyMax);
cell_dbind(layout_cell(subwindow->haptic_effect_sublayout, 1, 2), HapticEffectSettings, uint32_t, amplitudeMax);
cell_dbind(layout_cell(subwindow->haptic_effect_sublayout, 7, 0), HapticEffectSettings, uint32_t, motorposition);
cell_dbind(layout_cell(subwindow->haptic_effect_sublayout, 3, 2), HapticEffectSettings, real64_t, threshold);
cell_dbind(layout_cell(subwindow->haptic_effect_sublayout, 5, 2), HapticEffectSettings, real64_t, duration);
cell_dbind(layout_cell(subwindow->haptic_effect_sublayout, 3, 0), HapticEffectSettings, uint32_t, modulation);
cell_dbind(layout_cell(subwindow->haptic_effect_sublayout, 5, 0), HapticEffectSettings, uint32_t, tyre);
cell_dbind(layout_cell(subwindow->haptic_effect_sublayout, 1, 0), HapticEffectSettings, uint32_t, effect_type);
layout_dbind(subwindow->haptic_effect_sublayout, NULL, HapticEffectSettings);
layout_dbind_obj(subwindow->haptic_effect_sublayout, &data->ds->hapticsettings, HapticEffectSettings);
// can i get this from the dbind
combo_selected(subwindow->combo_select_tyre, data->ds->hapticsettings.tyre);
combo_selected(subwindow->combo_select_effect, data->ds->hapticsettings.effect_type);
combo_selected(subwindow->combo_select_modulation, data->ds->hapticsettings.modulation);
for (uint32_t row = 0; row < 3; row++)
{
for (uint32_t col = 1; col < 8; col += 2)
{
layout_halign(subwindow->haptic_effect_sublayout, col, row, ekJUSTIFY);
}
}
}
static void addSerialDeviceLayout(AddDeviceData* data)
{
struct _add_device_t* subwindow = data;
subwindow->serial_device_sublayout = layout_create(4, 1);
subwindow->edit_baud = edit_create();
subwindow->label_baud = label_create();
label_text(subwindow->label_baud, "baud: ");
layout_label(subwindow->serial_device_sublayout, subwindow->label_baud, 0, 0);
layout_edit(subwindow->serial_device_sublayout, subwindow->edit_baud, 1, 0);
cell_dbind(layout_cell(subwindow->serial_device_sublayout, 1, 0), SerialDeviceSettings, uint32_t, baud);
layout_dbind(subwindow->serial_device_sublayout, NULL, SerialDeviceSettings);
layout_dbind_obj(subwindow->serial_device_sublayout, &data->ds->serialdevsettings, SerialDeviceSettings);
layout_update(subwindow->serial_device_sublayout);
}
static void addSoundDeviceLayout(AddDeviceData* data)
{
struct _add_device_t* subwindow = data;
subwindow->sound_device_sublayout = layout_create(6, 1);
subwindow->edit_channel = edit_create();
subwindow->edit_channels = edit_create();
subwindow->label_channel = label_create();
subwindow->label_channels = label_create();
subwindow->label_volume = label_create();
subwindow->edit_volume = edit_create();
label_text(subwindow->label_channel, "Channel: ");
label_text(subwindow->label_channels, "Channels: ");
label_text(subwindow->label_volume, "Volume:");
layout_label(subwindow->sound_device_sublayout, subwindow->label_channel, 0, 0);
layout_label(subwindow->sound_device_sublayout, subwindow->label_channels, 2, 0);
layout_label(subwindow->sound_device_sublayout, subwindow->label_volume, 4, 0);
layout_edit(subwindow->sound_device_sublayout, subwindow->edit_channel, 1, 0);
layout_edit(subwindow->sound_device_sublayout, subwindow->edit_channels, 3, 0);
layout_edit(subwindow->sound_device_sublayout, subwindow->edit_volume, 5, 0);
cell_dbind(layout_cell(subwindow->sound_device_sublayout, 1, 0), SoundDeviceSettings, uint32_t, pan);
cell_dbind(layout_cell(subwindow->sound_device_sublayout, 3, 0), SoundDeviceSettings, uint32_t, channels);
cell_dbind(layout_cell(subwindow->sound_device_sublayout, 5, 0), SoundDeviceSettings, uint32_t, volume);
layout_dbind(subwindow->sound_device_sublayout, NULL, SoundDeviceSettings);
layout_dbind_obj(subwindow->sound_device_sublayout, &data->ds->sounddevsettings, SoundDeviceSettings);
layout_update(subwindow->sound_device_sublayout);
}
static void addLedDeviceLayout(AddDeviceData* data)
{
struct _add_device_t* subwindow = data;
subwindow->led_device_sublayout = layout_create(6,1);
subwindow->edit_num_leds = edit_create();
subwindow->edit_start_led = edit_create();
subwindow->edit_end_led = edit_create();
subwindow->label_num_leds = label_create();
subwindow->label_start_led = label_create();
subwindow->label_end_led = label_create();
label_text(subwindow->label_num_leds, "Leds: ");
label_text(subwindow->label_start_led, "Start Led: ");
label_text(subwindow->label_end_led, "End Led: ");
layout_label(subwindow->led_device_sublayout, subwindow->label_num_leds, 0, 0);
layout_label(subwindow->led_device_sublayout, subwindow->label_start_led, 2, 0);
layout_label(subwindow->led_device_sublayout, subwindow->label_end_led, 4, 0);
layout_edit(subwindow->led_device_sublayout, subwindow->edit_num_leds, 1, 0);
layout_edit(subwindow->led_device_sublayout, subwindow->edit_start_led, 3, 0);
layout_edit(subwindow->led_device_sublayout, subwindow->edit_end_led, 5, 0);
cell_dbind(layout_cell(subwindow->led_device_sublayout, 1, 0), SerialDeviceSettings, uint32_t, numleds);
cell_dbind(layout_cell(subwindow->led_device_sublayout, 3, 0), SerialDeviceSettings, uint32_t, startled);
cell_dbind(layout_cell(subwindow->led_device_sublayout, 5, 0), SerialDeviceSettings, uint32_t, endled);
layout_dbind(subwindow->led_device_sublayout, NULL, SerialDeviceSettings);
layout_dbind_obj(subwindow->led_device_sublayout, &data->ds->serialdevsettings, SerialDeviceSettings);
layout_update(subwindow->led_device_sublayout);
}
/*---------------------------------------------------------------------------*/
static void i_OnSaveDevice(AddDeviceData *data, Event *e)
{
update_device_data_from_controls(data);
slogd("Attempting save of device class %i device type %i device %s", data->ds->dev_type, data->ds->dev_subtype, data->ds->dev);
save_device_config(data->ms->cfg, data->ms->config_str, data->confignum, data->devicenum, data->ds);
}
static void i_OnDeviceSubtypeSelect(AddDeviceData *data, Event *e)
{
const EvButton *p = event_params(e, EvButton);
cell_visible(data->cell_led_device_layout, false);
cell_visible(data->cell_haptic_effect_layout, false);
data->ds->has_led_effects = false;
data->ds->has_haptic_effects = false;
uint32_t selected = combo_get_selected(data->combo_select_device_subtype);
const char_t *text = combo_get_text(data->combo_select_device_subtype, selected);
if (text != NULL && strcmp(text, "SIMLED") == 0)
{
cell_visible(data->cell_led_device_layout, true);
data->ds->has_led_effects = true;
}
if (text != NULL && strcmp(text, "SOUNDHAPTIC") == 0)
{
cell_visible(data->cell_haptic_effect_layout, true);
data->ds->has_haptic_effects = true;
}
panel_update(data->panel);
}
static void populateSerialSubTypesCombo(Combo* combo)
{
combo_add_elem(combo, "ShiftLights", NULL);
combo_add_elem(combo, "SimWind", NULL);
combo_add_elem(combo, "Haptic", NULL);
combo_add_elem(combo, "Wheel", NULL);
combo_add_elem(combo, "SimLed", NULL);
combo_add_elem(combo, "Custom", NULL);
}
static void i_OnDeviceClassSelect(AddDeviceData *data, Event *e)
{
const EvButton *p = event_params(e, EvButton);
panel_visible_layout(data->device_specific_panel, 0);
cell_visible(data->cell_led_device_layout, false);
cell_visible(data->cell_haptic_effect_layout, false);
panel_update(data->device_specific_panel);
combo_clear(data->combo_select_device);
combo_text(data->combo_select_device, "");
combo_clear(data->combo_select_device_subtype);
combo_text(data->combo_select_device_subtype, "");
uint32_t selected = combo_get_selected(data->combo_select_device_class);
const char_t *text = combo_get_text(data->combo_select_device_class, selected);
if (text != NULL && strcmp(text, "Serial") == 0)
{
add_serial_devices_to_combo(data->combo_select_device);
populateSerialSubTypesCombo(data->combo_select_device_subtype);
panel_visible_layout(data->device_specific_panel, 1);
// since SIMLED will be selected we will show led settings
cell_visible(data->cell_led_device_layout, true);
data->ds->has_led_effects = true;
}
if (text != NULL && strcmp(text, "Sound") == 0)
{
add_sound_devices_to_combo(data->combo_select_device);
combo_add_elem(data->combo_select_device_subtype, "Haptic", NULL);
panel_visible_layout(data->device_specific_panel, 2);
// since HAPTIC will be selected we will show haptic settings
cell_visible(data->cell_haptic_effect_layout, true);
data->ds->has_haptic_effects = true;
}
combo_selected(data->combo_select_device, 0);
combo_selected(data->combo_select_device_subtype, 0);
panel_update(data->device_specific_panel);
panel_update(data->panel);
}
static Panel* device_specific_panel_create(AddDeviceData *data)
{
Panel *panel = panel_create();
data->empty_device_sublayout = layout_create(4, 1);
addSerialDeviceLayout(data);
addSoundDeviceLayout(data);
panel_layout(panel, data->empty_device_sublayout);
panel_layout(panel, data->serial_device_sublayout);
panel_layout(panel, data->sound_device_sublayout);
return panel;
}
static Layout *i_modal_layout(AddDeviceData *data, App* app, MonocoqueSettings* ms, enum device_action dev_action, int confignum, int devicenum)
{
struct _add_device_t* subwindow = data;
subwindow->main_outer_layout = layout_create(1,11);
subwindow->top_selection_layout = layout_create(4,1);
subwindow->general_settings_layout = layout_create(4, 1);
// device class specific settings ( max 1 row so far )
// possible led specific settings ( 1 row )
// possible haptic specific settings ( 3 rows )
subwindow->add_save_test_layout = layout_create(2,1);
subwindow->combo_select_device_class = combo_create();
subwindow->combo_select_device_subtype = combo_create();
subwindow->combo_select_device = combo_create();
subwindow->label_select_device_class = label_create();
subwindow->label_select_device_subtype = label_create();
subwindow->label_select_device = label_create();
label_text(subwindow->label_select_device_class, "Device Class: ");
label_text(subwindow->label_select_device_subtype, "Device SubType: ");
label_text(subwindow->label_select_device, "Device: ");
layout_label(subwindow->top_selection_layout, subwindow->label_select_device_class, 0, 0);
layout_combo(subwindow->top_selection_layout, subwindow->combo_select_device_class, 1, 0);
layout_label(subwindow->top_selection_layout, subwindow->label_select_device_subtype, 2, 0);
layout_combo(subwindow->top_selection_layout, subwindow->combo_select_device_subtype, 3, 0);
layout_label(subwindow->general_settings_layout, subwindow->label_select_device, 2, 0);
layout_combo(subwindow->general_settings_layout, subwindow->combo_select_device, 3, 0);
subwindow->edit_fps = edit_create();
subwindow->label_fps = label_create();
label_text(subwindow->label_fps, "fps: ");
layout_label(subwindow->general_settings_layout, subwindow->label_fps, 0, 0);
layout_edit(subwindow->general_settings_layout, subwindow->edit_fps, 1, 0);
cell_dbind(layout_cell(subwindow->top_selection_layout, 1, 0), DeviceSettings, uint32_t, dev_type);
cell_dbind(layout_cell(subwindow->top_selection_layout, 3, 0), DeviceSettings, uint32_t, dev_subtype);
cell_dbind(layout_cell(subwindow->general_settings_layout, 1, 0), DeviceSettings, uint32_t, fps);
layout_dbind(subwindow->general_settings_layout, NULL, DeviceSettings);
layout_dbind(subwindow->top_selection_layout, NULL, DeviceSettings);
layout_dbind_obj(subwindow->general_settings_layout, data->ds, DeviceSettings);
subwindow->device_specific_panel = device_specific_panel_create(data);
panel_visible_layout(data->device_specific_panel, 0);
addLedDeviceLayout(data);
addHapticDeviceLayout(data);
subwindow->test_button = button_push();
subwindow->save_button = button_push();
button_text(subwindow->test_button, "Test");
button_text(subwindow->save_button, "Save");
layout_button(subwindow->add_save_test_layout, subwindow->test_button, 0, 0);
layout_button(subwindow->add_save_test_layout, subwindow->save_button, 1, 0);
button_OnClick(subwindow->test_button, listener(data, i_OnTestDevice, AddDeviceData));
button_OnClick(subwindow->save_button, listener(data, i_OnSaveDevice, AddDeviceData));
layout_layout(subwindow->main_outer_layout, subwindow->top_selection_layout, 0, 0);
layout_layout(subwindow->main_outer_layout, subwindow->general_settings_layout, 0, 1);
layout_panel(subwindow->main_outer_layout, subwindow->device_specific_panel, 0, 2);
layout_layout(subwindow->main_outer_layout, subwindow->led_device_sublayout, 0, 3);
layout_layout(subwindow->main_outer_layout, subwindow->haptic_effect_sublayout, 0, 4);
layout_layout(subwindow->main_outer_layout, subwindow->add_save_test_layout, 0, 7);
subwindow->cell_led_device_layout = layout_cell(subwindow->main_outer_layout, 0, 3);
subwindow->cell_haptic_effect_layout = layout_cell(subwindow->main_outer_layout, 0, 4);
cell_visible(subwindow->cell_led_device_layout, false);
cell_visible(subwindow->cell_haptic_effect_layout, false);
if(dev_action == DEVICE_ACTION_ADD)
{
combo_add_elem(subwindow->combo_select_device_class, "USB", NULL);
combo_add_elem(subwindow->combo_select_device_class, "Sound", NULL);
combo_add_elem(subwindow->combo_select_device_class, "Serial", NULL);
combo_OnSelect(subwindow->combo_select_device_class, listener(data, i_OnDeviceClassSelect, AddDeviceData));
combo_OnSelect(subwindow->combo_select_device_subtype, listener(data, i_OnDeviceSubtypeSelect, AddDeviceData));
return subwindow->main_outer_layout;
}
char str0[255];
sprintf(str0, "%s", data->ds->dev);
combo_add_elem(subwindow->combo_select_device, str0, NULL);
subwindow->cell_top_selection_layout = layout_cell(subwindow->main_outer_layout, 0, 0);
cell_enabled(subwindow->cell_top_selection_layout, false);
if(data->ds->dev_type == SIMDEV_SERIAL)
{
combo_add_elem(subwindow->combo_select_device_class, "Serial", NULL);
add_serial_devices_to_combo(data->combo_select_device);
populateSerialSubTypesCombo(data->combo_select_device_subtype);
combo_selected(subwindow->combo_select_device_subtype, data->ds->dev_subtype - SerialDevicesOffset);
panel_visible_layout(data->device_specific_panel, 1);
}
if(data->ds->dev_type == SIMDEV_SOUND)
{
combo_add_elem(subwindow->combo_select_device_class, "Sound", NULL);
add_sound_devices_to_combo(data->combo_select_device);
combo_add_elem(data->combo_select_device_subtype, "Haptic", NULL);
combo_selected(subwindow->combo_select_device_subtype, data->ds->dev_subtype);
panel_visible_layout(data->device_specific_panel, 2);
cell_visible(data->cell_haptic_effect_layout, true);
}
if(data->ds->dev_type == SIMDEV_USB)
{
combo_selected(subwindow->combo_select_device_subtype, data->ds->dev_subtype - USBDevicesOffset);
panel_visible_layout(data->device_specific_panel, 2);
}
if(data->ds->dev_subtype == SIMDEVTYPE_SIMLED) {
cell_visible(data->cell_led_device_layout, true);
}
combo_selected(subwindow->combo_select_device_class, 0);
return subwindow->main_outer_layout;
}
/*---------------------------------------------------------------------------*/
Panel *adddevice_window(Window *parent, App* app, MonocoqueSettings* ms, enum device_action dev_action, int confignum, int devicenum)
{
Panel *panel = panel_create();
AddDeviceData *data = i_modal_data(parent);
data->panel = panel;
data->devicenum = devicenum;
data->confignum = confignum;
data->ds = malloc(sizeof(DeviceSettings));
// move this somewhere better or use dbind to set this stuff
data->ds->dev_type = 0;
data->ds->dev_subtype = 0;
data->ds->dev_subsubtype = 0;
data->ds->dev = NULL;
data->ds->fps = 60;
data->ds->specific_config_file = NULL;
data->ds->has_config = false;
data->ds->serialdevsettings.baud = 115200;
data->ds->serialdevsettings.startled = 0;
data->ds->serialdevsettings.endled = 0;
data->ds->serialdevsettings.numleds = 0;
data->ds->sounddevsettings.channels = 2;
data->ds->sounddevsettings.pan = 0;
data->ds->sounddevsettings.volume = 50;
data->ds->sounddevsettings.noise = 0;
data->ds->hapticsettings.frequency = 32;
data->ds->hapticsettings.amplitude = 50;
data->ds->hapticsettings.frequencyMax = 0;
data->ds->hapticsettings.amplitudeMax = 0;
data->ds->hapticsettings.motorposition = 0;
data->ds->hapticsettings.threshold = 0.0;
data->ds->hapticsettings.duration = 0.0;
data->ds->hapticsettings.modulation = 1;
data->ds->hapticsettings.tyre = 0;
data->ds->hapticsettings.effect_type = 0;
data->ds->has_led_effects = false;
data->ds->has_haptic_effects = false;
data->ms = ms;
data->apppointer = app;
if(dev_action == DEVICE_ACTION_EDIT)
{
int numdevices = getsingledevice(ms->config_str, confignum, devicenum, ms, data->ds);
}
Layout *layout = i_modal_layout(data, app, ms, dev_action, confignum, devicenum);
panel_layout(panel, layout);
panel_data(panel, &data, i_destroy_modal_data, AddDeviceData);
return panel;
}
+97
View File
@@ -0,0 +1,97 @@
#include <gui/gui.hxx>
#include "mainwindow.h"
Panel *adddevice_window(Window *parent, App* app, MonocoqueSettings* ms, enum device_action dev_action, int confignum, int devicenum);
typedef struct _add_device_t AddDeviceData;
struct _add_device_t
{
Layout* main_outer_layout;
Layout* top_selection_layout;
Label* label_select_device_class;
Label* label_select_device_subtype;
Label* label_select_device;
Combo* combo_select_device_class;
Combo* combo_select_device_subtype;
Combo* combo_select_device;
Edit* edit_select_device;
Layout* general_settings_layout;
Label* label_fps;
Edit* edit_fps;
Button* check_use_lua;
Layout* empty_device_sublayout;
Panel* device_specific_panel;
// sound sublayout
Layout* sound_device_sublayout;
Label* label_channel;
Label* label_channels;
Label* label_volume;
Edit* edit_channel;
Edit* edit_channels;
Edit* edit_volume;
// serial sublayout
Layout* serial_device_sublayout;
Label* label_baud;
Edit* edit_baud;
// led sublayout
Layout* led_device_sublayout;
Label* label_num_leds;
Label* label_start_led;
Label* label_end_led;
Edit* edit_num_leds;
Edit* edit_start_led;
Edit* edit_end_led;
Layout* haptic_effect_sublayout;
Label* label_select_effect;
Combo* combo_select_effect;
Label* label_select_modulation;
Combo* combo_select_modulation;
Label* label_select_tyre;
Combo* combo_select_tyre;
Label* label_motorposition;
Edit* edit_motorposition;
Label* label_frequency;
Edit* edit_frequency;
Label* label_amplitude;
Edit* edit_amplitude;
Label* label_frequencyMax;
Edit* edit_frequencyMax;
Label* label_amplitudeMax;
Edit* edit_amplitudeMax;
Label* label_threshold;
Edit* edit_threshold;
Label* label_duration;
Edit* edit_duration;
Layout* add_save_test_layout;
Button* test_button;
Button* save_button;
Cell* cell_top_selection_layout;
Cell* cell_led_device_layout;
Cell* cell_haptic_effect_layout;
uint32_t type;
Label *label;
MonocoqueSettings* ms;
DeviceSettings* ds;
Panel* panel;
App* apppointer;
int confignum;
int devicenum;
Window *parent;
};
+44
View File
@@ -0,0 +1,44 @@
#include "../helper/confighelper.h"
#pragma once
enum device_action {
DEVICE_ACTION_ADD, DEVICE_ACTION_EDIT, DEVICE_ACTION_DELETE
};
typedef struct _app_t App;
struct _app_t
{
bool_t is_running;
Panel *main_panel;
Panel *subpanel;
Layout *layout1;
Layout *layout2;
Layout *layout3;
Layout *layout4;
Layout *sub_layout;
Window *window;
Window *window2;
Window *window3;
Layout* layout;
Combo* combo_configurations;
ListBox* listbox_devices;
Button *button_startstop;
Button *button_add;
Button *button_edit;
Button *button_del;
Button *button_test;
Label *label_configurations;
Label *label_game_status;
Label *label_simd_status;
Cell* devices_subpanel;
};
+188
View File
@@ -0,0 +1,188 @@
/* Listboxes */
#include "testaction.h"
#include "../gameloop/gameloop.h"
#include "mainwindow.h"
#include <gui/guiall.h>
typedef struct _modal_data_t gModalData;
struct _modal_data_t
{
uint32_t type;
Label *label;
Window *parent;
};
/*---------------------------------------------------------------------------*/
static Layout *i_modal_layout(gModalData *data, Window *window, App *app);
/*---------------------------------------------------------------------------*/
static gModalData *i_modal_data(Window *parent)
{
gModalData *data = heap_new0(gModalData);
data->parent = parent;
data->type = UINT32_MAX;
return data;
}
/*---------------------------------------------------------------------------*/
static void i_OnTestDone(Window *window, Event *e)
{
Button *button = event_sender(e, Button);
window_stop_modal(window, button_get_tag(button));
}
/*---------------------------------------------------------------------------*/
static void i_OnTestDataUpdate(Window* window, Event* e)
{
Button *button = event_sender(e, Button);
//SimData* simdata = get_test_simdata();
}
/*---------------------------------------------------------------------------*/
static void i_destroy_modal_data(gModalData **data)
{
monocoque_testloop_stop();
heap_delete(data, gModalData);
}
/*---------------------------------------------------------------------------*/
static Layout *i_modal_layout(gModalData *data, Window* window, App *app)
{
struct _test_device_t subwindow;
subwindow.main_outer_layout = layout_create(1,7);
subwindow.data_edit_layout = layout_create(8,6);
subwindow.add_save_test_layout = layout_create(2,1);
subwindow.edit_gear = edit_create();
subwindow.edit_velocity = edit_create();
subwindow.edit_maxrpm = edit_create();
subwindow.edit_rpm = edit_create();
subwindow.label_gear = label_create();
subwindow.label_velocity = label_create();
subwindow.label_maxrpm = label_create();
subwindow.label_rpm = label_create();
subwindow.label_tyre_diameter_1 = label_create();
subwindow.label_tyre_diameter_2 = label_create();
subwindow.label_tyre_diameter_3 = label_create();
subwindow.label_tyre_diameter_4 = label_create();
subwindow.label_tyre_rps_1 = label_create();
subwindow.label_tyre_rps_2 = label_create();
subwindow.label_tyre_rps_3 = label_create();
subwindow.label_tyre_rps_4 = label_create();
subwindow.edit_tyre_diameter_1 = edit_create();
subwindow.edit_tyre_diameter_2 = edit_create();
subwindow.edit_tyre_diameter_3 = edit_create();
subwindow.edit_tyre_diameter_4 = edit_create();
subwindow.edit_tyre_rps_1 = edit_create();
subwindow.edit_tyre_rps_2 = edit_create();
subwindow.edit_tyre_rps_3 = edit_create();
subwindow.edit_tyre_rps_4 = edit_create();
subwindow.update_button = button_push();
subwindow.done_button = button_push();
label_text(subwindow.label_gear, "gear: ");
label_text(subwindow.label_velocity, "velocity: ");
label_text(subwindow.label_maxrpm, "max rpm: ");
label_text(subwindow.label_rpm, "rpm: ");
button_text(subwindow.update_button, "Update");
button_text(subwindow.done_button, "Done");
button_OnClick(subwindow.done_button, listener(window, i_OnTestDone, Window));
button_OnClick(subwindow.update_button, listener(window, i_OnTestDataUpdate, Window));
layout_layout(subwindow.main_outer_layout, subwindow.data_edit_layout, 0, 2);
layout_layout(subwindow.main_outer_layout, subwindow.add_save_test_layout, 0, 5);
layout_label(subwindow.data_edit_layout, subwindow.label_gear, 0, 0);
layout_label(subwindow.data_edit_layout, subwindow.label_velocity, 2, 0);
layout_label(subwindow.data_edit_layout, subwindow.label_maxrpm, 0, 1);
layout_label(subwindow.data_edit_layout, subwindow.label_rpm, 2, 1);
layout_edit(subwindow.data_edit_layout, subwindow.edit_gear, 1, 0);
layout_edit(subwindow.data_edit_layout, subwindow.edit_velocity, 3, 0);
layout_edit(subwindow.data_edit_layout, subwindow.edit_maxrpm, 1, 1);
layout_edit(subwindow.data_edit_layout, subwindow.edit_rpm, 3, 1);
layout_label(subwindow.data_edit_layout, subwindow.label_tyre_rps_1, 0, 2);
layout_label(subwindow.data_edit_layout, subwindow.label_tyre_rps_2, 2, 2);
layout_label(subwindow.data_edit_layout, subwindow.label_tyre_rps_3, 4, 2);
layout_label(subwindow.data_edit_layout, subwindow.label_tyre_rps_4, 6, 2);
layout_label(subwindow.data_edit_layout, subwindow.label_tyre_diameter_1, 0, 3);
layout_label(subwindow.data_edit_layout, subwindow.label_tyre_diameter_2, 2, 3);
layout_label(subwindow.data_edit_layout, subwindow.label_tyre_diameter_3, 4, 3);
layout_label(subwindow.data_edit_layout, subwindow.label_tyre_diameter_4, 6, 3);
label_text(subwindow.label_tyre_rps_1, "rps_lf");
label_text(subwindow.label_tyre_rps_2, "rps_rf");
label_text(subwindow.label_tyre_rps_3, "rps_lr");
label_text(subwindow.label_tyre_rps_4, "rps_rr");
label_text(subwindow.label_tyre_diameter_1, "diameter_lf");
label_text(subwindow.label_tyre_diameter_2, "diameter_rf");
label_text(subwindow.label_tyre_diameter_3, "diameter_lr");
label_text(subwindow.label_tyre_diameter_4, "diameter_rr");
layout_edit(subwindow.data_edit_layout, subwindow.edit_tyre_rps_1, 1, 2);
layout_edit(subwindow.data_edit_layout, subwindow.edit_tyre_rps_2, 3, 2);
layout_edit(subwindow.data_edit_layout, subwindow.edit_tyre_rps_3, 5, 2);
layout_edit(subwindow.data_edit_layout, subwindow.edit_tyre_rps_4, 7, 2);
layout_edit(subwindow.data_edit_layout, subwindow.edit_tyre_diameter_1, 1, 3);
layout_edit(subwindow.data_edit_layout, subwindow.edit_tyre_diameter_2, 3, 3);
layout_edit(subwindow.data_edit_layout, subwindow.edit_tyre_diameter_3, 5, 3);
layout_edit(subwindow.data_edit_layout, subwindow.edit_tyre_diameter_4, 7, 3);
cell_dbind(layout_cell(subwindow.data_edit_layout, 1, 0), SimData, uint32_t, gear);
cell_dbind(layout_cell(subwindow.data_edit_layout, 3, 0), SimData, uint32_t, velocity);
cell_dbind(layout_cell(subwindow.data_edit_layout, 1, 1), SimData, uint32_t, maxrpm);
cell_dbind(layout_cell(subwindow.data_edit_layout, 3, 1), SimData, uint32_t, rpms);
cell_dbind(layout_cell(subwindow.data_edit_layout, 1, 2), SimData, real64_t, tyreRPS[0]);
cell_dbind(layout_cell(subwindow.data_edit_layout, 3, 2), SimData, real64_t, tyreRPS[1]);
cell_dbind(layout_cell(subwindow.data_edit_layout, 5, 2), SimData, real64_t, tyreRPS[2]);
cell_dbind(layout_cell(subwindow.data_edit_layout, 7, 2), SimData, real64_t, tyreRPS[3]);
cell_dbind(layout_cell(subwindow.data_edit_layout, 1, 3), SimData, real64_t, tyrediameter[0]);
cell_dbind(layout_cell(subwindow.data_edit_layout, 3, 3), SimData, real64_t, tyrediameter[1]);
cell_dbind(layout_cell(subwindow.data_edit_layout, 5, 3), SimData, real64_t, tyrediameter[2]);
cell_dbind(layout_cell(subwindow.data_edit_layout, 7, 3), SimData, real64_t, tyrediameter[3]);
SimData* simdata = get_test_simdata();
layout_dbind(subwindow.data_edit_layout, NULL, SimData);
layout_dbind_obj(subwindow.data_edit_layout, simdata, SimData);
layout_button(subwindow.add_save_test_layout, subwindow.update_button, 0, 0);
layout_button(subwindow.add_save_test_layout, subwindow.done_button, 1, 0);
return subwindow.main_outer_layout;
}
/*---------------------------------------------------------------------------*/
Panel *test_window(Window *parent, App* app)
{
Panel *panel = panel_create();
gModalData *data = i_modal_data(parent);
Layout *layout = i_modal_layout(data, parent, app);
panel_layout(panel, layout);
panel_data(panel, &data, i_destroy_modal_data, gModalData);
return panel;
}
+44
View File
@@ -0,0 +1,44 @@
#include <gui/gui.hxx>
#include "mainwindow.h"
Panel *test_window(Window *parent, App* app);
struct _test_device_t
{
Layout* main_outer_layout;
Layout* data_edit_layout;
Layout* add_save_test_layout;
Label* label_gear;
Label* label_velocity;
Label* label_maxrpm;
Label* label_rpm;
Edit* edit_gear;
Edit* edit_velocity;
Edit* edit_maxrpm;
Edit* edit_rpm;
Label* label_tyre_diameter_1;
Label* label_tyre_diameter_2;
Label* label_tyre_diameter_3;
Label* label_tyre_diameter_4;
Label* label_tyre_rps_1;
Label* label_tyre_rps_2;
Label* label_tyre_rps_3;
Label* label_tyre_rps_4;
Edit* edit_tyre_diameter_1;
Edit* edit_tyre_diameter_2;
Edit* edit_tyre_diameter_3;
Edit* edit_tyre_diameter_4;
Edit* edit_tyre_rps_1;
Edit* edit_tyre_rps_2;
Edit* edit_tyre_rps_3;
Edit* edit_tyre_rps_4;
Button* update_button;
Button* done_button;
};
+336
View File
@@ -0,0 +1,336 @@
#include <string.h>
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <gui/combo.h>
#include <gui/listbox.h>
#include <libserialport.h>
#include <libconfig.h>
#include "../helper/confighelper.h"
typedef struct pa_devicelist {
uint8_t initialized;
char name[512];
uint32_t index;
char description[256];
} pa_devicelist_t;
static int matches_any(const char *value)
{
return strcmp(value, "default") == 0 || strcmp(value, "all") == 0;
}
int find_default_config(config_setting_t *configs)
{
int count = config_setting_length(configs);
for (int i = 0; i < count; i++)
{
config_setting_t *entry = config_setting_get_elem(configs, i);
// A key that isn't in the entry doesn't constrain the match. This used
// to require all three, which no real config has: conf/monocoque.config
// -- the example the README points at -- sets sim and car and never
// mentions api, so every entry was skipped, -1 came back, and the
// caller dereferenced the NULL that produced.
const char *sim = "default";
const char *api = "default";
const char *car = "default";
config_setting_lookup_string(entry, "sim", &sim);
config_setting_lookup_string(entry, "api", &api);
config_setting_lookup_string(entry, "car", &car);
if (matches_any(sim) && matches_any(api) && matches_any(car))
{
return i;
}
}
return -1;
}
int add_serial_devices_to_combo(Combo *combo)
{
// from libserialport examples
struct sp_port **port_list;
enum sp_return result = sp_list_ports(&port_list);
if (result != SP_OK)
{
return -1;
}
int i;
for (i = 0; port_list[i] != NULL; i++)
{
struct sp_port *port = port_list[i];
char port_name[256];
const char *name_ptr = sp_get_port_name(port);
if (name_ptr != NULL)
{
snprintf(port_name, sizeof(port_name), "%s", name_ptr);
combo_add_elem(combo, port_name, NULL);
}
else
{
port_name[0] = '\0';
}
}
sp_free_port_list(port_list);
return 0;
}
void populate_combo_box(Combo* combo, config_t* cfg)
{
config_setting_t *configs;
int count;
int i;
configs = config_lookup(cfg, "configs");
if (configs == NULL || !config_setting_is_list(configs))
return;
count = config_setting_length(configs);
for (i = 0; i < count; i++)
{
config_setting_t *cfg_entry;
const char *sim = "";
const char *car = "";
const char *api = "";
char text[256];
cfg_entry = config_setting_get_elem(configs, i);
config_setting_lookup_string(cfg_entry, "sim", &sim);
config_setting_lookup_string(cfg_entry, "car", &car);
config_setting_lookup_string(cfg_entry, "api", &api);
snprintf(text, sizeof(text), "%s / %s / %s", sim, car, api);
combo_add_elem(combo, text, NULL);
}
}
void populate_device_list(ListBox *listbox, config_t* cfg)
{
int count;
config_setting_t* devices;
devices = config_lookup(cfg, "configs");
if (devices == NULL)
{
fprintf(stderr, "No configs section found\n");
return;
}
config_setting_t* config = NULL;
config = config_lookup(cfg, "configs");
int config_num = find_default_config(config);
if (config_num < 0)
{
// No entry claims to be the default one: list the first, which is what
// a single-entry config means anyway, rather than nothing.
config_num = 0;
}
config_setting_t* selectedconfig = config_setting_get_elem(config, config_num);
if (selectedconfig == NULL)
{
fprintf(stderr, "No config entry to list devices for\n");
return;
}
config_setting_t* config_devices = NULL;
config_devices = config_setting_lookup(selectedconfig, "devices");
if (config_devices == NULL)
{
// config_setting_lookup dereferences its argument, so reaching here
// with a NULL selectedconfig used to segfault inside libconfig --
// gmonocoque died on launch, before drawing a window, for anyone whose
// config didn't produce a match above.
fprintf(stderr, "Config entry has no devices section\n");
return;
}
count = config_setting_length(config_devices);
for (int i = 0; i < count; i++)
{
config_setting_t *dev = config_setting_get_elem(config_devices, i);
const char *device = "";
const char *type = "";
const char *subtype = "";
const char *effect = "";
const char *tyre = "";
config_setting_lookup_string(dev, "device", &device);
char text[256];
if (strcasecmp(device, "sound") == 0)
{
config_setting_lookup_string(dev, "effect", &effect);
config_setting_lookup_string(dev, "tyre", &tyre);
snprintf(text, sizeof(text), "%s - %s - %s",
device, effect, tyre);
}
else
{
config_setting_lookup_string(dev, "type", &type);
config_setting_lookup_string(dev, "subtype", &subtype);
snprintf(text, sizeof(text), "%s - %s - %s",
device, type, subtype);
}
listbox_add_elem(listbox, text, NULL);
}
}
void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
pa_devicelist_t *pa_devicelist = userdata;
int ctr = 0;
// If eol is set to a positive number, you're at the end of the list
if (eol > 0) {
return;
}
// We know we've allocated 16 slots to hold devices. Loop through our
// structure and find the first one that's "uninitialized." Copy the
// contents into it and we're done. If we receive more than 16 devices,
// they're going to get dropped. You could make this dynamically allocate
// space for the device list, but this is a simple example.
for (ctr = 0; ctr < 16; ctr++) {
if (! pa_devicelist[ctr].initialized) {
strncpy(pa_devicelist[ctr].name, l->name, 511);
strncpy(pa_devicelist[ctr].description, l->description, 255);
pa_devicelist[ctr].index = l->index;
pa_devicelist[ctr].initialized = 1;
break;
}
}
}
int add_sound_devices_to_combo(Combo *combo)
{
pa_mainloop *mainloop;
pa_context *context;
pa_operation *operation;
pa_context_state_t state;
pa_devicelist_t output[16] = {0};
int retval;
//pa_devicelist_t* output = malloc(sizeof(pa_devicelist_t) * 16);
//memset(output, 0, sizeof(pa_devicelist_t) * 16);
mainloop = pa_mainloop_new();
if (mainloop == NULL)
return 0;
context = pa_context_new(
pa_mainloop_get_api(mainloop),
"Monocoque");
if (context == NULL)
{
pa_mainloop_free(mainloop);
return 0;
}
if (pa_context_connect(context, NULL, 0, NULL) < 0)
{
pa_context_unref(context);
pa_mainloop_free(mainloop);
return 0;
}
/*
* Wait for PulseAudio to become ready.
*/
while (1)
{
if (pa_mainloop_iterate(mainloop, 1, &retval) < 0)
goto error;
state = pa_context_get_state(context);
if (state == PA_CONTEXT_READY)
break;
if (!PA_CONTEXT_IS_GOOD(state))
goto error;
}
/*
* Enumerate all PulseAudio output devices (sinks).
*/
operation = pa_context_get_sink_info_list(
context,
pa_sinklist_cb,
&output);
if (operation == NULL)
goto error;
/*
* Run the mainloop until enumeration is complete.
*/
while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
{
if (pa_mainloop_iterate(mainloop, 1, &retval) < 0)
{
pa_operation_unref(operation);
goto error;
}
}
for (size_t i = 0; i < 16; i++)
{
if (!output[i].initialized)
{
continue;
}
if (output[i].name[0] == '\0')
{
continue;
}
combo_add_elem(combo, output[i].name, NULL);
}
pa_operation_unref(operation);
pa_context_disconnect(context);
pa_context_unref(context);
pa_mainloop_free(mainloop);
return 1;
error:
pa_context_disconnect(context);
pa_context_unref(context);
pa_mainloop_free(mainloop);
return 0;
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <gui/combo.h>
#include <gui/listbox.h>
#include <libconfig.h>
#include "../helper/confighelper.h"
int find_default_config(config_setting_t *configs);
void populate_combo_box(Combo* combo, config_t* cfg);
void populate_device_list(ListBox *listbox, config_t* cfg);
void add_serial_devices_to_combo(Combo *combo);
void add_sound_devices_to_combo(Combo *combo);
+345
View File
@@ -0,0 +1,345 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <basedir_fs.h>
#include <libconfig.h>
#include "gameloop/gameloop.h"
#include "gameloop/tachconfig.h"
#include "devices/simdevice.h"
#include "devices/sound.h"
#include "helper/parameters.h"
#include "helper/dirhelper.h"
#include "helper/confighelper.h"
#include "simulatorapi/simapi/simapi/simdata.h"
#include "slog/slog.h"
#define PROGRAM_NAME "monocoque"
int appstate = 0;
void display_banner()
{
printf("______ ______________ ___________________________________ ___________\n");
printf("___ |/ /_ __ \\__ | / /_ __ \\_ ____/_ __ \\_ __ \\_ / / /__ ____/\n");
printf("__ /|_/ /_ / / /_ |/ /_ / / / / _ / / / / / / / / /__ __/ \n");
printf("_ / / / / /_/ /_ /| / / /_/ // /___ / /_/ // /_/ // /_/ / _ /___ \n");
printf("/_/ /_/ \\____/ /_/ |_/ \\____/ \\____/ \\____/ \\___\\_\\\\____/ /_____/ \n");
}
void SetSettingsFromParameters(Parameters* p, MonocoqueSettings* ms, char* configdir_str, char* cachedir_str)
{
if(p->user_specified_config_file == true && does_file_exist(p->config_filepath))
{
ms->config_str = strdup(p->config_filepath);
}
else
{
if(p->user_specified_config_dir == true && does_directory_exist(p->config_dirpath))
{
asprintf(&ms->config_str, "%s/%s", p->config_dirpath, "monocoque.config");
}
else
{
asprintf(&ms->config_str, "%s%s", configdir_str, "monocoque.config");
}
}
if(p->user_specified_log_file == true && does_file_exist(p->log_fullfilename_str))
{
ms->log_dirname_str = strdup(p->log_dirname_str);
ms->log_filename_str = strdup(p->log_filename_str);
}
else
{
ms->log_dirname_str = strdup(cachedir_str);
ms->log_filename_str = strdup("monocoque.log");
}
ms->fps = p->fps;
ms->verbosity_count = p->verbosity_count;
ms->program_action = A_TEST;
if (p->program_action == A_PLAY)
{
ms->program_action = A_PLAY;
}
if (p->program_action == A_CONFIG_TACH)
{
ms->program_action = A_CONFIG_TACH;
}
ms->force_udp_mode = false;
ms->disable_audio = p->disable_audio;
}
int main(int argc, char** argv)
{
appstate = 0;
display_banner();
char* home_dir_str = gethome();
if(home_dir_str == NULL)
{
fprintf(stderr, "You need a home directory");
return 0;
}
Parameters* p = NULL;
// calloc, not malloc: a --help or --version run jumps straight to
// cleanup_final before a single field is set, and the cleanup path frees
// every pointer in both structs. Uninitialised heap made that a free() of
// whatever junk was there -- confirmed on Debian forky, where the
// released .deb aborted with `free(): invalid pointer` in
// monocoquesettingsfree() on `monocoque --help`. Older glibc happened not
// to notice, which is the only reason this looked fine on stable and
// Fedora.
p = calloc(1, sizeof(Parameters));
MonocoqueSettings* ms = calloc(1, sizeof(MonocoqueSettings));
ConfigError ppe = getParameters(argc, argv, p);
if (ppe == E_SUCCESS_AND_EXIT || ppe == E_SOMETHING_BAD)
{
printf("invalid parameters\n");
goto cleanup_final;
}
xdgHandle xdg;
if(!xdgInitHandle(&xdg))
{
fprintf(stderr, "Function xdgInitHandle() failed, is $HOME unset?\n");
goto cleanup_final;
}
const char* config_home_str = xdgConfigHome(&xdg);
const char* cache_home_str = xdgCacheHome(&xdg);
char* cachedir_str = NULL;
char* configdir_str = NULL;
if(p->user_specified_config_file == false && p->user_specified_config_dir == false)
{
create_xdg_dir(config_home_str);
configdir_str = create_user_dir(home_dir_str, ".config", PROGRAM_NAME);
}
if(p->user_specified_log_file == false)
{
create_xdg_dir(cache_home_str);
cachedir_str = create_user_dir(home_dir_str, ".cache", PROGRAM_NAME);
}
fprintf(stderr, "applying settings\n");
SetSettingsFromParameters(p, ms, configdir_str, cachedir_str);
if(cachedir_str != NULL)
{
free(cachedir_str);
}
if(configdir_str != NULL)
{
free(configdir_str);
}
fprintf(stderr, "settings applied\n");
slog_init("monocoque", SLOG_FLAGS_ALL, 1);
slog_config_t slgCfg;
slog_config_get(&slgCfg);
slgCfg.eColorFormat = SLOG_COLORING_TAG;
slgCfg.eDateControl = SLOG_TIME_ONLY;
strcpy(slgCfg.sFileName, ms->log_filename_str);
strcpy(slgCfg.sFilePath, ms->log_dirname_str);
slgCfg.nTraceTid = 0;
slgCfg.nToScreen = 1;
slgCfg.nUseHeap = 0;
slgCfg.nToFile = 1;
slgCfg.nFlush = 0;
slgCfg.nFlags = SLOG_FLAGS_ALL;
slog_config_set(&slgCfg);
if (ms->verbosity_count < 2)
{
slog_disable(SLOG_TRACE);
}
if (ms->verbosity_count < 1)
{
slog_disable(SLOG_DEBUG);
}
xdgWipeHandle(&xdg);
slogi("checking for diameters config");
char* diameters_file_str;
asprintf(&diameters_file_str, "%s/.config/monocoque/diameters.config", home_dir_str);
ms->tyre_diameter_config = strdup(diameters_file_str);
free(diameters_file_str);
ms->useconfig = 0;
ms->configcheck = 0;
slogi("Testing monocoque config file: %s", ms->config_str);
slogd("using diameters file %s %i", ms->tyre_diameter_config, ms->configcheck);
config_t cfg;
config_init(&cfg);
config_setting_t* config_devices = NULL;
if (!config_read_file(&cfg, ms->config_str))
{
sloge("Issue with monocoque config file: %s:%d - %s", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
fprintf(stderr, "Issue with monocoque config file: %s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
slog_destroy();
goto cleanup_final;
}
else
{
slogi("Opened and validated monocoque configuration file");
}
config_destroy(&cfg);
if (ms->program_action == A_CONFIG_TACH)
{
int error = 0;
SimDevice* tachdev = malloc(sizeof(SimDevice));
tachdev->initialized = false;
SimData* sdata = malloc(sizeof(SimData));
SimInfo* siminfo = malloc(sizeof(SimInfo));
simapi_set_faux_siminfo(siminfo);
DeviceSettings* ds = malloc(sizeof(DeviceSettings));
error = devsetup("USB", "Tachometer", "None", ms, ds, NULL);
if (error != MONOCOQUE_ERROR_NONE)
{
sloge("Could not proceed with tachometer configuration due to error: %i", error);
}
else
{
int devices = 0;
devices = devinit(tachdev, siminfo, 1, ds, ms);
if(devices < 1)
{
error = MONOCOQUE_ERROR_INVALID_DEV;
}
}
if (error != MONOCOQUE_ERROR_NONE)
{
sloge("Could not proceed with tachometer configuration due to error: %i", error);
}
else
{
slogi("configuring tachometer with max revs: %i, granularity: %i, saving to %s", p->max_revs, p->granularity, p->save_file);
config_tachometer(p->max_revs, p->granularity, p->save_file, tachdev, sdata);
}
slogt("freeing devices if necessary");
if(tachdev->initialized == true)
{
slogt("freeing tachmoeter device");
tachdev->free(tachdev);
}
//free(tachdev);
free(sdata);
free(ds);
}
else
{
int error = 0;
error = MONOCOQUE_ERROR_NONE;
//setupsound();
bool pulseaudio = false;
if (ms->program_action == A_PLAY)
{
ms->useconfig = 1;
slogi("running monocoque in gameloop mode..");
//#ifdef USE_PULSEAUDIO
//pa_threaded_mainloop_unlock(mainloop);
pulseaudio = true;
//#endif
if(ms->disable_audio == false)
{
setupsound();
}
//error = looper(devices, numdevices, p);
error = monocoque_mainloop(ms);
if (error == MONOCOQUE_ERROR_NONE)
{
slogi("Game loop exited succesfully with error code: %i", error);
}
else
{
sloge("Game loop exited with error code: %i", error);
}
if(ms->disable_audio == false)
{
freesound();
}
}
else
{
slogi("running monocoque in test mode...");
if(ms->disable_audio == false)
{
setupsound();
}
ms->useconfig = 0;
int configs = getNumberOfConfigs(ms->config_str);
int confignum = getconfigtouse(ms->config_str, "default", configs-1);
int configureddevices;
configcheck(ms->config_str, confignum, &configureddevices);
DeviceSettings* ds = malloc(configureddevices * sizeof(DeviceSettings));
slogd("loading confignum %i, with %i devices.", confignum, configureddevices);
int numdevices = uiloadconfig(ms->config_str, confignum, configureddevices, ms, ds);
SimDevice* simdevices = malloc(numdevices * sizeof(SimDevice));
SimInfo* siminfo = malloc(sizeof(SimInfo));
simapi_set_faux_siminfo(siminfo);
int initdevices = devinit(simdevices, siminfo, configureddevices, ds, ms);
for( int i = 0; i < configureddevices; i++)
{
settingsfree(ds[i]);
}
free(ds);
error = tester(simdevices, numdevices);
if (error == MONOCOQUE_ERROR_NONE)
{
slogi("Test exited succesfully with error code: %i", error);
}
else
{
sloge("Test exited with error code: %i", error);
}
for (int x = 0; x < numdevices; x++)
{
if (simdevices[x].initialized == true)
{
simdevices[x].free(&simdevices[x]);
}
}
if(ms->disable_audio == false)
{
freesound();
}
}
slog_destroy();
}
cleanup_final:
monocoquesettingsfree(ms);
free(ms);
freeparams(p);
free(p);
exit(0);
}
+543
View File
@@ -0,0 +1,543 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <basedir_fs.h>
#include <libconfig.h>
#include "mgui/uiconfighelper.h"
#include "mgui/addeditaction.h"
#include "mgui/mainwindow.h"
#include "gameloop/gameloop.h"
#include "gameloop/loopdata.h"
#include "gameloop/tachconfig.h"
#include "devices/simdevice.h"
#include "devices/sound.h"
#include "helper/parameters.h"
#include "helper/dirhelper.h"
#include "helper/confighelper.h"
#include "simulatorapi/simapi/simapi/simdata.h"
#include "slog/slog.h"
#include <nappgui.h>
#include <gui/guiall.h>
#define PROGRAM_NAME "monocoque"
MonocoqueSettings ms;
Parameters* p;
char **m_argv;
int m_argc;
int appstate = 0;
void SetSettingsFromParameters(Parameters* p, MonocoqueSettings* ms, char* configdir_str, char* cachedir_str)
{
if(p->user_specified_config_file == true && does_file_exist(p->config_filepath))
{
ms->config_str = strdup(p->config_filepath);
}
else
{
if(p->user_specified_config_dir == true && does_directory_exist(p->config_dirpath))
{
asprintf(&ms->config_str, "%s/%s", p->config_dirpath, "monocoque.config");
}
else
{
asprintf(&ms->config_str, "%s%s", configdir_str, "monocoque.config");
}
}
if(p->user_specified_log_file == true && does_file_exist(p->log_fullfilename_str))
{
ms->log_dirname_str = strdup(p->log_dirname_str);
ms->log_filename_str = strdup(p->log_filename_str);
}
else
{
ms->log_dirname_str = strdup(cachedir_str);
ms->log_filename_str = strdup("monocoque.log");
}
ms->fps = p->fps;
ms->verbosity_count = p->verbosity_count;
ms->program_action = A_TEST;
if (p->program_action == A_PLAY)
{
ms->program_action = A_PLAY;
}
if (p->program_action == A_CONFIG_TACH)
{
ms->program_action = A_CONFIG_TACH;
}
ms->force_udp_mode = false;
ms->disable_audio = p->disable_audio;
}
/*---------------------------------------------------------------------------*/
int monocoque_initialize(int argc, char** argv)
{
char* home_dir_str = gethome();
if(home_dir_str == NULL)
{
fprintf(stderr, "You need a home directory");
return 0;
}
p = NULL;
// calloc, not malloc: freeparams() walks every pointer in this struct
// on the early-exit paths, before getParameters has set them. See the
// same change in monocoque-cli.c.
p = calloc(1, sizeof(Parameters));
p->config_dirpath = NULL;
p->config_filepath = NULL;
p->log_filename_str = NULL;
p->log_fullfilename_str = NULL;
p->log_dirname_str = NULL;
ConfigError ppe = getParameters(argc, argv, p);
if (ppe == E_SUCCESS_AND_EXIT || ppe == E_SOMETHING_BAD)
{
printf("invalid parameters\n");
//goto cleanup_final;
}
xdgHandle xdg;
if(!xdgInitHandle(&xdg))
{
fprintf(stderr, "Function xdgInitHandle() failed, is $HOME unset?\n");
//goto cleanup_final;
}
const char* config_home_str = xdgConfigHome(&xdg);
const char* cache_home_str = xdgCacheHome(&xdg);
char* cachedir_str = NULL;
char* configdir_str = NULL;
if(p->user_specified_config_file == false && p->user_specified_config_dir == false)
{
create_xdg_dir(config_home_str);
configdir_str = create_user_dir(home_dir_str, ".config", PROGRAM_NAME);
}
if(p->user_specified_log_file == false)
{
create_xdg_dir(cache_home_str);
cachedir_str = create_user_dir(home_dir_str, ".cache", PROGRAM_NAME);
}
SetSettingsFromParameters(p, &ms, configdir_str, cachedir_str);
if(cachedir_str != NULL)
{
free(cachedir_str);
}
if(configdir_str != NULL)
{
free(configdir_str);
}
slog_init("monocoque", SLOG_FLAGS_ALL, 1);
slog_config_t slgCfg;
slog_config_get(&slgCfg);
slgCfg.eColorFormat = SLOG_COLORING_TAG;
slgCfg.eDateControl = SLOG_TIME_ONLY;
strcpy(slgCfg.sFileName, ms.log_filename_str);
strcpy(slgCfg.sFilePath, ms.log_dirname_str);
slgCfg.nTraceTid = 0;
slgCfg.nToScreen = 1;
slgCfg.nUseHeap = 0;
slgCfg.nToFile = 1;
slgCfg.nFlush = 0;
slgCfg.nFlags = SLOG_FLAGS_ALL;
slog_config_set(&slgCfg);
if (ms.verbosity_count < 2)
{
slog_disable(SLOG_TRACE);
}
if (ms.verbosity_count < 1)
{
slog_disable(SLOG_DEBUG);
}
xdgWipeHandle(&xdg);
if(ms.disable_audio == false)
{
setupsound();
}
}
/*---------------------------------------------------------------------------*/
int monocoque_gameloop_start()
{
start_loop(&ms);
}
/*---------------------------------------------------------------------------*/
static void i_OnStart(App *app, Event *e)
{
unref(e);
app->is_running = TRUE;
int rc = 0;
if(appstate == 0)
{
rc = monocoque_gameloop_start();
}
else
{
monocoque_mainloop_stop(&ms);
}
}
/*---------------------------------------------------------------------------*/
static void i_OnStop(App *app, Event *e)
{
monocoque_mainloop_stop(&ms);
}
/*---------------------------------------------------------------------------*/
static void i_OnClose(App *app, Event *e)
{
close_monocoque_config(ms.cfg);
monocoquesettingsfree(&ms);
freeparams(p);
free(p);
for (int i = 0; i < (uint32_t)m_argc; ++i)
free(m_argv[i]);
free(m_argv);
osapp_finish();
unref(app);
unref(e);
}
/*---------------------------------------------------------------------------*/
static void i_OnAddClose(App *app, Event *e)
{
}
static void i_OnDelete(App* app, Event *e)
{
int devicenum = listbox_get_selected(app->listbox_devices);
if(devicenum == UINT32_MAX)
{
return;
}
delete_device_config(ms.cfg, ms.config_str, 0, devicenum);
listbox_clear(app->listbox_devices);
combo_clear(app->combo_configurations);
populate_device_list(app->listbox_devices, ms.cfg);
populate_combo_box(app->combo_configurations, ms.cfg);
}
/*---------------------------------------------------------------------------*/
static void i_OnAddEdit(App* app, Event *e, enum device_action device_action)
{
Panel *panel = NULL;
int devicenum = listbox_get_selected(app->listbox_devices);
if(devicenum == UINT32_MAX && device_action == DEVICE_ACTION_EDIT)
{
return;
}
if(device_action == DEVICE_ACTION_ADD)
{
devicenum = listbox_count(app->listbox_devices);
}
app->window2 = window_create(ekWINDOW_STDRES);
panel = adddevice_window(app->window2, app, &ms, device_action, 0, devicenum);
window_panel(app->window2, panel);
window_origin(app->window2, v2df(800, 200));
window_title(app->window2, "monocoque");
window_OnClose(app->window2, listener(app, i_OnAddClose, App));
window_modal(app->window2, app->window);
window_destroy(&app->window2);
listbox_clear(app->listbox_devices);
combo_clear(app->combo_configurations);
populate_device_list(app->listbox_devices, ms.cfg);
populate_combo_box(app->combo_configurations, ms.cfg);
}
/*---------------------------------------------------------------------------*/
static void i_OnAdd(App *app, Event *e)
{
i_OnAddEdit(app, e, DEVICE_ACTION_ADD);
}
/*---------------------------------------------------------------------------*/
static void i_OnEdit(App *app, Event *e)
{
i_OnAddEdit(app, e, DEVICE_ACTION_EDIT);
}
/*---------------------------------------------------------------------------*/
static void set_bottom_label_status(Label* label)
{
char status_string[256];
snprintf(status_string, 256, "Game: %s Game Status: None Simd: %s", get_simexe_name(), get_simd_onoff());
label_text(label, status_string);
}
/*---------------------------------------------------------------------------*/
static Panel *i_panel(App *app, MonocoqueSettings* ms)
{
app->main_panel = panel_create();
app->subpanel = panel_scroll(FALSE, TRUE);
app->layout = layout_create(8,5);
app->listbox_devices = listbox_create();
listbox_size(app->listbox_devices, s2df(50, 50));
app->combo_configurations = combo_create();
app->label_configurations = label_create();
label_text(app->label_configurations, "Configuration: ");
app->label_game_status = label_create();
app->button_startstop = button_push();
app->button_add = button_push();
app->button_edit = button_push();
app->button_del = button_push();
app->layout1 = layout_create(8,1);
app->layout2 = layout_create(8,1);
app->layout3 = layout_create(1,1);
app->layout4 = layout_create(1,2);
layout_layout(app->layout, app->layout1, 0, 0);
layout_layout(app->layout, app->layout2, 0, 1);
layout_layout(app->layout, app->layout3, 0, 2);
layout_layout(app->layout, app->layout4, 0, 3);
appstate = 0;
set_bottom_label_status(app->label_game_status);
populate_device_list(app->listbox_devices, ms->cfg);
listbox_select(app->listbox_devices, 0, true);
populate_combo_box(app->combo_configurations, ms->cfg);
button_text(app->button_startstop, "Start");
button_text(app->button_add, "Add Device");
button_text(app->button_del, "Delete Device");
button_text(app->button_edit, "Edit Device");
button_OnClick(app->button_startstop, listener(app, i_OnStart, App));
button_OnClick(app->button_add, listener(app, i_OnAdd, App));
button_OnClick(app->button_edit, listener(app, i_OnEdit, App));
button_OnClick(app->button_del, listener(app, i_OnDelete, App));
layout_label(app->layout1, app->label_configurations, 0, 0);
layout_combo(app->layout1, app->combo_configurations, 1, 0);
layout_button(app->layout2, app->button_add, 3, 0);
layout_button(app->layout2, app->button_edit, 4, 0);
layout_button(app->layout2, app->button_del, 5, 0);
layout_listbox(app->layout3, app->listbox_devices, 0, 0);
layout_button(app->layout4, app->button_startstop, 0, 0);
layout_label(app->layout4, app->label_game_status, 0, 1);
panel_layout(app->subpanel, app->layout3);
layout_panel(app->layout, app->subpanel, 0, 2);
panel_layout(app->main_panel, app->layout);
app->devices_subpanel = layout_cell(app->layout, 0, 2);
// spacing
layout_valign(app->layout4, 0, 0, ekBOTTOM);
layout_valign(app->layout4, 0, 1, ekBOTTOM);
layout_halign(app->layout4, 0, 0, ekJUSTIFY);
layout_halign(app->layout4, 0, 1, ekJUSTIFY);
layout_hexpand(app->layout, 0);
layout_hexpand(app->layout4, 0);
layout_vexpand(app->layout, 0);
layout_vexpand(app->layout, 2);
layout_vexpand(app->layout4, 0);
button_vpadding(app->button_startstop, 0);
button_hpadding(app->button_startstop, 0);
layout_hexpand(app->layout1, 1);
return app->main_panel;
}
static App *i_create(void)
{
uint32_t i;
int old_argc = (int)osapp_argc();
m_argc = old_argc + 1;
m_argv = malloc((m_argc+1) * sizeof(char *));
m_argv[m_argc] = NULL;
for (i = 0; i < (uint32_t)m_argc; ++i)
{
char buffer[128];
m_argv[i] = NULL;
if (i == 1)
{
m_argv[i] = strdup("play");
}
else
{
uint32_t old_i = (i > 1) ? i - 1 : i;
osapp_argv(old_i, buffer, sizeof(buffer));
m_argv[i] = strdup(buffer);
}
}
// do some monocoque things here
ms.tyre_diameter_config = NULL;
ms.config_str = NULL;
ms.log_filename_str = NULL;
ms.log_dirname_str = NULL;
monocoque_initialize(m_argc, m_argv);
ms.cfg = open_monocoque_config(ms.config_str);
// find a better spot for this
dbind(SimData, uint32_t, velocity);
dbind(SimData, uint32_t, rpms);
dbind(SimData, uint32_t, gear);
dbind(SimData, uint32_t, maxrpm);
dbind(SimData, real64_t, tyreRPS[0]);
dbind(SimData, real64_t, tyreRPS[1]);
dbind(SimData, real64_t, tyreRPS[2]);
dbind(SimData, real64_t, tyreRPS[3]);
dbind(SimData, real64_t, tyrediameter[0]);
dbind(SimData, real64_t, tyrediameter[1]);
dbind(SimData, real64_t, tyrediameter[2]);
dbind(SimData, real64_t, tyrediameter[3]);
dbind(DeviceSettings, HapticEffectSettings, hapticsettings);
dbind(HapticEffectSettings, uint32_t, frequency);
dbind(HapticEffectSettings, uint32_t, frequencyMax);
dbind(HapticEffectSettings, uint32_t, amplitude);
dbind(HapticEffectSettings, uint32_t, amplitudeMax);
dbind(HapticEffectSettings, uint32_t, motorposition);
dbind(HapticEffectSettings, real64_t, threshold);
dbind(HapticEffectSettings, real64_t, duration);
dbind_enum(VibrationEffectType, EFFECT_ENGINERPM, "Engine RPM");
dbind_enum(VibrationEffectType, EFFECT_GEARSHIFT, "Gear Shift");
dbind_enum(VibrationEffectType, EFFECT_ABSBRAKES, "ABS Brakes");
dbind_enum(VibrationEffectType, EFFECT_TYRESLIP, "Tyre Slip");
dbind_enum(VibrationEffectType, EFFECT_TYRELOCK, "Tyre Lock");
dbind_enum(VibrationEffectType, EFFECT_SUSPENSION, "Suspension");
dbind_enum(EffectModulationType, EFFECT_MODULATION_NONE, "None");
dbind_enum(EffectModulationType, EFFECT_MODULATION_FREQUENCY, "Frequency");
dbind_enum(EffectModulationType, EFFECT_MODULATION_AMPLIFY, "Amplify");
dbind_enum(MonocoqueTyreIdentifier, FRONTLEFT, "Front Left");
dbind_enum(MonocoqueTyreIdentifier, FRONTRIGHT, "Front Right");
dbind_enum(MonocoqueTyreIdentifier, REARLEFT, "Rear Left");
dbind_enum(MonocoqueTyreIdentifier, REARRIGHT, "Rear Right");
dbind_enum(MonocoqueTyreIdentifier, FRONTS, "Fronts");
dbind_enum(MonocoqueTyreIdentifier, REARS, "Rears");
dbind_enum(MonocoqueTyreIdentifier, ALLFOUR, "All Four");
dbind(HapticEffectSettings, uint32_t, modulation);
dbind(HapticEffectSettings, uint32_t, tyre);
dbind(HapticEffectSettings, uint32_t, effect_type);
dbind(DeviceSettings, SerialDeviceSettings, serialdevsettings);
dbind(SerialDeviceSettings, uint32_t, baud);
dbind(SoundDeviceSettings, uint32_t, pan);
dbind(SoundDeviceSettings, uint32_t, channels);
dbind(SoundDeviceSettings, uint32_t, volume);
dbind(DeviceSettings, SerialDeviceSettings, serialdevsettings);
dbind(SerialDeviceSettings, uint32_t, startled);
dbind(SerialDeviceSettings, uint32_t, numleds);
dbind(SerialDeviceSettings, uint32_t, endled);
dbind_enum(DeviceType, SIMDEV_USB, "USB");
dbind_enum(DeviceType, SIMDEV_SOUND, "Sound");
dbind_enum(DeviceType, SIMDEV_SERIAL, "Serial");
dbind(DeviceSettings, DeviceType, dev_type);
dbind(DeviceSettings, uint32_t, dev_subtype);
dbind(DeviceSettings, uint32_t, fps);
//gui stuff
App *app = heap_new0(App);
Panel *panel = i_panel(app, &ms);
app->window = window_create(ekWINDOW_STDRES);
window_panel(app->window, panel);
window_origin(app->window, v2df(800, 200));
window_title(app->window, "monocoque");
window_OnClose(app->window, listener(app, i_OnClose, App));
window_show(app->window);
return app;
}
/*---------------------------------------------------------------------------*/
static void i_destroy(App **app)
{
//monocoquesettingsfree(&ms);
//freeparams(p);
//free(p);
osapp_finish();
window_destroy(&(*app)->window);
heap_delete(app, App);
}
/*---------------------------------------------------------------------------*/
static void i_update(App *app, const real64_t prtime, const real64_t ctime)
{
if (app->is_running == TRUE)
{
if(appstate == 2)
{
button_text(app->button_startstop, "Playing... Press to Stop");
cell_enabled(app->devices_subpanel, false);
}
if(appstate == 1)
{
button_text(app->button_startstop, "Searching for data... Press to Stop");
cell_enabled(app->devices_subpanel, false);
}
if(appstate <= 0)
{
button_text(app->button_startstop, "Start");
cell_enabled(app->devices_subpanel, true);
}
set_bottom_label_status(app->label_game_status);
}
}
/*---------------------------------------------------------------------------*/
#include <osapp/osmain.h>
osmain_sync(.04, i_create, i_destroy, i_update, "", App)
+10 -2
View File
@@ -84,8 +84,16 @@ int main(int argc, char** argv)
return 0; return 0;
} }
Parameters* p = NULL; Parameters* p = NULL;
p = malloc(sizeof(Parameters)); // calloc, not malloc: a --help or --version run jumps straight to
MonocoqueSettings* ms = malloc(sizeof(MonocoqueSettings));; // cleanup_final before a single field is set, and the cleanup path frees
// every pointer in both structs. Uninitialised heap made that a free() of
// whatever junk was there -- confirmed on Debian forky, where the
// released .deb aborted with `free(): invalid pointer` in
// monocoquesettingsfree() on `monocoque --help`. Older glibc happened not
// to notice, which is the only reason this looked fine on stable and
// Fedora.
p = calloc(1, sizeof(Parameters));
MonocoqueSettings* ms = calloc(1, sizeof(MonocoqueSettings));
ConfigError ppe = getParameters(argc, argv, p); ConfigError ppe = getParameters(argc, argv, p);
if (ppe == E_SUCCESS_AND_EXIT || ppe == E_SOMETHING_BAD) if (ppe == E_SUCCESS_AND_EXIT || ppe == E_SOMETHING_BAD)
+2 -1
View File
@@ -40,7 +40,8 @@ set(simulatorapi_source_files
simapi/simapi/mapping/wreckfest2mapper.c simapi/simapi/mapping/wreckfest2mapper.c
simapi/simapi/mapping/forzamapper.c simapi/simapi/mapping/forzamapper.c
simapi/simapi/getpid.c simapi/simapi/getpid.c
simapi/simapi/getpid.h
) )
add_library(simulatorapi STATIC ${simulatorapi_source_files}) add_library(simulatorapi STATIC ${simulatorapi_source_files})
set_property(TARGET simulatorapi PROPERTY POSITION_INDEPENDENT_CODE ON)
+2
View File
@@ -4,3 +4,5 @@ set(slog_source_files
) )
add_library(slog STATIC ${slog_source_files}) add_library(slog STATIC ${slog_source_files})
set_property(TARGET slog PROPERTY POSITION_INDEPENDENT_CODE ON)
+11
View File
@@ -0,0 +1,11 @@
# Automated CI test suite — only built/run when ENABLE_TESTS is on (see
# root CMakeLists.txt). Manual, interactive, and hardware-dependent scratch
# tools live in tests/manual/ instead and are never built by this file.
add_executable(confighelper_test confighelper_test.c)
# helper (confighelper.c specifically) references slog/xml2/simulatorapi
# symbols elsewhere in the same translation unit — static libs resolve per
# object file, so all of these are needed even though this test only
# exercises strtodevsubsubtype.
target_link_libraries(confighelper_test helper config slog xml2 simulatorapi m proc2)
add_test(NAME confighelper_test COMMAND confighelper_test)
+45
View File
@@ -0,0 +1,45 @@
// Proof-of-plumbing test for the new CTest wiring — not an attempt at
// comprehensive coverage. Exercises strtodevsubsubtype (helper/confighelper.c),
// a pure string->enum mapping with no I/O/hardware, including the R8/R3
// aliasing onto SIMDEVSUBTYPE_MOZAR5 and the fallback for unrecognized input.
#include <stdio.h>
#include <string.h>
#include "../src/monocoque/helper/confighelper.h"
static int failures = 0;
static void check(const char* input, DeviceSubSubType expected)
{
DeviceSettings ds = {0};
strtodevsubsubtype(input, &ds);
if (ds.dev_subsubtype != expected)
{
fprintf(stderr, "FAIL: strtodevsubsubtype(\"%s\") = %d, expected %d\n",
input, ds.dev_subsubtype, expected);
failures++;
}
}
int main(void)
{
check("MozaR5", SIMDEVSUBTYPE_MOZAR5);
check("MozaR8", SIMDEVSUBTYPE_MOZAR5); // aliased onto the same protocol as R5
check("MozaR3", SIMDEVSUBTYPE_MOZAR5); // same
check("MozaNew", SIMDEVSUBTYPE_MOZA_NEW);
check("MozaKSProWheel", SIMDEVSUBTYPE_MOZA_KS_PRO_WHEEL);
check("CammusC5", SIMDEVSUBTYPE_CAMMUSC5);
check("CammusC12", SIMDEVSUBTYPE_CAMMUSC12);
check("LogitechG29", SIMDEVSUBTYPE_LOGITECH_G29);
check("not-a-real-subtype", SIMDEVSUBTYPE_UNKNOWN);
check("", SIMDEVSUBTYPE_UNKNOWN);
if (failures > 0)
{
fprintf(stderr, "%d check(s) failed\n", failures);
return 1;
}
printf("confighelper_test: all checks passed\n");
return 0;
}
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
# Collect licence and copyright information for everything an AppImage bundles.
#
# usage: tools/appimage-collect-licenses.sh <AppDir>
#
# A .deb declares only what it ships, because its dependencies remain separate
# packages carrying their own copyright files. An AppImage carries those
# libraries inside it, so their notices have to travel with it -- and most of
# them here are LGPL (GTK, glib, pango and friends), where the obligation also
# covers saying where the corresponding source can be had.
#
# The mapping is mechanical rather than researched: every bundled library came
# from a Debian/Ubuntu package on the build host, so dpkg can name it, and the
# archive already holds a copyright file for each. Run this after linuxdeploy
# has populated the AppDir and before it packages the image.
set -euo pipefail
APPDIR="${1:?AppDir path}"
DOCDIR="$APPDIR/usr/share/doc"
MANIFEST="$DOCDIR/BUNDLED-LIBRARIES.txt"
mkdir -p "$DOCDIR"
{
echo "Libraries bundled in this AppImage"
echo "=================================="
echo
echo "Each library below was taken from the Debian/Ubuntu package named"
echo "beside it on the build host. That package's copyright file is included"
echo "under usr/share/doc/<package>/copyright in this image."
echo
echo "The corresponding source for any of them can be obtained with:"
echo " apt-get source <package>=<version>"
echo "from the distribution and release recorded below."
echo
echo "Build host: $( (. /etc/os-release && echo "$PRETTY_NAME") 2>/dev/null || echo unknown)"
echo
printf '%-44s %-34s %s\n' "LIBRARY" "PACKAGE" "VERSION"
} > "$MANIFEST"
found=0
unowned=0
while IFS= read -r so; do
base="$(basename "$so")"
pkg=""
# Try the usual multiarch location first, then fall back to a name search.
for candidate in "/usr/lib/x86_64-linux-gnu/$base" "/lib/x86_64-linux-gnu/$base" "/usr/lib/$base"; do
pkg="$(dpkg -S "$candidate" 2>/dev/null | head -1 | cut -d: -f1 || true)"
[ -n "$pkg" ] && break
done
if [ -z "$pkg" ]; then
pkg="$(dpkg -S "$base" 2>/dev/null | head -1 | cut -d: -f1 || true)"
fi
if [ -n "$pkg" ]; then
ver="$(dpkg-query -W -f='${Version}' "$pkg" 2>/dev/null || echo unknown)"
printf '%-44s %-34s %s\n' "$base" "$pkg" "$ver" >> "$MANIFEST"
if [ -f "/usr/share/doc/$pkg/copyright" ] && [ ! -f "$DOCDIR/$pkg/copyright" ]; then
install -Dm644 "/usr/share/doc/$pkg/copyright" "$DOCDIR/$pkg/copyright"
fi
found=$((found + 1))
else
# Built by this job rather than installed from a package -- its licence is
# collected separately, from its own source tree.
printf '%-44s %-34s %s\n' "$base" "(built from source in this job)" "-" >> "$MANIFEST"
unowned=$((unowned + 1))
fi
done < <(find "$APPDIR" -name '*.so*' -type f | sort)
echo >> "$MANIFEST"
echo "$found libraries from distribution packages, $unowned built in this job." >> "$MANIFEST"
echo "collected copyright for $found bundled libraries ($unowned built here)"
echo "manifest: $MANIFEST"
+24
View File
@@ -0,0 +1,24 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: monocoque
Upstream-Contact: Paul Jones <paul@spacefreak18.xyz>
Source: https://github.com/spacefreak18/monocoque
Files:
*
Copyright: 2022 Paul Jones
License: GPL-3+
Files:
src/monocoque/slog/slog.?
Copyright: 2015-2025 Sandro Kalatozishvili (s.kalatoz@gmail.com)
License: MIT
Files:
src/monocoque/mgui/nappgui_src/*
Copyright: 2021 Francisco García Collado
License: MIT
Files:
src/monocoque/simulatorapi/simapi/*
Copyright: 2022 Paul Jones
License: LGPL
@@ -5,4 +5,4 @@ Priority: optional
Architecture: amd64 Architecture: amd64
Maintainer: Paul Jones <paul@spacefreak18.xyz> Maintainer: Paul Jones <paul@spacefreak18.xyz>
Description: Racing Simulator Device Manager Description: Racing Simulator Device Manager
Depends: libpulse0,libconfig11,libargtable2-0,liblua5.4-0,libuv1t64,libserialport0,libxml2,libxdg-basedir1,libhidapi-hidraw0 Depends: libpulse0,libconfig11,libargtable2-0,liblua5.4-0,libuv1t64,libserialport0,libxml2-16,libxdg-basedir1,libhidapi-hidraw0,libgtk-3-0t64,libcurl4t64,libglu1-mesa
@@ -5,4 +5,4 @@ Priority: optional
Architecture: amd64 Architecture: amd64
Maintainer: Paul Jones <paul@spacefreak18.xyz> Maintainer: Paul Jones <paul@spacefreak18.xyz>
Description: Racing Simulator Device Manager Description: Racing Simulator Device Manager
Depends: libpulse0,libconfig11,libargtable2-0,liblua5.4-0,libuv1t64,libserialport0,libxml2,libxdg-basedir1,libhidapi-hidraw0 Depends: libpulse0,libconfig11,libargtable2-0,liblua5.4-0,libuv1t64,libserialport0,libxml2,libxdg-basedir1,libhidapi-hidraw0,libgtk-3-0t64,libglu1-mesa,libcurl4t64
@@ -5,4 +5,4 @@ Priority: optional
Architecture: amd64 Architecture: amd64
Maintainer: Paul Jones <paul@spacefreak18.xyz> Maintainer: Paul Jones <paul@spacefreak18.xyz>
Description: Racing Simulator Device Manager Description: Racing Simulator Device Manager
Depends: libpulse0,libconfig9,libargtable2-0,liblua5.4-0,libuv1t64,libserialport0,libxml2,libxdg-basedir1,libhidapi-hidraw0 Depends: libpulse0,libconfig11,libargtable2-0,liblua5.4-0,libuv1t64,libserialport0,libxml2-16,libxdg-basedir1,libhidapi-hidraw0,libglu1-mesa,libcurl4t64,libgtk-3-0t64
+280
View File
@@ -0,0 +1,280 @@
#!/bin/bash
# Monocoque Distrobox Installer
# For immutable Linux distributions (Bazzite, Silverblue, etc.)
# where native package managers are unavailable.
#
# Installs monocoque and dependencies inside an Arch Linux distrobox container,
# with wrapper scripts on the host for transparent access.
set -euo pipefail
SCRIPT_VERSION="1.0.0"
CONTAINER="simracing"
INSTALL_DIR="$HOME/.local/share/simracing"
BIN_DIR="$HOME/.local/bin"
CONFIG_DIR="$HOME/.config/monocoque"
# Color output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_header() {
echo ""
echo "╔══════════════════════════════════════════════════════════════════╗"
echo "║ Monocoque Distrobox Installer v${SCRIPT_VERSION} ║"
echo "╚══════════════════════════════════════════════════════════════════╝"
echo ""
}
# --- Main ---
print_header
# Check ~/.local/bin is in PATH
if [[ ":$PATH:" != *":$BIN_DIR:"* ]]; then
log_error "$BIN_DIR is not in your PATH."
log_info "Add this to your shell config:"
echo " export PATH=\"\$HOME/.local/bin:\$PATH\""
exit 1
fi
# Check distrobox is available
if ! command -v distrobox &>/dev/null; then
log_error "distrobox is not installed."
log_info "Install distrobox first: https://distrobox.it/#installation"
exit 1
fi
# Ensure dialout group exists and user is a member
log_info "Checking serial device access (dialout group)..."
if ! grep -q '^dialout:' /etc/group 2>/dev/null; then
if grep -q '^dialout:' /usr/lib/group 2>/dev/null; then
log_info "Adding dialout group to /etc/group (Fedora Atomic fix)..."
grep -E '^dialout:' /usr/lib/group | sudo tee -a /etc/group >/dev/null
else
log_info "Creating dialout group..."
sudo groupadd dialout
fi
fi
if ! groups | grep -qw dialout; then
log_info "Adding $USER to the dialout group for serial device access..."
sudo usermod -aG dialout "$USER"
log_warn "You will need to REBOOT after setup completes for serial access to work."
echo ""
fi
# Prevent ModemManager from grabbing Moza serial devices
UDEV_RULE="/etc/udev/rules.d/99-moza-serial.rules"
if [ ! -f "$UDEV_RULE" ]; then
if systemctl is-active --quiet ModemManager 2>/dev/null; then
echo ""
log_warn "ModemManager is running on your system."
echo " Moza wheel bases use USB serial devices that ModemManager may claim,"
echo " preventing monocoque from communicating with the wheel."
echo ""
echo " A udev rule can tell ModemManager to ignore Moza devices (vendor 346e)."
echo " This only affects Moza USB devices — other modems will work normally."
echo ""
read -rp "Install udev rule to prevent ModemManager from grabbing Moza devices? [Y/n]: " mm_confirm
if [[ "$mm_confirm" =~ ^[Nn]$ ]]; then
echo ""
log_info "Skipped udev rule. If monocoque can't talk to your wheel, run:"
echo " sudo tee $UDEV_RULE <<< 'ACTION==\"add|change\", SUBSYSTEM==\"usb\", ATTR{idVendor}==\"346e\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"'"
echo " sudo udevadm control --reload-rules && sudo udevadm trigger"
echo ""
else
log_info "Installing udev rule..."
echo 'ACTION=="add|change", SUBSYSTEM=="usb", ATTR{idVendor}=="346e", ENV{ID_MM_DEVICE_IGNORE}="1"' | sudo tee "$UDEV_RULE" >/dev/null
sudo udevadm control --reload-rules
sudo udevadm trigger
log_success "ModemManager udev rule installed"
fi
else
# ModemManager not running — install the rule silently
echo 'ACTION=="add|change", SUBSYSTEM=="usb", ATTR{idVendor}=="346e", ENV{ID_MM_DEVICE_IGNORE}="1"' | sudo tee "$UDEV_RULE" >/dev/null
sudo udevadm control --reload-rules
sudo udevadm trigger
fi
fi
# Create the Arch Linux distrobox container
if ! distrobox list 2>/dev/null | grep -q "$CONTAINER"; then
log_info "Creating Arch Linux container '$CONTAINER'..."
distrobox create \
--image docker.io/library/archlinux:latest \
--name "$CONTAINER" \
--root \
--additional-flags "--privileged --security-opt seccomp=unconfined" \
--pre-init-hooks "sed -i 's/^SigLevel.*/SigLevel = Never/' /etc/pacman.conf && pacman -Sy --noconfirm archlinux-keyring && pacman-key --init && pacman-key --populate archlinux && sed -i 's/^SigLevel.*/SigLevel = Required DatabaseOptional/' /etc/pacman.conf"
log_success "Container '$CONTAINER' created"
else
log_info "Container '$CONTAINER' already exists, skipping creation."
fi
# Install packages inside the container
log_info "Installing packages inside container (this may take a few minutes)..."
distrobox enter --root "$CONTAINER" -- bash -c '
set -euo pipefail
# Enable the multilib repo for 32-bit runtime support — Proton bridge
# apps (e.g. acbridge.exe) are 32-bit wine processes and fail with a
# bare "No such file or directory" (not a permissions/PATH error) when
# /lib/ld-linux.so.2, the 32-bit dynamic linker, is missing. A fresh
# Arch container image does not ship multilib enabled by default, and
# (confirmed on a real install) not even the commented-out [multilib]
# stanza is present to uncomment — it has to be appended outright.
# Idempotent so re-running this script is still safe.
if ! grep -q "^\[multilib\]" /etc/pacman.conf; then
echo "Enabling multilib repository (needed for 32-bit wine/Proton bridge support)..."
cat >> /etc/pacman.conf << "PACMANCONF_EOF"
[multilib]
Include = /etc/pacman.d/mirrorlist
PACMANCONF_EOF
fi
# Install build dependencies
echo "Installing build dependencies..."
# wine + lib32-glibc pull in the well-tested 32-bit dependency chain
# the Proton-bundled wine build each game uses needs to actually
# execute (this Arch wine package is not run directly — each game
# launches its own Proton version bridge via BridgeScripts.txt-style
# commands — installing the package is just the simplest reliable way
# to get every lib32-* dependency it needs without hand-picking each
# one).
sudo pacman -Syu --needed --noconfirm base-devel git mingw-w64-gcc wine lib32-glibc
# Install yay (AUR helper) if not present
if ! command -v yay &>/dev/null; then
echo "Installing yay AUR helper..."
TMPDIR=$(mktemp -d)
git clone https://aur.archlinux.org/yay-bin.git "$TMPDIR/yay-bin"
cd "$TMPDIR/yay-bin"
makepkg -si --noconfirm
cd /
rm -rf "$TMPDIR"
fi
# Install AUR packages sequentially (simd depends on simapi, monocoque depends on both)
echo "Installing AUR packages..."
yay -S --needed --noconfirm simapi-git
yay -S --needed --noconfirm simd-git
yay -S --needed --noconfirm monocoque-git
# Build simshmbridge (not on AUR)
INSTALL_DIR="$HOME/.local/share/simracing"
mkdir -p "$INSTALL_DIR"
cd "$INSTALL_DIR"
if [ ! -d "simshmbridge" ]; then
echo "Cloning simshmbridge..."
git clone --recurse-submodules "https://github.com/Spacefreak18/simshmbridge.git"
else
echo "simshmbridge already cloned, pulling latest..."
cd simshmbridge && git pull && git submodule update --init --recursive && cd ..
fi
echo "Building simshmbridge..."
cd "$INSTALL_DIR/simshmbridge"
git submodule update --init --recursive
make clean 2>/dev/null || true
make -j$(nproc)
echo ""
echo "All components built successfully inside container."
'
# Install wrapper scripts to ~/.local/bin
log_info "Installing wrapper scripts..."
mkdir -p "$BIN_DIR"
cat > "$BIN_DIR/start-simd" << 'EOF'
#!/usr/bin/env bash
exec distrobox enter --root simracing -- simd "$@"
EOF
chmod +x "$BIN_DIR/start-simd"
cat > "$BIN_DIR/start-monocoque" << 'EOF'
#!/usr/bin/env bash
exec distrobox enter --root simracing -- monocoque play "$@"
EOF
chmod +x "$BIN_DIR/start-monocoque"
cat > "$BIN_DIR/test-monocoque" << 'EOF'
#!/usr/bin/env bash
exec distrobox enter --root simracing -- monocoque test -vv "$@"
EOF
chmod +x "$BIN_DIR/test-monocoque"
log_success "Wrapper scripts installed"
# Setup monocoque config
mkdir -p "$CONFIG_DIR"
if [ ! -f "$CONFIG_DIR/monocoque.config" ]; then
log_info "Creating monocoque config template..."
cat > "$CONFIG_DIR/monocoque.config" << 'EOF'
configs = (
{
sim = "default";
car = "default";
devices = (
{
device = "Serial";
type = "Wheel";
subtype = "MozaNew";
devpath = "/dev/serial/by-id/CHANGE_ME_TO_YOUR_MOZA_BASE";
baud = 115200;
});
}
);
EOF
log_success "Config created at $CONFIG_DIR/monocoque.config"
else
log_info "Config already exists, skipping"
fi
# Done
echo ""
echo "╔══════════════════════════════════════════════════════════════════╗"
echo "║ Installation Complete! ║"
echo "╚══════════════════════════════════════════════════════════════════╝"
echo ""
log_success "Monocoque (distrobox) installed!"
echo ""
echo " Installed:"
echo " AUR packages: simapi-git, simd-git, monocoque-git (in '$CONTAINER' container)"
echo " Bridge: $INSTALL_DIR/simshmbridge/"
echo " Config: $CONFIG_DIR/monocoque.config"
echo " Commands: start-simd, start-monocoque, test-monocoque"
echo ""
echo " Next steps:"
echo " 1. Set your device path in $CONFIG_DIR/monocoque.config"
echo " (replace CHANGE_ME_TO_YOUR_MOZA_BASE with your device from /dev/serial/by-id/)"
echo " 2. Test with: test-monocoque"
echo " 3. Set Steam launch options for your games to start the telemetry stack automatically:"
echo " In Steam > Game Properties > Launch Options, add:"
echo " start-simd & start-monocoque & <bridge_path> %command%"
echo " For example, for Assetto Corsa:"
echo " start-simd & start-monocoque & $INSTALL_DIR/simshmbridge/assets/acbridge.exe %command%"
echo ""
echo " For tools that automate steps 1 and 3 (Moza auto-detection, Steam launch"
echo " option configuration, unified game launcher), visit:"
echo " https://github.com/cescofry/simracing-utilties"
echo ""
@@ -0,0 +1,102 @@
#!/bin/bash
# Monocoque Distrobox Uninstaller
# Removes the distrobox-based monocoque installation.
set -euo pipefail
CONTAINER="simracing"
BIN_DIR="$HOME/.local/bin"
INSTALL_DIR="$HOME/.local/share/simracing"
CONFIG_DIR="$HOME/.config/monocoque"
UDEV_RULE="/etc/udev/rules.d/99-moza-serial.rules"
# Color output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_header() {
echo ""
echo "╔══════════════════════════════════════════════════════════════════╗"
echo "║ Monocoque Distrobox Uninstaller ║"
echo "╚══════════════════════════════════════════════════════════════════╝"
echo ""
}
print_header
log_warn "This will remove:"
echo " - Distrobox container '$CONTAINER'"
echo " - Wrapper scripts ($BIN_DIR/start-simd, start-monocoque, test-monocoque)"
echo " - simshmbridge source ($INSTALL_DIR)"
echo ""
echo "This will NOT remove:"
echo " - Configuration files ($CONFIG_DIR)"
echo " - Convenience scripts (simracing-launch, configure-moza, etc.)"
echo ""
read -rp "Continue with uninstallation? [y/N]: " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
log_info "Uninstallation cancelled"
exit 0
fi
echo ""
# Remove wrapper scripts
log_info "Removing wrapper scripts..."
rm -f "$BIN_DIR/start-simd"
rm -f "$BIN_DIR/start-monocoque"
rm -f "$BIN_DIR/test-monocoque"
log_success "Wrapper scripts removed"
# Remove simshmbridge source
if [ -d "$INSTALL_DIR" ]; then
log_info "Removing simshmbridge source..."
rm -rf "$INSTALL_DIR"
log_success "simshmbridge source removed"
fi
# Remove distrobox container
if distrobox list --root 2>/dev/null | grep -q "$CONTAINER"; then
log_info "Removing distrobox container '$CONTAINER'..."
distrobox rm --root --force "$CONTAINER"
log_success "Container '$CONTAINER' removed"
else
log_info "Container '$CONTAINER' not found, skipping"
fi
# Optionally remove udev rule
if [ -f "$UDEV_RULE" ]; then
echo ""
read -rp "Remove ModemManager udev rule ($UDEV_RULE)? [y/N]: " remove_udev
if [[ "$remove_udev" =~ ^[Yy]$ ]]; then
sudo rm -f "$UDEV_RULE"
sudo udevadm control --reload-rules
sudo udevadm trigger
log_success "udev rule removed"
else
log_info "Keeping udev rule"
fi
fi
echo ""
log_success "Uninstallation complete!"
echo ""
log_info "Config files preserved at $CONFIG_DIR"
echo " To remove those too: rm -rf $CONFIG_DIR"
echo ""
+18 -9
View File
@@ -1,4 +1,4 @@
#dnf install pulseaudio-libs-devel argtable-devel libconfig-devel hidapi-devel libserialport-devel lua-devel libuv-devel libxdg-basedir-devel libxml2-devel #dnf install pulseaudio-libs-devel argtable-devel libconfig-devel hidapi-devel libserialport-devel lua-devel libuv-devel libxdg-basedir-devel libxml2-devel procps-ng-devel
Summary: A device manager for racing sims Summary: A device manager for racing sims
Name: monocoque Name: monocoque
Version: 0.0.5 Version: 0.0.5
@@ -10,30 +10,39 @@ URL: https://spacefreak18.github.io/simapi
Distribution: Fedora Linux Distribution: Fedora Linux
Vendor: spacefreak18 Vendor: spacefreak18
Packager: Paul Jones <paul@spacefreak18.xyz> Packager: Paul Jones <paul@spacefreak18.xyz>
Requires: pulseaudio-libs argtable libconfig hidapi libserialport libuv libxdg-basedir lua-libs libxml2 Requires: pulseaudio-libs argtable libconfig hidapi libserialport libuv libxdg-basedir lua-libs libxml2 procps-ng gtk3 libcurl mesa-libGL
%description %description
A device manager for Racing sims A device manager for Racing sims
# Builds whatever tree has been staged at %{_sourcedir}/monocoque, cloning it
# from upstream master only if nothing is staged. The unconditional clone this
# replaced meant an rpm's contents tracked master rather than the tag being
# built -- so a fix on the branch being released was absent from its own
# release. CI stages the checked-out tree; a bare `rpmbuild -ba` on a
# workstation still works exactly as before.
%prep %prep
rm -rf $RPM_BUILD_DIR/monocoque rm -rf $RPM_BUILD_DIR/monocoque
rm -rf $RPM_SOURCE_DIR/monocoque if [ ! -d $RPM_SOURCE_DIR/monocoque ]; then
cd $RPM_SOURCE_DIR cd $RPM_SOURCE_DIR
git clone https://github.com/spacefreak18/monocoque git clone https://github.com/spacefreak18/monocoque
cd monocoque cd monocoque
git submodule update --init --recursive git submodule update --init --recursive
cd .. cd ..
fi
cp -r $RPM_SOURCE_DIR/monocoque $RPM_BUILD_DIR/ cp -r $RPM_SOURCE_DIR/monocoque $RPM_BUILD_DIR/
%build %build
cd $RPM_BUILD_DIR/monocoque cd $RPM_BUILD_DIR/monocoque
cmake -B build cmake -B build -DBUILD_GUI=on
cd build cd build
make make
%install %install
mkdir -p $RPM_BUILD_ROOT/usr/bin mkdir -p $RPM_BUILD_ROOT/usr/bin
cp $RPM_BUILD_DIR/monocoque/build/monocoque $RPM_BUILD_ROOT/usr/bin/monocoque cp $RPM_BUILD_DIR/monocoque/build/monocoque $RPM_BUILD_ROOT/usr/bin/monocoque
cp $RPM_BUILD_DIR/monocoque/build/gmonocoque $RPM_BUILD_ROOT/usr/bin/gmonocoque
%files %files
/usr/bin/monocoque /usr/bin/monocoque
/usr/bin/gmonocoque
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
"""Generate a third-party notices file from the Flatpak manifest.
A .deb lists only what it actually ships, because its dependencies are separate
packages carrying their own copyright files. A Flatpak or AppImage distributes
those libraries itself, so the notices have to travel with the artifact -- and
for the LGPL ones (libconfig, argtable2, libserialport, orcania, yder) that
includes saying where the corresponding source can be obtained.
The manifest already pins every dependency by URL and sha256, which is exactly
that information, so this is generated from it rather than maintained by hand
where it would drift.
"""
import re
import sys
from pathlib import Path
manifest = Path(sys.argv[1] if len(sys.argv) > 1
else "flatpak/io.github.spacefreak18.monocoque.yml")
text = manifest.read_text()
entries, name = [], None
for line in text.splitlines():
m = re.match(r"^ - name: (\S+)", line)
if m:
name = m.group(1)
entries.append({"name": name, "url": None, "sha256": None, "commit": None, "tag": None})
elif entries:
for key in ("url", "sha256", "commit", "tag"):
k = re.match(rf"^\s+{key}: (\S+)", line)
if k and entries[-1][key] is None:
entries[-1][key] = k.group(1)
out = [
"# Third-party notices",
"",
"This package bundles the libraries below. Each one's own licence text is",
"installed alongside it under `/app/share/licenses/<name>/`.",
"",
"Where a bundled library is under the LGPL, the corresponding source is the",
"exact archive listed here, identified by URL and SHA-256; the same version",
"can be rebuilt from it, and the Flatpak links these libraries dynamically.",
"",
"| Component | Source | Checksum / revision |",
"|---|---|---|",
]
for e in entries:
if not e["url"]:
continue
rev = e["sha256"] or e["commit"] or e["tag"] or ""
out.append(f"| {e['name']} | {e['url']} | `{rev[:16]}{'…' if len(rev) > 16 else ''}` |")
out += [
"",
"Generated by tools/generate-third-party-notices.py from the Flatpak",
"manifest -- do not edit by hand.",
"",
]
print("\n".join(out))
+9
View File
@@ -0,0 +1,9 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=Monocoque
Comment=Launch Monocoque
Exec=gmonocoque
Icon=monocoque
Terminal=false
Categories=Game;Development;
+49
View File
@@ -0,0 +1,49 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100%" height="100%">
<!-- Square Flatpak icon canvas wrapping the real monocoque.svg artwork
(fetched from https://repo.spacefreak18.xyz/monocoque/icons/monocoque.svg,
viewBox 0 0 100 50), vertically centered via translate(0,25) rather
than stretched. Flatpak's export step requires square icons and
appstreamcli additionally requires any desktop-application to have
one at all, confirmed via two failed real CI runs. -->
<g transform="translate(0,25)">
<!-- Main Body / Chassis -->
<path d="M 25 28 C 35 28, 40 22, 55 22 L 72 24 C 76 24, 80 28, 85 28 L 85 34 L 25 34 Z" fill="#E10600" />
<!-- Nose Cone -->
<path d="M 25 28 L 12 30 C 10 30.5, 10 32.5, 12 33 L 25 34 Z" fill="#C00500" />
<!-- Front Wing -->
<rect x="5" y="31" width="10" height="4" rx="1" fill="#1A1A1A" />
<path d="M 12 31 L 12 35" stroke="#FFFFFF" stroke-width="1" />
<!-- Rear Wing -->
<path d="M 82 28 L 84 15 L 92 15 L 90 28 Z" fill="#1A1A1A" />
<rect x="80" y="13" width="14" height="4" rx="0.5" fill="#1A1A1A" />
<!-- Engine Cover / Airbox Intake -->
<path d="M 58 22 C 58 16, 64 16, 66 16 L 70 24 Z" fill="#E10600" />
<ellipse cx="61" cy="19" rx="2" ry="1.5" fill="#1A1A1A" />
<!-- Cockpit / Driver Helmet -->
<path d="M 48 23 C 48 20, 54 20, 54 23 Z" fill="#1A1A1A" />
<circle cx="52" cy="19" r="2.5" fill="#FFFFFF" />
<path d="M 51.5 17.5 L 54.5 18.5 L 53.5 20.5 L 51.5 19.5 Z" fill="#1A1A1A" /> <!-- Visor -->
<!-- Bargeboard / Sidepod Detail -->
<path d="M 38 27 L 55 25 L 58 34 L 36 34 Z" fill="#B00400" />
<!-- Wheels (Tires & Rims) -->
<!-- Rear Wheel -->
<circle cx="78" cy="32" r="11" fill="#1A1A1A" />
<circle cx="78" cy="32" r="5" fill="#4A4A4A" />
<circle cx="78" cy="32" r="2" fill="#D4AF37" />
<!-- Front Wheel -->
<circle cx="22" cy="33" r="9" fill="#1A1A1A" />
<circle cx="22" cy="33" r="4.2" fill="#4A4A4A" />
<circle cx="22" cy="33" r="1.8" fill="#D4AF37" />
<!-- Ground Shadow -->
<ellipse cx="50" cy="44" rx="42" ry="2" fill="#000000" fill-opacity="0.2" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+1
View File
@@ -0,0 +1 @@
0.3.6