Compare commits

...

154 Commits

Author SHA1 Message Date
dsc 330edacab9 lost seed script
2 weeks ago
dsc ec6474a840 new restore heights
3 months ago
dsc 69e1749856 Bump version to 4.1.1
7 months ago
dsc e93488af9b Stopped support for Mac OS as I lack the resources to deal with the various problems that come up during release engineering and/or testing. Qt + wownero + Mac OS is not a happy combination.
7 months ago
dsc a8861c62ea bump minor version 4.1.0
7 months ago
dsc e6651d55ff remove linux activation lol
7 months ago
dsc 9c58913ee2 update onion URL to new server, fix forum listing
7 months ago
dsc 24ff2b7120 Merge pull request 'remove ded explorer and add muchwow.lol' (#110) from wowario/wowlet:remove into master
12 months ago
dsc 7c4f99c85d update submodule, bump version
1 year ago
wowario 7cc5fd880e
add muchwow.lol explorer
1 year ago
wowario ed9edb5ad6
remove ded explorer
1 year ago
dsc c0323ee329 Merge pull request 'bump to beta-6, 3.2.0 - fix ring error, update Dockerfile build' (#109) from ringfix into master
1 year ago
dsc 40493475ba bump to beta-6, 3.2.0 - fix ring error, update Dockerfile build
1 year ago
dsc 4098e8c0e5 Merge pull request 'wowify seed' (#105) from wownero-seed into master
2 years ago
dsc ca234008b9 wowify seed
2 years ago
dsc 2ebb41a371 Merge pull request 'Linux activation' (#104) from activate into master
2 years ago
dsc 6cf4299f78 Linux activation
2 years ago
dsc df0459da69 Merge pull request 'mining: dont exit when binding fails - we dont need to bind to any ports when we just want to mine' (#102) from mining/no-bind into master
2 years ago
dsc 46accb1077 mining: dont exit when binding fails - we dont need to bind to any ports when we just want to mine
2 years ago
dsc 7a91ba5a84 CMake: improve error message
2 years ago
dsc f83ceb2a96 Fix builds for Windows
2 years ago
dsc d332121d7c Include QtQuick (QML) inside the Linux buildbot and get rid of the alpha warning
2 years ago
dsc d151b47895 Merge pull request 'Bunch of changes' (#100) from m1 into master
2 years ago
dsc caa8731410 - Bumped to version 3.1.0
2 years ago
dsc dc3ee66e3b Merge pull request 'Fixes fiat balance, adds fiat columns to history table' (#99) from fix-historical-fiat-prices into master
2 years ago
dsc 289f9ab1d2 The balance fiat display does not 'round to ceiling' anymore, instead introduced some more decimals:
2 years ago
dsc 73747e05a7 Merge pull request 'Contact widget: New contact button' (#98) from new-contact into master
2 years ago
dsc 3051ce5118 This commit introduces a button to create a new contact because when the contact table is full of contacts, you cannot do 'right-click -> New contact'.
2 years ago
dsc fb32fa2fd2 Update README
2 years ago
dsc b3eab6085f Merge pull request 'Settings node selection: Remove double click to connect, it is bugged' (#96) from nodes-remove-double-click into master
2 years ago
dsc 835aecb79d Settings node selection: Remove double click to connect, it is bugged
2 years ago
dsc 6cba5d0487 Merge pull request 'Kryfi as default block explorer' (#95) from kryfi-block-explorer into master
2 years ago
dsc 50b78cee51 Kryfi as default block explorer
2 years ago
dsc 1b6f648a0b Merge pull request 'Solo mining' (#89) from solo-mining into master
2 years ago
dsc 6b2f8f847e Introduce the QML mining interface
2 years ago
dsc 917f8b5812 Make sure wownerod is stopped on application quit
2 years ago
dsc a62fb95fbf Fix Windows build
2 years ago
dsc c3b0d00a72 Move mining tab a few positions
2 years ago
dsc d6dfd678b8 Brings back the mining tab and adds support for solo mining.
2 years ago
dsc 8b215c1e73 Merge pull request 'hide on close' (#90) from hide-on-close into master
2 years ago
dsc 96295a52de Default disabled
2 years ago
dsc ccd0e8e64b hide on close
2 years ago
dsc 3b3ec89306 truncate the yellowpages suffix for contacts
2 years ago
dsc 373fe8e02a Merge pull request 'YellWOWpages integration' (#88) from yellowpages into master
2 years ago
dsc 14d9793193 Automatically populate the contacts widget via [YellWOWPages](yellow.wownero.com/). One may still add custom contacts - they will get saved normally like before.
2 years ago
dsc 65ceab6323 Merge pull request 'Fix building the base image' (#82) from bruh/wowlet:bruh-patch-new-boost-link into master
3 years ago
bruh 6b2118ecf6 Fix building the base image
3 years ago
dsc ca78025735 Merge pull request 'docs: adding Qt build hints for mac os - only v 5.15.1 worked' (#80) from leonardgit6/wowlet:update-docs into master
3 years ago
leonardgit6 c5eb13145f docs: fixing Qt configure command
3 years ago
leonardgit6 946443bf8c docs: adding Qt build hints for mac os
3 years ago
dsc ae33c2f1b0 Merge pull request '3.0.0 beta-4 (bulletproof+, solo-mining)' (#79) from v3 into master
3 years ago
dsc c97c0d597b beta-4 (bulletproof+, solo-mining)
3 years ago
dsc ee3713b16b Merge pull request 'Bump version to v2.1.0 and fix compile' (#75) from dsc/wowlet:bump-version3 into master
3 years ago
dsc ae39e71061 Bump version to v2.1.0 and fix compile
3 years ago
dsc 7e5cac9fa3 Merge pull request 'Improve QR code detection for VR' (#73) from dsc/wowlet:improve-qr-code-detection into master
3 years ago
dsc b61b2b1630 Improve QR code detection
3 years ago
wowario 1b1d1db14d Merge pull request 'Failsafe kill feature' (#72) from dsc/wowlet:killme into master
3 years ago
dsc 81ec0183ea Failsafe kill feature; to be used in case of an exploit against wowlet and/or a bug that results in loss of funds
3 years ago
wowario f5046cea54 Merge pull request 'Custom SuchWow donation tip amount' (#71) from dsc/wowlet:suchwow-custom-donation-amount into master
3 years ago
dsc 624f13b2d9 Custom SuchWow donation tip amount
3 years ago
wowario 0d502f0e45 Merge pull request 'Warn when there is a new version available' (#70) from dsc/wowlet:warn-on-outdated-version into master
3 years ago
dsc 6549ca4e1d Warn when there is a new version available
3 years ago
dsc 37578dde7b Merge pull request 'add imgs to assets list' (#65) from wowario/wowlet:banner1 into master
3 years ago
wowario 225ae1233e
add imgs to assets list
3 years ago
wowario d8cb29c4d4 Merge pull request 'Forum post widget showing latest from forum.wownero.com' (#64) from dsc/wowlet:forum-posts-homewidget into master
3 years ago
wowario 502785f233 Merge pull request 'New header image for about dialog' (#63) from dsc/wowlet:image-about-page into master
3 years ago
wowario e3cf87906b Merge pull request 'Fix config option reddit' (#62) from dsc/wowlet:fix-settings-reddit into master
3 years ago
wowario 5c3821007d Merge pull request 'Optionally (only) hide fiat balance' (#61) from dsc/wowlet:hide-fiat-balance into master
3 years ago
dsc c02e084dcf Forum post widget showing latest from forum.wownero.com
3 years ago
dsc 85d4e0ac6d New header image for about dialog
3 years ago
dsc fbca9c9340 Fix config option reddit
3 years ago
dsc d9d0ac1830 Optionally (only) hide fiat balance
3 years ago
wowario 3adb2a6fe7 Merge pull request 'Show random banner on each Wizard window spawn' (#58) from dsc/wowlet:random-banner into master
3 years ago
wowario 742ad82b88 Merge pull request 'Redesigned icon by cisme' (#59) from dsc/wowlet:redesigned-icon into master
3 years ago
dsc a1ce8f866a Merge pull request 'mo imgs' (#60) from wowario/wowlet:banners into master
3 years ago
wowario 24e8942ac5
mo imgs
3 years ago
dsc 3163d68e9e Show random banner on each Wizard window spawn
3 years ago
dsc a6e1a6877f Redesigned icon by cisme
3 years ago
wowario ac1ed1873a Merge pull request 'Include Satoshi ticker, include Monero ticker, and introduce 24h pct change for WOW' (#57) from dsc/wowlet:refactor-tickers into master
3 years ago
dsc 2ccefe0883 Include Satoshi ticker, include Monero ticker, and introduce 24h pct change for WOW
3 years ago
wowario 06f09e1f2e Merge pull request 'Fixes connect bug (via context menu) for custom nodes' (#56) from dsc/wowlet:connect-bug into master
3 years ago
dsc c3e9fbb25a Fixes connect bug (via context menu) for custom nodes
3 years ago
wowario 096b28318c Merge pull request 'SuchWow improvements' (#55) from dsc/wowlet:suchwow-improvements into master
3 years ago
wowario 562d071c62 Merge pull request 'Detect wakeup from hibernate, re-establish websocket connection' (#54) from dsc/wowlet:hibernate-timer into master
3 years ago
dsc 474d4b1994 - only download thumbnails for the overview. Download the large format images later - when requesting to view the image. This saves bandwidth.
3 years ago
dsc 49b55768f7 Detect wakeup from hibernate, re-establish websocket connection
3 years ago
dsc 640a4d72e0 Merge pull request 'Update .deb builder and include XMRig functionality by default on Linux' (#53) from dsc/wowlet:update-deb-and-xmrig into master
3 years ago
dsc 0e37f97aa2 Update .deb builder and including XMRig functionality by default for Linux
3 years ago
dsc 499ad4a3aa Merge pull request 'Android app proof-of-concept' (#48) from dsc/wowlet:android into master
3 years ago
dsc 8b5bdc4c6a Initial Android app proof-of-concept
3 years ago
dsc cfee938516 Prepare Wowlet codebase for an Android app
3 years ago
dsc c024323eab Prepare CMake for Android deployment and development
3 years ago
dsc a2ad4692a3 Dockerfile for Android compiles. Most of this work is done by xiphon of the Monero GUI team.
3 years ago
dsc c50a341527 Merge pull request 'Bump version' (#47) from dsc/wowlet:bump-version2 into master
3 years ago
dsc a68ebf3564 Bump version
3 years ago
dsc 98b1982157 Merge pull request 'shuffle welcome banner' (#46) from dsc/wowlet:welcome into master
3 years ago
wowario c86228a404 remove unused imgs
3 years ago
wowario a389589fbb add more welcome imgs
3 years ago
wowario 392e9b99cf rename banner folder
3 years ago
wowario 51c7422f6e shuffle welcome banner
3 years ago
dsc 7c8d070ab5 Merge pull request 'move package stuff to contrib folder' (#42) from wowario/wowlet:contrib into master
3 years ago
dsc aa89690788 Merge pull request 'move docs to docs folder' (#41) from wowario/wowlet:docs into master
3 years ago
dsc c82cad87fa Merge pull request 'OpenVR support plus some fixes' (#45) from dsc/wowlet:openvr-qml-windows into master
3 years ago
dsc a5f3b915e5 get rekt
3 years ago
dsc 395cbcd9db Fixes builds for Linux
3 years ago
dsc ee247cba8a Disable saving of QR code for now
3 years ago
dsc bcc0aeeaa9 Change websocket server
3 years ago
dsc de5bea9cdf Fix balance display in streamer mode
3 years ago
dsc d846790905 Fixes linking against libz
3 years ago
dsc 13331ee5e7 QR code feature beta and streamer mode
3 years ago
dsc 5bb95053fb Fixes DPI scaling and added quirc submodule
3 years ago
dsc bd38e5e1d8 Refactor numpad code, transfer screen
3 years ago
dsc 8b1250030b Ready for beta
3 years ago
dsc c0cb90bf79 Development
3 years ago
dsc c3723ac58a Initial QML interface code
3 years ago
dsc aad58b1c83 Vendoring OpenVR
3 years ago
dsc 96034902d1 Initial code for supporting an alternative QtQuick based UI for OpenVR
3 years ago
dsc e918955210 Prepare build system for OpenVR and QtQuick (QML)
3 years ago
wowario 5f746999c2 Merge pull request 'rename to wow' (#40) from wowario/wowlet:rename1 into master
3 years ago
wowario 1c51407109
move package stuff to contrib folder
3 years ago
wowario 5f402464ad
move docs to docs folder
3 years ago
wowario 7a14d53ba0
rename to wow
3 years ago
wowario ca72462125 Merge pull request 'defeather files' (#38) from wowario/wowlet:feather1 into master
3 years ago
wowario 11048f96fa
defeather files
3 years ago
wowario 144c19fab1 Merge pull request 'remove ded file' (#37) from wowario/wowlet:file into master
3 years ago
wowario 32bb391c2e
remove ded file
3 years ago
wowario d23ffd1972 Merge pull request 'rename freather to wowlet' (#36) from wowario/wowlet:wow-rename into master
3 years ago
wowario 9a75c5a986
defeather
3 years ago
wowario 82ac1c2daf Merge pull request 'update .desktop file name' (#32) from wowario/wowlet:desktop into master
3 years ago
wowario 6a256bb1a0
update .desktop file name
3 years ago
wowario fafc8e4d7d Merge pull request 'initialize submodules before checking hashes' (#31) from wowario/wowlet:sub into master
3 years ago
wowario fdc7e348f0
initialize submodules before checking hashes
3 years ago
wowario 2bdef17e18 Merge pull request 'add man page' (#29) from wowario/wowlet:man into master
3 years ago
wowario 718741b036
add man page
3 years ago
wowario 8dc0a5bfaf Merge pull request '--background flag for wowlet - start websocket server' (#26) from dsc/wowlet:wsserver into master
3 years ago
wowario 6b0ea884cb Merge pull request 'update deb build script' (#28) from wowario/wowlet:deb-script into master
3 years ago
wowario a488a24514 Merge pull request 'clean up appicons' (#27) from wowario/wowlet:mo-icons into master
3 years ago
wowario fb3fc3bd8d
update deb build script
3 years ago
dsc 8968a8cbce This commit introduces a websocket server via the `--daemon` argument.
3 years ago
wowario a7d2c67e67
clean up appicons
3 years ago
wowario 40a575a5d6 Merge pull request 'add wow theme' (#25) from wowario/wowlet:theme into master
3 years ago
wowario f5b23c2e7e
add wow theme
3 years ago
wowario 7993442a34 Merge pull request 'fix .icns icon' (#24) from wowario/wowlet:icons into master
3 years ago
wowario 8f4acf3b60
fix .icns icon
3 years ago
wowario c59627b661 Merge pull request 'wow icons for mac' (#23) from wowario/wowlet:icons into master
3 years ago
wowario 705426d489
wow icons for mac
3 years ago
wowario 81ad7bdb46 Merge pull request 'minor touch ups' (#20) from wowario/wowlet:popup into master
continuous-integration/drone/push Build is passing Details
3 years ago
wowario f110cad24b Merge pull request 'add deb build script [CI SKIP]' (#21) from wowario/wowlet:deb into master
3 years ago
wowario f989d2ae83 Merge pull request '[drone] only build windows when tagged' (#22) from wowario/wowlet:trigger into master
continuous-integration/drone/push Build was killed Details
3 years ago
wowario 686b37e630
[drone] only build windows when tagged
continuous-integration/drone/pr Build is passing Details
3 years ago
wowario 1265307bd0
add deb build script [CI SKIP]
3 years ago
wowario 955167d37f
update issues url
continuous-integration/drone/pr Build is passing Details
3 years ago
wowario 1f83c35103
drop ded node
3 years ago
wowario a517fedd96
correct typo
3 years ago

@ -26,9 +26,9 @@ volumes:
---
kind: pipeline
type: docker
name: windows-build
name: windows-deploy
steps:
- name: windows-build
- name: windows-deploy
image: wowlet/wowlet-win:v0.1
volumes:
- name: cache
@ -46,5 +46,9 @@ volumes:
- name: files
host:
path: /home/wow/wowlet_windows
trigger:
branch:
- master
event:
- tag
...

6
.gitignore vendored

@ -8,8 +8,8 @@ build/*
CMakeCache.txt
CMakeFiles
cmake_install.cmake
feather_autogen/
feather.cbp
wowlet_autogen/
wowlet.cbp
src/tor/*
!src/tor/.gitkeep
src/config-feather.h
src/config-wowlet.h

10
.gitmodules vendored

@ -1,7 +1,9 @@
[submodule "contrib/KDMacTouchBar"]
path = contrib/KDMacTouchBar
url = https://github.com/KDAB/KDMacTouchBar.git
[submodule "monero"]
path = monero
url = https://git.wownero.com/wownero/wownero
branch = wowlet
[submodule "contrib/quirc"]
path = contrib/quirc
url = https://github.com/dlbeer/quirc.git
[submodule "wownero"]
path = wownero
url = https://git.wownero.com/wownero/wownero.git

@ -1,108 +0,0 @@
## Buildbot builds
The docker build bins can be found here: https://build.wownero.org/files/
## Docker static builds
Static builds via Docker are done in 3 steps:
1. Cloning this repository (+submodules)
2. Creating a base Docker image
3. Using the base image to compile a build
### Linux (reproducible)
The docker image for reproducible Linux static builds uses Ubuntu 16.04 and compiles the required libraries statically
so that the resulting Feather binary is static. For more information, check the Dockerfile: `Dockerfile`.
#### 1. Clone
```bash
git clone --branch master --recursive https://git.wownero.com/wowlet/wowlet.git
cd wowlet
```
Replace `master` with the desired version tag (e.g. `beta-4`) to build the release binary.
#### 2. Base image
```bash
docker build --tag wowlet:linux --build-arg THREADS=4 .
```
Building the base image takes a while. You only need to build the base image once.
#### 3. Build
##### Standalone binary
```bash
docker run --rm -it -v $PWD:/wowlet -w /wowlet wowlet:linux sh -c 'make release-static -j4'
```
If you're re-running a build make sure to `rm -rf build/` first.
The resulting binary can be found in `build/bin/wowlet`.
##### AppImage
First create the standalone binary using the Docker command in the previous step.
```bash
docker run --rm -it -v $PWD:/wowlet -w /wowlet wowlet:linux contrib/build-appimage.sh
```
### Windows (reproducible)
#### 1. Clone
```bash
git clone --branch master --recursive https://git.wownero.com/wowlet/wowlet.git
cd wowlet
```
Replace `master` with the desired version tag (e.g. `beta-4`) to build the release binary.
#### 2. Base image
```bash
docker build -f Dockerfile.windows --tag wowlet:win --build-arg THREADS=4 .
```
Building the base image takes a while. You only need to build the base image once.
#### 3. Build
```bash
docker run --rm -it -v $PWD:/wowlet -w /wowlet wowlet:win sh -c 'make depends root=/depends target=x86_64-w64-mingw32 tag=win-x64 -j4'
```
If you're re-running a build make sure to `rm -rf build/` first.
The resulting binary can be found in `build/x86_64-w64-mingw32/release/bin/wowlet.exe`.
## macOS
For MacOS it's easiest to leverage [brew](https://brew.sh) to install the required dependencies.
```bash
HOMEBREW_OPTFLAGS="-march=core2" HOMEBREW_OPTIMIZATION_LEVEL="O0" \
brew install boost zmq openssl libpgm miniupnpc libsodium expat libunwind-headers protobuf libgcrypt qrencode ccache cmake pkgconfig git
```
Clone the repository.
```bash
git clone --recursive https://git.wownero.com/wowlet/wowlet.git
```
Get the latest LTS from here: https://www.qt.io/offline-installers and install.
Build Feather.
```bash
CMAKE_PREFIX_PATH=~/Qt5.15.1/5.15.1/clang_64 make mac-release
```
The resulting Mac OS application can be found `build/bin/wowlet.app` and will **not** have Tor embedded.

@ -1,20 +1,21 @@
cmake_minimum_required(VERSION 3.13)
project(feather)
project(wowlet)
message(STATUS "Initiating compile using CMake ${CMAKE_VERSION}")
set(THREADS_PREFER_PTHREAD_FLAG ON)
set(VERSION_MAJOR "0")
set(VERSION_MAJOR "4")
set(VERSION_MINOR "1")
set(VERSION_REVISION "0")
set(VERSION "beta-1")
set(VERSION_REVISION "1")
set(VERSION "beta-8")
option(FETCH_DEPS "Download dependencies if they are not found" ON)
option(XMRIG "Include XMRig module" ON)
option(TOR_BIN "Path to Tor binary to embed inside Feather" OFF)
option(OPENVR "Include OpenVR support")
option(ANDROID "Android deployment")
option(ANDROID_DEBUG "View the Android app on desktop")
option(TOR_BIN "Path to Tor binary to embed inside WOWlet")
option(STATIC "Link libraries statically, requires static Qt")
option(USE_DEVICE_TREZOR "Trezor support compilation" OFF)
option(DONATE_BEG "Prompt donation window every once in a while" ON)
option(USE_DEVICE_TREZOR "Trezor support compilation")
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake")
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
@ -24,26 +25,20 @@ include(FindCcache)
include(CheckIncludeFile)
include(CheckSymbolExists)
if(DEBUG)
set(CMAKE_VERBOSE_MAKEFILE ON)
endif()
# Update and init submodules by default
if(NOT MANUAL_SUBMODULES)
find_package(Git)
if(GIT_FOUND)
message(STATUS "Initializing submodules")
execute_process(COMMAND git "submodule" "update" "--init" "--recursive")
endif()
endif()
set(WOWNERO_HEAD "f611d5c9e32bc62f1735f6571b0bdb95cc020531")
set(WOWNERO_HEAD "a21819cc22587e16af00e2c3d8f70156c11310a0")
set(BUILD_GUI_DEPS ON)
set(ARCH "x86-64")
set(BUILD_64 ON)
set(BUILD_64 ON CACHE BOOL "Build 64-bit binaries")
set(INSTALL_VENDORED_LIBUNBOUND ${STATIC})
set(USE_SINGLE_BUILDDIR ON)
# Are we in debug mode?
set(_CMAKE_BUILD_TYPE "")
string(TOUPPER "${CMAKE_BUILD_TYPE}" _CMAKE_BUILD_TYPE)
if("${_CMAKE_BUILD_TYPE}" STREQUAL "DEBUG")
set(DEBUG ON)
set(CMAKE_VERBOSE_MAKEFILE ON)
endif()
check_include_file(sys/prctl.h HAVE_SYS_PRCTL_H)
check_symbol_exists(prctl "sys/prctl.h" HAVE_PRCTL)
@ -56,7 +51,7 @@ if(STATIC)
# manually set the unbound submodule the right commit that has the fix.
# This only works with -DMANUAL_SUBMODULES=1
message(STATUS "applying unbound static build fix contrib/unbound_static.patch")
execute_process(COMMAND bash -c "git -C ${CMAKE_SOURCE_DIR}/monero/external/unbound apply ${CMAKE_SOURCE_DIR}/contrib/unbound_static.patch")
execute_process(COMMAND bash -c "git -C ${CMAKE_SOURCE_DIR}/wownero/external/unbound apply ${CMAKE_SOURCE_DIR}/contrib/unbound_static.patch")
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME ON)
@ -89,31 +84,18 @@ function (add_linker_flag_if_supported flag var)
endfunction()
find_package(Git)
if(GIT_FOUND)
execute_process(COMMAND git rev-parse "HEAD" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/monero OUTPUT_VARIABLE _WOWNERO_HEAD OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT _WOWNERO_HEAD STREQUAL WOWNERO_HEAD)
message(FATAL_ERROR "[submodule] Monero HEAD was at ${_WOWNERO_HEAD} but should be at ${WOWNERO_HEAD}")
else()
message(STATUS "[submodule] Monero HEAD @ ${WOWNERO_HEAD}")
endif()
endif()
add_subdirectory(monero)
set_property(TARGET wallet_merged PROPERTY FOLDER "monero")
get_directory_property(ARCH_WIDTH DIRECTORY "monero" DEFINITION ARCH_WIDTH)
get_directory_property(UNBOUND_LIBRARY DIRECTORY "monero" DEFINITION UNBOUND_LIBRARY)
add_subdirectory(wownero)
get_directory_property(ARCH_WIDTH DIRECTORY "wownero" DEFINITION ARCH_WIDTH)
include(CMakePackageConfigHelpers)
include(VersionMonero)
include(VersionFeather)
include(VersionWowlet)
include_directories(${EASYLOGGING_INCLUDE})
link_directories(${EASYLOGGING_LIBRARY_DIRS})
# OpenSSL
if(APPLE AND NOT OPENSSL_ROOT_DIR)
execute_process(COMMAND brew --prefix openssl OUTPUT_VARIABLE OPENSSL_ROOT_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
find_package(OpenSSL REQUIRED)
message(STATUS "OpenSSL: Version ${OPENSSL_VERSION}")
message(STATUS "OpenSSL: include dir at ${OPENSSL_INCLUDE_DIR}")
@ -126,49 +108,28 @@ message(STATUS "libsodium: libraries at ${SODIUM_LIBRARY}")
# HIDApi
set(HIDAPI_FOUND OFF)
# Unbound
find_package(Unbound REQUIRED)
# QrEncode
find_package(QREncode REQUIRED)
# Tevador 14 word Monero seed
find_package(monero-seed CONFIG)
if(NOT monero-seed_FOUND)
if(FETCH_DEPS)
FetchContent_Declare(monero-seed
GIT_REPOSITORY https://git.wownero.com/feather/monero-seed.git)
FetchContent_GetProperties(monero-seed)
if(NOT monero-seed_POPULATED)
message(STATUS "Fetching monero-seed")
FetchContent_Populate(monero-seed)
add_subdirectory(${monero-seed_SOURCE_DIR} ${monero-seed_BINARY_DIR})
endif()
add_library(monero-seed::monero-seed ALIAS monero-seed)
else()
message(FATAL_ERROR "monero-seed was not installed and fetching deps is disabled")
endif()
endif()
# Tevador 14 word seed (https://git.wownero.com/wowlet/wownero-seed)
find_package(wownero-seed CONFIG REQUIRED)
# Boost
if(DEBUG)
set(Boost_DEBUG ON)
endif()
if(APPLE AND NOT BOOST_ROOT)
execute_process(COMMAND brew --prefix boost OUTPUT_VARIABLE BOOST_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
if(MINGW)
set(Boost_THREADAPI win32)
endif()
find_package(Boost 1.58 REQUIRED COMPONENTS
system
filesystem
thread
date_time
chrono
regex
serialization
program_options
locale)
if(UNIX AND NOT APPLE)
set(_BOOST_COMPONENTS system filesystem thread date_time chrono regex serialization program_options locale)
find_package(Boost 1.58 REQUIRED COMPONENTS ${_BOOST_COMPONENTS})
if(UNIX AND NOT ANDROID)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
# https://github.com/monero-project/monero-gui/issues/3142#issuecomment-705940446
set(CMAKE_SKIP_RPATH ON)
@ -190,40 +151,57 @@ if("$ENV{DRONE}" STREQUAL "true")
message(STATUS "We are inside a static compile with Drone CI")
endif()
# To build Feather with embedded (and static) Tor, pass CMake -DTOR_BIN=/path/to/tor
if(TOR_BIN)
if(APPLE)
execute_process(COMMAND bash -c "touch ${CMAKE_CURRENT_SOURCE_DIR}/src/tor/libevent-2.1.7.dylib")
if(UNIX)
if(NOT CMAKE_PREFIX_PATH AND DEFINED ENV{CMAKE_PREFIX_PATH})
message(STATUS "Using CMAKE_PREFIX_PATH environment variable: '$ENV{CMAKE_PREFIX_PATH}'")
set(CMAKE_PREFIX_PATH $ENV{CMAKE_PREFIX_PATH})
endif()
execute_process(COMMAND bash -c "${TOR_BIN} --version --quiet" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE out RESULT_VARIABLE ret)
if (ret EQUAL "0")
set(TOR_VERSION "${out}")
if(APPLE AND NOT CMAKE_PREFIX_PATH)
execute_process(COMMAND brew --prefix qt5 OUTPUT_VARIABLE QT5_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
list(APPEND CMAKE_PREFIX_PATH ${QT5_DIR})
endif()
message(STATUS "${TOR_VERSION}")
configure_file("cmake/config-feather.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/src/config-feather.h")
endif()
# on the buildbot Tor is baked into the image
if(TOR_BIN)
# To build WOWlet with embedded & static Tor, pass CMake -DTOR_BIN=/path/to/tor_executable
# The CMake below will copy the Tor binary into src/assets/exec
#
# For release:
# ## Linux / Window
# on the buildbot(s) Tor is baked into the image
# - linux: See `Dockerfile`
# - windows: See `Dockerfile.windows`
# - macos: taken from Tor Browser official release
if(REPRODUCIBLE) # Always copy Tor when doing a reproducible build to prevent old versions from getting included
set(TOR_COPY_CMD "cp ${TOR_BIN} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/tor")
else()
set(TOR_COPY_CMD "cp -u ${TOR_BIN} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/tor")
if(NOT EXISTS "${TOR_BIN}")
message(FATAL_ERROR "TOR_BIN is set, but file does not exist: '${TOR_BIN}'")
endif()
message(STATUS "${TOR_COPY_CMD}")
# copy the Tor executable over
set(TOR_COPY_CMD "cp ${TOR_BIN} ${CMAKE_CURRENT_SOURCE_DIR}/src/assets/exec/tor")
message(STATUS "Tor cmd: ${TOR_COPY_CMD}")
execute_process(COMMAND bash -c "${TOR_COPY_CMD}" RESULT_VARIABLE ret)
if(ret EQUAL "1")
message(FATAL_ERROR "Tor copy failure: ${TOR_COPY_CMD}")
endif()
# get Tor version while we're at it
if(NOT TOR_VERSION)
execute_process(COMMAND bash -c "${TOR_BIN} --version --quiet | head -n1" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE out RESULT_VARIABLE ret)
if (ret EQUAL "0")
set(TOR_VERSION "${out}")
endif()
endif()
message(STATUS "Tor version: ${TOR_VERSION}")
configure_file("cmake/config-wowlet.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/src/config-wowlet.h")
message(STATUS "Embedding Tor binary at ${TOR_BIN}")
else()
message(STATUS "Skipping Tor inclusion because -DTOR_BIN=Off")
endif()
if(MINGW)
find_package(Iconv REQUIRED)
string(REGEX MATCH "^[^/]:/[^/]*" msys2_install_path "${CMAKE_C_COMPILER}")
message(STATUS "MSYS location: ${msys2_install_path}")
set(CMAKE_INCLUDE_PATH "${msys2_install_path}/mingw${ARCH_WIDTH}/include")
@ -248,8 +226,6 @@ if(MINGW)
else()
set(ICU_LIBRARIES icuio icuin icuuc icudt icutu iconv)
endif()
elseif(APPLE)
set(EXTRA_LIBRARIES "-framework AppKit")
elseif(OPENBSD)
set(EXTRA_LIBRARIES "")
elseif(FREEBSD)
@ -259,30 +235,13 @@ elseif(DRAGONFLY)
set(EXTRA_LIBRARIES execinfo ${COMPAT})
elseif(CMAKE_SYSTEM_NAME MATCHES "(SunOS|Solaris)")
set(EXTRA_LIBRARIES socket nsl resolv)
elseif(NOT MSVC AND NOT DEPENDS)
elseif(NOT MSVC AND NOT DEPENDS AND NOT ANDROID)
find_library(RT rt)
set(EXTRA_LIBRARIES ${RT})
endif()
list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS})
if(APPLE)
include_directories(SYSTEM /usr/include/malloc)
if(POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -fvisibility=default -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default -DGTEST_HAS_TR1_TUPLE=0")
endif()
if (APPLE AND NOT IOS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -fvisibility=default -std=c++11")
endif()
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default -DGTEST_HAS_TR1_TUPLE=0")
endif()
# warnings
# @TODO: enable these 2 for migration to Qt 6
#add_c_flag_if_supported(-Werror C_SECURITY_FLAGS)
@ -317,12 +276,7 @@ if (NOT (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_C_COMPILER_VERSION VER
endif()
# linker
if (APPLE)
add_linker_flag_if_supported(-Wl,-bind_at_load LD_SECURITY_FLAGS)
add_linker_flag_if_supported(-Wl,-dead_strip LD_SECURITY_FLAGS)
add_linker_flag_if_supported(-Wl,-dead_strip_dylibs LD_SECURITY_FLAGS)
endif()
if (NOT APPLE AND NOT (WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "GNU"))
if (NOT (WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "GNU"))
# Windows binaries die on startup with PIE when compiled with GCC
add_linker_flag_if_supported(-pie LD_SECURITY_FLAGS)
endif()
@ -352,6 +306,11 @@ if(STATIC)
endif()
endif()
if(LINUX_ACTIVATION)
find_package(Cairo REQUIRED)
find_package(Xfixes REQUIRED)
endif()
# With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that
# is fixed in the code (Issue #847), force compiler to be conservative.
add_c_flag_if_supported(-fno-strict-aliasing C_SECURITY_FLAGS)
@ -368,8 +327,21 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 ${C_SECURITY_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${CXX_SECURITY_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${STATIC_FLAGS}")
if(APPLE)
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/contrib/KDMacTouchBar")
if(OPENVR)
# Add contrib/openvr as library
add_definitions(-DVR_API_PUBLIC)
add_definitions(-DOPENVR_BUILD_STATIC) # is this needed?
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/contrib/openvr")
endif()
if(WITH_SCANNER)
add_library(quirc STATIC
contrib/quirc/lib/decode.c
contrib/quirc/lib/identify.c
contrib/quirc/lib/quirc.c
contrib/quirc/lib/version_db.c
)
target_include_directories(quirc PUBLIC contrib/quirc/lib)
endif()
add_subdirectory(src)

@ -17,7 +17,6 @@ RUN wget https://www.openssl.org/source/openssl-1.1.1i.tar.gz && \
cd openssl-1.1.1i && \
./config no-shared no-dso --prefix=/usr/local/openssl && \
make -j$THREADS && \
make test && \
make -j$THREADS install_sw && \
rm -rf $(pwd)
@ -66,10 +65,10 @@ RUN git clone -b tor-0.4.5.5-rc --depth 1 https://git.torproject.org/tor.git &&
rm -rf $(pwd) && \
strip -s -D /usr/local/tor/bin/tor
FROM ubuntu:16.04
FROM ubuntu:18.04
ARG THREADS=1
ARG QT_VERSION=5.15.2
ARG QT_VERSION=v5.15.2
ENV CFLAGS="-fPIC"
ENV CPPFLAGS="-fPIC"
@ -95,7 +94,7 @@ RUN apt-get update && \
# libusb
libudev-dev \
# fontconfig
autopoint gettext gperf libpng12-dev \
autopoint gettext gperf libpng-dev \
# libxcb
libpthread-stubs0-dev \
# xorgproto
@ -161,7 +160,7 @@ RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
cd libxcb-util && \
git reset --hard acf790d7752f36e450d476ad79807d4012ec863b && \
git submodule init && \
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git clone https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
./autogen.sh --enable-shared --disable-static && \
make -j$THREADS && \
@ -172,7 +171,7 @@ RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
cd libxcb-image && \
git reset --hard d882052fb2ce439c6483fce944ba8f16f7294639 && \
git submodule init && \
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git clone https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
./autogen.sh --enable-shared --disable-static && \
make -j$THREADS && \
@ -183,7 +182,7 @@ RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
cd libxcb-keysyms && \
git reset --hard 0e51ee5570a6a80bdf98770b975dfe8a57f4eeb1 && \
git submodule init && \
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git clone https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
./autogen.sh --enable-shared --disable-static && \
make -j$THREADS && \
@ -194,7 +193,7 @@ RUN git clone -b 0.3.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
cd libxcb-render-util && \
git reset --hard 0317caf63de532fd7a0493ed6afa871a67253747 && \
git submodule init && \
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git clone https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
./autogen.sh --enable-shared --disable-static && \
make -j$THREADS && \
@ -205,7 +204,7 @@ RUN git clone -b 0.4.1 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
cd libxcb-wm && \
git reset --hard 24eb17df2e1245885e72c9d4bbb0a0f69f0700f2 && \
git submodule init && \
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git clone https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
./autogen.sh --enable-shared --disable-static && \
make -j$THREADS && \
@ -262,7 +261,7 @@ RUN git clone -b release-64-2 --depth 1 https://github.com/unicode-org/icu && \
make -j$THREADS install && \
rm -rf $(pwd)
RUN wget https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.gz && \
RUN wget https://boostorg.jfrog.io/artifactory/main/release/1.73.0/source/boost_1_73_0.tar.gz && \
echo "9995e192e68528793755692917f9eb6422f3052a53c5e13ba278a228af6c7acf boost_1_73_0.tar.gz" | sha256sum -c && \
tar -xzf boost_1_73_0.tar.gz && \
rm boost_1_73_0.tar.gz && \
@ -278,22 +277,46 @@ RUN wget https://www.openssl.org/source/openssl-1.1.1i.tar.gz && \
cd openssl-1.1.1i && \
./config no-shared no-dso --prefix=/usr/local/openssl && \
make -j$THREADS && \
make test && \
make -j$THREADS install_sw && \
rm -rf $(pwd)
RUN wget https://github.com/libexpat/libexpat/releases/download/R_2_4_8/expat-2.4.8.tar.bz2 && \
echo "a247a7f6bbb21cf2ca81ea4cbb916bfb9717ca523631675f99b3d4a5678dcd16 expat-2.4.8.tar.bz2" | sha256sum -c && \
tar -xf expat-2.4.8.tar.bz2 && \
rm expat-2.4.8.tar.bz2 && \
cd expat-2.4.8 && \
./configure --enable-static --disable-shared --prefix=/usr/local/expat/ && \
make -j$THREADS && \
make -j$THREADS install && \
rm -rf $(pwd)
RUN wget https://www.nlnetlabs.nl/downloads/unbound/unbound-1.16.2.tar.gz && \
echo "2e32f283820c24c51ca1dd8afecfdb747c7385a137abe865c99db4b257403581 unbound-1.16.2.tar.gz" | sha256sum -c && \
tar -xzf unbound-1.16.2.tar.gz && \
rm unbound-1.16.2.tar.gz && \
cd unbound-1.16.2 && \
./configure --disable-shared --enable-static --without-pyunbound --with-libexpat=/usr/local/expat/ --with-ssl=/usr/local/openssl/ --with-libevent=no --without-pythonmodule --disable-flto --with-pthreads --with-libunbound-only --with-pic && \
make -j$THREADS && \
make -j$THREADS install && \
rm -rf $(pwd)
RUN rm /usr/lib/x86_64-linux-gnu/libX11.a && \
rm /usr/lib/x86_64-linux-gnu/libXext.a && \
rm /usr/lib/x86_64-linux-gnu/libX11-xcb.a && \
git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \
cd qt5 && \
git clone git://code.qt.io/qt/qtbase.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtdeclarative.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtgraphicaleffects.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtimageformats.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtmultimedia.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtquickcontrols.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtquickcontrols2.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtsvg.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qttools.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qttranslations.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtx11extras.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtxmlpatterns.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtwebsockets.git -b ${QT_VERSION} --depth 1 && \
sed -ri s/\(Libs:.*\)/\\1\ -lexpat/ /usr/local/lib/pkgconfig/fontconfig.pc && \
sed -ri s/\(Libs:.*\)/\\1\ -lz/ /usr/local/lib/pkgconfig/freetype2.pc && \
@ -301,18 +324,14 @@ RUN rm /usr/lib/x86_64-linux-gnu/libX11.a && \
sed -i s/\\/usr\\/X11R6\\/lib64/\\/usr\\/local\\/lib/ qtbase/mkspecs/linux-g++-64/qmake.conf && \
OPENSSL_LIBS="-lssl -lcrypto -lpthread -ldl" \
./configure --prefix=/usr -platform linux-g++-64 -opensource -confirm-license -release -static -no-avx \
-no-opengl -qpa xcb --xcb -xcb-xlib -feature-xlib -openssl-linked -I /usr/local/openssl/include \
-L /usr/local/openssl/lib -system-freetype -fontconfig -glib \
-no-dbus -no-sql-sqlite -no-use-gold-linker -no-kms \
-opengl desktop -qpa xcb -xcb -xcb-xlib -feature-xlib -system-freetype -fontconfig -glib \
-no-dbus -no-feature-qml-worker-script -no-linuxfb -no-openssl -no-sql-sqlite -no-kms -no-use-gold-linker \
-qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \
-skip qt3d -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d \
-skip qtdoc -skip qtquickcontrols -skip qtquickcontrols2 -skip qtspeech -skip qtgamepad \
-skip qtlocation -skip qtmacextras -skip qtnetworkauth -skip qtpurchasing -optimize-size \
-skip qtdoc -skip qtgamepad -skip qtlocation -skip qtmacextras -skip qtnetworkauth -skip qtpurchasing \
-skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qttools \
-skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebview \
-skip qtwinextras -skip qtx11extras -skip gamepad -skip serialbus -skip location -skip webengine \
-skip qtdeclarative \
-no-feature-cups -no-feature-ftp -no-feature-pdf -no-feature-animation \
-nomake examples -nomake tests -nomake tools && \
make -j$THREADS && \
make -j$THREADS install && \
@ -392,9 +411,9 @@ RUN git clone -b v4.0.2 --depth 1 https://github.com/fukuchi/libqrencode.git &&
make -j$THREADS install && \
rm -rf $(pwd)
RUN git clone https://git.wownero.com/feather/monero-seed.git && \
cd monero-seed && \
git reset --hard 4674ef09b6faa6fe602ab5ae0b9ca8e1fd7d5e1b && \
RUN git clone https://git.wownero.com/wowlet/wownero-seed.git && \
cd wownero-seed && \
git reset --hard ef6910b6bb3b61757c36e2e5db0927d75f1731c8 && \
cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \
make -Cbuild -j$THREADS && \
make -Cbuild install && \
@ -414,3 +433,7 @@ RUN mkdir linuxdeployqt && \
chmod +x linuxdeployqt-7-x86_64.AppImage && \
./linuxdeployqt-7-x86_64.AppImage --appimage-extract && \
rm linuxdeployqt-7-x86_64.AppImage
RUN apt-get update && \
apt-get -o Dpkg::Options::="--force-confold" install -q -y --force-yes libcairo2-dev libxinerama-dev
RUN git config --global --add safe.directory /wowlet

@ -0,0 +1,244 @@
FROM debian:stretch
ARG THREADS=1
ARG ANDROID_NDK_REVISION=21d
ARG ANDROID_NDK_HASH=bcf4023eb8cb6976a4c7cff0a8a8f145f162bf4d
ARG ANDROID_SDK_REVISION=4333796
ARG ANDROID_SDK_HASH=92ffee5a1d98d856634e8b71132e8a95d96c83a63fde1099be3d86df3106def9
ARG QT_VERSION=5.15.2
WORKDIR /opt/android
ENV WORKDIR=/opt/android
ENV ANDROID_NATIVE_API_LEVEL=28
ENV ANDROID_API=android-${ANDROID_NATIVE_API_LEVEL}
ENV ANDROID_CLANG=aarch64-linux-android${ANDROID_NATIVE_API_LEVEL}-clang
ENV ANDROID_CLANGPP=aarch64-linux-android${ANDROID_NATIVE_API_LEVEL}-clang++
ENV ANDROID_NDK_ROOT=${WORKDIR}/android-ndk-r${ANDROID_NDK_REVISION}
ENV ANDROID_SDK_ROOT=${WORKDIR}/tools
ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
ENV PATH=${JAVA_HOME}/bin:${PATH}
ENV PREFIX=${WORKDIR}/prefix
ENV TOOLCHAIN_DIR=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64
RUN apt-get update \
&& apt-get install -y ant automake build-essential ca-certificates-java file gettext git libc6 libncurses5 \
libssl-dev libstdc++6 libtinfo5 libtool libz1 openjdk-8-jdk-headless openjdk-8-jre-headless pkg-config python3 \
unzip wget
RUN wget -q https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_REVISION}.zip \
&& unzip -q sdk-tools-linux-${ANDROID_SDK_REVISION}.zip \
&& rm -f sdk-tools-linux-${ANDROID_SDK_REVISION}.zip
RUN wget -q https://dl.google.com/android/repository/android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip \
&& unzip -q android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip \
&& rm -f android-ndk-r${ANDROID_NDK_REVISION}-linux-x86_64.zip
RUN cd ${ANDROID_SDK_ROOT} && echo y | ./bin/sdkmanager "platform-tools" "platforms;${ANDROID_API}" "tools" > /dev/null
RUN cp -r ${WORKDIR}/platforms ${WORKDIR}/platform-tools ${ANDROID_SDK_ROOT}
ENV HOST_PATH=${PATH}
ENV PATH=${TOOLCHAIN_DIR}/aarch64-linux-android/bin:${TOOLCHAIN_DIR}/bin:${PATH}
ARG ZLIB_VERSION=1.2.11
ARG ZLIB_HASH=c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
RUN wget -q https://zlib.net/zlib-${ZLIB_VERSION}.tar.gz \
&& tar -xzf zlib-${ZLIB_VERSION}.tar.gz \
&& rm zlib-${ZLIB_VERSION}.tar.gz \
&& cd zlib-${ZLIB_VERSION} \
&& CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} ./configure --prefix=${PREFIX} --static \
&& make -j${THREADS} \
&& make -j${THREADS} install \
&& rm -rf $(pwd)
RUN git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 \
&& cd qt5 \
&& perl init-repository --module-subset=default,-qtwebengine \
&& PATH=${HOST_PATH} ./configure -v -developer-build -release \
-xplatform android-clang \
-android-ndk-platform ${ANDROID_API} \
-android-ndk ${ANDROID_NDK_ROOT} \
-android-sdk ${ANDROID_SDK_ROOT} \
-android-ndk-host linux-x86_64 \
-no-dbus \
-opengl es2 \
-no-use-gold-linker \
-no-sql-mysql \
-opensource -confirm-license \
-android-arch arm64-v8a \
-prefix ${PREFIX} \
-nomake tools -nomake tests -nomake examples \
-skip qtwebengine \
-skip qtserialport \
-skip qtconnectivity \
-skip qttranslations \
-skip qtpurchasing \
-skip qtgamepad -skip qtscript -skip qtdoc \
-no-warnings-are-errors \
&& sed -i '213,215d' qtbase/src/3rdparty/pcre2/src/sljit/sljitConfigInternal.h \
&& PATH=${HOST_PATH} make -j${THREADS} \
&& PATH=${HOST_PATH} make -j${THREADS} install \
&& cd qttools/src/linguist/lrelease \
&& ../../../../qtbase/bin/qmake \
&& PATH=${HOST_PATH} make -j${THREADS} install \
&& cd ../../../.. \
&& rm -rf $(pwd)
ARG ICONV_VERSION=1.16
ARG ICONV_HASH=e6a1b1b589654277ee790cce3734f07876ac4ccfaecbee8afa0b649cf529cc04
RUN wget -q http://ftp.gnu.org/pub/gnu/libiconv/libiconv-${ICONV_VERSION}.tar.gz \
&& echo "${ICONV_HASH} libiconv-${ICONV_VERSION}.tar.gz" | sha256sum -c \
&& tar -xzf libiconv-${ICONV_VERSION}.tar.gz \
&& rm -f libiconv-${ICONV_VERSION}.tar.gz \
&& cd libiconv-${ICONV_VERSION} \
&& CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} ./configure --build=x86_64-linux-gnu --host=aarch64 --prefix=${PREFIX} --disable-rpath \
&& make -j${THREADS} \
&& make -j${THREADS} install
ARG BOOST_VERSION=1_74_0
ARG BOOST_VERSION_DOT=1.74.0
ARG BOOST_HASH=83bfc1507731a0906e387fc28b7ef5417d591429e51e788417fe9ff025e116b1
RUN wget -q https://dl.bintray.com/boostorg/release/${BOOST_VERSION_DOT}/source/boost_${BOOST_VERSION}.tar.bz2 \
&& echo "${BOOST_HASH} boost_${BOOST_VERSION}.tar.bz2" | sha256sum -c \
&& tar -xf boost_${BOOST_VERSION}.tar.bz2 \
&& rm -f boost_${BOOST_VERSION}.tar.bz2 \
&& cd boost_${BOOST_VERSION} \
&& PATH=${HOST_PATH} ./bootstrap.sh --prefix=${PREFIX} \
&& PATH=${TOOLCHAIN_DIR}/bin:${HOST_PATH} ./b2 --build-type=minimal link=static runtime-link=static \
--with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization \
--with-system --with-thread --with-locale --build-dir=android --stagedir=android toolset=clang threading=multi \
threadapi=pthread target-os=android -sICONV_PATH=${PREFIX} \
cflags='--target=aarch64-linux-android' \
cxxflags='--target=aarch64-linux-android' \
linkflags='--target=aarch64-linux-android --sysroot=${ANDROID_NDK_ROOT}/platforms/${ANDROID_API}/arch-arm64 ${ANDROID_NDK_ROOT}/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so -nostdlib++' \
install -j${THREADS} \
&& rm -rf $(pwd)
ARG OPENSSL_VERSION=1.1.1g
ARG OPENSSL_HASH=ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46
RUN wget -q https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz \
&& tar -xzf openssl-${OPENSSL_VERSION}.tar.gz \
&& rm openssl-${OPENSSL_VERSION}.tar.gz \
&& cd openssl-${OPENSSL_VERSION} \
&& ANDROID_NDK_HOME=${ANDROID_NDK_ROOT} ./Configure CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} \
android-arm64 no-asm no-shared --static \
--with-zlib-include=${PREFIX}/include --with-zlib-lib=${PREFIX}/lib \
--prefix=${PREFIX} --openssldir=${PREFIX} \
&& sed -i 's/CNF_EX_LIBS=-ldl -pthread//g;s/BIN_CFLAGS=-pie $(CNF_CFLAGS) $(CFLAGS)//g' Makefile \
&& ANDROID_NDK_HOME=${ANDROID_NDK_ROOT} make -j${THREADS} \
&& make -j${THREADS} install \
&& rm -rf $(pwd)
ARG ZMQ_VERSION=v4.3.3
ARG ZMQ_HASH=04f5bbedee58c538934374dc45182d8fc5926fa3
RUN git clone https://github.com/zeromq/libzmq.git -b ${ZMQ_VERSION} --depth 1 \
&& cd libzmq \
&& git checkout ${ZMQ_HASH} \
&& ./autogen.sh \
&& CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} ./configure --prefix=${PREFIX} --host=aarch64-linux-android \
--enable-static --disable-shared \
&& make -j${THREADS} \
&& make -j${THREADS} install \
&& rm -rf $(pwd)
ARG SODIUM_VERSION=1.0.18
ARG SODIUM_HASH=4f5e89fa84ce1d178a6765b8b46f2b6f91216677
RUN set -ex \
&& git clone https://github.com/jedisct1/libsodium.git -b ${SODIUM_VERSION} --depth 1 \
&& cd libsodium \
&& test `git rev-parse HEAD` = ${SODIUM_HASH} || exit 1 \
&& ./autogen.sh \
&& CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} ./configure --prefix=${PREFIX} --host=aarch64-linux-android --enable-static --disable-shared \
&& make -j${THREADS} install \
&& rm -rf $(pwd)
RUN git clone -b libgpg-error-1.38 --depth 1 git://git.gnupg.org/libgpg-error.git \
&& cd libgpg-error \
&& git reset --hard 71d278824c5fe61865f7927a2ed1aa3115f9e439 \
&& ./autogen.sh \
&& CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} ./configure --host=aarch64-linux-android --prefix=${PREFIX} --disable-rpath --disable-shared --enable-static --disable-doc --disable-tests \
&& PATH=${TOOLCHAIN_DIR}/bin:${HOST_PATH} make -j${THREADS} \
&& make -j${THREADS} install \
&& rm -rf $(pwd)
RUN git clone -b libgcrypt-1.8.5 --depth 1 git://git.gnupg.org/libgcrypt.git \
&& cd libgcrypt \
&& git reset --hard 56606331bc2a80536db9fc11ad53695126007298 \
&& ./autogen.sh \
&& CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} ./configure --host=aarch64-linux-android --prefix=${PREFIX} --with-gpg-error-prefix=${PREFIX} --disable-shared --enable-static --disable-doc --disable-tests \
&& PATH=${TOOLCHAIN_DIR}/bin:${HOST_PATH} make -j${THREADS} \
&& make -j${THREADS} install \
&& rm -rf $(pwd)
RUN cd tools \
&& wget -q http://dl-ssl.google.com/android/repository/tools_r25.2.5-linux.zip \
&& unzip -q tools_r25.2.5-linux.zip \
&& rm -f tools_r25.2.5-linux.zip \
&& echo y | ${ANDROID_SDK_ROOT}/tools/android update sdk --no-ui --all --filter build-tools-28.0.3
RUN git clone -b v3.19.7 --depth 1 https://github.com/Kitware/CMake \
&& cd CMake \
&& git reset --hard 22612dd53a46c7f9b4c3f4b7dbe5c78f9afd9581 \
&& PATH=${HOST_PATH} ./bootstrap \
&& PATH=${HOST_PATH} make -j${THREADS} \
&& PATH=${HOST_PATH} make -j${THREADS} install \
&& rm -rf $(pwd)
RUN git clone -b v1.6.35 --depth 1 https://github.com/glennrp/libpng.git && \
cd libpng && \
git reset --hard c17d164b4467f099b4484dfd4a279da0bc1dbd4a \
&& CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} ./configure --with-zlib-prefix="${PREFIX}" --host=aarch64-linux-android --prefix=${PREFIX} --disable-shared --enable-static \
&& PATH=${TOOLCHAIN_DIR}/bin:${HOST_PATH} make -j${THREADS} \
&& make -j${THREADS} install \
&& rm -rf $(pwd)
# @TODO: don't hardcode ANDROID_PLATFORM
RUN git clone -b v4.0.2 --depth 1 https://github.com/fukuchi/libqrencode.git && \
cd libqrencode && \
git reset --hard 59ee597f913fcfda7a010a6e106fbee2595f68e4 && \
CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} cmake \
-DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" \
-DANDROID_PLATFORM="28" \
-DBUILD_SHARED_LIBS=OFF \
-DARCH="armv8-a" \
-DANDROID_ABI="arm64-v8a" \
-DANDROID_TOOLCHAIN=clang \
-DCMAKE_PREFIX_PATH="${PREFIX}" \
-DPNG_PNG_INCLUDE_DIR="${PREFIX}/include/libpng16/" \
-DPNG_LIBRARY="${PREFIX}/lib/libqtlibpng_arm64-v8a.a" \
-DICONV_LIBRARY=/opt/android/prefix/lib/libiconv.a \
-DICONV_INCLUDE_DIR=/opt/android/prefix/include/ \
-DCMAKE_INSTALL_PREFIX="${PREFIX}" && \
make -j$THREADS && \
make -j$THREADS install && \
rm -rf $(pwd)
RUN ls -al && uname -a
# @TODO: switch to Release
CMD set -ex \
&& cd /wowlet \
&& mkdir -p build/Android/release \
&& cd build/Android/release \
&& E=1 cmake \
-DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" \
-DCMAKE_PREFIX_PATH="${PREFIX}" \
-DCMAKE_FIND_ROOT_PATH="${PREFIX}" \
-DCMAKE_BUILD_TYPE=Release \
-DARCH="armv8-a" \
-DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} \
-DANDROID_ABI="arm64-v8a" \
-DANDROID_TOOLCHAIN=clang \
-DBoost_USE_STATIC_RUNTIME=ON \
-DLRELEASE_PATH="${PREFIX}/bin" \
-DQT_ANDROID_APPLICATION_BINARY="wowlet" \
-DWITH_SCANNER=ON \
-DUSE_DEVICE_TREZOR=OFF \
-DUSE_SINGLE_BUILDDIR=ON \
-DMANUAL_SUBMODULES=1 \
-DUSE_SINGLE_BUILDDIR=ON \
-DANDROID=ON \
../../.. \
&& PATH=${HOST_PATH} make generate_translations_header \
&& make -j${THREADS} -C src \
&& make -j${THREADS} apk

@ -1,23 +1,24 @@
FROM ubuntu:20.04
ARG THREADS=1
ARG QT_VERSION=5.15.2
ARG QT_VERSION=v5.15.2
ENV SOURCE_DATE_EPOCH=1397818193
ENV OPENSSL_ROOT_DIR=/usr/local/openssl/
ENV TOR_BIN=/usr/local/tor/bin/tor.exe
ENV TOR_VERSION='tor-0.4.5.7'
RUN apt update && \
DEBIAN_FRONTEND=noninteractive apt install -y curl wget zip automake build-essential cmake gcc-mingw-w64 g++-mingw-w64 gettext git libtool pkg-config \
DEBIAN_FRONTEND=noninteractive apt install -y curl nano wget zip automake build-essential cmake gcc-mingw-w64 g++-mingw-w64 gettext git libtool pkg-config \
python && \
rm -rf /var/lib/apt/lists/*
RUN update-alternatives --set x86_64-w64-mingw32-g++ $(which x86_64-w64-mingw32-g++-posix) && \
update-alternatives --set x86_64-w64-mingw32-gcc $(which x86_64-w64-mingw32-gcc-posix)
RUN git clone -b v0.17.1.9 --depth 1 https://github.com/monero-project/monero && \
RUN git clone -b v0.18.2.0 --depth 1 https://github.com/monero-project/monero && \
cd monero && \
git reset --hard 8fef32e45c80aec41f25be9d1d8fb75adc883c64 && \
git reset --hard 99be9a044f3854f339548e2d99c539c18d7b1b01 && \
cp -a contrib/depends / && \
cd .. && \
rm -rf monero
@ -27,8 +28,12 @@ RUN make -j$THREADS -C /depends HOST=x86_64-w64-mingw32 NO_QT=1
RUN git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \
cd qt5 && \
git clone git://code.qt.io/qt/qtbase.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtdeclarative.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtgraphicaleffects.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtimageformats.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtmultimedia.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtquickcontrols.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtquickcontrols2.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qtsvg.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qttools.git -b ${QT_VERSION} --depth 1 && \
git clone git://code.qt.io/qt/qttranslations.git -b ${QT_VERSION} --depth 1 && \
@ -38,8 +43,8 @@ RUN git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \
./configure --prefix=/depends/x86_64-w64-mingw32 -xplatform win32-g++ \
-device-option CROSS_COMPILE=/usr/bin/x86_64-w64-mingw32- \
-I $(pwd)/qtbase/src/3rdparty/angle/include \
-opensource -confirm-license -release -static -static-runtime -no-opengl \
-no-avx -openssl -I /depends/x86_64-w64-mingw32/include -L /depends/x86_64-w64-mingw32/lib \
-opensource -confirm-license -release -static -static-runtime -opengl dynamic -no-angle \
-no-feature-qml-worker-script -no-avx -openssl -I /depends/x86_64-w64-mingw32/include -L /depends/x86_64-w64-mingw32/lib \
-qt-freetype -qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \
-skip gamepad -skip location -skip qt3d -skip qtactiveqt -skip qtandroidextras \
-skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d -skip qtdoc \
@ -47,7 +52,6 @@ RUN git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \
-skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport \
-skip qtspeech -skip qttools -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel \
-skip qtwebengine -skip qtwebview -skip qtwinextras -skip qtx11extras \
-skip qtdeclarative -skip qtquickcontrols -skip qtquickcontrols2 \
-skip serialbus -skip webengine \
-nomake examples -nomake tests -nomake tools && \
make -j$THREADS && \
@ -92,9 +96,9 @@ RUN git clone -b v1.2.11 --depth 1 https://github.com/madler/zlib && \
rm -rf $(pwd)
# libpng -> libqrencode
RUN git clone -b libpng16 --depth 1 https://github.com/glennrp/libpng.git && \
RUN git clone -b libpng16 https://github.com/glennrp/libpng.git && \
cd libpng && \
git reset --hard dbe3e0c43e549a1602286144d94b0666549b18e6 && \
git reset --hard a37d4836519517bdce6cb9d956092321eca3e73b && \
CPPFLAGS="-I/depends/x86_64-w64-mingw32/include" LDFLAGS="-L/depends/x86_64-w64-mingw32/lib" \
./configure --host=x86_64-w64-mingw32 --prefix=/depends/x86_64-w64-mingw32 && \
make -j$THREADS && \
@ -122,11 +126,11 @@ RUN wget https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.16.tar.gz && \
rm -rf $(pwd)
# OpenSSL -> Tor
RUN wget https://www.openssl.org/source/openssl-1.1.1i.tar.gz && \
echo "e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242 openssl-1.1.1i.tar.gz" | sha256sum -c && \
tar -xzf openssl-1.1.1i.tar.gz && \
rm openssl-1.1.1i.tar.gz && \
cd openssl-1.1.1i && \
RUN wget https://www.openssl.org/source/openssl-1.1.1k.tar.gz && \
echo "892a0875b9872acd04a9fde79b1f943075d5ea162415de3047c327df33fbaee5 openssl-1.1.1k.tar.gz" | sha256sum -c && \
tar -xzf openssl-1.1.1k.tar.gz && \
rm openssl-1.1.1k.tar.gz && \
cd openssl-1.1.1k && \
./Configure mingw64 no-shared no-dso --cross-compile-prefix=x86_64-w64-mingw32- --prefix=/usr/local/openssl && \
make -j$THREADS && \
make -j$THREADS install_sw && \
@ -146,10 +150,9 @@ RUN wget https://github.com/libevent/libevent/releases/download/release-2.1.11-s
make -j$THREADS install && \
rm -rf $(pwd)
ENV TOR_VERSION=0.4.5.5-rc
RUN git clone -b tor-0.4.5.5-rc --depth 1 https://git.torproject.org/tor.git && \
RUN git clone -b tor-0.4.5.7 --depth 1 https://git.torproject.org/tor.git && \
cd tor && \
git reset --hard b36a00e9a9d3eb4b2949951afaa72e45fb7e68cd && \
git reset --hard 83f895c015de55201e5f226f84a866f30f5ee14b && \
./autogen.sh && \
./configure --host=x86_64-w64-mingw32 \
--disable-asciidoc \
@ -172,12 +175,15 @@ RUN git clone -b tor-0.4.5.5-rc --depth 1 https://git.torproject.org/tor.git &&
rm -rf $(pwd) && \
strip -s -D /usr/local/tor/bin/tor.exe
RUN git clone https://git.wownero.com/feather/monero-seed.git && \
cd monero-seed && \
git reset --hard 4674ef09b6faa6fe602ab5ae0b9ca8e1fd7d5e1b && \
RUN git clone https://git.wownero.com/wowlet/wownero-seed.git && \
cd wownero-seed && \
git reset --hard ef6910b6bb3b61757c36e2e5db0927d75f1731c8 && \
cmake -DCMAKE_INSTALL_PREFIX=/depends/x86_64-w64-mingw32 \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=/depends/x86_64-w64-mingw32/share/toolchain.cmake -Bbuild && \
make -Cbuild -j$THREADS && \
make -Cbuild install && \
rm -rf $(pwd)
RUN git config --global --add safe.directory /wowlet
RUN git config --global --add safe.directory /wowlet/wownero

@ -48,7 +48,7 @@ RUN apt install -y \
wget \
xz-utils
RUN git clone -b feather-patch --depth 1 https://git.wownero.com/feather/mxe.git && \
RUN git clone -b wowlet-patch --depth 1 https://git.wownero.com/wowlet/mxe.git && \
cd mxe && \
make -j$THREADS MXE_TARGETS='x86_64-w64-mingw32.static' gcc libqrencode pkgconf libgpg_error libgcrypt cmake libsodium lzma readline libzmq boost qtbase qtsvg qtwebsockets qtimageformats qtmultimedia
@ -67,7 +67,7 @@ RUN ln -s /mxe/usr/x86_64-w64-mingw32.static/lib/libsicuin.a /mxe/usr/x86_64-w64
ENV PATH="/mxe/usr/bin/:$PATH"
RUN git clone https://git.wownero.com/feather/monero-seed.git && \
RUN git clone https://git.wownero.com/wowlet/monero-seed.git && \
cd monero-seed && \
cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && \
make -Cbuild -j$THREADS && \

@ -1,13 +0,0 @@
# this image is used internally for the buildbot
FROM ubuntu:20.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt clean && apt update
RUN apt install -y git build-essential wget curl ngrep unzip file ssh zip
RUN cat /dev/zero | ssh-keygen -q -N ""
RUN cat ~/.ssh/id_rsa.pub
RUN printf "Host *\n StrictHostKeyChecking no" > ~/.ssh/config

@ -1,104 +0,0 @@
# Documentation for developers
Feather is developed primarily on Linux. It uses Qt 5.15.* and chances are that your
distro's package manager has a lower version. It is therefore recommended that you install
Qt manually using the online installer, which can be found here: https://www.qt.io/download
(under open-source).
## Jetbrains Clion
Feather was developed using JetBrains Clion since it integrates nicely
with CMake and comes with a built-in debugger. To pass CMake flags to CLion,
go to `File->Settings->Build->CMake`, set Build Type to `Debug` and set your
preferred CMake options/definitions.
## Requirements
### Ubuntu/Debian
```bash
apt install -y git cmake libqrencode-dev build-essential cmake libboost-all-dev \
miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev \
libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev \
libprotobuf-dev protobuf-compiler libgcrypt20-dev
```
## Mac OS
```bash
brew install boost zmq openssl libpgm miniupnpc libsodium expat libunwind-headers \
protobuf libgcrypt qrencode ccache cmake pkgconfig git
```
## CMake
After installing Qt you might have a folder called `/home/$user/Qt/`. You need to pass this to CMake
via the `CMAKE_PREFIX_PATH` definition. For me this is:
```
-DCMAKE_PREFIX_PATH=/home/dsc/QtNew/5.15.0/gcc_64
```
There are some Monero/Feather related options/definitions that you may pass:
- `-DXMRIG=OFF` - disable XMRig feature
- `-DTOR_BIN=/path/to/tor` - Embed a Tor executable inside Feather
- `-DDONATE_BEG=OFF` - disable the dreaded donate requests
And:
```
-DMANUAL_SUBMODULES=1
-DUSE_DEVICE_TREZOR=OFF
-DUSE_SINGLE_BUILDDIR=ON
-DDEV_MODE=ON
```
If you have OpenSSL installed in a custom location, try:
```
-DOPENSSL_INCLUDE_DIR=/usr/local/lib/openssl-1.1.1g/include
-DOPENSSL_SSL_LIBRARY=/usr/local/lib/openssl-1.1.1g/libssl.so.1.1
-DOPENSSL_CRYPTO_LIBRARY=/usr/local/lib/openssl-1.1.1g/libcrypto.so.1.1
```
I prefer also enabling verbose makefiles, which may be useful in some situations.
```
-DCMAKE_VERBOSE_MAKEFILE=ON
```
Enable debugging symbols:
```bash
-DCMAKE_BUILD_TYPE=Debug
```
## Feather
It's best to install Tor locally as a service and start Feather with `--use-local-tor`, this
prevents the child process from starting up and saves time.
#### Ubuntu/Debian
```bash
apt install -y tor
sudo service tor start
```
#### Mac OS
```bash
brew install tor
brew services start tor
```
To skip the wizards and open a wallet directly use `--wallet-file`:
```bash
./feather --use-local-tor --wallet-file /home/user/Monero/wallets/bla.keys
```
It is recommended that you use `--stagenet` for development. Testnet is also possible,
but you'll have to provide Feather a testnet node of your own.

@ -27,10 +27,9 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
CMAKEFLAGS = \
-DARCH=x86_64 \
-DBUILD_64=On \
-DBUILD_TESTS=Off \
-DXMRIG=Off \
-DOPENVR=Off \
-DTOR_BIN=Off \
-DCMAKE_CXX_STANDARD=11 \
-DCMAKE_VERBOSE_MAKEFILE=On \
@ -41,6 +40,8 @@ CMAKEFLAGS = \
$(CMAKEFLAGS_EXTRA)
release-static: CMAKEFLAGS += -DBUILD_TAG="linux-x64"
release-static: CMAKEFLAGS += -DXMRIG=OFF
release-static: CMAKEFLAGS += -DARCH=x86-64
release-static: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
release-static: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
release-static: CMAKEFLAGS += -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF)
@ -52,8 +53,14 @@ depends:
mkdir -p build/$(target)/release
cd build/$(target)/release && cmake -D STATIC=ON -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF) -DTOR_VERSION=$(or ${TOR_VERSION}, OFF) -DTOR_BIN=$(or ${TOR_BIN},OFF) -D DEV_MODE=$(or ${DEV_MODE},OFF) -D BUILD_TAG=$(tag) -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=$(root)/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
windows:
mkdir -p build/$(target)/release
cd build/$(target)/release && cmake -D STATIC=ON -DZLIB_ROOT=/usr/x86_64-w64-mingw32/ -DREPRODUCIBLE=$(or ${SOURCE_DATE_EPOCH},OFF) -DTOR_VERSION=$(or ${TOR_VERSION}, OFF) -DOPENVR=ON -DWITH_SCANNER=ON -DTOR_BIN=$(or ${TOR_BIN},OFF) -D DEV_MODE=$(or ${DEV_MODE},OFF) -D BUILD_TAG=$(tag) -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=$(root)/$(target)/share/toolchain.cmake ../../.. && $(MAKE)
windows-mxe-release: CMAKEFLAGS += -DBUILD_TAG="win-x64"
windows-mxe-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
windows-mxe-debug: CMAKEFLAGS += -DOPENVR=On
windows-mxe-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
windows-mxe-release:
cmake -Bbuild $(CMAKEFLAGS)
@ -61,16 +68,8 @@ windows-mxe-release:
windows-mxe-debug: CMAKEFLAGS += -DBUILD_TAG="win-x64"
windows-mxe-debug: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
windows-mxe-debug: CMAKEFLAGS += -DOPENVR=On
windows-mxe-debug: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Debug
windows-mxe-debug:
cmake -Bbuild $(CMAKEFLAGS)
$(MAKE) -Cbuild
mac-release: CMAKEFLAGS += -DSTATIC=Off
mac-release: CMAKEFLAGS += -DTOR_BIN=$(or ${TOR_BIN},OFF)
mac-release: CMAKEFLAGS += -DBUILD_TAG="mac-x64"
mac-release: CMAKEFLAGS += -DCMAKE_BUILD_TYPE=Release
mac-release:
cmake -Bbuild $(CMAKEFLAGS)
$(MAKE) -Cbuild
$(MAKE) -Cbuild deploy

@ -1,12 +1,14 @@
[![Build Status](https://ci.wownero.com/api/badges/wowlet/wowlet/status.svg)](https://ci.wownero.com/wowlet/wowlet)
# WOWlet- a free Wownero desktop wallet
WOWlet is a free, open-source Wownero client for Linux, Mac OS, and Windows.
WOWlet is a free, open-source Wownero client for Linux with ports for Mac OS and Windows.
![https://i.imgur.com/l7fUf0f.png](https://i.imgur.com/l7fUf0f.png)
## Development resources
* Git: [git.wownero.com/wowlet/wowlet](https://git.wownero.com/wowlet/wowlet)
* IRC: `#wownero` on Freenode
* IRC: `#wownero-dev` on [OFTC](https://oftc.net/)
* [Building WOWlet from source](https://git.wownero.com/wowlet/wowlet/src/branch/master/docs/BUILDING.md)
* [Working on WOWlet](https://git.wownero.com/wowlet/wowlet/src/branch/master/docs/HACKING.md)
Copyright (c) 2020-2021 The Monero Project.

@ -2,27 +2,4 @@ if(APPLE OR (WIN32 AND NOT STATIC))
add_custom_target(deploy)
get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION)
get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY)
if(APPLE AND NOT IOS)
find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${_qt_bin_dir}")
add_custom_command(TARGET deploy
POST_BUILD
COMMAND "${MACDEPLOYQT_EXECUTABLE}" "$<TARGET_FILE_DIR:wowlet>/../.." -always-overwrite
COMMENT "Running macdeployqt..."
)
# workaround for a Qt bug that requires manually adding libqsvg.dylib to bundle
find_file(_qt_svg_dylib "libqsvg.dylib" PATHS "${CMAKE_PREFIX_PATH}/plugins/imageformats" NO_DEFAULT_PATH)
if(_qt_svg_dylib)
add_custom_command(TARGET deploy
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${_qt_svg_dylib} $<TARGET_FILE_DIR:wowlet>/../PlugIns/imageformats/
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${CMAKE_PREFIX_PATH}/lib/QtGui.framework/Versions/5/QtGui" "@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui" $<TARGET_FILE_DIR:wowlet>/../PlugIns/imageformats/libqsvg.dylib
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${CMAKE_PREFIX_PATH}/lib/QtWidgets.framework/Versions/5/QtWidgets" "@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui" $<TARGET_FILE_DIR:wowlet>/../PlugIns/imageformats/libqsvg.dylib
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${CMAKE_PREFIX_PATH}/lib/QtSvg.framework/Versions/5/QtSvg" "@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui" $<TARGET_FILE_DIR:wowlet>/../PlugIns/imageformats/libqsvg.dylib
COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${CMAKE_PREFIX_PATH}/lib/QtCore.framework/Versions/5/QtCore" "@executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui" $<TARGET_FILE_DIR:wowlet>/../PlugIns/imageformats/libqsvg.dylib
COMMENT "Copying libqsvg.dylib, running install_name_tool"
)
endif()
endif()
endif()

@ -0,0 +1,81 @@
# - Try to find Cairo
# Once done, this will define
#
# CAIRO_FOUND - system has Cairo
# CAIRO_INCLUDE_DIRS - the Cairo include directories
# CAIRO_LIBRARIES - link these to use Cairo
#
# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
FIND_PACKAGE(PkgConfig)
PKG_CHECK_MODULES(PC_CAIRO cairo) # FIXME: After we require CMake 2.8.2 we can pass QUIET to this call.
FIND_PATH(CAIRO_INCLUDE_DIRS
NAMES cairo.h
HINTS ${PC_CAIRO_INCLUDEDIR}
${PC_CAIRO_INCLUDE_DIRS}
PATH_SUFFIXES cairo
)
FIND_LIBRARY(CAIRO_LIBRARIES
NAMES cairo
HINTS ${PC_CAIRO_LIBDIR}
${PC_CAIRO_LIBRARY_DIRS}
)
IF (CAIRO_INCLUDE_DIRS)
IF (EXISTS "${CAIRO_INCLUDE_DIRS}/cairo-version.h")
FILE(READ "${CAIRO_INCLUDE_DIRS}/cairo-version.h" CAIRO_VERSION_CONTENT)
STRING(REGEX MATCH "#define +CAIRO_VERSION_MAJOR +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}")
SET(CAIRO_VERSION_MAJOR "${CMAKE_MATCH_1}")
STRING(REGEX MATCH "#define +CAIRO_VERSION_MINOR +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}")
SET(CAIRO_VERSION_MINOR "${CMAKE_MATCH_1}")
STRING(REGEX MATCH "#define +CAIRO_VERSION_MICRO +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}")
SET(CAIRO_VERSION_MICRO "${CMAKE_MATCH_1}")
SET(CAIRO_VERSION "${CAIRO_VERSION_MAJOR}.${CAIRO_VERSION_MINOR}.${CAIRO_VERSION_MICRO}")
ENDIF ()
ENDIF ()
# FIXME: Should not be needed anymore once we start depending on CMake 2.8.3
SET(VERSION_OK TRUE)
IF (Cairo_FIND_VERSION)
IF (Cairo_FIND_VERSION_EXACT)
IF ("${Cairo_FIND_VERSION}" VERSION_EQUAL "${CAIRO_VERSION}")
# FIXME: Use IF (NOT ...) with CMake 2.8.2+ to get rid of the ELSE block
ELSE ()
SET(VERSION_OK FALSE)
ENDIF ()
ELSE ()
IF ("${Cairo_FIND_VERSION}" VERSION_GREATER "${CAIRO_VERSION}")
SET(VERSION_OK FALSE)
ENDIF ()
ENDIF ()
ENDIF ()
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Cairo DEFAULT_MSG CAIRO_INCLUDE_DIRS CAIRO_LIBRARIES VERSION_OK)

@ -0,0 +1,26 @@
# - Find XFixes
# Find the XFixes libraries
#
# This module defines the following variables:
# XFIXES_FOUND - 1 if XFIXES_INCLUDE_DIR & XFIXES_LIBRARY are found, 0 otherwise
# XFIXES_INCLUDE_DIR - where to find Xlib.h, etc.
# XFIXES_LIBRARY - the X11 library
#
find_path( XFIXES_INCLUDE_DIR
NAMES X11/extensions/Xfixes.h
PATH_SUFFIXES X11/extensions
DOC "The XFixes include directory" )
find_library( XFIXES_LIBRARY
NAMES Xfixes
PATHS /usr/lib /lib
DOC "The XFixes library" )
if( XFIXES_INCLUDE_DIR AND XFIXES_LIBRARY )
set( XFIXES_FOUND 1 )
else()
set( XFIXES_FOUND 0 )
endif()
mark_as_advanced( XFIXES_INCLUDE_DIR XFIXES_LIBRARY )

@ -35,7 +35,7 @@ if(RET)
message(WARNING "Cannot determine current commit. Make sure that you are building either from a Git working tree or from a source archive.")
set(VERSIONTAG "unknown")
set(VERSION_IS_RELEASE "false")
configure_file("monero/src/version.cpp.in" "${TO}")
configure_file("wownero/src/version.cpp.in" "${TO}")
else()
string(SUBSTRING ${COMMIT} 0 9 COMMIT)
message(STATUS "You are currently on commit ${COMMIT}")
@ -61,5 +61,5 @@ else()
set(VERSION_IS_RELEASE "false")
endif()
endif()
configure_file("monero/src/version.cpp.in" "${TO}")
configure_file("wownero/src/version.cpp.in" "${TO}")
endif()

@ -4,7 +4,7 @@
find_package(Git QUIET)
# Check what commit we're on
execute_process(COMMAND "${GIT_EXECUTABLE}" rev-parse --short=9 HEAD RESULT_VARIABLE RET OUTPUT_VARIABLE COMMIT OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/monero)
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/wownero)
if(RET)
# Something went wrong, set the version tag to -unknown
@ -37,7 +37,7 @@ endif()
# Check latest tagged release
execute_process(COMMAND "${GIT_EXECUTABLE}" describe --abbrev=0 RESULT_VARIABLE RET OUTPUT_VARIABLE TAG OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/monero)
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/wownero)
if(RET)
message(WARNING "Cannot determine most recent tag. Make sure that you are building either from a Git working tree or from a source archive.")
@ -46,4 +46,4 @@ else ()
set(MONERO_VERSION "${TAG}")
endif()
configure_file("cmake/config-feather.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/src/config-feather.h")
configure_file("cmake/config-wowlet.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/src/config-wowlet.h")

@ -10,8 +10,8 @@ if(RET)
# Something went wrong, set the version tag to -unknown
message(WARNING "Cannot determine current commit. Make sure that you are building either from a Git working tree or from a source archive.")
set(FEATHER_BRANCH "unknown")
configure_file("cmake/config-feather.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/src/config-feather.h")
set(WOWLET_BRANCH "unknown")
configure_file("cmake/config-wowlet.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/src/config-wowlet.h")
else()
string(SUBSTRING ${COMMIT} 0 9 COMMIT)
message(STATUS "You are currently on commit ${COMMIT}")
@ -21,19 +21,19 @@ else()
if(NOT TAGGEDCOMMIT)
message(STATUS "Cannot determine most recent tag. Make sure that you are building either from a Git working tree or from a source archive.")
set(FEATHER_BRANCH "${COMMIT}")
set(WOWLET_BRANCH "${COMMIT}")
else()
message(STATUS "The most recent tag was at ${TAGGEDCOMMIT}")
# Check if we're building that tagged commit or a different one
if(COMMIT STREQUAL TAGGEDCOMMIT)
message(STATUS "You are building a tagged release")
set(FEATHER_BRANCH "release")
set(WOWLET_BRANCH "release")
else()
message(STATUS "You are ahead of or behind a tagged release")
set(FEATHER_BRANCH "${COMMIT}")
set(WOWLET_BRANCH "${COMMIT}")
endif()
endif()
configure_file("cmake/config-feather.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/src/config-feather.h")
configure_file("cmake/config-wowlet.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/src/config-wowlet.h")
endif()

@ -1,12 +0,0 @@
#ifndef FEATHER_VERSION_H
#define FEATHER_VERSION_H
#define FEATHER_VERSION "@VERSION@"
#define FEATHER_BRANCH "@FEATHER_BRANCH@"
#define MONERO_VERSION "@MONERO_VERSION@"
#define MONERO_BRANCH "@MONERO_BRANCH@"
#define TOR_VERSION "@TOR_VERSION@"
#endif //FEATHER_VERSION_H

@ -0,0 +1,13 @@
#ifndef WOWLET_VERSION_H
#define WOWLET_VERSION_H
#define WOWLET_VERSION "@VERSION@"
#define WOWLET_BRANCH "@WOWLET_BRANCH@"
#define WOWLET_VERSION_SEMVER "@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_REVISION@"
#define MONERO_VERSION "@MONERO_VERSION@"
#define MONERO_BRANCH "@MONERO_BRANCH@"
#define TOR_VERSION "@TOR_VERSION@"
#endif //WOWLET_VERSION_H

@ -1,33 +0,0 @@
#!/bin/bash
set -e
unset SOURCE_DATE_EPOCH
APPDIR="$PWD/wowlet.AppDir"
mkdir -p "$APPDIR"
mkdir -p "$APPDIR/usr/share/applications/"
mkdir -p "$APPDIR/usr/bin"
cp "$PWD/src/assets/wowlet.desktop" "$APPDIR/usr/share/applications/wowlet.desktop"
cp "$PWD/src/assets/images/appicons/64x64.png" "$APPDIR/wowlet.png"
cp "$PWD/build/bin/wowlet" "$APPDIR/usr/bin/wowlet"
LD_LIBRARY_PATH=/usr/local/lib /linuxdeployqt/squashfs-root/AppRun wowlet.AppDir/usr/share/applications/wowlet.desktop -bundle-non-qt-libs
find wowlet.AppDir/ -exec touch -h -a -m -t 202101010100.00 {} \;
# Manually create AppImage (reproducibly)
# download runtime
wget -nc https://github.com/AppImage/AppImageKit/releases/download/12/runtime-x86_64
echo "24da8e0e149b7211cbfb00a545189a1101cb18d1f27d4cfc1895837d2c30bc30 runtime-x86_64" | sha256sum -c
mksquashfs wowlet.AppDir wowlet.squashfs -info -root-owned -no-xattrs -noappend -fstime 0
# mksquashfs writes a timestamp to the header
printf '\x00\x00\x00\x00' | dd conv=notrunc of=wowlet.squashfs bs=1 seek=$((0x8))
rm -f wowlet.AppImage
cat runtime-x86_64 >> wowlet.AppImage
cat wowlet.squashfs >> wowlet.AppImage
chmod a+x wowlet.AppImage

@ -0,0 +1,46 @@
#!/bin/bash
set -e
# Make directories
DEBDIR="$PWD/wowlet.DebDir"
mkdir -p "$DEBDIR"
mkdir -p "$DEBDIR/DEBIAN/"
mkdir -p "$DEBDIR/debian/"
mkdir -p "$DEBDIR/usr/bin/"
mkdir -p "$DEBDIR/usr/share/doc/wowlet/"
mkdir -p "$DEBDIR/usr/share/metainfo/"
mkdir -p "$DEBDIR/usr/share/applications/"
mkdir -p "$DEBDIR/usr/share/man/man1/"
mkdir -p "$DEBDIR/usr/share/icons/hicolor/scalable/apps/"
mkdir -p "$DEBDIR/usr/share/icons/hicolor/48x48/apps/"
mkdir -p "$DEBDIR/usr/share/icons/hicolor/64x64/apps/"
mkdir -p "$DEBDIR/usr/share/icons/hicolor/96x96/apps/"
mkdir -p "$DEBDIR/usr/share/icons/hicolor/128x128/apps/"
mkdir -p "$DEBDIR/usr/share/icons/hicolor/192x192/apps/"
mkdir -p "$DEBDIR/usr/share/icons/hicolor/256x256/apps/"
# Copy over assets
cp "$PWD/contrib/debian/control" "$DEBDIR/DEBIAN/control"
cp "$PWD/contrib/debian/copyright" "$DEBDIR/debian/copyright"
cp "$PWD/contrib/debian/copyright" "$DEBDIR/usr/share/doc/wowlet/copyright"
cp "$PWD/contrib/debian/changelog.gz" "$DEBDIR/usr/share/doc/wowlet/changelog.gz"
cp "$PWD/src/assets/org.wowlet.wowlet.metainfo.xml" "$DEBDIR/usr/share/metainfo/org.wowlet.wowlet.metainfo.xml"
cp "$PWD/README.md" "$DEBDIR/usr/share/doc/wowlet/README"
cp "$PWD/docs/SECURITY.md" "$DEBDIR/usr/share/doc/wowlet/SECURITY"
cp "$PWD/build/bin/wowlet" "$DEBDIR/usr/bin/wowlet"
cp "$PWD/src/assets/org.wowlet.wowlet.desktop" "$DEBDIR/usr/share/applications/org.wowlet.wowlet.desktop"
cp "$PWD/src/assets/wowlet.1.gz" "$DEBDIR/usr/share/man/man1/wowlet.1.gz"
cp "$PWD/src/assets/images/appicons/wowlet.svg" "$DEBDIR/usr/share/icons/hicolor/scalable/apps/wowlet.svg"
cp "$PWD/src/assets/images/appicons/48x48.png" "$DEBDIR/usr/share/icons/hicolor/48x48/apps/wowlet.png"
cp "$PWD/src/assets/images/appicons/64x64.png" "$DEBDIR/usr/share/icons/hicolor/64x64/apps/wowlet.png"
cp "$PWD/src/assets/images/appicons/96x96.png" "$DEBDIR/usr/share/icons/hicolor/96x96/apps/wowlet.png"
cp "$PWD/src/assets/images/appicons/128x128.png" "$DEBDIR/usr/share/icons/hicolor/128x128/apps/wowlet.png"
cp "$PWD/src/assets/images/appicons/192x192.png" "$DEBDIR/usr/share/icons/hicolor/192x192/apps/wowlet.png"
cp "$PWD/src/assets/images/appicons/256x256.png" "$DEBDIR/usr/share/icons/hicolor/256x256/apps/wowlet.png"
# Build deb package
dpkg-deb --build $DEBDIR
mv wowlet.DebDir.deb wowlet_2.1_amd64.deb

Binary file not shown.

@ -0,0 +1,12 @@
Package: wowlet
Version: 2.1
Section: net
Priority: optional
Architecture: amd64
Essential: no
Installed-Size: 76800
Maintainer: wowario <wowario@protonmail.com>
Description: WOWlet is a free, open-source Wownero client for Linux with ports for Mac OS and Windows.
Tag: office::finance
Homepage: https://git.wownero.com/wowlet/wowlet
Depends: libxcb-icccm4, libxcb-image0, libxcb-keysyms1, libxcb-render-util0, libxcb-xkb1, libxkbcommon-x11-0

@ -0,0 +1,31 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: wowlet
Source: https://git.wownero.com/wowlet/wowlet
Files: *
Copyright: (c) 2020-2021 The Monero Project
License: BSD-3-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,93 @@
# Set project name.
project(OpenVRSDK)
# Fetch the version from the headers
set(VERSION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/headers/openvr.h")
set(VERSION_MAJOR_REGEX "\tstatic const uint32_t k_nSteamVRVersionMajor = (.+);")
set(VERSION_MINOR_REGEX "\tstatic const uint32_t k_nSteamVRVersionMinor = (.+);")
set(VERSION_BUILD_REGEX "\tstatic const uint32_t k_nSteamVRVersionBuild = (.+);")
file(STRINGS "${VERSION_FILE}" VERSION_MAJOR_STRING REGEX "${VERSION_MAJOR_REGEX}")
file(STRINGS "${VERSION_FILE}" VERSION_MINOR_STRING REGEX "${VERSION_MINOR_REGEX}")
file(STRINGS "${VERSION_FILE}" VERSION_BUILD_STRING REGEX "${VERSION_BUILD_REGEX}")
string(REGEX REPLACE "${VERSION_MAJOR_REGEX}" "\\1" VERSION_MAJOR ${VERSION_MAJOR_STRING})
string(REGEX REPLACE "${VERSION_MINOR_REGEX}" "\\1" VERSION_MINOR ${VERSION_MINOR_STRING})
string(REGEX REPLACE "${VERSION_BUILD_REGEX}" "\\1" VERSION_BUILD ${VERSION_BUILD_STRING})
set(OPENVR_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_BUILD}")
# Setup some options.
option(BUILD_SHARED "Builds the library as shared library" OFF)
option(BUILD_FRAMEWORK "Builds the library as an apple Framework" OFF)
option(BUILD_UNIVERSAL "Builds the shared or framework as a universal (fat, 32- & 64-bit) binary" ON)
option(BUILD_OSX_I386 "Builds the shared or framework as a 32-bit binary, even on a 64-bit platform" OFF)
option(USE_LIBCXX "Uses libc++ instead of libstdc++" ON)
option(USE_CUSTOM_LIBCXX "Uses a custom libc++" OFF)
add_definitions( -DVR_API_PUBLIC )
# Check if 32 or 64 bit system.
set(SIZEOF_VOIDP ${CMAKE_SIZEOF_VOID_P})
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(PROCESSOR_ARCH "64")
else()
set(PROCESSOR_ARCH "32")
endif()
# Get platform.
if(WIN32)
set(PLATFORM_NAME "win")
if(NOT BUILD_SHARED)
add_definitions(-DOPENVR_BUILD_STATIC)
endif()
elseif(UNIX AND NOT APPLE)
if(CMAKE_SYSTEM_NAME MATCHES ".*Linux")
set(PLATFORM_NAME "linux")
add_definitions(-DLINUX -DPOSIX)
if(PROCESSOR_ARCH MATCHES "64")
add_definitions(-DLINUX64)
endif()
endif()
elseif(APPLE)
if(CMAKE_SYSTEM_NAME MATCHES ".*Darwin.*" OR CMAKE_SYSTEM_NAME MATCHES ".*MacOS.*")
set(PLATFORM_NAME "osx")
add_definitions(-DOSX -DPOSIX)
if(BUILD_UNIVERSAL)
set(CMAKE_OSX_ARCHITECTURES "i386;x86_64")
endif()
if(BUILD_OSX_I386)
set(PROCESSOR_ARCH "32")
set(CMAKE_OSX_ARCHITECTURES "i386")
endif()
endif()
endif()
# Set output folder for static and shared libraries
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin/${PLATFORM_NAME}${PROCESSOR_ARCH})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin/${PLATFORM_NAME}${PROCESSOR_ARCH})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin/${PLATFORM_NAME}${PROCESSOR_ARCH})
# Enable some properties.
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
# Enable c++11 and hide symbols which shouldn't be visible
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fPIC -fvisibility=hidden")
# Set custom libc++ usage here
if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND USE_LIBCXX)
if(USE_CUSTOM_LIBCXX)
if(BUILD_SHARED)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdinc++")
include_directories( ${LIBCXX_INCLUDE} ${LIBCXX_ABI_INCLUDE})
message(STATUS "Using custom libc++")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
message(STATUS "Using libc++")
endif()
endif()
endif()
add_subdirectory(src)

@ -0,0 +1,27 @@
Copyright (c) 2015, Valve Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,19 @@
### Warning
**Hard forked from commit `4c85abcb7f7f1f02adaf3812018c99fc593bc341` @ openvr**.
At the time of writing, the above version does not compile. Had to make several changes. In addition,
binary blobs where removed from the repository.
### OpenVR SDK
OpenVR is an API and runtime that allows access to VR hardware from multiple
vendors without requiring that applications have specific knowledge of the
hardware they are targeting. This repository is an SDK that contains the API
and samples. The runtime is under SteamVR in Tools on Steam.
### Documentation
Documentation for the API is available on the [GitHub Wiki](https://github.com/ValveSoftware/openvr/wiki/API-Documentation)
More information on OpenVR and SteamVR can be found on http://steamvr.com

@ -0,0 +1,9 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,116 @@
# Project name.
project(openvr_api)
set( LIBNAME "openvr_api" )
set(OPENVR_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../headers)
# Set some properies for specific files.
if(APPLE)
set(CMAKE_MACOSX_RPATH 1)
if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
set_source_files_properties(vrcommon/pathtools_public.cpp vrcommon/vrpathregistry_public.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++")
endif()
if(BUILD_SHARED OR BUILD_FRAMEWORK)
find_library(FOUNDATION_FRAMEWORK Foundation)
mark_as_advanced(FOUNDATION_FRAMEWORK)
set(EXTRA_LIBS ${EXTRA_LIBS} ${FOUNDATION_FRAMEWORK})
endif(BUILD_SHARED OR BUILD_FRAMEWORK)
elseif(WIN32)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
add_definitions( -DWIN64 )
set( LIBNAME "openvr_api64" )
endif()
endif()
# Add include folders.
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../headers ${CMAKE_CURRENT_SOURCE_DIR}/vrcommon)
if(USE_CUSTOM_LIBCXX)
link_directories(
${LIBCXX_LIB_DIR}
)
endif()
# Set the source group and files.
set(CORE_FILES
openvr_api_public.cpp
jsoncpp.cpp
)
set(VRCOMMON_FILES
vrcommon/dirtools_public.cpp
vrcommon/envvartools_public.cpp
vrcommon/pathtools_public.cpp
vrcommon/sharedlibtools_public.cpp
vrcommon/hmderrors_public.cpp
vrcommon/vrpathregistry_public.cpp
vrcommon/strtools_public.cpp
)
set(SOURCE_FILES
${CORE_FILES}
${VRCOMMON_FILES}
)
set(PUBLIC_HEADER_FILES
${OPENVR_HEADER_DIR}/openvr_driver.h
${OPENVR_HEADER_DIR}/openvr_capi.h
${OPENVR_HEADER_DIR}/openvr.h
)
source_group("Src" FILES
${CORE_FILES}
)
source_group("VRCommon" FILES
${VRCOMMON_FILES}
)
# Build the library.
if(BUILD_SHARED)
add_library(${LIBNAME} SHARED ${SOURCE_FILES})
elseif(BUILD_FRAMEWORK)
set( LIBNAME "OpenVR" )
add_library( ${LIBNAME}
SHARED ${SOURCE_FILES}
${CMAKE_SOURCE_DIR}/headers/openvr.h
${CMAKE_SOURCE_DIR}/headers/openvr_api.cs
${CMAKE_SOURCE_DIR}/headers/openvr_api.json
${CMAKE_SOURCE_DIR}/headers/openvr_capi.h
${CMAKE_SOURCE_DIR}/headers/openvr_driver.h
)
set_target_properties(OpenVR PROPERTIES
FRAMEWORK TRUE
FRAMEWORK_VERSION A
MACOSX_FRAMEWORK_IDENTIFIER com.valvesoftware.OpenVR.framework
MACOSX_FRAMEWORK_INFO_PLIST ${CMAKE_SOURCE_DIR}/src/Info.plist
# "current version" in semantic format in Mach-O binary file
VERSION 1.0.6
# "compatibility version" in semantic format in Mach-O binary file
SOVERSION 1.0.0
PUBLIC_HEADER "${CMAKE_SOURCE_DIR}/headers/openvr.h;${CMAKE_SOURCE_DIR}/headers/openvr_api.cs;${CMAKE_SOURCE_DIR}/headers/openvr_api.json;${CMAKE_SOURCE_DIR}/headers/openvr_capi.h;${CMAKE_SOURCE_DIR}/headers/openvr_driver.h"
LINKER_LANGUAGE CXX
)
else()
add_library(${LIBNAME} STATIC ${SOURCE_FILES})
endif()
if(USE_CUSTOM_LIBCXX)
set(EXTRA_LIBS ${EXTRA_LIBS} c++ c++abi)
endif()
target_link_libraries(${LIBNAME} ${EXTRA_LIBS} ${CMAKE_DL_LIBS})
target_include_directories(${LIBNAME} PUBLIC ${OPENVR_HEADER_DIR})
install(TARGETS ${LIBNAME} DESTINATION lib)
install(FILES ${PUBLIC_HEADER_FILES} DESTINATION include/openvr)
# Generate a .pc file for linux environments
if(PLATFORM_NAME MATCHES "linux")
set(INSTALL_PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" CACHE PATH "Installation directory for pkgconfig (.pc) files")
CONFIGURE_FILE("openvr.pc.in" "openvr.pc" @ONLY)
set(OPENVR_PC ${CMAKE_CURRENT_BINARY_DIR}/openvr.pc)
if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL )
install(FILES ${OPENVR_PC} DESTINATION "${INSTALL_PKGCONFIG_DIR}")
endif()
endif()

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>com.valvesoftware.OpenVR.framework</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>OpenVR</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
</plist>

@ -0,0 +1,39 @@
This is the source code for the OpenVR API client binding library which connects
OpenVR applications to the SteamVR runtime, taking into account the version
of the OpenVR interface they were compiled against.
The client binding library - openvr_api.dll on Windows, openvr_api.so on
Linux, and openvr_api.dylib or OpenVR.framework on macOS - knows how to find
and read the SteamVR runtime installation information which allows it to
find and dynamically connect to the installed runtime. In combination with the
interface version identifiers from /include/openvr.h which are baked
into applications at the time they are built, the OpenVR API client
binding library captures and conveys to the SteamVR runtime the version
of the OpenVR API interface behavior that the application expects.
Applications carry with them a private/local copy of the client binding
library when they ship, and they should install it locally to their
application. Applications should not install the client binding library
globally or attempt to link to a globally installed client binding library.
Doing so negates at least part of the ability for the client binding library
to accurately reflect the version of the OpenVR API that the application
was built against, and so hinders compatibility support in the face of
API changes.
Most applications should simply link to and redistribute with their application
the pre-built client binding library found in the /bin directory of this
repository. Some small number of applications which have specific requirements
around redistributing only binaries they build themselves should build
the client library from this source and either statically link it into
their application or redistribute the binary they build.
This is a cmake project, to build it use the version of cmake appropriate
for your platform. For example, to build on a POSIX system simply perform
cd src; mkdir _build; cd _build; cmake ..; make
and you will end up with the static library /src/bin/<arch>/libopenvr_api.a
To build a shared library, pass -DBUILD_SHARED=1 to cmake.
To build as a framework on apple platforms, pass -DBUILD_FRAMEWORK=1 to cmake.
To see a complete list of configurable build options, use `cmake -LAH`

@ -0,0 +1,35 @@
//========= Copyright Valve Corporation ============//
#pragma once
namespace vr
{
class IVRClientCore
{
public:
/** Initializes the system */
virtual EVRInitError Init( vr::EVRApplicationType eApplicationType, const char *pStartupInfo ) = 0;
/** cleans up everything in vrclient.dll and prepares the DLL to be unloaded */
virtual void Cleanup() = 0;
/** checks to see if the specified interface/version is supported in this vrclient.dll */
virtual EVRInitError IsInterfaceVersionValid( const char *pchInterfaceVersion ) = 0;
/** Retrieves any interface from vrclient.dll */
virtual void *GetGenericInterface( const char *pchNameAndVersion, EVRInitError *peError ) = 0;
/** Returns true if any driver has an HMD attached. Can be called outside of Init/Cleanup */
virtual bool BIsHmdPresent() = 0;
/** Returns an English error string from inside vrclient.dll which might be newer than the API DLL */
virtual const char *GetEnglishStringForHmdError( vr::EVRInitError eError ) = 0;
/** Returns an error symbol from inside vrclient.dll which might be newer than the API DLL */
virtual const char *GetIDForVRInitError( vr::EVRInitError eError ) = 0;
};
static const char * const IVRClientCore_Version = "IVRClientCore_003";
}

@ -0,0 +1,284 @@
/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).
/// It is intended to be used with #include "json/json-forwards.h"
/// This header provides forward declaration for all JsonCpp types.
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
/*
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
The author (Baptiste Lepilleur) explicitly disclaims copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
Copyright (c) 2007-2010 Baptiste Lepilleur
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.
========================================================================
(END LICENSE TEXT)
The MIT license is compatible with both the GPL and commercial
software, affording one all of the rights of Public Domain with the
minor nuisance of being required to keep the above copyright notice
and license text in the source code. Note also that by accepting the
Public Domain "license" you can re-license your copy using whatever
license you like.
*/
// //////////////////////////////////////////////////////////////////////
// End of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
# define JSON_FORWARD_AMALGATED_H_INCLUDED
/// If defined, indicates that the source file is amalgated
/// to prevent private header inclusion.
#define JSON_IS_AMALGAMATION
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
/// If defined, indicates that json library is embedded in CppTL library.
//# define JSON_IN_CPPTL 1
/// If defined, indicates that json may leverage CppTL library
//# define JSON_USE_CPPTL 1
/// If defined, indicates that cpptl vector based map should be used instead of
/// std::map
/// as Value container.
//# define JSON_USE_CPPTL_SMALLMAP 1
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION
#define JSON_USE_EXCEPTION 1
#endif
/// If defined, indicates that the source file is amalgated
/// to prevent private header inclusion.
/// Remarks: it is automatically defined in the generated amalgated header.
// #define JSON_IS_AMALGAMATION
#ifdef JSON_IN_CPPTL
#include <cpptl/config.h>
#ifndef JSON_USE_CPPTL
#define JSON_USE_CPPTL 1
#endif
#endif
#ifdef JSON_IN_CPPTL
#define JSON_API CPPTL_API
#elif defined(JSON_DLL_BUILD)
#if defined(_MSC_VER)
#define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#elif defined(JSON_DLL)
#if defined(_MSC_VER)
#define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#endif // ifdef JSON_IN_CPPTL
#if !defined(JSON_API)
#define JSON_API
#endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
#if defined(_MSC_VER) // MSVC
# if _MSC_VER <= 1200 // MSVC 6
// Microsoft Visual Studio 6 only support conversion from __int64 to double
// (no conversion from unsigned __int64).
# define JSON_USE_INT64_DOUBLE_CONVERSION 1
// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
// characters in the debug information)
// All projects I've ever seen with VS6 were using this globally (not bothering
// with pragma push/pop).
# pragma warning(disable : 4786)
# endif // MSVC 6
# if _MSC_VER >= 1500 // MSVC 2008
/// Indicates that the following function is deprecated.
# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
# endif
#endif // defined(_MSC_VER)
#ifndef JSON_HAS_RVALUE_REFERENCES
#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // MSVC >= 2010
#ifdef __clang__
#if __has_feature(cxx_rvalue_references)
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // has_feature
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
#define JSON_HAS_RVALUE_REFERENCES 1
#endif // GXX_EXPERIMENTAL
#endif // __clang__ || __GNUC__
#endif // not defined JSON_HAS_RVALUE_REFERENCES
#ifndef JSON_HAS_RVALUE_REFERENCES
#define JSON_HAS_RVALUE_REFERENCES 0
#endif
#ifdef __clang__
#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
# endif // GNUC version
#endif // __clang__ || __GNUC__
#if !defined(JSONCPP_DEPRECATED)
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
namespace Json {
typedef int Int;
typedef unsigned int UInt;
#if defined(JSON_NO_INT64)
typedef int LargestInt;
typedef unsigned int LargestUInt;
#undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio
typedef __int64 Int64;
typedef unsigned __int64 UInt64;
#else // if defined(_MSC_VER) // Other platforms, use long long
typedef long long int Int64;
typedef unsigned long long int UInt64;
#endif // if defined(_MSC_VER)
typedef Int64 LargestInt;
typedef UInt64 LargestUInt;
#define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64)
} // end namespace Json
#endif // JSON_CONFIG_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
// writer.h
class FastWriter;
class StyledWriter;
// reader.h
class Reader;
// features.h
class Features;
// value.h
typedef unsigned int ArrayIndex;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=${prefix}/lib
includedir=${prefix}/include/openvr
Name: openvr
Description: OpenVR is an API and runtime that allos access to VR hardware.
Version: @OPENVR_VERSION@
Libs: -L${libdir} -lopenvr_api -ldl
Cflags: -I${includedir}

@ -0,0 +1,356 @@
//========= Copyright Valve Corporation ============//
#define VR_API_EXPORT 1
#define VR_API_PUBLIC 1
#include "openvr.h"
#include "ivrclientcore.h"
#include <vrcommon/pathtools_public.h>
#include <vrcommon/sharedlibtools_public.h>
#include <vrcommon/envvartools_public.h>
#include "hmderrors_public.h"
#include <vrcommon/strtools_public.h>
#include <vrcommon/vrpathregistry_public.h>
#include <mutex>
using vr::EVRInitError;
using vr::IVRSystem;
using vr::IVRClientCore;
using vr::VRInitError_None;
// figure out how to import from the VR API dll
#if defined(_WIN32)
#define DYNAMIC_LIB_EXT ".dll"
#define PROGRAM_EXT ".exe"
#if !defined(OPENVR_BUILD_STATIC)
#define VR_EXPORT_INTERFACE extern "C" __declspec( dllexport )
#else
#define VR_EXPORT_INTERFACE extern "C"
#endif
#elif defined(__GNUC__) || defined(COMPILER_GCC) || defined(__APPLE__)
#define VR_EXPORT_INTERFACE extern "C" __attribute__((visibility("default")))
#else
#error "Unsupported Platform."
#endif
namespace vr
{
static void *g_pVRModule = NULL;
static IVRClientCore *g_pHmdSystem = NULL;
static std::recursive_mutex g_mutexSystem;
typedef void* (*VRClientCoreFactoryFn)(const char *pInterfaceName, int *pReturnCode);
static uint32_t g_nVRToken = 0;
uint32_t VR_GetInitToken()
{
return g_nVRToken;
}
EVRInitError VR_LoadHmdSystemInternal();
void CleanupInternalInterfaces();
uint32_t VR_InitInternal2( EVRInitError *peError, vr::EVRApplicationType eApplicationType, const char *pStartupInfo )
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
EVRInitError err = VR_LoadHmdSystemInternal();
if ( err == vr::VRInitError_None )
{
err = g_pHmdSystem->Init( eApplicationType, pStartupInfo );
}
if ( peError )
*peError = err;
if ( err != VRInitError_None )
{
SharedLib_Unload( g_pVRModule );
g_pHmdSystem = NULL;
g_pVRModule = NULL;
return 0;
}
return ++g_nVRToken;
}
VR_INTERFACE uint32_t VR_CALLTYPE VR_InitInternal( EVRInitError *peError, EVRApplicationType eApplicationType );
uint32_t VR_InitInternal( EVRInitError *peError, vr::EVRApplicationType eApplicationType )
{
return VR_InitInternal2( peError, eApplicationType, nullptr );
}
void VR_ShutdownInternal()
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
#if !defined( VR_API_PUBLIC )
CleanupInternalInterfaces();
#endif
if ( g_pHmdSystem )
{
g_pHmdSystem->Cleanup();
g_pHmdSystem = NULL;
}
if ( g_pVRModule )
{
SharedLib_Unload( g_pVRModule );
g_pVRModule = NULL;
}
++g_nVRToken;
}
EVRInitError VR_LoadHmdSystemInternal()
{
std::string sRuntimePath, sConfigPath, sLogPath;
bool bReadPathRegistry = CVRPathRegistry_Public::GetPaths( &sRuntimePath, &sConfigPath, &sLogPath, NULL, NULL );
if( !bReadPathRegistry )
{
return vr::VRInitError_Init_PathRegistryNotFound;
}
// figure out where we're going to look for vrclient.dll
// see if the specified path actually exists.
if( !Path_IsDirectory( sRuntimePath ) )
{
return vr::VRInitError_Init_InstallationNotFound;
}
// Because we don't have a way to select debug vs. release yet we'll just
// use debug if it's there
#if defined( LINUX64 ) || defined( LINUXARM64 )
std::string sTestPath = Path_Join( sRuntimePath, "bin", PLATSUBDIR );
#else
std::string sTestPath = Path_Join( sRuntimePath, "bin" );
#endif
if( !Path_IsDirectory( sTestPath ) )
{
return vr::VRInitError_Init_InstallationCorrupt;
}
#if defined( WIN64 )
std::string sDLLPath = Path_Join( sTestPath, "vrclient_x64" DYNAMIC_LIB_EXT );
#else
std::string sDLLPath = Path_Join( sTestPath, "vrclient" DYNAMIC_LIB_EXT );
#endif
// only look in the override
void *pMod = SharedLib_Load( sDLLPath.c_str() );
// nothing more to do if we can't load the DLL
if( !pMod )
{
return vr::VRInitError_Init_VRClientDLLNotFound;
}
VRClientCoreFactoryFn fnFactory = ( VRClientCoreFactoryFn )( SharedLib_GetFunction( pMod, "VRClientCoreFactory" ) );
if( !fnFactory )
{
SharedLib_Unload( pMod );
return vr::VRInitError_Init_FactoryNotFound;
}
int nReturnCode = 0;
g_pHmdSystem = static_cast< IVRClientCore * > ( fnFactory( vr::IVRClientCore_Version, &nReturnCode ) );
if( !g_pHmdSystem )
{
SharedLib_Unload( pMod );
return vr::VRInitError_Init_InterfaceNotFound;
}
g_pVRModule = pMod;
return VRInitError_None;
}
void *VR_GetGenericInterface(const char *pchInterfaceVersion, EVRInitError *peError)
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
if (!g_pHmdSystem)
{
if (peError)
*peError = vr::VRInitError_Init_NotInitialized;
return NULL;
}
return g_pHmdSystem->GetGenericInterface(pchInterfaceVersion, peError);
}
bool VR_IsInterfaceVersionValid(const char *pchInterfaceVersion)
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
if (!g_pHmdSystem)
{
return false;
}
return g_pHmdSystem->IsInterfaceVersionValid(pchInterfaceVersion) == VRInitError_None;
}
bool VR_IsHmdPresent()
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
if( g_pHmdSystem )
{
// if we're already initialized, just call through
return g_pHmdSystem->BIsHmdPresent();
}
else
{
// otherwise we need to do a bit more work
EVRInitError err = VR_LoadHmdSystemInternal();
if( err != VRInitError_None )
return false;
bool bHasHmd = g_pHmdSystem->BIsHmdPresent();
g_pHmdSystem = NULL;
SharedLib_Unload( g_pVRModule );
g_pVRModule = NULL;
return bHasHmd;
}
}
/** Returns true if the OpenVR runtime is installed. */
bool VR_IsRuntimeInstalled()
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
if( g_pHmdSystem )
{
// if we're already initialized, OpenVR is obviously installed
return true;
}
else
{
// otherwise we need to do a bit more work
std::string sRuntimePath, sConfigPath, sLogPath;
bool bReadPathRegistry = CVRPathRegistry_Public::GetPaths( &sRuntimePath, &sConfigPath, &sLogPath, NULL, NULL );
if( !bReadPathRegistry )
{
return false;
}
// figure out where we're going to look for vrclient.dll
// see if the specified path actually exists.
if( !Path_IsDirectory( sRuntimePath ) )
{
return false;
}
// the installation may be corrupt in some way, but it certainly looks installed
return true;
}
}
// -------------------------------------------------------------------------------
// Purpose: This is the old Runtime Path interface that is no longer exported in the
// latest header. We still want to export it from the DLL, though, so updating
// to a new DLL doesn't break old compiled code. This version was not thread
// safe and could change the buffer pointer to by a previous result on a
// subsequent call
// -------------------------------------------------------------------------------
VR_EXPORT_INTERFACE const char *VR_CALLTYPE VR_RuntimePath();
/** Returns where OpenVR runtime is installed. */
const char *VR_RuntimePath()
{
static char rchBuffer[1024];
uint32_t unRequiredSize;
if ( VR_GetRuntimePath( rchBuffer, sizeof( rchBuffer ), &unRequiredSize ) && unRequiredSize < sizeof( rchBuffer ) )
{
return rchBuffer;
}
else
{
return nullptr;
}
}
/** Returns where OpenVR runtime is installed. */
bool VR_GetRuntimePath( char *pchPathBuffer, uint32_t unBufferSize, uint32_t *punRequiredBufferSize )
{
// otherwise we need to do a bit more work
std::string sRuntimePath;
*punRequiredBufferSize = 0;
bool bReadPathRegistry = CVRPathRegistry_Public::GetPaths( &sRuntimePath, nullptr, nullptr, nullptr, nullptr );
if ( !bReadPathRegistry )
{
return false;
}
// figure out where we're going to look for vrclient.dll
// see if the specified path actually exists.
if ( !Path_IsDirectory( sRuntimePath ) )
{
return false;
}
*punRequiredBufferSize = (uint32_t)sRuntimePath.size() + 1;
if ( sRuntimePath.size() >= unBufferSize )
{
*pchPathBuffer = '\0';
}
else
{
strcpy_safe( pchPathBuffer, unBufferSize, sRuntimePath.c_str() );
}
return true;
}
/** Returns the symbol version of an HMD error. */
const char *VR_GetVRInitErrorAsSymbol( EVRInitError error )
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
if( g_pHmdSystem )
return g_pHmdSystem->GetIDForVRInitError( error );
else
return GetIDForVRInitError( error );
}
/** Returns the english string version of an HMD error. */
const char *VR_GetVRInitErrorAsEnglishDescription( EVRInitError error )
{
std::lock_guard<std::recursive_mutex> lock( g_mutexSystem );
if ( g_pHmdSystem )
return g_pHmdSystem->GetEnglishStringForHmdError( error );
else
return GetEnglishStringForHmdError( error );
}
VR_INTERFACE const char *VR_CALLTYPE VR_GetStringForHmdError( vr::EVRInitError error );
/** Returns the english string version of an HMD error. */
const char *VR_GetStringForHmdError( EVRInitError error )
{
return VR_GetVRInitErrorAsEnglishDescription( error );
}
}

@ -0,0 +1,101 @@
//========= Copyright Valve Corporation ============//
#include <vrcommon/dirtools_public.h>
#include <vrcommon/strtools_public.h>
#include <vrcommon/pathtools_public.h>
#include <errno.h>
#include <string.h>
#ifdef _WIN32
#include "windows.h"
#else
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif
#if defined( OSX )
#include <sys/syslimits.h>
#endif
//-----------------------------------------------------------------------------
// Purpose: utility function to create dirs & subdirs
//-----------------------------------------------------------------------------
bool BCreateDirectoryRecursive( const char *pchPath )
{
// Does it already exist?
if ( Path_IsDirectory( pchPath ) )
return true;
// copy the path into something we can munge
int len = (int)strlen( pchPath );
char *path = (char *)malloc( len + 1 );
strcpy( path, pchPath );
// Walk backwards to first non-existing dir that we find
char *s = path + len - 1;
const char slash = Path_GetSlash();
while ( s > path )
{
if ( *s == slash )
{
*s = '\0';
bool bExists = Path_IsDirectory( path );
*s = slash;
if ( bExists )
{
++s;
break;
}
}
--s;
}
// and then move forwards from there
while ( *s )
{
if ( *s == slash )
{
*s = '\0';
BCreateDirectory( path );
*s = slash;
}
s++;
}
bool bRetVal = BCreateDirectory( path );
free( path );
return bRetVal;
}
//-----------------------------------------------------------------------------
// Purpose: Creates the directory, returning true if it is created, or if it already existed
//-----------------------------------------------------------------------------
bool BCreateDirectory( const char *pchPath )
{
#ifdef WIN32
std::wstring wPath = UTF8to16( pchPath );
if ( ::CreateDirectoryW( wPath.c_str(), NULL ) )
return true;
if ( ::GetLastError() == ERROR_ALREADY_EXISTS )
return true;
return false;
#else
int i = mkdir( pchPath, S_IRWXU | S_IRWXG | S_IRWXO );
if ( i == 0 )
return true;
if ( errno == EEXIST )
return true;
return false;
#endif
}

@ -0,0 +1,17 @@
//========= Copyright Valve Corporation ============//
#pragma once
#include <stdint.h>
#include <string>
#if !defined(_WIN32)
#include <sys/types.h>
#include <sys/stat.h>
#endif
extern bool BCreateDirectoryRecursive( const char *pchPath );
extern bool BCreateDirectory( const char *pchPath );

@ -0,0 +1,88 @@
//========= Copyright Valve Corporation ============//
#include <vrcommon/envvartools_public.h>
#include <vrcommon/strtools_public.h>
#include <stdlib.h>
#include <string>
#include <cctype>
#if defined(_WIN32)
#include <windows.h>
#undef GetEnvironmentVariable
#undef SetEnvironmentVariable
#endif
std::string GetEnvironmentVariable( const char *pchVarName )
{
#if defined(_WIN32)
char rchValue[32767]; // max size for an env var on Windows
DWORD cChars = GetEnvironmentVariableA( pchVarName, rchValue, sizeof( rchValue ) );
if( cChars == 0 )
return "";
else
return rchValue;
#elif defined(POSIX)
char *pchValue = getenv( pchVarName );
if( pchValue )
return pchValue;
else
return "";
#else
#error "Unsupported Platform"
#endif
}
bool GetEnvironmentVariableAsBool( const char *pchVarName, bool bDefault )
{
std::string sValue = GetEnvironmentVariable( pchVarName );
if ( sValue.empty() )
{
return bDefault;
}
sValue = StringToLower( sValue );
std::string sYesValues[] = { "y", "yes", "true" };
std::string sNoValues[] = { "n", "no", "false" };
for ( std::string &sMatch : sYesValues )
{
if ( sMatch == sValue )
{
return true;
}
}
for ( std::string &sMatch : sNoValues )
{
if ( sMatch == sValue )
{
return false;
}
}
if ( std::isdigit( sValue.at(0) ) )
{
return atoi( sValue.c_str() ) != 0;
}
fprintf( stderr,
"GetEnvironmentVariableAsBool(%s): Unable to parse value '%s', using default %d\n",
pchVarName, sValue.c_str(), bDefault );
return bDefault;
}
bool SetEnvironmentVariable( const char *pchVarName, const char *pchVarValue )
{
#if defined(_WIN32)
return 0 != SetEnvironmentVariableA( pchVarName, pchVarValue );
#elif defined(POSIX)
if( pchVarValue == NULL )
return 0 == unsetenv( pchVarName );
else
return 0 == setenv( pchVarName, pchVarValue, 1 );
#else
#error "Unsupported Platform"
#endif
}

@ -0,0 +1,8 @@
//========= Copyright Valve Corporation ============//
#pragma once
#include <string>
std::string GetEnvironmentVariable( const char *pchVarName );
bool GetEnvironmentVariableAsBool( const char *pchVarName, bool bDefault );
bool SetEnvironmentVariable( const char *pchVarName, const char *pchVarValue );

@ -0,0 +1,338 @@
//========= Copyright Valve Corporation ============//
#include "openvr.h"
#include "hmderrors_public.h"
#include <stdio.h>
#include <algorithm>
using namespace vr;
#define RETURN_ENUM_AS_STRING(enumValue) case enumValue: return #enumValue;
const char *GetEnglishStringForHmdError( vr::EVRInitError eError )
{
switch( eError )
{
case VRInitError_None: return "No Error (0)";
case VRInitError_Init_InstallationNotFound: return "Installation Not Found (100)";
case VRInitError_Init_InstallationCorrupt: return "Installation Corrupt (101)";
case VRInitError_Init_VRClientDLLNotFound: return "vrclient Shared Lib Not Found (102)";
case VRInitError_Init_FileNotFound: return "File Not Found (103)";
case VRInitError_Init_FactoryNotFound: return "Factory Function Not Found (104)";
case VRInitError_Init_InterfaceNotFound: return "Interface Not Found (105)";
case VRInitError_Init_InvalidInterface: return "Invalid Interface (106)";
case VRInitError_Init_UserConfigDirectoryInvalid: return "User Config Directory Invalid (107)";
case VRInitError_Init_HmdNotFound: return "Hmd Not Found (108)";
case VRInitError_Init_NotInitialized: return "Not Initialized (109)";
case VRInitError_Init_PathRegistryNotFound: return "Installation path could not be located (110)";
case VRInitError_Init_NoConfigPath: return "Config path could not be located (111)";
case VRInitError_Init_NoLogPath: return "Log path could not be located (112)";
case VRInitError_Init_PathRegistryNotWritable: return "Unable to write path registry (113)";
case VRInitError_Init_AppInfoInitFailed: return "App info manager init failed (114)";
case VRInitError_Init_Retry: return "Internal Retry (115)";
case VRInitError_Init_InitCanceledByUser: return "User Canceled Init (116)";
case VRInitError_Init_AnotherAppLaunching: return "Another app was already launching (117)";
case VRInitError_Init_SettingsInitFailed: return "Settings manager init failed (118)";
case VRInitError_Init_ShuttingDown: return "VR system shutting down (119)";
case VRInitError_Init_TooManyObjects: return "Too many tracked objects (120)";
case VRInitError_Init_NoServerForBackgroundApp: return "Not starting vrserver for background app (121)";
case VRInitError_Init_NotSupportedWithCompositor: return "The requested interface is incompatible with the compositor and the compositor is running (122)";
case VRInitError_Init_NotAvailableToUtilityApps: return "This interface is not available to utility applications (123)";
case VRInitError_Init_Internal: return "vrserver internal error (124)";
case VRInitError_Init_HmdDriverIdIsNone: return "Hmd DriverId is invalid (125)";
case VRInitError_Init_HmdNotFoundPresenceFailed: return "Hmd Not Found Presence Failed (126)";
case VRInitError_Init_VRMonitorNotFound: return "VR Monitor Not Found (127)";
case VRInitError_Init_VRMonitorStartupFailed: return "VR Monitor startup failed (128)";
case VRInitError_Init_LowPowerWatchdogNotSupported: return "Low Power Watchdog Not Supported (129)";
case VRInitError_Init_InvalidApplicationType: return "Invalid Application Type (130)";
case VRInitError_Init_NotAvailableToWatchdogApps: return "Not available to watchdog apps (131)";
case VRInitError_Init_WatchdogDisabledInSettings: return "Watchdog disabled in settings (132)";
case VRInitError_Init_VRDashboardNotFound: return "VR Dashboard Not Found (133)";
case VRInitError_Init_VRDashboardStartupFailed: return "VR Dashboard startup failed (134)";
case VRInitError_Init_VRHomeNotFound: return "VR Home Not Found (135)";
case VRInitError_Init_VRHomeStartupFailed: return "VR home startup failed (136)";
case VRInitError_Init_RebootingBusy: return "Rebooting In Progress (137)";
case VRInitError_Init_FirmwareUpdateBusy: return "Firmware Update In Progress (138)";
case VRInitError_Init_FirmwareRecoveryBusy: return "Firmware Recovery In Progress (139)";
case VRInitError_Init_USBServiceBusy: return "USB Service Busy (140)";
case VRInitError_Driver_Failed: return "Driver Failed (200)";
case VRInitError_Driver_Unknown: return "Driver Not Known (201)";
case VRInitError_Driver_HmdUnknown: return "HMD Not Known (202)";
case VRInitError_Driver_NotLoaded: return "Driver Not Loaded (203)";
case VRInitError_Driver_RuntimeOutOfDate: return "Driver runtime is out of date (204)";
case VRInitError_Driver_HmdInUse: return "HMD already in use by another application (205)";
case VRInitError_Driver_NotCalibrated: return "Device is not calibrated (206)";
case VRInitError_Driver_CalibrationInvalid: return "Device Calibration is invalid (207)";
case VRInitError_Driver_HmdDisplayNotFound: return "HMD detected over USB, but Monitor not found (208)";
case VRInitError_Driver_TrackedDeviceInterfaceUnknown: return "Driver Tracked Device Interface unknown (209)";
// case VRInitError_Driver_HmdDisplayNotFoundAfterFix: return "HMD detected over USB, but Monitor not found after attempt to fix (210)"; // taken out upon Ben's request: He thinks that there is no need to separate that error from 208
case VRInitError_Driver_HmdDriverIdOutOfBounds: return "Hmd DriverId is our of bounds (211)";
case VRInitError_Driver_HmdDisplayMirrored: return "HMD detected over USB, but Monitor may be mirrored instead of extended (212)";
case VRInitError_Driver_HmdDisplayNotFoundLaptop: return "On laptop, HMD detected over USB, but Monitor not found (213)";
case VRInitError_IPC_ServerInitFailed: return "VR Server Init Failed (300)";
case VRInitError_IPC_ConnectFailed: return "Connect to VR Server Failed (301)";
case VRInitError_IPC_SharedStateInitFailed: return "Shared IPC State Init Failed (302)";
case VRInitError_IPC_CompositorInitFailed: return "Shared IPC Compositor Init Failed (303)";
case VRInitError_IPC_MutexInitFailed: return "Shared IPC Mutex Init Failed (304)";
case VRInitError_IPC_Failed: return "Shared IPC Failed (305)";
case VRInitError_IPC_CompositorConnectFailed: return "Shared IPC Compositor Connect Failed (306)";
case VRInitError_IPC_CompositorInvalidConnectResponse: return "Shared IPC Compositor Invalid Connect Response (307)";
case VRInitError_IPC_ConnectFailedAfterMultipleAttempts: return "Shared IPC Connect Failed After Multiple Attempts (308)";
case VRInitError_IPC_ConnectFailedAfterTargetExited: return "Shared IPC Connect Failed After Target Exited (309)";
case VRInitError_IPC_NamespaceUnavailable: return "Shared IPC Namespace Unavailable (310)";
case VRInitError_Compositor_Failed: return "Compositor failed to initialize (400)";
case VRInitError_Compositor_D3D11HardwareRequired: return "Compositor failed to find DX11 hardware (401)";
case VRInitError_Compositor_FirmwareRequiresUpdate: return "Compositor requires mandatory firmware update (402)";
case VRInitError_Compositor_OverlayInitFailed: return "Compositor initialization succeeded, but overlay init failed (403)";
case VRInitError_Compositor_ScreenshotsInitFailed: return "Compositor initialization succeeded, but screenshot init failed (404)";
case VRInitError_Compositor_UnableToCreateDevice: return "Compositor unable to create graphics device (405)";
// Oculus
case VRInitError_VendorSpecific_UnableToConnectToOculusRuntime: return "Unable to connect to Oculus Runtime (1000)";
case VRInitError_VendorSpecific_OculusRuntimeBadInstall: return "Unable to connect to Oculus Runtime, possible bad install (1114)";
// Lighthouse
case VRInitError_VendorSpecific_HmdFound_CantOpenDevice: return "HMD found, but can not open device (1101)";
case VRInitError_VendorSpecific_HmdFound_UnableToRequestConfigStart: return "HMD found, but unable to request config (1102)";
case VRInitError_VendorSpecific_HmdFound_NoStoredConfig: return "HMD found, but no stored config (1103)";
case VRInitError_VendorSpecific_HmdFound_ConfigFailedSanityCheck: return "HMD found, but failed configuration check (1113)";
case VRInitError_VendorSpecific_HmdFound_ConfigTooBig: return "HMD found, but config too big (1104)";
case VRInitError_VendorSpecific_HmdFound_ConfigTooSmall: return "HMD found, but config too small (1105)";
case VRInitError_VendorSpecific_HmdFound_UnableToInitZLib: return "HMD found, but unable to init ZLib (1106)";
case VRInitError_VendorSpecific_HmdFound_CantReadFirmwareVersion: return "HMD found, but problems with the data (1107)";
case VRInitError_VendorSpecific_HmdFound_UnableToSendUserDataStart: return "HMD found, but problems with the data (1108)";
case VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataStart: return "HMD found, but problems with the data (1109)";
case VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataNext: return "HMD found, but problems with the data (1110)";
case VRInitError_VendorSpecific_HmdFound_UserDataAddressRange: return "HMD found, but problems with the data (1111)";
case VRInitError_VendorSpecific_HmdFound_UserDataError: return "HMD found, but problems with the data (1112)";
case VRInitError_Steam_SteamInstallationNotFound: return "Unable to find Steam installation (2000)";
default:
return GetIDForVRInitError( eError );
}
}
const char *GetIDForVRInitError( vr::EVRInitError eError )
{
switch( eError )
{
RETURN_ENUM_AS_STRING( VRInitError_None );
RETURN_ENUM_AS_STRING( VRInitError_Unknown );
RETURN_ENUM_AS_STRING( VRInitError_Init_InstallationNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_InstallationCorrupt );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRClientDLLNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_FileNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_FactoryNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_InterfaceNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_InvalidInterface );
RETURN_ENUM_AS_STRING( VRInitError_Init_UserConfigDirectoryInvalid );
RETURN_ENUM_AS_STRING( VRInitError_Init_HmdNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_NotInitialized );
RETURN_ENUM_AS_STRING( VRInitError_Init_PathRegistryNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_NoConfigPath );
RETURN_ENUM_AS_STRING( VRInitError_Init_NoLogPath );
RETURN_ENUM_AS_STRING( VRInitError_Init_PathRegistryNotWritable );
RETURN_ENUM_AS_STRING( VRInitError_Init_AppInfoInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_Retry );
RETURN_ENUM_AS_STRING( VRInitError_Init_InitCanceledByUser );
RETURN_ENUM_AS_STRING( VRInitError_Init_AnotherAppLaunching );
RETURN_ENUM_AS_STRING( VRInitError_Init_SettingsInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_ShuttingDown );
RETURN_ENUM_AS_STRING( VRInitError_Init_TooManyObjects );
RETURN_ENUM_AS_STRING( VRInitError_Init_NoServerForBackgroundApp );
RETURN_ENUM_AS_STRING( VRInitError_Init_NotSupportedWithCompositor );
RETURN_ENUM_AS_STRING( VRInitError_Init_NotAvailableToUtilityApps );
RETURN_ENUM_AS_STRING( VRInitError_Init_Internal );
RETURN_ENUM_AS_STRING( VRInitError_Init_HmdDriverIdIsNone );
RETURN_ENUM_AS_STRING( VRInitError_Init_HmdNotFoundPresenceFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRMonitorNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRMonitorStartupFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_LowPowerWatchdogNotSupported );
RETURN_ENUM_AS_STRING( VRInitError_Init_InvalidApplicationType );
RETURN_ENUM_AS_STRING( VRInitError_Init_NotAvailableToWatchdogApps );
RETURN_ENUM_AS_STRING( VRInitError_Init_WatchdogDisabledInSettings );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRDashboardStartupFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRHomeNotFound );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRHomeStartupFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_RebootingBusy );
RETURN_ENUM_AS_STRING( VRInitError_Init_FirmwareUpdateBusy );
RETURN_ENUM_AS_STRING( VRInitError_Init_FirmwareRecoveryBusy );
RETURN_ENUM_AS_STRING( VRInitError_Init_USBServiceBusy );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRWebHelperStartupFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_TrackerManagerInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_AlreadyRunning );
RETURN_ENUM_AS_STRING( VRInitError_Init_FailedForVrMonitor);
RETURN_ENUM_AS_STRING( VRInitError_Init_PropertyManagerInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_WebServerFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_IllegalTypeTransition );
RETURN_ENUM_AS_STRING( VRInitError_Init_MismatchedRuntimes );
RETURN_ENUM_AS_STRING( VRInitError_Init_InvalidProcessId );
RETURN_ENUM_AS_STRING( VRInitError_Init_VRServiceStartupFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_PrismNeedsNewDrivers );
RETURN_ENUM_AS_STRING( VRInitError_Init_PrismStartupTimedOut );
RETURN_ENUM_AS_STRING( VRInitError_Init_CouldNotStartPrism );
RETURN_ENUM_AS_STRING( VRInitError_Init_CreateDriverDirectDeviceFailed );
RETURN_ENUM_AS_STRING( VRInitError_Init_PrismExitedUnexpectedly );
RETURN_ENUM_AS_STRING( VRInitError_Driver_Failed );
RETURN_ENUM_AS_STRING( VRInitError_Driver_Unknown );
RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdUnknown);
RETURN_ENUM_AS_STRING( VRInitError_Driver_NotLoaded);
RETURN_ENUM_AS_STRING( VRInitError_Driver_RuntimeOutOfDate);
RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdInUse);
RETURN_ENUM_AS_STRING( VRInitError_Driver_NotCalibrated);
RETURN_ENUM_AS_STRING( VRInitError_Driver_CalibrationInvalid);
RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayNotFound);
RETURN_ENUM_AS_STRING( VRInitError_Driver_TrackedDeviceInterfaceUnknown );
// RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayNotFoundAfterFix );
RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDriverIdOutOfBounds );
RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayMirrored );
RETURN_ENUM_AS_STRING( VRInitError_Driver_HmdDisplayNotFoundLaptop );
RETURN_ENUM_AS_STRING( VRInitError_IPC_ServerInitFailed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_ConnectFailed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_SharedStateInitFailed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_CompositorInitFailed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_MutexInitFailed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_Failed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_CompositorConnectFailed);
RETURN_ENUM_AS_STRING( VRInitError_IPC_CompositorInvalidConnectResponse);
RETURN_ENUM_AS_STRING( VRInitError_IPC_ConnectFailedAfterMultipleAttempts );
RETURN_ENUM_AS_STRING( VRInitError_IPC_ConnectFailedAfterTargetExited );
RETURN_ENUM_AS_STRING( VRInitError_IPC_NamespaceUnavailable );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_Failed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_D3D11HardwareRequired );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FirmwareRequiresUpdate );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_OverlayInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_ScreenshotsInitFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_UnableToCreateDevice );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_SharedStateIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_NotificationManagerIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_ResourceManagerClientIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_MessageOverlaySharedStateInitFailure );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_PropertiesInterfaceIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFullscreenWindowFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_SettingsInterfaceIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToShowWindow );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_DistortInterfaceIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_DisplayFrequencyFailure );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_RendererInitializationFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_DXGIFactoryInterfaceIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_DXGIFactoryCreateFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_DXGIFactoryQueryFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidAdapterDesktop );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidHmdAttachment );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidOutputDesktop );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidDeviceProvided );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_D3D11RendererInitializationFailed );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToFindDisplayMode );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateSwapChain );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToGetBackBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateRenderTarget );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateDXGI2SwapChain );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedtoGetDXGI2BackBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateDXGI2RenderTarget );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToGetDXGIDeviceInterface );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_SelectDisplayMode );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateNvAPIRenderTargets );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_NvAPISetDisplayMode );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateDirectModeDisplay );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_InvalidHmdPropertyContainer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_UpdateDisplayFrequency );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateRasterizerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateWireframeRasterizerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateSamplerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateClampToBorderSamplerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateAnisoSamplerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlaySamplerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreatePanoramaSamplerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFontSamplerState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateNoBlendState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateAlphaBlendState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendStateMaskR );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendStateMaskG );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateBlendStateMaskB );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDepthStencilState );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDepthStencilStateNoWrite );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDepthStencilStateNoDepth );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFlushTexture );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDistortionSurfaces );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateHmdPoseConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateHmdPoseStagingConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateSharedFrameInfoConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateSceneTextureIndexConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateReadableSceneTextureIndexConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLayerGraphicsTextureIndexConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLayerComputeTextureIndexConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLayerComputeSceneTextureIndexConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateComputeHmdPoseConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateGeomConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreatePanelMaskConstantBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreatePixelSimUBO );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateMSAARenderTextures );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateResolveRenderTextures );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateComputeResolveRenderTextures );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateDriverDirectModeResolveTextures );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_OpenDriverDirectModeResolveTextures );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateFallbackSyncTexture );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_ShareFallbackSyncTexture );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayIndexBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayVertexBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateTextVertexBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateTextIndexBuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateMirrorTextures );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateLastFrameRenderTexture );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateMirrorOverlay );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateVirtualDisplayBackbuffer );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_DisplayModeNotSupported );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayInvalidCall );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_CreateOverlayAlreadyInitialized );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_FailedToCreateMailbox );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_WindowInterfaceIsNull );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_SystemLayerCreateInstance );
RETURN_ENUM_AS_STRING( VRInitError_Compositor_SystemLayerCreateSession );
// Vendor-specific errors
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_UnableToConnectToOculusRuntime);
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_WindowsNotInDevMode );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_OculusRuntimeBadInstall );
// Lighthouse
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_CantOpenDevice);
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToRequestConfigStart);
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_NoStoredConfig);
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_ConfigFailedSanityCheck );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_ConfigTooBig );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_ConfigTooSmall );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToInitZLib );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_CantReadFirmwareVersion );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToSendUserDataStart );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataStart );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UnableToGetUserDataNext );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UserDataAddressRange );
RETURN_ENUM_AS_STRING( VRInitError_VendorSpecific_HmdFound_UserDataError );
RETURN_ENUM_AS_STRING( VRInitError_Steam_SteamInstallationNotFound );
default:
{
static char buf[128];
sprintf( buf, "Unknown error (%d)", eError );
return buf;
}
}
}

@ -0,0 +1,6 @@
//========= Copyright Valve Corporation ============//
#pragma once
const char *GetEnglishStringForHmdError( vr::EVRInitError eError );
const char *GetIDForVRInitError( vr::EVRInitError eError );

@ -0,0 +1,988 @@
//========= Copyright Valve Corporation ============//
#include <vrcommon/strtools_public.h>
#include <vrcommon/pathtools_public.h>
#if defined( _WIN32)
#include <windows.h>
#include <direct.h>
#include <shobjidl.h>
#include <knownfolders.h>
#include <shlobj.h>
#include <share.h>
#undef GetEnvironmentVariable
#else
#include <dlfcn.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <alloca.h>
#endif
#if defined OSX
#include <Foundation/Foundation.h>
#include <AppKit/AppKit.h>
#include <mach-o/dyld.h>
#define _S_IFDIR S_IFDIR // really from tier0/platform.h which we dont have yet
#endif
#include <sys/stat.h>
#include <algorithm>
/** Returns the path (including filename) to the current executable */
std::string Path_GetExecutablePath()
{
#if defined( _WIN32 )
wchar_t *pwchPath = new wchar_t[MAX_UNICODE_PATH];
char *pchPath = new char[MAX_UNICODE_PATH_IN_UTF8];
::GetModuleFileNameW( NULL, pwchPath, MAX_UNICODE_PATH );
WideCharToMultiByte( CP_UTF8, 0, pwchPath, -1, pchPath, MAX_UNICODE_PATH_IN_UTF8, NULL, NULL );
delete[] pwchPath;
std::string sPath = pchPath;
delete[] pchPath;
return sPath;
#elif defined( OSX )
char rchPath[1024];
uint32_t nBuff = sizeof( rchPath );
bool bSuccess = _NSGetExecutablePath(rchPath, &nBuff) == 0;
rchPath[nBuff-1] = '\0';
if( bSuccess )
return rchPath;
else
return "";
#elif defined LINUX
char rchPath[1024];
size_t nBuff = sizeof( rchPath );
ssize_t nRead = readlink("/proc/self/exe", rchPath, nBuff-1 );
if ( nRead != -1 )
{
rchPath[ nRead ] = 0;
return rchPath;
}
else
{
return "";
}
#else
AssertMsg( false, "Implement Plat_GetExecutablePath" );
return "";
#endif
}
/** Returns the path of the current working directory */
std::string Path_GetWorkingDirectory()
{
std::string sPath;
#if defined( _WIN32 )
wchar_t buf[MAX_UNICODE_PATH];
sPath = UTF16to8( _wgetcwd( buf, MAX_UNICODE_PATH ) );
#else
char buf[ 1024 ];
sPath = getcwd( buf, sizeof( buf ) );
#endif
return sPath;
}
/** Sets the path of the current working directory. Returns true if this was successful. */
bool Path_SetWorkingDirectory( const std::string & sPath )
{
bool bSuccess;
#if defined( _WIN32 )
std::wstring wsPath = UTF8to16( sPath.c_str() );
bSuccess = 0 == _wchdir( wsPath.c_str() );
#else
bSuccess = 0 == chdir( sPath.c_str() );
#endif
return bSuccess;
}
/** Gets the path to a temporary directory. */
std::string Path_GetTemporaryDirectory()
{
#if defined( _WIN32 )
wchar_t buf[MAX_UNICODE_PATH];
if ( GetTempPathW( MAX_UNICODE_PATH, buf ) == 0 )
return Path_GetWorkingDirectory();
return UTF16to8( buf );
#else
const char *pchTmpDir = getenv( "TMPDIR" );
if ( pchTmpDir == NULL )
{
return "";
}
return pchTmpDir;
#endif
}
/** Returns the specified path without its filename */
std::string Path_StripFilename( const std::string & sPath, char slash )
{
if( slash == 0 )
slash = Path_GetSlash();
std::string::size_type n = sPath.find_last_of( slash );
if( n == std::string::npos )
return sPath;
else
return std::string( sPath.begin(), sPath.begin() + n );
}
/** returns just the filename from the provided full or relative path. */
std::string Path_StripDirectory( const std::string & sPath, char slash )
{
if( slash == 0 )
slash = Path_GetSlash();
std::string::size_type n = sPath.find_last_of( slash );
if( n == std::string::npos )
return sPath;
else
return std::string( sPath.begin() + n + 1, sPath.end() );
}
/** returns just the filename with no extension of the provided filename.
* If there is a path the path is left intact. */
std::string Path_StripExtension( const std::string & sPath )
{
for( std::string::const_reverse_iterator i = sPath.rbegin(); i != sPath.rend(); i++ )
{
if( *i == '.' )
{
return std::string( sPath.begin(), i.base() - 1 );
}
// if we find a slash there is no extension
if( *i == '\\' || *i == '/' )
break;
}
// we didn't find an extension
return sPath;
}
/** returns just extension of the provided filename (if any). */
std::string Path_GetExtension( const std::string & sPath )
{
for ( std::string::const_reverse_iterator i = sPath.rbegin(); i != sPath.rend(); i++ )
{
if ( *i == '.' )
{
return std::string( i.base(), sPath.end() );
}
// if we find a slash there is no extension
if ( *i == '\\' || *i == '/' )
break;
}
// we didn't find an extension
return "";
}
bool Path_IsAbsolute( const std::string & sPath )
{
if( sPath.empty() )
return false;
#ifdef _WIN32
if ( sPath.size() < 3 ) // must be c:\x or \\x at least
return false;
if ( sPath[1] == ':' ) // drive letter plus slash, but must test both slash cases
{
if ( sPath[2] == '\\' || sPath[2] == '/' )
return true;
}
else if ( sPath[0] == '\\' && sPath[1] == '\\' ) // UNC path
return true;
#else
if( sPath[0] == '\\' || sPath[0] == '/' ) // any leading slash
return true;
#endif
return false;
}
/** Makes an absolute path from a relative path and a base path */
std::string Path_MakeAbsolute( const std::string & sRelativePath, const std::string & sBasePath )
{
if( Path_IsAbsolute( sRelativePath ) )
return Path_Compact( sRelativePath );
else
{
if( !Path_IsAbsolute( sBasePath ) )
return "";
std::string sCompacted = Path_Compact( Path_Join( sBasePath, sRelativePath ) );
if( Path_IsAbsolute( sCompacted ) )
return sCompacted;
else
return "";
}
}
/** Fixes the directory separators for the current platform */
std::string Path_FixSlashes( const std::string & sPath, char slash )
{
if( slash == 0 )
slash = Path_GetSlash();
std::string sFixed = sPath;
for( std::string::iterator i = sFixed.begin(); i != sFixed.end(); i++ )
{
if( *i == '/' || *i == '\\' )
*i = slash;
}
return sFixed;
}
char Path_GetSlash()
{
#if defined(_WIN32)
return '\\';
#else
return '/';
#endif
}
/** Jams two paths together with the right kind of slash */
std::string Path_Join( const std::string & first, const std::string & second, char slash )
{
if( slash == 0 )
slash = Path_GetSlash();
// only insert a slash if we don't already have one
std::string::size_type nLen = first.length();
if( !nLen )
return second;
#if defined(_WIN32)
if( first.back() == '\\' || first.back() == '/' )
nLen--;
#else
char last_char = first[first.length()-1];
if (last_char == '\\' || last_char == '/')
nLen--;
#endif
return first.substr( 0, nLen ) + std::string( 1, slash ) + second;
}
std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, char slash )
{
return Path_Join( Path_Join( first, second, slash ), third, slash );
}
std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, const std::string &fourth, char slash )
{
return Path_Join( Path_Join( Path_Join( first, second, slash ), third, slash ), fourth, slash );
}
std::string Path_Join(
const std::string & first,
const std::string & second,
const std::string & third,
const std::string & fourth,
const std::string & fifth,
char slash )
{
return Path_Join( Path_Join( Path_Join( Path_Join( first, second, slash ), third, slash ), fourth, slash ), fifth, slash );
}
std::string Path_RemoveTrailingSlash( const std::string & sRawPath, char slash )
{
if ( slash == 0 )
slash = Path_GetSlash();
std::string sPath = sRawPath;
std::string::size_type nCurrent = sRawPath.length();
if ( nCurrent == 0 )
return sPath;
int nLastFound = -1;
nCurrent--;
while( nCurrent != 0 )
{
if ( sRawPath[ nCurrent ] == slash )
{
nLastFound = (int)nCurrent;
nCurrent--;
}
else
{
break;
}
}
if ( nLastFound >= 0 )
{
sPath.erase( nLastFound, std::string::npos );
}
return sPath;
}
/** Removes redundant <dir>/.. elements in the path. Returns an empty path if the
* specified path has a broken number of directories for its number of ..s */
std::string Path_Compact( const std::string & sRawPath, char slash )
{
if( slash == 0 )
slash = Path_GetSlash();
std::string sPath = Path_FixSlashes( sRawPath, slash );
std::string sSlashString( 1, slash );
// strip out all /./
for( std::string::size_type i = 0; (i + 3) < sPath.length(); )
{
if( sPath[ i ] == slash && sPath[ i+1 ] == '.' && sPath[ i+2 ] == slash )
{
sPath.replace( i, 3, sSlashString );
}
else
{
++i;
}
}
// get rid of trailing /. but leave the path separator
if( sPath.length() > 2 )
{
std::string::size_type len = sPath.length();
if( sPath[ len-1 ] == '.' && sPath[ len-2 ] == slash )
{
sPath.pop_back();
//Not sure why the following line of code was used for a while. It causes problems with strlen.
//sPath[len-1] = 0; // for now, at least
}
}
// get rid of leading ./
if( sPath.length() > 2 )
{
if( sPath[ 0 ] == '.' && sPath[ 1 ] == slash )
{
sPath.replace( 0, 2, "" );
}
}
// each time we encounter .. back up until we've found the previous directory name
// then get rid of both
std::string::size_type i = 0;
while( i < sPath.length() )
{
if( i > 0 && sPath.length() - i >= 2
&& sPath[i] == '.'
&& sPath[i+1] == '.'
&& ( i + 2 == sPath.length() || sPath[ i+2 ] == slash )
&& sPath[ i-1 ] == slash )
{
// check if we've hit the start of the string and have a bogus path
if( i == 1 )
return "";
// find the separator before i-1
std::string::size_type iDirStart = i-2;
while( iDirStart > 0 && sPath[ iDirStart - 1 ] != slash )
--iDirStart;
// remove everything from iDirStart to i+2
sPath.replace( iDirStart, (i - iDirStart) + 3, "" );
// start over
i = 0;
}
else
{
++i;
}
}
return sPath;
}
/** Returns true if these two paths are the same without respect for internal . or ..
* sequences, slash type, or case (on case-insensitive platforms). */
bool Path_IsSamePath( const std::string & sPath1, const std::string & sPath2 )
{
std::string sCompact1 = Path_Compact( sPath1 );
std::string sCompact2 = Path_Compact( sPath2 );
#if defined(WIN32)
return !stricmp( sCompact1.c_str(), sCompact2.c_str() );
#else
return !strcmp( sCompact1.c_str(), sCompact2.c_str() );
#endif
}
/** Returns the path to the current DLL or exe */
std::string Path_GetThisModulePath()
{
// gets the path of vrclient.dll itself
#ifdef WIN32
HMODULE hmodule = NULL;
::GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast<LPCTSTR>(Path_GetThisModulePath), &hmodule );
wchar_t *pwchPath = new wchar_t[MAX_UNICODE_PATH];
char *pchPath = new char[ MAX_UNICODE_PATH_IN_UTF8 ];
::GetModuleFileNameW( hmodule, pwchPath, MAX_UNICODE_PATH );
WideCharToMultiByte( CP_UTF8, 0, pwchPath, -1, pchPath, MAX_UNICODE_PATH_IN_UTF8, NULL, NULL );
delete[] pwchPath;
std::string sPath = pchPath;
delete [] pchPath;
return sPath;
#elif defined( OSX ) || defined( LINUX )
// get the addr of a function in vrclient.so and then ask the dlopen system about it
Dl_info info;
dladdr( (void *)Path_GetThisModulePath, &info );
return info.dli_fname;
#endif
}
/** returns true if the specified path exists and is a directory */
bool Path_IsDirectory( const std::string & sPath )
{
std::string sFixedPath = Path_FixSlashes( sPath );
if( sFixedPath.empty() )
return false;
char cLast = sFixedPath[ sFixedPath.length() - 1 ];
if( cLast == '/' || cLast == '\\' )
sFixedPath.erase( sFixedPath.end() - 1, sFixedPath.end() );
// see if the specified path actually exists.
#if defined(POSIX)
struct stat buf;
if ( stat( sFixedPath.c_str(), &buf ) == -1 )
{
return false;
}
#if defined( LINUX ) || defined( OSX )
return S_ISDIR( buf.st_mode );
#else
return (buf.st_mode & _S_IFDIR) != 0;
#endif
#else
struct _stat buf;
std::wstring wsFixedPath = UTF8to16( sFixedPath.c_str() );
if ( _wstat( wsFixedPath.c_str(), &buf ) == -1 )
{
return false;
}
return (buf.st_mode & _S_IFDIR) != 0;
#endif
}
/** returns true if the specified path represents an app bundle */
bool Path_IsAppBundle( const std::string & sPath )
{
#if defined(OSX)
@autoreleasepool {
NSBundle *bundle = [ NSBundle bundleWithPath: [ NSString stringWithUTF8String:sPath.c_str() ] ];
bool bisAppBundle = ( nullptr != bundle );
return bisAppBundle;
}
#else
return false;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: returns true if the the path exists
//-----------------------------------------------------------------------------
bool Path_Exists( const std::string & sPath )
{
std::string sFixedPath = Path_FixSlashes( sPath );
if( sFixedPath.empty() )
return false;
#ifdef _WIN32
struct _stat buf;
std::wstring wsFixedPath = UTF8to16( sFixedPath.c_str() );
if ( _wstat( wsFixedPath.c_str(), &buf ) == -1 )
{
return false;
}
#else
struct stat buf;
if ( stat ( sFixedPath.c_str(), &buf ) == -1)
{
return false;
}
#endif
return true;
}
//-----------------------------------------------------------------------------
// Purpose: helper to find a directory upstream from a given path
//-----------------------------------------------------------------------------
std::string Path_FindParentDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName )
{
std::string strFoundPath = "";
std::string strCurrentPath = Path_FixSlashes( strStartDirectory );
if ( strCurrentPath.length() == 0 )
return "";
bool bExists = Path_Exists( strCurrentPath );
std::string strCurrentDirectoryName = Path_StripDirectory( strCurrentPath );
if ( bExists && stricmp( strCurrentDirectoryName.c_str(), strDirectoryName.c_str() ) == 0 )
return strCurrentPath;
while( bExists && strCurrentPath.length() != 0 )
{
strCurrentPath = Path_StripFilename( strCurrentPath );
strCurrentDirectoryName = Path_StripDirectory( strCurrentPath );
bExists = Path_Exists( strCurrentPath );
if ( bExists && stricmp( strCurrentDirectoryName.c_str(), strDirectoryName.c_str() ) == 0 )
return strCurrentPath;
}
return "";
}
//-----------------------------------------------------------------------------
// Purpose: helper to find a subdirectory upstream from a given path
//-----------------------------------------------------------------------------
std::string Path_FindParentSubDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName )
{
std::string strFoundPath = "";
std::string strCurrentPath = Path_FixSlashes( strStartDirectory );
if ( strCurrentPath.length() == 0 )
return "";
bool bExists = Path_Exists( strCurrentPath );
while( bExists && strCurrentPath.length() != 0 )
{
strCurrentPath = Path_StripFilename( strCurrentPath );
bExists = Path_Exists( strCurrentPath );
if( Path_Exists( Path_Join( strCurrentPath, strDirectoryName ) ) )
{
strFoundPath = Path_Join( strCurrentPath, strDirectoryName );
break;
}
}
return strFoundPath;
}
//-----------------------------------------------------------------------------
// Purpose: reading and writing files in the vortex directory
//-----------------------------------------------------------------------------
std::vector<uint8_t> Path_ReadBinaryFile( const std::string & strFilename )
{
FILE *f;
#if defined( POSIX )
f = fopen( strFilename.c_str(), "rb" );
#else
std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
// the open operation needs to be sharable, therefore use of _wfsopen instead of _wfopen_s
f = _wfsopen( wstrFilename.c_str(), L"rb", _SH_DENYNO );
#endif
std::vector<uint8_t> vecFileContents;
if ( f != NULL )
{
fseek( f, 0, SEEK_END );
int size = ftell( f );
fseek( f, 0, SEEK_SET );
vecFileContents.resize( size );
if ( fread( &vecFileContents[ 0 ], size, 1, f ) == 1 )
{
}
else
{
vecFileContents.clear();
}
fclose( f );
}
return vecFileContents ;
}
unsigned char * Path_ReadBinaryFile( const std::string &strFilename, int *pSize )
{
FILE *f;
#if defined( POSIX )
f = fopen( strFilename.c_str(), "rb" );
#else
std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
// the open operation needs to be sharable, therefore use of _wfsopen instead of _wfopen_s
f = _wfsopen( wstrFilename.c_str(), L"rb", _SH_DENYNO );
#endif
unsigned char* buf = NULL;
if ( f != NULL )
{
fseek(f, 0, SEEK_END);
int size = ftell(f);
fseek(f, 0, SEEK_SET);
buf = new unsigned char[size];
if (buf && fread(buf, size, 1, f) == 1)
{
if (pSize)
*pSize = size;
}
else
{
delete[] buf;
buf = 0;
}
fclose(f);
}
return buf;
}
uint32_t Path_ReadBinaryFile( const std::string &strFilename, unsigned char *pBuffer, uint32_t unSize )
{
FILE *f;
#if defined( POSIX )
f = fopen( strFilename.c_str(), "rb" );
#else
std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"rb" );
if ( err != 0 )
{
f = NULL;
}
#endif
uint32_t unSizeToReturn = 0;
if ( f != NULL )
{
fseek( f, 0, SEEK_END );
uint32_t size = (uint32_t)ftell( f );
fseek( f, 0, SEEK_SET );
if ( size > unSize || !pBuffer )
{
unSizeToReturn = (uint32_t)size;
}
else
{
if ( fread( pBuffer, size, 1, f ) == 1 )
{
unSizeToReturn = (uint32_t)size;
}
}
fclose( f );
}
return unSizeToReturn;
}
bool Path_WriteBinaryFile(const std::string &strFilename, unsigned char *pData, unsigned nSize)
{
FILE *f;
#if defined( POSIX )
f = fopen(strFilename.c_str(), "wb");
#else
std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"wb" );
if (err != 0)
{
f = NULL;
}
#endif
size_t written = 0;
if (f != NULL) {
written = fwrite(pData, sizeof(unsigned char), nSize, f);
fclose(f);
}
return written == nSize ? true : false;
}
std::string Path_ReadTextFile( const std::string &strFilename )
{
// doing it this way seems backwards, but I don't
// see an easy way to do this with C/C++ style IO
// that isn't worse...
int size;
unsigned char* buf = Path_ReadBinaryFile( strFilename, &size );
if (!buf)
return "";
// convert CRLF -> LF
size_t outsize = 1;
for (int i=1; i < size; i++)
{
if (buf[i] == '\n' && buf[i-1] == '\r') // CRLF
buf[outsize-1] = '\n'; // ->LF
else
buf[outsize++] = buf[i]; // just copy
}
std::string ret((char *)buf, outsize);
delete[] buf;
return ret;
}
bool Path_MakeWritable( const std::string &strFilename )
{
#if defined ( _WIN32 )
std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
DWORD dwAttrs = GetFileAttributesW( wstrFilename.c_str() );
if ( dwAttrs != INVALID_FILE_ATTRIBUTES && ( dwAttrs & FILE_ATTRIBUTE_READONLY ) )
{
return SetFileAttributesW( wstrFilename.c_str(), dwAttrs & ~FILE_ATTRIBUTE_READONLY );
}
#else
struct stat sb;
if ( stat( strFilename.c_str(), &sb ) == 0 && !( sb.st_mode & S_IWUSR ) )
{
return ( chmod( strFilename.c_str(), sb.st_mode | S_IWUSR ) == 0 );
}
#endif
return true;
}
bool Path_WriteStringToTextFile( const std::string &strFilename, const char *pchData )
{
FILE *f;
#if defined( POSIX )
f = fopen( strFilename.c_str(), "w" );
#else
std::wstring wstrFilename = UTF8to16( strFilename.c_str() );
errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"w" );
if ( err != 0 )
{
f = NULL;
}
#endif
bool ok = false;
if ( f != NULL )
{
ok = fputs( pchData, f) >= 0;
fclose(f);
}
return ok;
}
bool Path_WriteStringToTextFileAtomic( const std::string &strFilename, const char *pchData )
{
std::string strTmpFilename = strFilename + ".tmp";
if ( !Path_WriteStringToTextFile( strTmpFilename, pchData ) )
return false;
// Platform specific atomic file replacement
#if defined( _WIN32 )
std::wstring wsFilename = UTF8to16( strFilename.c_str() );
std::wstring wsTmpFilename = UTF8to16( strTmpFilename.c_str() );
if ( !::ReplaceFileW( wsFilename.c_str(), wsTmpFilename.c_str(), nullptr, 0, 0, 0 ) )
{
// if we couldn't ReplaceFile, try a non-atomic write as a fallback
if ( !Path_WriteStringToTextFile( strFilename, pchData ) )
return false;
}
#elif defined( POSIX )
if ( rename( strTmpFilename.c_str(), strFilename.c_str() ) == -1 )
return false;
#else
#error Do not know how to write atomic file
#endif
return true;
}
#if defined(WIN32)
#define FILE_URL_PREFIX "file:///"
#else
#define FILE_URL_PREFIX "file://"
#endif
// ----------------------------------------------------------------------------------------------------------------------------
// Purpose: Turns a path to a file on disk into a URL (or just returns the value if it's already a URL)
// ----------------------------------------------------------------------------------------------------------------------------
std::string Path_FilePathToUrl( const std::string & sRelativePath, const std::string & sBasePath )
{
if ( StringHasPrefix( sRelativePath, "http://" )
|| StringHasPrefix( sRelativePath, "https://" )
|| StringHasPrefix( sRelativePath, "vr-input-workshop://" )
|| StringHasPrefix( sRelativePath, "file://" )
)
{
return sRelativePath;
}
else
{
std::string sAbsolute = Path_MakeAbsolute( sRelativePath, sBasePath );
if ( sAbsolute.empty() )
return sAbsolute;
sAbsolute = Path_FixSlashes( sAbsolute, '/' );
size_t unBufferSize = sAbsolute.length() * 3;
char *pchBuffer = (char *)alloca( unBufferSize );
V_URLEncodeFullPath( pchBuffer, (int)unBufferSize, sAbsolute.c_str(), (int)sAbsolute.length() );
return std::string( FILE_URL_PREFIX ) + pchBuffer;
}
}
// -----------------------------------------------------------------------------------------------------
// Purpose: Strips off file:// off a URL and returns the path. For other kinds of URLs an empty string is returned
// -----------------------------------------------------------------------------------------------------
std::string Path_UrlToFilePath( const std::string & sFileUrl )
{
if ( !strnicmp( sFileUrl.c_str(), FILE_URL_PREFIX, strlen( FILE_URL_PREFIX ) ) )
{
char *pchBuffer = (char *)alloca( sFileUrl.length() );
V_URLDecodeNoPlusForSpace( pchBuffer, (int)sFileUrl.length(),
sFileUrl.c_str() + strlen( FILE_URL_PREFIX ), (int)( sFileUrl.length() - strlen( FILE_URL_PREFIX ) ) );
return Path_FixSlashes( pchBuffer );
}
else
{
return "";
}
}
// -----------------------------------------------------------------------------------------------------
// Purpose: Returns the root of the directory the system wants us to store user documents in
// -----------------------------------------------------------------------------------------------------
std::string GetUserDocumentsPath()
{
#ifdef _WIN32
WCHAR rwchPath[MAX_PATH];
if ( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_MYDOCUMENTS | CSIDL_FLAG_CREATE, NULL, 0, rwchPath ) ) )
{
return "";
}
// Convert the path to UTF-8 and store in the output
std::string sUserPath = UTF16to8( rwchPath );
return sUserPath;
#elif defined( OSX )
@autoreleasepool {
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES );
if ( [paths count] == 0 )
{
return "";
}
return [[paths objectAtIndex:0] UTF8String];
}
#elif defined( LINUX )
// @todo: not solved/changed as part of OSX - still not real - just removed old class based steam cut and paste
const char *pchHome = getenv( "HOME" );
if ( pchHome == NULL )
{
return "";
}
return pchHome;
#endif
}
// -----------------------------------------------------------------------------------------------------
// Purpose: deletes / unlinks a single file
// -----------------------------------------------------------------------------------------------------
bool Path_UnlinkFile( const std::string &strFilename )
{
#ifdef _WIN32
std::wstring wsFilename = UTF8to16( strFilename.c_str() );
return ( 0 != DeleteFileW( wsFilename.c_str() ) );
#else
return ( 0 == unlink( strFilename.c_str() ) );
#endif
}
// -----------------------------------------------------------------------------------------------------
// Limits the set of characters that are allowed in filenames
// -----------------------------------------------------------------------------------------------------
std::string Path_SanitizeFilename( const std::string& sFilename )
{
std::string sFixed = sFilename;
std::string::iterator iLastDot = sFixed.end();
for ( std::string::iterator i = sFixed.begin(); i != sFixed.end(); i++ )
{
if ( *i == '.' )
{
iLastDot = i;
}
// path-related characters are forbidden (except the last period)
switch ( *i )
{
case '\0':
case '.':
case '\\':
case '/':
case ':':
case '|':
case '?':
case '>':
case '<':
case '&':
case '%':
case '@':
case '$':
case '*':
case '\"':
*i = '_';
break;
default:
if ( *i < 32 )
{
*i = '_';
}
break;
}
}
if ( iLastDot != sFixed.end() && iLastDot != sFixed.begin()
&& iLastDot+1 != sFixed.end() )
{
*iLastDot = '.';
}
return sFixed;
}

@ -0,0 +1,153 @@
//========= Copyright Valve Corporation ============//
#pragma once
#include <string>
#include <vector>
#include <stdint.h>
/** Returns the path (including filename) to the current executable */
std::string Path_GetExecutablePath();
/** Returns the path of the current working directory */
std::string Path_GetWorkingDirectory();
/** Sets the path of the current working directory. Returns true if this was successful. */
bool Path_SetWorkingDirectory( const std::string & sPath );
/** Gets the path to a temporary directory. */
std::string Path_GetTemporaryDirectory();
/** returns the path (including filename) of the current shared lib or DLL */
std::string Path_GetThisModulePath();
/** Returns the specified path without its filename.
* If slash is unspecified the native path separator of the current platform
* will be used. */
std::string Path_StripFilename( const std::string & sPath, char slash = 0 );
/** returns just the filename from the provided full or relative path. */
std::string Path_StripDirectory( const std::string & sPath, char slash = 0 );
/** returns just the filename with no extension of the provided filename.
* If there is a path the path is left intact. */
std::string Path_StripExtension( const std::string & sPath );
/** returns just extension of the provided filename (if any). */
std::string Path_GetExtension( const std::string & sPath );
/** Returns true if the path is absolute */
bool Path_IsAbsolute( const std::string & sPath );
/** Makes an absolute path from a relative path and a base path */
std::string Path_MakeAbsolute( const std::string & sRelativePath, const std::string & sBasePath );
/** Fixes the directory separators for the current platform.
* If slash is unspecified the native path separator of the current platform
* will be used. */
std::string Path_FixSlashes( const std::string & sPath, char slash = 0 );
/** Returns the path separator for the current platform */
char Path_GetSlash();
/** Jams two paths together with the right kind of slash */
std::string Path_Join( const std::string & first, const std::string & second, char slash = 0 );
std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, char slash = 0 );
std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, const std::string &fourth, char slash = 0 );
std::string Path_Join(
const std::string & first,
const std::string & second,
const std::string & third,
const std::string & fourth,
const std::string & fifth,
char slash = 0 );
/** Removes redundant <dir>/.. elements in the path. Returns an empty path if the
* specified path has a broken number of directories for its number of ..s.
* If slash is unspecified the native path separator of the current platform
* will be used. */
std::string Path_Compact( const std::string & sRawPath, char slash = 0 );
/** Returns true if these two paths are the same without respect for internal . or ..
* sequences, slash type, or case (on case-insensitive platforms). */
bool Path_IsSamePath( const std::string & sPath1, const std::string & sPath2 );
//** Removed trailing slashes */
std::string Path_RemoveTrailingSlash( const std::string & sRawPath, char slash = 0 );
/** returns true if the specified path exists and is a directory */
bool Path_IsDirectory( const std::string & sPath );
/** returns true if the specified path represents an app bundle */
bool Path_IsAppBundle( const std::string & sPath );
/** returns true if the the path exists */
bool Path_Exists( const std::string & sPath );
/** Helper functions to find parent directories or subdirectories of parent directories */
std::string Path_FindParentDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName );
std::string Path_FindParentSubDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName );
/** Make a text file writable. */
bool Path_MakeWritable( const std::string &strFilename );
/** Path operations to read or write text/binary files */
unsigned char * Path_ReadBinaryFile( const std::string &strFilename, int *pSize );
uint32_t Path_ReadBinaryFile( const std::string &strFilename, unsigned char *pBuffer, uint32_t unSize );
std::vector<uint8_t> Path_ReadBinaryFile( const std::string & strFilename );
bool Path_WriteBinaryFile( const std::string &strFilename, unsigned char *pData, unsigned nSize );
std::string Path_ReadTextFile( const std::string &strFilename );
bool Path_WriteStringToTextFile( const std::string &strFilename, const char *pchData );
bool Path_WriteStringToTextFileAtomic( const std::string &strFilename, const char *pchData );
/** Returns a file:// url for paths, or an http or https url if that's what was provided */
std::string Path_FilePathToUrl( const std::string & sRelativePath, const std::string & sBasePath );
/** Strips off file:// off a URL and returns the path. For other kinds of URLs an empty string is returned */
std::string Path_UrlToFilePath( const std::string & sFileUrl );
/** Returns the root of the directory the system wants us to store user documents in */
std::string GetUserDocumentsPath();
/** deletes / unlinks a single file */
bool Path_UnlinkFile( const std::string &strFilename );
std::string Path_SanitizeFilename( const std::string& sFilename );
#ifndef MAX_UNICODE_PATH
#define MAX_UNICODE_PATH 32767
#endif
#ifndef MAX_UNICODE_PATH_IN_UTF8
#define MAX_UNICODE_PATH_IN_UTF8 (MAX_UNICODE_PATH * 4)
#endif
//-----------------------------------------------------------------------------
#if defined(WIN32)
#define DYNAMIC_LIB_EXT ".dll"
#define PROGRAM_EXT ".exe"
#ifdef _WIN64
#define PLATSUBDIR "win64"
#else
#define PLATSUBDIR "win32"
#endif
#elif defined(OSX)
#define DYNAMIC_LIB_EXT ".dylib"
#define PLATSUBDIR "osx32"
#define PROGRAM_EXT ""
#elif defined(LINUX)
#define DYNAMIC_LIB_EXT ".so"
#define PROGRAM_EXT ""
#if defined( LINUX32 )
#define PLATSUBDIR "linux32"
#elif defined( ANDROIDARM64 )
#define PLATSUBDIR "androidarm64"
#elif defined( LINUXARM64 )
#define PLATSUBDIR "linuxarm64"
#else
#define PLATSUBDIR "linux64"
#endif
#else
#warning "Unknown platform for PLATSUBDIR"
#define PLATSUBDIR "unknown_platform"
#endif

@ -0,0 +1,63 @@
//========= Copyright Valve Corporation ============//
#include <vrcommon/sharedlibtools_public.h>
#include <string.h>
#if defined(_WIN32)
#include <windows.h>
#endif
#if defined(POSIX)
#include <dlfcn.h>
#endif
SharedLibHandle SharedLib_Load( const char *pchPath, uint32_t *pErrorCode )
{
SharedLibHandle pHandle = nullptr;
#if defined( _WIN32)
pHandle = ( SharedLibHandle )LoadLibraryEx( pchPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
#elif defined(POSIX)
pHandle = (SharedLibHandle) dlopen(pchPath, RTLD_LOCAL|RTLD_NOW);
#endif
if ( pErrorCode )
{
if ( pHandle == nullptr )
{
#if defined( _WIN32)
*pErrorCode = ( uint32_t ) GetLastError();
#elif defined(POSIX)
*pErrorCode = 1;
#endif
}
else
{
*pErrorCode = 0;
}
}
return pHandle;
}
void *SharedLib_GetFunction( SharedLibHandle lib, const char *pchFunctionName)
{
#if defined( _WIN32)
return (void*)GetProcAddress( (HMODULE)lib, pchFunctionName );
#elif defined(POSIX)
return dlsym( lib, pchFunctionName );
#endif
}
void SharedLib_Unload( SharedLibHandle lib )
{
if ( !lib )
return;
#if defined( _WIN32)
FreeLibrary( (HMODULE)lib );
#elif defined(POSIX)
dlclose( lib );
#endif
}

@ -0,0 +1,12 @@
//========= Copyright Valve Corporation ============//
#pragma once
#include <stdint.h>
typedef void *SharedLibHandle;
SharedLibHandle SharedLib_Load( const char *pchPath, uint32_t *pErrorCode = nullptr );
void *SharedLib_GetFunction( SharedLibHandle lib, const char *pchFunctionName);
void SharedLib_Unload( SharedLibHandle lib );

@ -0,0 +1,606 @@
//========= Copyright Valve Corporation ============//
#include <vrcommon/strtools_public.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <codecvt>
#include <iostream>
#include <functional>
#include <locale>
#include <codecvt>
#include <stdarg.h>
#if defined( _WIN32 )
#include <windows.h>
#endif
#if defined( OSX ) || defined( LINUX )
//-----------------------------------------------------------------------------
// Purpose: stricmp -> strcasecmp bridge
//-----------------------------------------------------------------------------
int stricmp( const char *pStr1, const char *pStr2 )
{
return strcasecmp( pStr1, pStr2 );
}
//-----------------------------------------------------------------------------
// Purpose: strincmp -> strncasecmp bridge
//-----------------------------------------------------------------------------
int strnicmp( const char *pStr1, const char *pStr2, size_t unBufferLen )
{
return strncasecmp( pStr1, pStr2, unBufferLen );
}
#endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool StringHasPrefix( const std::string & sString, const std::string & sPrefix )
{
return 0 == strnicmp( sString.c_str(), sPrefix.c_str(), sPrefix.length() );
}
bool StringHasPrefixCaseSensitive( const std::string & sString, const std::string & sPrefix )
{
return 0 == strncmp( sString.c_str(), sPrefix.c_str(), sPrefix.length() );
}
bool StringHasSuffix( const std::string &sString, const std::string &sSuffix )
{
size_t cStrLen = sString.length();
size_t cSuffixLen = sSuffix.length();
if ( cSuffixLen > cStrLen )
return false;
std::string sStringSuffix = sString.substr( cStrLen - cSuffixLen, cSuffixLen );
return 0 == stricmp( sStringSuffix.c_str(), sSuffix.c_str() );
}
bool StringHasSuffixCaseSensitive( const std::string &sString, const std::string &sSuffix )
{
size_t cStrLen = sString.length();
size_t cSuffixLen = sSuffix.length();
if ( cSuffixLen > cStrLen )
return false;
std::string sStringSuffix = sString.substr( cStrLen - cSuffixLen, cSuffixLen );
return 0 == strncmp( sStringSuffix.c_str(), sSuffix.c_str(),cSuffixLen );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
typedef std::codecvt_utf8< wchar_t > convert_type;
std::string UTF16to8(const wchar_t * in)
{
static std::wstring_convert< convert_type, wchar_t > s_converter; // construction of this can be expensive (or even serialized) depending on locale
try
{
return s_converter.to_bytes( in );
}
catch ( ... )
{
return std::string();
}
}
std::string UTF16to8( const std::wstring & in ) { return UTF16to8( in.c_str() ); }
std::wstring UTF8to16(const char * in)
{
static std::wstring_convert< convert_type, wchar_t > s_converter; // construction of this can be expensive (or even serialized) depending on locale
try
{
return s_converter.from_bytes( in );
}
catch ( ... )
{
return std::wstring();
}
}
std::wstring UTF8to16( const std::string & in ) { return UTF8to16( in.c_str() ); }
//-----------------------------------------------------------------------------
// Purpose: Format string to std::string converter
//-----------------------------------------------------------------------------
std::string Format( const char *pchFormat, ... )
{
static constexpr size_t k_ulMaxStackString = 4096;
va_list args;
char pchBuffer[k_ulMaxStackString];
va_start( args, pchFormat );
int unSize = vsnprintf( pchBuffer, sizeof( pchBuffer ), pchFormat, args );
va_end( args );
// Something went fairly wrong
if ( unSize < 0 )
{
//AssertMsg( false, "Format string parse failure" );
return "";
}
// Processing on the stack worked, success
if ( unSize < k_ulMaxStackString )
{
return pchBuffer;
}
// If processing on the stack failed, fallback to a dynamic allocation
std::vector< char > vecChar{};
vecChar.resize( unSize + 1 );
va_start( args, pchFormat );
unSize = vsnprintf( vecChar.data(), vecChar.size(), pchFormat, args );
va_end( args );
// Double check, just in case
if ( unSize < 0 )
{
//AssertMsg( false, "Format string parse failure" );
return "";
}
return vecChar.data();
}
#if defined( _WIN32 )
//-----------------------------------------------------------------------------
// Purpose: Convert LPSTR in the default CodePage to UTF8
//-----------------------------------------------------------------------------
std::string DefaultACPtoUTF8( const char *pszStr )
{
if ( GetACP() == CP_UTF8 )
{
return pszStr;
}
else
{
std::vector<wchar_t> vecBuf( strlen( pszStr ) + 1 ); // should be guaranteed to be enough
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pszStr, -1, vecBuf.data(), (int) vecBuf.size() );
return UTF16to8( vecBuf.data() );
}
}
#endif
// --------------------------------------------------------------------
// Purpose:
// --------------------------------------------------------------------
void strcpy_safe( char *pchBuffer, size_t unBufferSizeBytes, const char *pchSource )
{
strncpy( pchBuffer, pchSource, unBufferSizeBytes - 1 );
pchBuffer[unBufferSizeBytes - 1] = '\0';
}
// --------------------------------------------------------------------
// Purpose: converts a string to upper case
// --------------------------------------------------------------------
std::string StringToUpper( const std::string & sString )
{
std::string sOut;
sOut.reserve( sString.size() + 1 );
for( std::string::const_iterator i = sString.begin(); i != sString.end(); i++ )
{
sOut.push_back( (char)toupper( *i ) );
}
return sOut;
}
// --------------------------------------------------------------------
// Purpose: converts a string to lower case
// --------------------------------------------------------------------
std::string StringToLower( const std::string & sString )
{
std::string sOut;
sOut.reserve( sString.size() + 1 );
for( std::string::const_iterator i = sString.begin(); i != sString.end(); i++ )
{
sOut.push_back( (char)tolower( *i ) );
}
return sOut;
}
uint32_t ReturnStdString( const std::string & sValue, char *pchBuffer, uint32_t unBufferLen )
{
uint32_t unLen = (uint32_t)sValue.length() + 1;
if( !pchBuffer || !unBufferLen )
return unLen;
if( unBufferLen < unLen )
{
pchBuffer[0] = '\0';
}
else
{
memcpy( pchBuffer, sValue.c_str(), unLen );
}
return unLen;
}
/** Returns a std::string from a uint64_t */
std::string Uint64ToString( uint64_t ulValue )
{
char buf[ 22 ];
#if defined( _WIN32 )
sprintf_s( buf, "%llu", ulValue );
#else
snprintf( buf, sizeof( buf ), "%llu", (long long unsigned int ) ulValue );
#endif
return buf;
}
/** returns a uint64_t from a string */
uint64_t StringToUint64( const std::string & sValue )
{
return strtoull( sValue.c_str(), NULL, 0 );
}
//-----------------------------------------------------------------------------
// Purpose: Helper for converting a numeric value to a hex digit, value should be 0-15.
//-----------------------------------------------------------------------------
char cIntToHexDigit( int nValue )
{
//Assert( nValue >= 0 && nValue <= 15 );
return "0123456789ABCDEF"[ nValue & 15 ];
}
//-----------------------------------------------------------------------------
// Purpose: Helper for converting a hex char value to numeric, return -1 if the char
// is not a valid hex digit.
//-----------------------------------------------------------------------------
int iHexCharToInt( char cValue )
{
int32_t iValue = cValue;
if ( (uint32_t)( iValue - '0' ) < 10 )
return iValue - '0';
iValue |= 0x20;
if ( (uint32_t)( iValue - 'a' ) < 6 )
return iValue - 'a' + 10;
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: These define the set of characters to filter for components (which
// need all the escaping we can muster) vs. paths (which don't want
// / and : escaped so we don't break less compliant URL handling code.
//-----------------------------------------------------------------------------
static bool CharNeedsEscape_Component( const char c )
{
return (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9')
&& c != '-' && c != '_' && c != '.');
}
static bool CharNeedsEscape_FullPath( const char c )
{
return (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9')
&& c != '-' && c != '_' && c != '.' && c != '/' && c != ':' );
}
//-----------------------------------------------------------------------------
// Purpose: Internal implementation of encode, works in the strict RFC manner, or
// with spaces turned to + like HTML form encoding.
//-----------------------------------------------------------------------------
void V_URLEncodeInternal( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen,
bool bUsePlusForSpace, std::function< bool(const char)> fnNeedsEscape )
{
//AssertMsg( nDestLen > 3*nSourceLen, "Target buffer for V_URLEncode should be 3x source length, plus one for terminating null\n" );
int iDestPos = 0;
for ( int i=0; i < nSourceLen; ++i )
{
// worst case we need 3 additional chars
if( (iDestPos+3) > nDestLen )
{
pchDest[0] = '\0';
// AssertMsg( false, "Target buffer too short\n" );
return;
}
// We allow only a-z, A-Z, 0-9, period, underscore, and hyphen to pass through unescaped.
// These are the characters allowed by both the original RFC 1738 and the latest RFC 3986.
// Current specs also allow '~', but that is forbidden under original RFC 1738.
if ( fnNeedsEscape( pchSource[i] ) )
{
if ( bUsePlusForSpace && pchSource[i] == ' ' )
{
pchDest[iDestPos++] = '+';
}
else
{
pchDest[iDestPos++] = '%';
uint8_t iValue = pchSource[i];
if ( iValue == 0 )
{
pchDest[iDestPos++] = '0';
pchDest[iDestPos++] = '0';
}
else
{
char cHexDigit1 = cIntToHexDigit( iValue % 16 );
iValue /= 16;
char cHexDigit2 = cIntToHexDigit( iValue );
pchDest[iDestPos++] = cHexDigit2;
pchDest[iDestPos++] = cHexDigit1;
}
}
}
else
{
pchDest[iDestPos++] = pchSource[i];
}
}
if( (iDestPos+1) > nDestLen )
{
pchDest[0] = '\0';
//AssertMsg( false, "Target buffer too short to terminate\n" );
return;
}
// Null terminate
pchDest[iDestPos++] = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Internal implementation of decode, works in the strict RFC manner, or
// with spaces turned to + like HTML form encoding.
//
// Returns the amount of space used in the output buffer.
//-----------------------------------------------------------------------------
size_t V_URLDecodeInternal( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen, bool bUsePlusForSpace )
{
if ( nDecodeDestLen < nEncodedSourceLen )
{
//AssertMsg( false, "V_URLDecode needs a dest buffer at least as large as the source" );
return 0;
}
int iDestPos = 0;
for( int i=0; i < nEncodedSourceLen; ++i )
{
if ( bUsePlusForSpace && pchEncodedSource[i] == '+' )
{
pchDecodeDest[ iDestPos++ ] = ' ';
}
else if ( pchEncodedSource[i] == '%' )
{
// Percent signifies an encoded value, look ahead for the hex code, convert to numeric, and use that
// First make sure we have 2 more chars
if ( i < nEncodedSourceLen - 2 )
{
char cHexDigit1 = pchEncodedSource[i+1];
char cHexDigit2 = pchEncodedSource[i+2];
// Turn the chars into a hex value, if they are not valid, then we'll
// just place the % and the following two chars direct into the string,
// even though this really shouldn't happen, who knows what bad clients
// may do with encoding.
bool bValid = false;
int iValue = iHexCharToInt( cHexDigit1 );
if ( iValue != -1 )
{
iValue *= 16;
int iValue2 = iHexCharToInt( cHexDigit2 );
if ( iValue2 != -1 )
{
iValue += iValue2;
pchDecodeDest[ iDestPos++ ] = (char)iValue;
bValid = true;
}
}
if ( !bValid )
{
pchDecodeDest[ iDestPos++ ] = '%';
pchDecodeDest[ iDestPos++ ] = cHexDigit1;
pchDecodeDest[ iDestPos++ ] = cHexDigit2;
}
}
// Skip ahead
i += 2;
}
else
{
pchDecodeDest[ iDestPos++ ] = pchEncodedSource[i];
}
}
// We may not have extra room to NULL terminate, since this can be used on raw data, but if we do
// go ahead and do it as this can avoid bugs.
if ( iDestPos < nDecodeDestLen )
{
pchDecodeDest[iDestPos] = 0;
}
return (size_t)iDestPos;
}
//-----------------------------------------------------------------------------
// Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
// This version of the call isn't a strict RFC implementation, but uses + for space as is
// the standard in HTML form encoding, despite it not being part of the RFC.
//
// Dest buffer should be at least as large as source buffer to guarantee room for decode.
//-----------------------------------------------------------------------------
void V_URLEncode( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
{
return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, true, CharNeedsEscape_Component );
}
void V_URLEncodeNoPlusForSpace( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
{
return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, false, CharNeedsEscape_Component );
}
void V_URLEncodeFullPath( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen )
{
return V_URLEncodeInternal( pchDest, nDestLen, pchSource, nSourceLen, false, CharNeedsEscape_FullPath );
}
//-----------------------------------------------------------------------------
// Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
// This version of the call isn't a strict RFC implementation, but uses + for space as is
// the standard in HTML form encoding, despite it not being part of the RFC.
//
// Dest buffer should be at least as large as source buffer to guarantee room for decode.
// Dest buffer being the same as the source buffer (decode in-place) is explicitly allowed.
//-----------------------------------------------------------------------------
size_t V_URLDecode( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen )
{
return V_URLDecodeInternal( pchDecodeDest, nDecodeDestLen, pchEncodedSource, nEncodedSourceLen, true );
}
size_t V_URLDecodeNoPlusForSpace( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen )
{
return V_URLDecodeInternal( pchDecodeDest, nDecodeDestLen, pchEncodedSource, nEncodedSourceLen, false );
}
//-----------------------------------------------------------------------------
void V_StripExtension( std::string &in )
{
// Find the last dot. If it's followed by a dot or a slash, then it's part of a
// directory specifier like ../../somedir/./blah.
std::string::size_type test = in.rfind( '.' );
if ( test != std::string::npos )
{
// This handles things like ".\blah" or "c:\my@email.com\abc\def\geh"
// Which would otherwise wind up with "" and "c:\my@email", respectively.
if ( in.rfind( '\\' ) < test && in.rfind( '/' ) < test )
{
in.resize( test );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Tokenizes a string into a vector of strings
//-----------------------------------------------------------------------------
std::vector<std::string> TokenizeString( const std::string & sString, char cToken )
{
std::vector<std::string> vecStrings;
std::istringstream stream( sString );
std::string s;
while ( std::getline( stream, s, cToken ) )
{
vecStrings.push_back( s );
}
if ( !sString.empty() && sString.back() == cToken )
{
vecStrings.push_back( "" );
}
return vecStrings;
}
//-----------------------------------------------------------------------------
// Purpose: Repairs a should-be-UTF-8 string to a for-sure-is-UTF-8 string, plus return boolean if we subbed in '?' somewhere
//-----------------------------------------------------------------------------
bool RepairUTF8( const char *pbegin, const char *pend, std::string & sOutputUtf8 )
{
typedef std::codecvt_utf8<char32_t> facet_type;
facet_type myfacet;
std::mbstate_t mystate = std::mbstate_t();
sOutputUtf8.clear();
sOutputUtf8.reserve( pend - pbegin );
bool bSqueakyClean = true;
const char *pmid = pbegin;
while ( pmid != pend )
{
bool bHasError = false;
bool bHasValidData = false;
char32_t out = 0xdeadbeef, *pout;
pbegin = pmid;
switch ( myfacet.in( mystate, pbegin, pend, pmid, &out, &out + 1, pout ) )
{
case facet_type::ok:
bHasValidData = true;
break;
case facet_type::noconv:
// unexpected! always converting type
bSqueakyClean = false;
break;
case facet_type::partial:
bHasError = pbegin == pmid;
if ( bHasError )
{
bSqueakyClean = false;
}
else
{
bHasValidData = true;
}
break;
case facet_type::error:
bHasError = true;
bSqueakyClean = false;
break;
}
if ( bHasValidData )
{
// could convert back, but no need
for ( const char *p = pbegin; p != pmid; ++p )
{
sOutputUtf8 += *p;
}
}
if ( bHasError )
{
sOutputUtf8 += '?';
}
if ( pmid == pbegin )
{
pmid++;
}
}
return bSqueakyClean;
}
//-----------------------------------------------------------------------------
// Purpose: Repairs a should-be-UTF-8 string to a for-sure-is-UTF-8 string, plus return boolean if we subbed in '?' somewhere
//-----------------------------------------------------------------------------
bool RepairUTF8( const std::string & sInputUtf8, std::string & sOutputUtf8 )
{
return RepairUTF8( sInputUtf8.data(), sInputUtf8.data() + sInputUtf8.size(), sOutputUtf8 );
}

@ -0,0 +1,154 @@
//========= Copyright Valve Corporation ============//
#pragma once
#include <string>
#include <stdint.h>
#include <sys/types.h>
#include <vector>
/** returns true if the string has the prefix */
bool StringHasPrefix( const std::string & sString, const std::string & sPrefix );
bool StringHasPrefixCaseSensitive( const std::string & sString, const std::string & sPrefix );
/** returns if the string has the suffix */
bool StringHasSuffix( const std::string &sString, const std::string &sSuffix );
bool StringHasSuffixCaseSensitive( const std::string &sString, const std::string &sSuffix );
/** converts a UTF-16 string to a UTF-8 string */
std::string UTF16to8( const wchar_t * in );
std::string UTF16to8( const std::wstring & in );
/** converts a UTF-8 string to a UTF-16 string */
std::wstring UTF8to16(const char * in);
std::wstring UTF8to16( const std::string & in );
#define Utf16FromUtf8 UTF8to16
#if defined( _WIN32 )
std::string DefaultACPtoUTF8( const char *pszStr );
#endif
/** Repairs a should-be-UTF-8 string to a for-sure-is-UTF-8 string, plus return boolean if we subbed in '?' somewhere */
bool RepairUTF8( const char *begin, const char *end, std::string & sOutputUtf8 );
bool RepairUTF8( const std::string & sInputUtf8, std::string & sOutputUtf8 );
/** safely copy a string into a buffer */
void strcpy_safe( char *pchBuffer, size_t unBufferSizeBytes, const char *pchSource );
template< size_t bufferSize >
void strcpy_safe( char (& buffer) [ bufferSize ], const char *pchSource )
{
strcpy_safe( buffer, bufferSize, pchSource );
}
/** Turns printf-style format args into a std::string */
std::string Format( const char *pchFormat, ... );
/** converts a string to upper case */
std::string StringToUpper( const std::string & sString );
/** converts a string to lower case */
std::string StringToLower( const std::string & sString );
// we stricmp (from WIN) but it isn't POSIX - OSX/LINUX have strcasecmp so just inline bridge to it
#if defined( OSX ) || defined( LINUX )
int stricmp(const char *pStr1, const char *pStr2);
#ifndef _stricmp
#define _stricmp stricmp
#endif
int strnicmp( const char *pStr1, const char *pStr2, size_t unBufferLen );
#ifndef _strnicmp
#define _strnicmp strnicmp
#endif
#ifndef _vsnprintf_s
#define _vsnprintf_s vsnprintf
#endif
#define _TRUNCATE ((size_t)-1)
#endif
#if defined( OSX )
// behaviors ensure NULL-termination at least as well as _TRUNCATE does, but
// wcsncpy_s/strncpy_s can non-NULL-terminate, wcslcpy/strlcpy can not.
inline errno_t wcsncpy_s(wchar_t *strDest, size_t numberOfElements, const wchar_t *strSource, size_t count)
{
return wcslcpy(strDest, strSource, numberOfElements);
}
inline errno_t strncpy_s(char *strDest, size_t numberOfElements, const char *strSource, size_t count)
{
return strlcpy(strDest, strSource, numberOfElements);
}
#endif
#if defined( LINUX )
// this implementation does not return whether or not the destination was
// truncated, but that is straightforward to fix if anybody actually needs the
// return code.
#include "string.h"
inline void wcsncpy_s(wchar_t *strDest, size_t numberOfElements, const wchar_t *strSource, size_t count)
{
wcsncpy(strDest, strSource, numberOfElements);
strDest[numberOfElements-1] = '\0';
}
inline void strncpy_s(char *strDest, size_t numberOfElements, const char *strSource, size_t count)
{
strncpy(strDest, strSource, numberOfElements);
strDest[numberOfElements-1] = '\0';
}
#endif
#if defined( _WIN32 ) && _MSC_VER < 1800
inline uint64_t strtoull(const char *str, char **endptr, int base) { return _strtoui64( str, endptr, base ); }
#endif
/* Handles copying a std::string into a buffer as would be provided in an API */
uint32_t ReturnStdString( const std::string & sValue, char *pchBuffer, uint32_t unBufferLen );
/** Returns a std::string from a uint64_t */
std::string Uint64ToString( uint64_t ulValue );
/** returns a uint64_t from a string */
uint64_t StringToUint64( const std::string & sValue );
//-----------------------------------------------------------------------------
// Purpose: Encodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
// This version of the call isn't a strict RFC implementation, but uses + for space as is
// the standard in HTML form encoding, despite it not being part of the RFC.
//
// Dest buffer should be at least as large as source buffer to guarantee room for decode.
//-----------------------------------------------------------------------------
void V_URLEncode( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen );
/** Same as V_URLEncode, but without plus for space. */
void V_URLEncodeNoPlusForSpace( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen );
/** Same as V_URLEncodeNoPlusForSpace, but without escaping / and : */
void V_URLEncodeFullPath( char *pchDest, int nDestLen, const char *pchSource, int nSourceLen );
//-----------------------------------------------------------------------------
// Purpose: Decodes a string (or binary data) from URL encoding format, see rfc1738 section 2.2.
// This version of the call isn't a strict RFC implementation, but uses + for space as is
// the standard in HTML form encoding, despite it not being part of the RFC.
//
// Dest buffer should be at least as large as source buffer to guarantee room for decode.
// Dest buffer being the same as the source buffer (decode in-place) is explicitly allowed.
//-----------------------------------------------------------------------------
size_t V_URLDecode( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen );
/** Same as V_URLDecode, but without plus for space. */
size_t V_URLDecodeNoPlusForSpace( char *pchDecodeDest, int nDecodeDestLen, const char *pchEncodedSource, int nEncodedSourceLen );
//-----------------------------------------------------------------------------
// Purpose: strip extension from a path
//-----------------------------------------------------------------------------
void V_StripExtension( std::string &in );
/** Tokenizes a string into a vector of strings */
std::vector<std::string> TokenizeString( const std::string & sString, char cToken );

@ -0,0 +1,483 @@
//========= Copyright Valve Corporation ============//
#include <vrcommon/vrpathregistry_public.h>
#include <json/json.h>
#include <vrcommon/pathtools_public.h>
#include <vrcommon/envvartools_public.h>
#include <vrcommon/strtools_public.h>
#include <vrcommon/dirtools_public.h>
#ifdef _WIN32
#include <windows.h>
#include <shlobj.h>
#undef GetEnvironmentVariable
#elif defined OSX
#include <Foundation/Foundation.h>
#include <AppKit/AppKit.h>
#elif defined(LINUX)
#include <dlfcn.h>
#include <stdio.h>
#endif
#include <algorithm>
#include <sstream>
#ifndef VRLog
#if defined( __MINGW32__ )
#define VRLog(args...) fprintf(stderr, args)
#elif defined( WIN32 )
#define VRLog(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
#define VRLog(args...) fprintf(stderr, args)
#endif
#endif
/** Returns the root of the directory the system wants us to store user config data in */
static std::string GetAppSettingsPath()
{
#ifdef _WIN32
WCHAR rwchPath[MAX_PATH];
if( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, rwchPath ) ) )
{
return "";
}
// Convert the path to UTF-8 and store in the output
std::string sUserPath = UTF16to8( rwchPath );
return sUserPath;
#elif defined( OSX )
std::string sSettingsDir;
@autoreleasepool {
// Search for the path
NSArray *paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES );
if ( [paths count] == 0 )
{
return "";
}
NSString *resolvedPath = [paths objectAtIndex:0];
resolvedPath = [resolvedPath stringByAppendingPathComponent: @"OpenVR"];
if ( ![[NSFileManager defaultManager] createDirectoryAtPath: resolvedPath withIntermediateDirectories:YES attributes:nil error:nil] )
{
return "";
}
sSettingsDir.assign( [resolvedPath UTF8String] );
}
return sSettingsDir;
#elif defined( LINUX )
// As defined by XDG Base Directory Specification
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
const char *pchHome = getenv("XDG_CONFIG_HOME");
if ( ( pchHome != NULL) && ( pchHome[0] != '\0' ) )
{
return pchHome;
}
//
// XDG_CONFIG_HOME is not defined, use ~/.config instead
//
pchHome = getenv( "HOME" );
if ( pchHome == NULL )
{
return "";
}
std::string sUserPath( pchHome );
sUserPath = Path_Join( sUserPath, ".config" );
return sUserPath;
#else
#warning "Unsupported platform"
#endif
}
// ---------------------------------------------------------------------------
// Purpose: Constructor
// ---------------------------------------------------------------------------
CVRPathRegistry_Public::CVRPathRegistry_Public()
{
}
// ---------------------------------------------------------------------------
// Purpose: Computes the registry filename
// ---------------------------------------------------------------------------
std::string CVRPathRegistry_Public::GetOpenVRConfigPath()
{
std::string sConfigPath = GetAppSettingsPath();
if( sConfigPath.empty() )
return "";
#if defined( _WIN32 ) || defined( LINUX )
sConfigPath = Path_Join( sConfigPath, "openvr" );
#elif defined ( OSX )
sConfigPath = Path_Join( sConfigPath, ".openvr" );
#else
#warning "Unsupported platform"
#endif
sConfigPath = Path_FixSlashes( sConfigPath );
return sConfigPath;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
std::string CVRPathRegistry_Public::GetVRPathRegistryFilename()
{
std::string sOverridePath = GetEnvironmentVariable( "VR_PATHREG_OVERRIDE" );
if ( !sOverridePath.empty() )
return sOverridePath;
std::string sPath = GetOpenVRConfigPath();
if ( sPath.empty() )
return "";
#if defined( _WIN32 )
sPath = Path_Join( sPath, "openvrpaths.vrpath" );
#elif defined ( POSIX )
sPath = Path_Join( sPath, "openvrpaths.vrpath" );
#else
#error "Unsupported platform"
#endif
sPath = Path_FixSlashes( sPath );
return sPath;
}
// ---------------------------------------------------------------------------
// Purpose: Converts JSON to a history array
// ---------------------------------------------------------------------------
static void ParseStringListFromJson( std::vector< std::string > *pvecHistory, const Json::Value & root, const char *pchArrayName )
{
if( !root.isMember( pchArrayName ) )
return;
const Json::Value & arrayNode = root[ pchArrayName ];
if( !arrayNode )
{
VRLog( "VR Path Registry node %s is not an array\n", pchArrayName );
return;
}
pvecHistory->clear();
pvecHistory->reserve( arrayNode.size() );
for( uint32_t unIndex = 0; unIndex < arrayNode.size(); unIndex++ )
{
std::string sPath( arrayNode[ unIndex ].asString() );
pvecHistory->push_back( sPath );
}
}
// ---------------------------------------------------------------------------
// Purpose: Converts a history array to JSON
// ---------------------------------------------------------------------------
static void StringListToJson( const std::vector< std::string > & vecHistory, Json::Value & root, const char *pchArrayName )
{
Json::Value & arrayNode = root[ pchArrayName ];
for( auto i = vecHistory.begin(); i != vecHistory.end(); i++ )
{
arrayNode.append( *i );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CVRPathRegistry_Public::ToJsonString( std::string &sJsonString )
{
std::string sRegPath = GetVRPathRegistryFilename();
if( sRegPath.empty() )
return false;
std::string sRegistryContents = Path_ReadTextFile( sRegPath );
if( sRegistryContents.empty() )
return false;
sJsonString = sRegistryContents;
return true;
}
// ---------------------------------------------------------------------------
// Purpose: Loads the config file from its well known location
// ---------------------------------------------------------------------------
bool CVRPathRegistry_Public::BLoadFromFile( std::string *psLoadError )
{
std::string sRegPath = GetVRPathRegistryFilename();
if( sRegPath.empty() )
{
if ( psLoadError )
{
*psLoadError = "Unable to determine VR Path Registry filename";
}
return false;
}
std::string sRegistryContents = Path_ReadTextFile( sRegPath );
if( sRegistryContents.empty() )
{
if ( psLoadError )
{
*psLoadError = "Unable to read VR Path Registry from " + sRegPath;
}
return false;
}
Json::Value root;
Json::CharReaderBuilder builder;
std::istringstream istream( sRegistryContents );
std::string sErrors;
try {
if ( !parseFromStream( builder, istream, &root, &sErrors ) )
{
if ( psLoadError )
{
*psLoadError = "Unable to parse " + sRegPath + ": " + sErrors;
}
return false;
}
ParseStringListFromJson( &m_vecRuntimePath, root, "runtime" );
ParseStringListFromJson( &m_vecConfigPath, root, "config" );
ParseStringListFromJson( &m_vecLogPath, root, "log" );
if ( root.isMember( "external_drivers" ) && root["external_drivers"].isArray() )
{
ParseStringListFromJson( &m_vecExternalDrivers, root, "external_drivers" );
}
}
catch ( ... )
{
if ( psLoadError )
{
*psLoadError = "Unable to parse " + sRegPath + ": exception thrown in JSON library";
}
return false;
}
return true;
}
// ---------------------------------------------------------------------------
// Purpose: Saves the config file to its well known location
// ---------------------------------------------------------------------------
bool CVRPathRegistry_Public::BSaveToFile() const
{
std::string sRegPath = GetVRPathRegistryFilename();
if( sRegPath.empty() )
return false;
Json::Value root;
root[ "version" ] = 1;
root[ "jsonid" ] = "vrpathreg";
StringListToJson( m_vecRuntimePath, root, "runtime" );
StringListToJson( m_vecConfigPath, root, "config" );
StringListToJson( m_vecLogPath, root, "log" );
StringListToJson( m_vecExternalDrivers, root, "external_drivers" );
Json::StreamWriterBuilder builder;
std::string sRegistryContents = Json::writeString( builder, root );
// make sure the directory we're writing into actually exists
std::string sRegDirectory = Path_StripFilename( sRegPath );
if( !BCreateDirectoryRecursive( sRegDirectory.c_str() ) )
{
VRLog( "Unable to create path registry directory %s\n", sRegDirectory.c_str() );
return false;
}
if( !Path_WriteStringToTextFile( sRegPath, sRegistryContents.c_str() ) )
{
VRLog( "Unable to write VR path registry to %s\n", sRegPath.c_str() );
return false;
}
return true;
}
// ---------------------------------------------------------------------------
// Purpose: Returns the current runtime path or NULL if no path is configured.
// ---------------------------------------------------------------------------
std::string CVRPathRegistry_Public::GetRuntimePath() const
{
if( m_vecRuntimePath.empty() )
return "";
else
return m_vecRuntimePath.front().c_str();
}
// ---------------------------------------------------------------------------
// Purpose: Returns the current config path or NULL if no path is configured.
// ---------------------------------------------------------------------------
std::string CVRPathRegistry_Public::GetConfigPath() const
{
if( m_vecConfigPath.empty() )
return "";
else
return m_vecConfigPath.front().c_str();
}
// ---------------------------------------------------------------------------
// Purpose: Returns the current log path or NULL if no path is configured.
// ---------------------------------------------------------------------------
std::string CVRPathRegistry_Public::GetLogPath() const
{
if( m_vecLogPath.empty() )
return "";
else
return m_vecLogPath.front().c_str();
}
// ---------------------------------------------------------------------------
// Purpose: Returns paths using the path registry and the provided override
// values. Pass NULL for any paths you don't care about.
// ---------------------------------------------------------------------------
bool CVRPathRegistry_Public::GetPaths( std::string *psRuntimePath, std::string *psConfigPath, std::string *psLogPath, const char *pchConfigPathOverride, const char *pchLogPathOverride, std::vector<std::string> *pvecExternalDrivers )
{
std::string sLoadError;
CVRPathRegistry_Public pathReg;
bool bLoadedRegistry = pathReg.BLoadFromFile( &sLoadError );
int nCountEnvironmentVariables = 0;
int nRequestedPaths = 0;
if( psRuntimePath )
{
nRequestedPaths++;
if ( GetEnvironmentVariable( k_pchRuntimeOverrideVar ).length() != 0 )
{
*psRuntimePath = GetEnvironmentVariable( k_pchRuntimeOverrideVar );
nCountEnvironmentVariables++;
}
else if( !pathReg.GetRuntimePath().empty() )
{
*psRuntimePath = pathReg.GetRuntimePath();
}
else
{
*psRuntimePath = "";
}
}
if( psConfigPath )
{
nRequestedPaths++;
if ( GetEnvironmentVariable( k_pchConfigOverrideVar ).length() != 0 )
{
*psConfigPath = GetEnvironmentVariable( k_pchConfigOverrideVar );
nCountEnvironmentVariables++;
}
else if( pchConfigPathOverride )
{
*psConfigPath = pchConfigPathOverride;
}
else if( !pathReg.GetConfigPath().empty() )
{
*psConfigPath = pathReg.GetConfigPath();
}
else
{
*psConfigPath = "";
}
}
if( psLogPath )
{
nRequestedPaths++;
if ( GetEnvironmentVariable( k_pchLogOverrideVar ).length() != 0 )
{
*psLogPath = GetEnvironmentVariable( k_pchLogOverrideVar );
nCountEnvironmentVariables++;
}
else if( pchLogPathOverride )
{
*psLogPath = pchLogPathOverride;
}
else if( !pathReg.GetLogPath().empty() )
{
*psLogPath = pathReg.GetLogPath();
}
else
{
*psLogPath = "";
}
}
if ( pvecExternalDrivers )
{
*pvecExternalDrivers = pathReg.m_vecExternalDrivers;
}
if ( nCountEnvironmentVariables == nRequestedPaths )
{
// all three environment variables were set, so we don't need the physical file
return true;
}
else if( !bLoadedRegistry )
{
VRLog( "%s\n", sLoadError.c_str() );
}
return bLoadedRegistry;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
uint32_t CVRPathRegistry_Public::GetSteamAppId()
{
#if !defined( REL_BRANCH_ONLY )
uint32_t nSteamAppId = k_unSteamVRMainAppId;
#else
uint32_t nSteamAppId = k_unSteamVRAppId;
#endif
return nSteamAppId;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CVRPathRegistry_Public::IsSteamVRMain()
{
#if defined( REL_BRANCH_ONLY )
return false;
#else
return true;
#endif
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
uint32_t CVRPathRegistry_Public::InitSteamAppId()
{
uint32_t nSteamAppId = CVRPathRegistry_Public::GetSteamAppId();
// Forcefully setting to what it should be before SteamAPI_Init() since SteamVR is more often
// started as child processes of the game app and otherwise Steam then considers us as the
// wrong app id.
SetEnvironmentVariable( "SteamAppId", std::to_string( nSteamAppId ).c_str() );
SetEnvironmentVariable( "SteamGameId", std::to_string( nSteamAppId ).c_str() );
return nSteamAppId;
}

@ -0,0 +1,52 @@
//========= Copyright Valve Corporation ============//
#pragma once
#include <string>
#include <vector>
#include <stdint.h>
static const char *k_pchRuntimeOverrideVar = "VR_OVERRIDE";
static const char *k_pchConfigOverrideVar = "VR_CONFIG_PATH";
static const char *k_pchLogOverrideVar = "VR_LOG_PATH";
static const uint32_t k_unSteamVRAppId = 250820;
static const uint32_t k_unSteamVRMainAppId = 330050;
class CVRPathRegistry_Public
{
public:
static std::string GetVRPathRegistryFilename();
static std::string GetOpenVRConfigPath();
static uint32_t GetSteamAppId();
static bool IsSteamVRMain();
static uint32_t InitSteamAppId();
public:
CVRPathRegistry_Public();
/** Returns paths using the path registry and the provided override values. Pass NULL for any paths you don't care about.
* Returns false if the path registry could not be read. Valid paths might still be returned based on environment variables. */
static bool GetPaths( std::string *psRuntimePath, std::string *psConfigPath, std::string *psLogPath, const char *pchConfigPathOverride, const char *pchLogPathOverride, std::vector<std::string> *pvecExternalDrivers = NULL );
bool BLoadFromFile( std::string *psError = nullptr );
bool BSaveToFile() const;
bool ToJsonString( std::string &sJsonString );
// methods to get the current values
std::string GetRuntimePath() const;
std::string GetConfigPath() const;
std::string GetLogPath() const;
protected:
typedef std::vector< std::string > StringVector_t;
// index 0 is the current setting
StringVector_t m_vecRuntimePath;
StringVector_t m_vecLogPath;
StringVector_t m_vecConfigPath;
// full list of external drivers
StringVector_t m_vecExternalDrivers;
};

@ -0,0 +1 @@
Subproject commit 9c0f555acb2531d818f2c405044418fc75379fb2

@ -0,0 +1,71 @@
# Building WOWlet
Building for Linux and Windows via Docker is done in 3 steps:
1. Cloning this repository (+submodules)
2. Creating a base Docker image
3. Using the base image to compile a build
**important:** you only have to do step 2 (base docker image) once.
For Mac OS, scroll down.
# Linux
For more information, check the Dockerfile: `Dockerfile`.
### 1. Clone
```bash
git clone --branch master --recursive https://git.wownero.com/wowlet/wowlet.git
cd wowlet
```
Replace `master` with the desired version tag (e.g. `v3.1.0`) to build the release binary.
### 2. Base image
```bash
docker build --tag wowlet:linux --build-arg THREADS=6 .
```
Building the base image takes a while. **You only need to build the base image once.**
### 3. Build
```bash
docker run --rm -it -v $PWD:/wowlet -w /wowlet wowlet:linux sh -c 'make release-static -j6'
```
If you're re-running a build make sure to `rm -rf build/` first.
The resulting binary can be found in `build/bin/wowlet`.
# Windows
### 1. Clone
```bash
git clone --branch master --recursive https://git.wownero.com/wowlet/wowlet.git
cd wowlet
```
Replace `master` with the desired version tag (e.g. `v3.1.0`) to build the release binary.
### 2. Base image
```bash
docker build -f Dockerfile.windows --tag wowlet:win --build-arg THREADS=6 .
```
Building the base image takes a while. **You only need to build the base image once.**
### 3. Build
```bash
docker run --rm -it -v $PWD:/wowlet -w /wowlet wowlet:win sh -c 'make windows root=/depends target=x86_64-w64-mingw32 tag=win-x64 -j6'
```
If you're re-running a build make sure to `rm -rf build/` first.
The resulting binary can be found in `build/x86_64-w64-mingw32/release/bin/wowlet.exe`.

@ -0,0 +1,89 @@
# Documentation for developers
WOWlet is developed primarily on Linux. It uses Qt 5.15.* and chances are that your
distro's package manager has a lower version. It is therefore recommended that you install
Qt manually using the online installer, which can be found here: https://www.qt.io/download
(under open-source).
## Jetbrains Clion
WOWlet was developed using JetBrains Clion since it integrates nicely
with CMake and comes with a built-in debugger. To pass CMake flags to CLion,
go to `File->Settings->Build->CMake`, set Build Type to `Debug` and set your
preferred CMake options/definitions.
## Man Page
There is a WOWlet's manual page, which can be accessed with: `man wowlet`
If a new option is introduced, please be sure to update the options section in
`src/assets/wowlet.1.md`, the month and year in line 3, and "manify" the document
by running this command: `pandoc wowlet.1.md -s -t man -o wowlet.1 && gzip wowlet.1`
## Requirements
(Possibly out-of-date)
### Ubuntu/Debian
```bash
apt install -y git cmake libqrencode-dev build-essential cmake libboost-all-dev \
miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev \
libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev \
libprotobuf-dev protobuf-compiler libgcrypt20-dev libpng-dev
```
## CMake
After installing Qt you might have a folder called `/home/$USER/Qt/`. You need to pass this to CMake
via the `CMAKE_PREFIX_PATH` definition.
```
-DCMAKE_PREFIX_PATH=/home/$USER/QtFooBar/5.15.0/gcc_64
```
There are some Wownero/WOWlet related options/definitions that you may pass, see also `CMakeLists.txt`.
At a bare minimum, recommended:
`-DMANUAL_SUBMODULES=1 -DUSE_DEVICE_TREZOR=OFF -DUSE_SINGLE_BUILDDIR=ON -DDEV_MODE=ON`
If you have OpenSSL installed at a custom location, try:
```
-DOPENSSL_INCLUDE_DIR=/usr/local/lib/openssl-1.1.1g/include
-DOPENSSL_SSL_LIBRARY=/usr/local/lib/openssl-1.1.1g/libssl.so.1.1
-DOPENSSL_CRYPTO_LIBRARY=/usr/local/lib/openssl-1.1.1g/libcrypto.so.1.1
```
I prefer also enabling verbose makefiles, which may be useful in some situations.
```
-DCMAKE_VERBOSE_MAKEFILE=ON
```
Enable debugging symbols:
```bash
-DCMAKE_BUILD_TYPE=Debug
```
## Wowlet
It's best to install Tor locally as a service and start `wowlet` with `--use-local-tor`, this
prevents the child process from starting up each time you launch WOWlet and thus saves time.
#### Ubuntu/Debian
```bash
apt install -y tor
sudo service tor start
```
To skip the wizards and open a wallet directly use `--wallet-file`:
```bash
./wowlet --use-local-tor --wallet-file /home/user/Wownero/wallets/bla.keys
```

@ -11,6 +11,5 @@ The following keys may be used to communicate sensitive information to developer
| Name | Fingerprint |
|------|-------------|
| dsc | 1BFD 40F9 B0E2 B40D C8C7 FD4A 521F 1E79 91AA 42DC |
| tobtoht | C5AB E5C0 E50F A2B3 F14A B92D 1CAD D27F 41F4 5C3C |
Public keys can be found in `utils/pubkeys`.

@ -1 +0,0 @@
Subproject commit f611d5c9e32bc62f1735f6571b0bdb95cc020531

@ -2,19 +2,11 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
# pthread
find_package(Threads REQUIRED)
find_package(ZLIB REQUIRED)
find_package(PNG REQUIRED)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets)
add_subdirectory(libwalletqt)
add_subdirectory(model)
add_subdirectory(utils)
add_subdirectory(openpgp)
qt5_add_resources(RESOURCES assets.qrc)
# Compile source files (.h/.cpp)
# Compile these source files (.h/.cpp)
file(GLOB SOURCE_FILES
"*.h"
"*.cpp"
@ -42,12 +34,43 @@ file(GLOB SOURCE_FILES
"dialog/*.cpp"
)
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui Network Svg Xml WebSockets Quick QuickWidgets Qml QuickControls2 QuickCompiler QmlImportScanner Multimedia)
if(OPENVR)
# include some extra files
qt5_add_resources(RESOURCES vr/qml.qrc)
file(GLOB SOURCE_FILES_QML
"vr/*.h"
"vr/*.cpp"
"vr/utils/*.h"
"vr/utils/*.cpp"
)
list(APPEND SOURCE_FILES ${SOURCE_FILES_QML})
endif()
if(ANDROID OR ANDROID_DEBUG)
qt5_add_resources(RESOURCES mobile/qml.qrc)
file(GLOB SOURCE_FILES_QML
"mobile/*.h"
"mobile/*.cpp"
)
list(APPEND SOURCE_FILES ${SOURCE_FILES_QML})
endif()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wno-deprecated-declarations") # @TODO: removeme
add_subdirectory(libwalletqt)
add_subdirectory(model)
add_subdirectory(utils)
if(WITH_SCANNER)
add_subdirectory(QR-Code-scanner)
endif()
qt5_add_resources(RESOURCES assets.qrc)
if(TOR_BIN)
if(APPLE)
set(ASSETS_TOR "assets_tor_macos.qrc")
else()
set(ASSETS_TOR "assets_tor.qrc")
endif()
endif()
set(EXECUTABLE_FLAG)
@ -70,11 +93,16 @@ if(APPLE)
list(APPEND RESOURCES ${ICON})
endif()
add_executable(wowlet ${EXECUTABLE_FLAG} main.cpp
if(NOT ANDROID)
add_executable(wowlet ${EXECUTABLE_FLAG} main.cpp
${SOURCE_FILES}
${RESOURCES}
${ASSETS_TOR}
)
)
else()
add_library(wowlet SHARED ${SOURCE_FILES} ${RESOURCES})
set_target_properties(wowlet PROPERTIES COMPILE_DEFINITIONS "ANDROID")
endif()
# mac os bundle
set_target_properties(wowlet PROPERTIES
@ -93,11 +121,11 @@ file(GLOB_RECURSE SRC_SOURCES *.cpp)
file(GLOB_RECURSE SRC_HEADERS *.h)
target_include_directories(wowlet PUBLIC
${CMAKE_BINARY_DIR}/src/feather_autogen/include
${CMAKE_SOURCE_DIR}/monero/include
${CMAKE_SOURCE_DIR}/monero/src
${CMAKE_SOURCE_DIR}/monero/external/easylogging++
${CMAKE_SOURCE_DIR}/monero/contrib/epee/include
${CMAKE_BINARY_DIR}/src/wowlet_autogen/include
${CMAKE_SOURCE_DIR}/wownero/include
${CMAKE_SOURCE_DIR}/wownero/src
${CMAKE_SOURCE_DIR}/wownero/external/easylogging++
${CMAKE_SOURCE_DIR}/wownero/contrib/epee/include
${CMAKE_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/daemon
@ -107,7 +135,9 @@ target_include_directories(wowlet PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/tor
${CMAKE_CURRENT_SOURCE_DIR}/qrcode
${X11_INCLUDE_DIR}
${QRENCODE_INCLUDE_DIR}
${Boost_INCLUDE_DIRS}
${Iconv_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIR}
${Qt5Core_INCLUDE_DIRS}
${Qt5Widgets_INCLUDE_DIRS}
@ -118,16 +148,32 @@ target_include_directories(wowlet PUBLIC
${Qt5WebSockets_INCLUDE_DIRS}
)
if(DONATE_BEG)
target_compile_definitions(wowlet PRIVATE DONATE_BEG=1)
if(LINUX_ACTIVATION)
target_include_directories(wowlet PUBLIC
${CAIRO_INCLUDE_DIRS}
${XFIXES_INCLUDE_DIR}
)
endif()
if(OPENVR)
target_include_directories(wowlet PUBLIC ${CMAKE_SOURCE_DIR}/contrib/)
endif()
if(TOR_BIN)
target_compile_definitions(wowlet PRIVATE HAS_TOR_BIN=1)
endif()
if(XMRIG)
target_compile_definitions(wowlet PRIVATE HAS_XMRIG=1)
if(ANDROID)
target_compile_definitions(wowlet PRIVATE HAS_ANDROID=1)
endif()
if(ANDROID_DEBUG)
target_compile_definitions(wowlet PRIVATE HAS_ANDROID_DEBUG=1)
endif()
if(OPENVR)
target_compile_definitions(wowlet PRIVATE HAS_OPENVR=1)
target_compile_definitions(wowlet PUBLIC VR_API_PUBLIC)
endif()
if(HAVE_SYS_PRCTL_H)
@ -138,8 +184,8 @@ if(STATIC)
target_compile_definitions(wowlet PRIVATE STATIC=1)
endif()
if(STATIC)
target_compile_definitions(wowlet PRIVATE STATIC=1)
if(LINUX_ACTIVATION)
target_compile_definitions(wowlet PRIVATE LINUX_ACTIVATION=1)
endif()
if("$ENV{DRONE}" STREQUAL "true")
@ -150,6 +196,15 @@ if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(wowlet PRIVATE QT_NO_DEBUG=1)
endif()
add_definitions(${QT_DEFINITIONS})
if(NOT "${CMAKE_BUILD_TYPE}" EQUAL "Debug")
add_definitions(-DQT_NO_DEBUG)
endif()
target_compile_definitions(wowlet PUBLIC VR_API_PUBLIC)
qt5_import_qml_plugins(${PROJECT_NAME})
target_compile_definitions(wowlet
PUBLIC
${Qt5Core_DEFINITIONS}
@ -163,73 +218,181 @@ target_compile_definitions(wowlet
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
if(UNIX AND NOT APPLE)
if(UNIX)
# https://stackoverflow.com/questions/57766620/cmake-add-library-doesnt-initialize-static-global-variable
# so that contrib/monero-seed/src/gf_elem.cpp properly initializes. A better solution is welcome.
target_link_libraries(wowlet -Wl,--whole-archive monero-seed::monero-seed -Wl,--no-whole-archive)
else()
target_link_libraries(wowlet monero-seed::monero-seed)
target_link_libraries(wowlet PUBLIC -Wl,--whole-archive wownero-seed::wownero-seed -Wl,--no-whole-archive)
endif()
if(ANDROID)
# yolo some hardcoded paths
target_include_directories(wowlet PUBLIC
/opt/android/prefix/include/QtAndroidExtras/
)
endif()
target_link_libraries(wowlet
wallet_merged
${LMDB_LIBRARY}
# Link Wownero core libraries
target_link_libraries(wowlet PUBLIC
epee
${UNBOUND_LIBRARY}
${SODIUM_LIBRARY}
wallet_api
easylogging
blockchain_db
hardforks
${Boost_LIBRARIES}
${OPENSSL_LIBRARIES}
${CMAKE_DL_LIBS}
${EXTRA_LIBRARIES}
${Iconv_LIBRARIES}
${UNBOUND_LIBRARIES}
# /usr/local/lib/libunbound.a
${EXTRA_LIBRARIES})
# Link Qt libraries
target_link_libraries(wowlet PUBLIC
Qt5::Core
Qt5::Widgets
Qt5::Gui
Qt5::Network
Qt5::Svg
Qt5::QSvgPlugin
Qt5::QSvgIconPlugin
Qt5::Xml
Qt5::WebSockets
${ICU_LIBRARIES}
openpgp
Qt5::Quick
Qt5::Qml
Qt5::QuickControls2
Qt5::QuickWidgets)
if(ANDROID)
# yolo some hardcoded paths
target_link_libraries(wowlet PUBLIC
/opt/android/prefix/lib/libQt5QuickTemplates2_arm64-v8a.so
/opt/android/prefix/lib/libQt5Quick_arm64-v8a.so
/opt/android/prefix/lib/libQt5QmlModels_arm64-v8a.so
/opt/android/prefix/lib/libQt5Qml_arm64-v8a.so
/opt/android/prefix/lib/libQt5Svg_arm64-v8a.so
/opt/android/prefix/lib/libQt5Widgets_arm64-v8a.so
/opt/android/prefix/lib/libQt5Gui_arm64-v8a.so
/opt/android/prefix/lib/libQt5Xml_arm64-v8a.so
/opt/android/prefix/lib/libQt5XmlPatterns_arm64-v8a.so
/opt/android/prefix/lib/libQt5Network_arm64-v8a.so
/opt/android/prefix/lib/libQt5Core_arm64-v8a.so
/opt/android/prefix/lib/libQt5VirtualKeyboard_arm64-v8a.so
/opt/android/prefix/lib/libQt5AndroidExtras_arm64-v8a.so
/opt/android/prefix/plugins/bearer/libplugins_bearer_qandroidbearer_arm64-v8a.so
GLESv2
log
z
jnigraphics
android
EGL
c++_shared
)
endif()
# Link random other stuff
target_link_libraries(wowlet PUBLIC
Threads::Threads
${QRENCODE_LIBRARY}
)
if(APPLE)
target_link_libraries(wowlet
KDMacTouchBar
# Link Cairo and Xfixes
if(LINUX_ACTIVATION)
target_link_libraries(wowlet PUBLIC
${CAIRO_LIBRARIES}
${XFIXES_LIBRARY}
${X11_Xinerama_LIB}
)
target_include_directories(wowlet
PUBLIC ../contrib/KDMacTouchBar)
endif()
if(NOT APPLE)
target_link_libraries(wowlet
Qt5::QSvgIconPlugin
Qt5::QSvgPlugin
# Link scanner
if(WITH_SCANNER)
target_link_libraries(wowlet PUBLIC qrdecoder qrscanner)
if(LINUX AND NOT ANDROID)
target_link_libraries(wowlet PUBLIC
jpeg
v4l2
v4lconvert
rt
)
endif()
endif()
# Link OpenVR
if(OPENVR)
if(MINGW)
target_link_libraries(wowlet PUBLIC
openvr_api64
gcc stdc++ winpthread ssp glu32 opengl32 glmf32 -dynamic)
else()
target_link_libraries(wowlet PUBLIC openvr_api)
endif()
endif()
target_link_libraries(wowlet PUBLIC
Qt5::QSvgIconPlugin
Qt5::QSvgPlugin
)
if(STATIC)
target_link_libraries(wowlet
target_link_libraries(wowlet PUBLIC
Qt5::QSvgIconPlugin
Qt5::QSvgPlugin)
if(UNIX AND NOT APPLE)
target_link_libraries(wowlet
if(UNIX)
target_link_libraries(wowlet PUBLIC
Qt5::QXcbIntegrationPlugin)
endif()
endif()
if(X11_FOUND)
target_link_libraries(wowlet ${X11_LIBRARIES})
endif()
if(APPLE)
include(Deploy)
target_link_libraries(wowlet PUBLIC ${X11_LIBRARIES})
endif()
install(TARGETS wowlet
DESTINATION ${CMAKE_INSTALL_PREFIX}
)
message(STATUS "\n====================================== SUMMARY")
if(GIT_FOUND)
execute_process(COMMAND git rev-parse "HEAD" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/wownero OUTPUT_VARIABLE _WOWNERO_HEAD OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT _WOWNERO_HEAD STREQUAL WOWNERO_HEAD)
message(STATUS "[+] WOWNERO HEAD: ${_WOWNERO_HEAD} ... while CMake requested ${WOWNERO_HEAD}")
else()
message(STATUS "[+] WOWNERO HEAD: ${WOWNERO_HEAD}")
endif()
endif()
message(STATUS "[+] VERSION: ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}-${VERSION}")
message(STATUS "[+] STATIC: ${STATIC}")
message(STATUS "[+] Include Valve's OpenVR library: ${OPENVR}")
message(STATUS "[+] This build is for Android: ${ANDROID}")
message(STATUS "[+] This build is for testing the Android app on desktop: ${ANDROID_DEBUG}")
message(STATUS "[+] TOR_BIN: ${TOR_BIN}")
message(STATUS "[+] LINUX_ACTIVATION: ${LINUX_ACTIVATION}")
message(STATUS "[+] OpenSSL")
message(STATUS " - version: ${OPENSSL_VERSION}")
message(STATUS " - dirs: ${OPENSSL_INCLUDE_DIR}")
message(STATUS " - libs: ${OPENSSL_LIBRARIES} ${OPENSSL_SSL_LIBRARIES}")
if(CAIRO_FOUND)
message(STATUS "[+] Cairo")
message(STATUS " - version: ${CAIRO_VERSION}")
message(STATUS " - dirs: ${CAIRO_INCLUDE_DIRS}")
message(STATUS " - libs: ${CAIRO_LIBRARIES}")
endif()
if(XFIXES_FOUND)
message(STATUS "[+] Xfixes")
message(STATUS " - dirs: ${XFIXES_INCLUDE_DIR}")
message(STATUS " - libs: ${XFIXES_LIBRARY}")
endif()
message(STATUS "[+] Boost")
message(STATUS " - version: ${Boost_VERSION}")
message(STATUS " - dirs: ${Boost_INCLUDE_DIRS}")
message(STATUS " - libs: ${Boost_LIBRARIES}")
if(Iconv_FOUND)
message(STATUS "[+] Iconv")
message(STATUS " - version: ${Iconv_VERSION}")
message(STATUS " - libs: ${Iconv_LIBRARIES}")
message(STATUS " - dirs: ${Iconv_INCLUDE_DIRS}")
endif()

@ -0,0 +1,22 @@
add_library(qrdecoder STATIC
Decoder.cpp
)
target_link_libraries(qrdecoder
PUBLIC
Qt5::Gui
PNG::PNG
PRIVATE
quirc
)
if(WITH_SCANNER)
add_library(qrscanner
QrCodeScanner.cpp
QrScanThread.cpp
)
target_link_libraries(qrscanner
PUBLIC
Qt5::Multimedia
qrdecoder
)
endif()

@ -0,0 +1,359 @@
// Copyright (c) 2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits>
#include <QDebug>
#include <QString>
#include "Decoder.h"
#include "quirc.h"
QrDecoder::QrDecoder()
: m_qr(quirc_new())
{
if (m_qr == nullptr)
{
throw std::runtime_error("QUIRC: failed to allocate memory");
}
}
QrDecoder::~QrDecoder()
{
quirc_destroy(m_qr);
}
std::vector<std::string> QrDecoder::decode(const QImage &image)
{
if (image.format() == QImage::Format_Grayscale8)
{
return decodeGrayscale8(image);
}
return decodeGrayscale8(image.convertToFormat(QImage::Format_Grayscale8));
}
std::vector<std::string> QrDecoder::decodePNG(QString pngPath) {
struct quirc *q;
std::vector<std::string> result;
auto pngPathStd = pngPath.toStdString();
auto pngPathCstr = pngPathStd.c_str();
q = quirc_new();
if (!q) {
qWarning() << "can't create quirc object";
return result;
}
int status = -1;
if (check_if_png(pngPathCstr)) {
status = load_png(q, pngPathCstr);
} else {
qWarning() << QString("Image is not a PNG: %1").arg(pngPath);
return result;
}
if (status < 0) {
quirc_destroy(q);
return result;
}
quirc_end(q);
auto count = quirc_count(q);
result.reserve(static_cast<size_t>(count));
for (int index = 0; index < count; ++index)
{
quirc_code code;
quirc_extract(q, index, &code);
quirc_data data;
const quirc_decode_error_t err = quirc_decode(&code, &data);
if (err == QUIRC_SUCCESS)
{
result.emplace_back(&data.payload[0], &data.payload[data.payload_len]);
}
}
quirc_destroy(q);
return result;
}
// I can't seem to get this function to work, we'll use dgbutil.h instead
std::vector<std::string> QrDecoder::decodeGrayscale8(const QImage &image)
{
if (quirc_resize(m_qr, image.width(), image.height()) < 0)
{
throw std::runtime_error("QUIRC: failed to allocate video memory");
}
uint8_t *rawImage = quirc_begin(m_qr, nullptr, nullptr);
if (rawImage == nullptr)
{
throw std::runtime_error("QUIRC: failed to get image buffer");
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
std::copy(image.constBits(), image.constBits() + image.sizeInBytes(), rawImage);
#else
std::copy(image.constBits(), image.constBits() + image.byteCount(), rawImage);
#endif
quirc_end(m_qr);
const int count = quirc_count(m_qr);
if (count < 0)
{
throw std::runtime_error("QUIRC: failed to get the number of recognized QR-codes");
}
std::vector<std::string> result;
result.reserve(static_cast<size_t>(count));
for (int index = 0; index < count; ++index)
{
quirc_code code;
quirc_extract(m_qr, index, &code);
quirc_data data;
const quirc_decode_error_t err = quirc_decode(&code, &data);
if (err == QUIRC_SUCCESS)
{
result.emplace_back(&data.payload[0], &data.payload[data.payload_len]);
}
}
return result;
}
/* quirc -- QR-code recognition library
* Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
static const char *data_type_str(int dt)
{
switch (dt) {
case QUIRC_DATA_TYPE_NUMERIC: return "NUMERIC";
case QUIRC_DATA_TYPE_ALPHA: return "ALPHA";
case QUIRC_DATA_TYPE_BYTE: return "BYTE";
case QUIRC_DATA_TYPE_KANJI: return "KANJI";
}
return "unknown";
}
void QrDecoder::dump_data(const struct quirc_data *data)
{
printf(" Version: %d\n", data->version);
printf(" ECC level: %c\n", "MLHQ"[data->ecc_level]);
printf(" Mask: %d\n", data->mask);
printf(" Data type: %d (%s)\n",
data->data_type, data_type_str(data->data_type));
printf(" Length: %d\n", data->payload_len);
printf(" Payload: %s\n", data->payload);
if (data->eci)
printf(" ECI: %d\n", data->eci);
}
void QrDecoder::dump_cells(const struct quirc_code *code)
{
int u, v;
printf(" %d cells, corners:", code->size);
for (u = 0; u < 4; u++)
printf(" (%d,%d)", code->corners[u].x,
code->corners[u].y);
printf("\n");
for (v = 0; v < code->size; v++) {
printf(" ");
for (u = 0; u < code->size; u++) {
int p = v * code->size + u;
if (code->cell_bitmap[p >> 3] & (1 << (p & 7)))
printf("[]");
else
printf(" ");
}
printf("\n");
}
}
/* hacked from https://dev.w3.org/Amaya/libpng/example.c
*
* Check if a file is a PNG image using png_sig_cmp(). Returns 1 if the given
* file is a PNG and 0 otherwise.
*/
#define PNG_BYTES_TO_CHECK 4
int QrDecoder::check_if_png(const char *filename)
{
int ret = 0;
FILE *infile = NULL;
unsigned char buf[PNG_BYTES_TO_CHECK];
/* Open the prospective PNG file. */
if ((infile = fopen(filename, "rb")) == NULL)
goto out;
/* Read in some of the signature bytes */
if (fread(buf, 1, PNG_BYTES_TO_CHECK, infile) != PNG_BYTES_TO_CHECK)
goto out;
/*
* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.
* png_sig_cmp() returns zero if the image is a PNG and nonzero if it
* isn't a PNG.
*/
if (png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK) == 0)
ret = 1;
/* FALLTHROUGH */
out:
if (infile)
fclose(infile);
return (ret);
}
int QrDecoder::load_png(struct quirc *q, const char *filename)
{
int width, height, rowbytes, interlace_type, number_passes = 1;
png_uint_32 trns;
png_byte color_type, bit_depth;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
FILE *infile = NULL;
uint8_t *image;
int ret = -1;
int pass;
if ((infile = fopen(filename, "rb")) == NULL)
goto out;
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
goto out;
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
goto out;
if (setjmp(png_jmpbuf(png_ptr)))
goto out;
png_init_io(png_ptr, infile);
png_read_info(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr, info_ptr);
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
interlace_type = png_get_interlace_type(png_ptr, info_ptr);
// Read any color_type into 8bit depth, Grayscale format.
// See http://www.libpng.org/pub/png/libpng-manual.txt
// PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth.
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png_ptr);
if ((trns = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)))
png_set_tRNS_to_alpha(png_ptr);
if (bit_depth == 16)
#if PNG_LIBPNG_VER >= 10504
png_set_scale_16(png_ptr);
#else
png_set_strip_16(png_ptr);
#endif
if ((trns) || color_type & PNG_COLOR_MASK_ALPHA)
png_set_strip_alpha(png_ptr);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png_ptr);
if (color_type == PNG_COLOR_TYPE_PALETTE ||
color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
png_set_rgb_to_gray_fixed(png_ptr, 1, -1, -1);
}
if (interlace_type != PNG_INTERLACE_NONE)
number_passes = png_set_interlace_handling(png_ptr);
png_read_update_info(png_ptr, info_ptr);
width = png_get_image_width(png_ptr, info_ptr);
height = png_get_image_height(png_ptr, info_ptr);
rowbytes = png_get_rowbytes(png_ptr, info_ptr);
if (rowbytes != width) {
fprintf(stderr,
"load_png: expected rowbytes to be %u but got %u\n",
width, rowbytes);
goto out;
}
if (quirc_resize(q, width, height) < 0)
goto out;
image = quirc_begin(q, NULL, NULL);
for (pass = 0; pass < number_passes; pass++) {
int y;
for (y = 0; y < height; y++) {
png_bytep row_pointer = image + y * width;
png_read_rows(png_ptr, &row_pointer, NULL, 1);
}
}
png_read_end(png_ptr, info_ptr);
ret = 0;
/* FALLTHROUGH */
out:
/* cleanup */
if (png_ptr) {
if (info_ptr)
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
else
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
}
if (infile)
fclose(infile);
return (ret);
}

@ -1,4 +1,4 @@
// Copyright (c) 2020-2021, The Monero Project
// Copyright (c) 2020, The Monero Project
//
// All rights reserved.
//
@ -26,63 +26,47 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <QImage>
#include <vector>
#include <png.h>
#include <span.h>
struct quirc;
#include "serialization.h"
namespace openpgp
{
class packet_stream
class QrDecoder
{
public:
packet_stream(const epee::span<const uint8_t> buffer)
: packet_stream(deserializer<epee::span<const uint8_t>>(buffer))
{
}
QrDecoder(const QrDecoder &) = delete;
QrDecoder &operator=(const QrDecoder &) = delete;
QrDecoder();
~QrDecoder();
std::vector<std::string> decode(const QImage &image);
std::vector<std::string> decodePNG(QString pngPath);
template <
typename byte_container,
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
packet_stream(deserializer<byte_container> buffer)
{
while (!buffer.empty())
{
packet_tag tag = buffer.read_packet_tag();
packets.push_back({std::move(tag), buffer.read(tag.length)});
}
}
private:
/* Dump decoded information on stdout. */
void dump_data(const struct quirc_data *data);
/* Dump a grid cell map on stdout. */
void dump_cells(const struct quirc_code *code);
const std::vector<uint8_t> *find_first(packet_tag::type type) const
{
for (const auto &packet : packets)
{
if (packet.first.packet_type == type)
{
return &packet.second;
}
}
return nullptr;
}
/* Check if a file is a PNG image.
*
* returns 1 if the given file is a PNG and 0 otherwise.
*/
int check_if_png(const char *filename);
template <typename Callback>
void for_each(packet_tag::type type, Callback &callback) const
{
for (const auto &packet : packets)
{
if (packet.first.packet_type == type)
{
callback(packet.second);
}
}
}
/* Read a PNG image into the decoder.
*
* Note that you must call quirc_end() if the function returns
* successfully (0).
*/
int load_png(struct quirc *q, const char *filename);
private:
std::vector<std::pair<packet_tag, std::vector<uint8_t>>> packets;
};
std::vector<std::string> decodeGrayscale8(const QImage &image);
} // namespace openpgp
private:
quirc *m_qr;
};

@ -0,0 +1,92 @@
// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "QrCodeScanner.h"
#include <QVideoProbe>
#include <QCamera>
QrCodeScanner::QrCodeScanner(QObject *parent)
: QObject(parent)
, m_processTimerId(-1)
, m_processInterval(750)
, m_enabled(true)
{
m_probe = new QVideoProbe(this);
m_thread = new QrScanThread(this);
m_thread->start();
QObject::connect(m_thread, SIGNAL(decoded(QString)), this, SIGNAL(decoded(QString)));
QObject::connect(m_thread, SIGNAL(notifyError(const QString &, bool)), this, SIGNAL(notifyError(const QString &, bool)));
connect(m_probe, SIGNAL(videoFrameProbed(QVideoFrame)), this, SLOT(processFrame(QVideoFrame)));
}
void QrCodeScanner::setSource(QCamera *camera)
{
m_probe->setSource(camera);
}
void QrCodeScanner::processFrame(QVideoFrame frame)
{
if(frame.isValid()){
m_curFrame = frame;
}
}
bool QrCodeScanner::enabled() const
{
return m_enabled;
}
void QrCodeScanner::setEnabled(bool enabled)
{
m_enabled = enabled;
if(!enabled && (m_processTimerId != -1) )
{
this->killTimer(m_processTimerId);
m_processTimerId = -1;
}
else if (enabled && (m_processTimerId == -1) )
{
m_processTimerId = this->startTimer(m_processInterval);
}
emit enabledChanged();
}
void QrCodeScanner::timerEvent(QTimerEvent *event)
{
if( (event->timerId() == m_processTimerId) ){
m_thread->addFrame(m_curFrame);
}
}
QrCodeScanner::~QrCodeScanner()
{
m_thread->stop();
m_thread->quit();
if(!m_thread->wait(5000))
{
m_thread->terminate();
m_thread->wait();
}
}

@ -1,4 +1,4 @@
// Copyright (c) 2020-2021, The Monero Project
// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@ -26,53 +26,48 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#ifndef QRCODESCANNER_H_
#define QRCODESCANNER_H_
#include <gcrypt.h>
#include <QImage>
#include <QVideoFrame>
#include "QrScanThread.h"
namespace openpgp
{
class QVideoProbe;
class QCamera;
class mpi
class QrCodeScanner : public QObject
{
public:
mpi(const mpi &) = delete;
mpi &operator=(const mpi &) = delete;
Q_OBJECT
mpi(mpi &&other)
: data(other.data)
{
other.data = nullptr;
}
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
template <
typename byte_container,
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
mpi(const byte_container &buffer, gcry_mpi_format format = GCRYMPI_FMT_USG)
: mpi(&buffer[0], buffer.size(), format)
{
}
public:
QrCodeScanner(QObject *parent = Q_NULLPTR);
~QrCodeScanner();
void setSource(QCamera*);
mpi(const void *buffer, size_t size, gcry_mpi_format format = GCRYMPI_FMT_USG)
{
if (gcry_mpi_scan(&data, format, buffer, size, nullptr) != GPG_ERR_NO_ERROR)
{
throw std::runtime_error("failed to read mpi from buffer");
}
}
bool enabled() const;
void setEnabled(bool enabled);
~mpi()
{
gcry_mpi_release(data);
}
public Q_SLOTS:
void processFrame(QVideoFrame);
const gcry_mpi_t &get() const
{
return data;
}
Q_SIGNALS:
void enabledChanged();
private:
gcry_mpi_t data;
void decoded(const QString &data);
void notifyError(const QString &error, bool warning = false);
protected:
void timerEvent(QTimerEvent *);
QrScanThread *m_thread;
int m_processTimerId;
int m_processInterval;
int m_enabled;
QVideoFrame m_curFrame;
QVideoProbe *m_probe;
};
} // namespace openpgp
#endif

@ -1,4 +1,4 @@
// Copyright (c) 2020-2021, The Monero Project
// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@ -26,82 +26,65 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include "QrScanThread.h"
#include <QtGlobal>
#include <QDebug>
#include <vector>
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
extern QImage qt_imageFromVideoFrame(const QVideoFrame &f);
#endif
#include <gcrypt.h>
#include <span.h>
namespace openpgp
QrScanThread::QrScanThread(QObject *parent)
: QThread(parent)
,m_running(true)
{
}
class hash
void QrScanThread::processQImage(const QImage &qimg)
{
public:
enum algorithm : uint8_t
{
sha256 = 8,
};
hash(const hash &) = delete;
hash &operator=(const hash &) = delete;
hash(uint8_t algorithm)
: algo(algorithm)
, consumed(0)
{
if (gcry_md_open(&md, algo, 0) != GPG_ERR_NO_ERROR)
try {
for (const std::string &code : m_decoder.decode(qimg))
{
throw std::runtime_error("failed to create message digest object");
emit decoded(QString::fromStdString(code));
}
}
~hash()
{
gcry_md_close(md);
}
hash &operator<<(uint8_t byte)
{
gcry_md_putc(md, byte);
++consumed;
return *this;
catch(std::exception &e) {
qDebug() << "ERROR: " << e.what();
emit notifyError(e.what());
}
}
hash &operator<<(const epee::span<const uint8_t> &bytes)
{
gcry_md_write(md, &bytes[0], bytes.size());
consumed += bytes.size();
return *this;
}
void QrScanThread::processVideoFrame(const QVideoFrame &frame)
{
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
processQImage( qt_imageFromVideoFrame(frame) );
#else
processQImage(frame.image());
#endif
}
hash &operator<<(const std::vector<uint8_t> &bytes)
{
return *this << epee::to_span(bytes);
}
void QrScanThread::stop()
{
m_running = false;
m_waitCondition.wakeOne();
}
std::vector<uint8_t> finish() const
{
std::vector<uint8_t> result(gcry_md_get_algo_dlen(algo));
const void *digest = gcry_md_read(md, algo);
if (digest == nullptr)
{
throw std::runtime_error("failed to read the digest");
}
memcpy(&result[0], digest, result.size());
return result;
}
void QrScanThread::addFrame(const QVideoFrame &frame)
{
QMutexLocker locker(&m_mutex);
m_queue.append(frame);
m_waitCondition.wakeOne();
}
size_t consumed_bytes() const
{
return consumed;
void QrScanThread::run()
{
QVideoFrame frame;
while(m_running) {
QMutexLocker locker(&m_mutex);
while(m_queue.isEmpty() && m_running)
m_waitCondition.wait(&m_mutex);
if(!m_queue.isEmpty())
processVideoFrame(m_queue.takeFirst());
}
private:
const uint8_t algo;
gcry_md_hd_t md;
size_t consumed;
};
}

@ -1,4 +1,4 @@
// Copyright (c) 2020-2021, The Monero Project
// Copyright (c) 2014-2019, The Monero Project
//
// All rights reserved.
//
@ -26,53 +26,41 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#ifndef _QRSCANTHREAD_H_
#define _QRSCANTHREAD_H_
#include <algorithm>
#include <stdexcept>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QEvent>
#include <QVideoFrame>
#include <QCamera>
#include <gcrypt.h>
#include "Decoder.h"
namespace openpgp
class QrScanThread : public QThread
{
Q_OBJECT
class s_expression
{
public:
s_expression(const s_expression &) = delete;
s_expression &operator=(const s_expression &) = delete;
template <typename... Args>
s_expression(Args... args)
{
if (gcry_sexp_build(&data, nullptr, args...) != GPG_ERR_NO_ERROR)
{
throw std::runtime_error("failed to build S-expression");
}
}
s_expression(s_expression &&other)
{
std::swap(data, other.data);
}
QrScanThread(QObject *parent = Q_NULLPTR);
void addFrame(const QVideoFrame &frame);
virtual void stop();
s_expression(gcry_sexp_t data)
: data(data)
{
}
Q_SIGNALS:
void decoded(const QString &data);
void notifyError(const QString &error, bool warning = false);
~s_expression()
{
gcry_sexp_release(data);
}
const gcry_sexp_t &get() const
{
return data;
}
protected:
virtual void run();
void processVideoFrame(const QVideoFrame &);
void processQImage(const QImage &);
private:
gcry_sexp_t data = nullptr;
QrDecoder m_decoder;
bool m_running;
QMutex m_mutex;
QWaitCondition m_waitCondition;
QList<QVideoFrame> m_queue;
};
} // namespace openpgp
#endif

@ -6,6 +6,7 @@
#include "appcontext.h"
#include "globals.h"
#include "config-wowlet.h"
// libwalletqt
#include "libwalletqt/TransactionHistory.h"
@ -21,91 +22,102 @@ TxFiatHistory *AppContext::txFiatHistory = nullptr;
double AppContext::balance = 0;
QMap<QString, QString> AppContext::txDescriptionCache;
QMap<QString, QString> AppContext::txCache;
bool AppContext::isQML = false;
AppContext::AppContext(QCommandLineParser *cmdargs) {
this->m_walletKeysFilesModel = new WalletKeysFilesModel(this, this);
this->network = new QNetworkAccessManager();
this->networkClearnet = new QNetworkAccessManager();
this->cmdargs = cmdargs;
AppContext::isQML = false;
// OS & env
#if defined(Q_OS_MAC)
this->isMac = true;
this->isTorSocks = qgetenv("DYLD_INSERT_LIBRARIES").indexOf("libtorsocks") >= 0;
#elif __ANDROID__
this->isAndroid = true;
#elif defined(Q_OS_LINUX)
this->isLinux = true;
this->isTorSocks = qgetenv("LD_PRELOAD").indexOf("libtorsocks") >= 0;
this->isTails = TailsOS::detect();
this->isWhonix = WhonixOS::detect();
#elif defined(Q_OS_WIN)
this->isWindows = true;
this->isTorSocks = false;
#endif
this->androidDebug = cmdargs->isSet("android-debug");
this->isTails = TailsOS::detect();
this->isWhonix = WhonixOS::detect();
//Paths
// Paths
this->pathGenericData = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
this->configRoot = QDir::homePath();
if (isTails) { // #if defined(PORTABLE)
QString portablePath = []{
QString appImagePath = qgetenv("APPIMAGE");
if (appImagePath.isEmpty()) {
qDebug() << "Not an appimage, using currentPath()";
return QDir::currentPath() + "/.wowlet";
}
QFileInfo appImageDir(appImagePath);
return appImageDir.absoluteDir().path() + "/.wowlet";
}();
if (QDir().mkpath(portablePath)) {
this->configRoot = portablePath;
} else {
qCritical() << "Unable to create portable directory: " << portablePath;
}
}
this->accountName = Utils::getUnixAccountName();
this->homeDir = QDir::homePath();
this->configDirectory = QString("%1/.config/wowlet/").arg(this->configRoot);
this->configDirectoryVR = QString("%1%2").arg(this->configDirectory, "vr");
if (isTails) this->setupPathsTails();
QString walletDir = config()->get(Config::walletDirectory).toString();
if (walletDir.isEmpty()) {
#if defined(Q_OS_LINUX) or defined(Q_OS_MAC)
this->defaultWalletDir = QString("%1/Wownero/wallets").arg(this->configRoot);
this->defaultWalletDirRoot = QString("%1/Wownero").arg(this->configRoot);
#elif defined(Q_OS_WIN)
this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Wownero";
this->defaultWalletDirRoot = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
#endif
if(walletDir.isEmpty()) {
if (isAndroid && !androidDebug) setupPathsAndroid();
else if (isWindows) setupPathsWindows();
else if (isLinux || isMac) setupPathsUnix();
} else {
this->defaultWalletDir = walletDir;
this->defaultWalletDirRoot = walletDir;
}
#ifdef __ANDROID__
// can haz disk I/O?
QVector<QString> perms = {
"android.permission.WRITE_EXTERNAL_STORAGE",
"android.permission.READ_EXTERNAL_STORAGE"
};
Utils::androidAskPermissions(perms);
#endif
// Create wallet dirs
qDebug() << "creating " << defaultWalletDir;
if (!QDir().mkpath(defaultWalletDir))
qCritical() << "Unable to create dir: " << defaultWalletDir;
this->configDirectory = QString("%1/.config/wowlet/").arg(this->configRoot);
#if defined(Q_OS_UNIX)
if(!this->configDirectory.endsWith('/'))
this->configDirectory = QString("%1/").arg(this->configDirectory);
#endif
// Config
// Create some directories
createConfigDirectory(this->configDirectory);
// if(this->cmdargs->isSet("stagenet"))
// this->networkType = NetworkType::STAGENET;
// else if(this->cmdargs->isSet("testnet"))
// this->networkType = NetworkType::TESTNET;
// else
this->networkType = NetworkType::MAINNET;
qDebug() << "configRoot: " << this->configRoot;
qDebug() << "homeDir: " << this->homeDir;
qDebug() << "customWalletDir: " << walletDir;
qDebug() << "defaultWalletDir: " << this->defaultWalletDir;
qDebug() << "defaultWalletDirRoot: " << this->defaultWalletDirRoot;
qDebug() << "configDirectory: " << this->configDirectory;
// auto nodeSourceUInt = config()->get(Config::nodeSource).toUInt();
// AppContext::nodeSource = static_cast<NodeSource>(nodeSourceUInt);
this->nodes = new Nodes(this, this->networkClearnet);
connect(this, &AppContext::nodeSourceChanged, this->nodes, &Nodes::onNodeSourceChanged);
connect(this, &AppContext::setCustomNodes, this->nodes, &Nodes::setCustomNodes);
// Tor & socks proxy
this->ws = new WSClient(this, m_wsUrl);
// init backend URLs
if(cmdargs->isSet("backend-host"))
this->backendHost = cmdargs->value("backend-host");
if(cmdargs->isSet("backend-host"))
this->backendPort = cmdargs->value("backend-port").toUInt();
if(cmdargs->isSet("backend-tls"))
this->backendTLS = true;
backendWSUrl = this->backendTLS ? "wss://" : "ws://";
backendWSUrl += QString("%1:%2").arg(this->backendHost).arg(this->backendPort);
backendHTTPUrl = this->backendTLS ? "https://" : "http://";
backendHTTPUrl += QString("%1:%2").arg(this->backendHost).arg(this->backendPort);
// init websocket client
this->ws = new WSClient(this);
connect(this->ws, &WSClient::WSMessage, this, &AppContext::onWSMessage);
connect(this->ws, &WSClient::connectionEstablished, this, &AppContext::wsConnected);
connect(this->ws, &WSClient::closed, this, &AppContext::wsDisconnected);
// Store the wallet every 2 minutes
m_storeTimer.start(2 * 60 * 1000);
@ -113,6 +125,24 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
this->storeWallet();
});
// If system clock skewed for >= 300 seconds, assume a wake-up from hibernate and reload the websocket connection
if(!this->isAndroid)
m_hibernateTimer.start(3 * 1000);
m_hibernatePreviousTime = std::chrono::steady_clock::now();
connect(&m_hibernateTimer, &QTimer::timeout, [this]() {
const auto now = std::chrono::steady_clock::now();
const auto elapsed = now - m_hibernatePreviousTime;
if(elapsed >= m_hibernateDetectInterval) {
qCritical() << "Clock skew detected, resetting websocket connection";
this->ws->webSocket.abort();
this->ws->start();
}
m_hibernatePreviousTime = now;
});
// restore height lookup
this->initRestoreHeights();
@ -133,15 +163,13 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
AppContext::prices = new Prices();
// XMRig
#ifdef HAS_XMRIG
this->XMRig = new XmRig(this->configDirectory, this);
this->XMRig->prepare();
#endif
this->walletManager = WalletManager::instance();
QString logPath = QString("%1/daemon.log").arg(configDirectory);
Monero::Utils::onStartup();
Monero::Wallet::init("", "feather", logPath.toStdString(), true);
Monero::Wallet::init("", "wowlet", logPath.toStdString(), true);
bool logLevelFromEnv;
int logLevel = qEnvironmentVariableIntValue("MONERO_LOG_LEVEL", &logLevelFromEnv);
@ -154,18 +182,25 @@ AppContext::AppContext(QCommandLineParser *cmdargs) {
// libwallet connects
connect(this->walletManager, &WalletManager::walletOpened, this, &AppContext::onWalletOpened);
// hideOnClose
auto hideOnClose = config()->get(Config::hideOnClose).toBool();
if(hideOnClose)
QApplication::setQuitOnLastWindowClosed(false);
}
void AppContext::initTor() {
this->tor = new Tor(this, this);
this->tor->start();
if (!(isWhonix)) {
if (!isWhonix && !backendHost.contains(".onion")) {
qDebug() << "'backend-host' did not contain '.onion' - running without Tor proxy.";
return;
}
this->networkProxy = new QNetworkProxy(QNetworkProxy::Socks5Proxy, Tor::torHost, Tor::torPort);
this->network->setProxy(*networkProxy);
if (m_wsUrl.host().endsWith(".onion"))
this->ws->webSocket.setProxy(*networkProxy);
}
}
void AppContext::initWS() {
@ -193,7 +228,7 @@ void AppContext::onSweepOutput(const QString &keyImage, QString address, bool ch
this->currentWallet->createTransactionSingleAsync(keyImage, address, outputs, this->tx_priority);
}
void AppContext::onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all) {
void AppContext::onCreateTransaction(QString address, quint64 amount, QString description, bool all) {
// tx creation
this->tmpTxDescription = description;
@ -299,6 +334,7 @@ void AppContext::onPreferredFiatCurrencyChanged(const QString &symbol) {
auto *model = this->currentWallet->transactionHistoryModel();
if(model != nullptr) {
model->preferredFiatSymbol = symbol;
this->currentWallet->transactionHistoryModel()->transactionHistory()->calcFiatInfo();
}
}
}
@ -329,6 +365,8 @@ void AppContext::onWalletOpened(Wallet *wallet) {
this->refreshed = false;
this->currentWallet = wallet;
this->walletPath = this->currentWallet->path() + ".keys";
QFileInfo fileInfo(this->currentWallet->path());
this->walletName = fileInfo.fileName();
this->walletViewOnly = this->currentWallet->viewOnly();
config()->set(Config::walletPath, this->walletPath);
@ -342,7 +380,14 @@ void AppContext::onWalletOpened(Wallet *wallet) {
connect(this->currentWallet, &Wallet::heightRefreshed, this, &AppContext::onHeightRefreshed);
connect(this->currentWallet, &Wallet::transactionCreated, this, &AppContext::onTransactionCreated);
emit walletOpened();
this->currentWallet->historyModel(); // load historyModel
auto *txHistory = this->currentWallet->history();
txHistory->refresh(this->currentWallet->currentSubaddressAccount());
connect(AppContext::prices, &Prices::fiatPricesUpdated, txHistory, &TransactionHistory::calcFiatInfo);
connect(AppContext::prices, &Prices::cryptoPricesUpdated, txHistory, &TransactionHistory::calcFiatInfo);
emit walletOpened(wallet);
connect(this->currentWallet, &Wallet::connectionStatusChanged, [this]{
this->nodes->autoConnect();
@ -350,10 +395,6 @@ void AppContext::onWalletOpened(Wallet *wallet) {
this->nodes->connectToNode();
this->updateBalance();
#ifdef DONATE_BEG
this->donateBeg();
#endif
// force trigger preferredFiat signal for history model
this->onPreferredFiatCurrencyChanged(config()->get(Config::preferredFiatCurrency).toString());
this->setWindowTitle();
@ -403,20 +444,23 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
if(changed)
emit blockHeightWSUpdated(this->heights);
}
else if(cmd == "nodes") {
else if(cmd == "yellwow") {
this->yellowPagesData = msg.value("data").toArray();
emit yellowUpdated();
}
else if(cmd == "rpc_nodes") {
this->onWSNodes(msg.value("data").toArray());
}
#if defined(HAS_XMRIG)
else if(cmd == "xmrig") {
this->XMRigDownloads(msg.value("data").toObject());
}
#endif
else if(cmd == "wownerod_releases") {
emit WownerodDownloads(msg.value("data").toObject());
}
else if(cmd == "crypto_rates") {
QJsonArray crypto_rates = msg.value("data").toArray();
AppContext::prices->cryptoPricesReceived(crypto_rates);
}
else if(cmd == "fiat_rates") {
QJsonObject fiat_rates = msg.value("data").toObject();
AppContext::prices->fiatPricesReceived(fiat_rates);
@ -425,25 +469,56 @@ void AppContext::onWSMessage(const QJsonObject &msg) {
QJsonArray reddit_data = msg.value("data").toArray();
this->onWSReddit(reddit_data);
}
else if(cmd == "wfs") {
else if(cmd == "forum") {
QJsonArray forum_data = msg.value("data").toArray();
this->onWSForum(forum_data);
}
else if(cmd == "funding_proposals") {
auto ccs_data = msg.value("data").toArray();
this->onWSCCS(ccs_data);
}
else if(cmd == "suchwow") {
QJsonArray such_data = msg.value("data").toArray();
emit suchWowUpdated(such_data);
}
else if(cmd == "txFiatHistory") {
auto txFiatHistory_data = msg.value("data").toObject();
AppContext::txFiatHistory->onWSData(txFiatHistory_data);
}
else if(cmd == "wowlet_releases") {
versionPending = msg.value("data").toObject();
auto version_str = versionPending.value("version").toString();
if(Utils::versionOutdated(WOWLET_VERSION_SEMVER, version_str))
emit versionOutdated(version_str, versionPending);
}
else if(cmd == "kill") {
// used *only* in dire emergencies
auto killme = msg.value("data").toBool();
if(killme)
QCoreApplication::quit();
}
#if defined(HAS_OPENVR)
else if(cmd == "requestPIN") {
auto pin = msg.value("data").toString();
emit pinReceived(pin);
}
else if(cmd == "lookupPIN") {
auto lookup_data = msg.value("data").toObject();
auto address = lookup_data.value("address").toString();
auto pin = lookup_data.value("PIN").toString();
if(address.isEmpty())
emit pinLookupErrorReceived();
else
emit pinLookupReceived(address, pin);
}
#endif
}
void AppContext::onWSNodes(const QJsonArray &nodes) {
QList<QSharedPointer<FeatherNode>> l;
QList<QSharedPointer<WowletNode>> l;
for (auto &&entry: nodes) {
auto obj = entry.toObject();
auto nettype = obj.value("nettype");
@ -462,17 +537,34 @@ void AppContext::onWSNodes(const QJsonArray &nodes) {
if(type == "tor" && (!(this->isTails || this->isWhonix || this->isTorSocks)))
continue;
auto node = new FeatherNode(
auto node = new WowletNode(
obj.value("address").toString(),
obj.value("height").toInt(),
obj.value("target_height").toInt(),
obj.value("online").toBool());
QSharedPointer<FeatherNode> r = QSharedPointer<FeatherNode>(node);
QSharedPointer<WowletNode> r = QSharedPointer<WowletNode>(node);
l.append(r);
}
this->nodes->onWSNodesReceived(l);
}
void AppContext::onWSForum(const QJsonArray& forum_data) {
QList<QSharedPointer<ForumPost>> l;
for (auto &&entry: forum_data) {
auto obj = entry.toObject();
auto forumPost = new ForumPost(
obj.value("thread").toString(),
obj.value("member_name").toString(),
obj.value("permalink").toString(),
obj.value("member_name").toString());
QSharedPointer<ForumPost> r = QSharedPointer<ForumPost>(forumPost);
l.append(r);
}
emit forumUpdated(l);
}
void AppContext::onWSReddit(const QJsonArray& reddit_data) {
QList<QSharedPointer<RedditPost>> l;
@ -522,8 +614,8 @@ void AppContext::onWSCCS(const QJsonArray &ccs_data) {
}
void AppContext::createConfigDirectory(const QString &dir) {
QString config_dir_tor = QString("%1%2").arg(dir).arg("tor");
QString config_dir_tordata = QString("%1%2").arg(dir).arg("tor/data");
auto config_dir_tor = QString("%1%2").arg(dir).arg("tor");
auto config_dir_tordata = QString("%1%2").arg(dir).arg("tor/data");
QStringList createDirs({dir, config_dir_tor, config_dir_tordata});
for(const auto &d: createDirs) {
@ -533,9 +625,24 @@ void AppContext::createConfigDirectory(const QString &dir) {
throw std::runtime_error("Could not create directory " + d.toStdString());
}
}
#ifdef HAS_OPENVR
auto config_dir_vr = QString("%1%2").arg(dir, "vr");
if(!Utils::dirExists(config_dir_vr)) {
qDebug() << QString("Creating directory: %1").arg(config_dir_vr);
if (!QDir().mkpath(config_dir_vr))
throw std::runtime_error("Could not create directory " + config_dir_vr.toStdString());
}
#endif
}
void AppContext::createWalletWithoutSpecifyingSeed(const QString &name, const QString &password) {
WowletSeed seed = WowletSeed(this->restoreHeights[this->networkType], this->coinName, this->seedLanguage);
auto path = QDir(this->defaultWalletDir).filePath(name);
this->createWallet(seed, path, password);
}
void AppContext::createWallet(FeatherSeed seed, const QString &path, const QString &password) {
void AppContext::createWallet(WowletSeed seed, const QString &path, const QString &password) {
if(Utils::fileExists(path)) {
auto err = QString("Failed to write wallet to path: \"%1\"; file already exists.").arg(path);
qCritical() << err;
@ -551,9 +658,9 @@ void AppContext::createWallet(FeatherSeed seed, const QString &path, const QStri
Wallet *wallet = nullptr;
if (seed.seedType == SeedType::TEVADOR) {
wallet = this->walletManager->createDeterministicWalletFromSpendKey(path, password, seed.language, this->networkType, seed.spendKey, seed.restoreHeight, this->kdfRounds);
wallet->setCacheAttribute("feather.seed", seed.mnemonic.join(" "));
wallet->setCacheAttribute("wowlet.seed", seed.mnemonic.join(" "));
}
if (seed.seedType == SeedType::MONERO) {
if (seed.seedType == SeedType::WOWNERO) {
wallet = this->walletManager->recoveryWallet(path, password, seed.mnemonic.join(" "), "", this->networkType, seed.restoreHeight, this->kdfRounds);
}
@ -604,6 +711,9 @@ void AppContext::createWalletFinish(const QString &password) {
this->currentWallet->store();
this->walletPassword = password;
emit walletCreated(this->currentWallet);
// emit signal on behalf of walletManager, open wallet
this->walletManager->walletOpened(this->currentWallet);
}
void AppContext::initRestoreHeights() {
@ -613,7 +723,7 @@ void AppContext::initRestoreHeights() {
}
void AppContext::onSetRestoreHeight(quint64 height){
auto seed = this->currentWallet->getCacheAttribute("feather.seed");
auto seed = this->currentWallet->getCacheAttribute("wowlet.seed");
if(!seed.isEmpty()) {
const auto msg = "This wallet has a 14 word mnemonic seed which has the restore height embedded.";
emit setRestoreHeightError(msg);
@ -672,21 +782,6 @@ void AppContext::onOpenAliasResolve(const QString &openAlias) {
emit openAliasResolveError(msg);
}
void AppContext::donateBeg() {
if(this->currentWallet == nullptr) return;
if(this->networkType != NetworkType::Type::MAINNET) return;
if(this->currentWallet->viewOnly()) return;
auto donationCounter = config()->get(Config::donateBeg).toInt();
if(donationCounter == -1)
return; // previously donated
donationCounter += 1;
if (donationCounter % m_donationBoundary == 0)
emit donationNag();
config()->set(Config::donateBeg, donationCounter);
}
AppContext::~AppContext() {}
// ############################################## LIBWALLET QT #########################################################
@ -761,17 +856,83 @@ void AppContext::onHeightRefreshed(quint64 walletHeight, quint64 daemonHeight, q
}
void AppContext::onTransactionCreated(PendingTransaction *tx, const QVector<QString> &address) {
for (auto &addr : address) {
if (addr == this->donationAddress) {
this->donationSending = true;
}
}
// Let UI know that the transaction was constructed
emit endTransaction();
// Some validation
auto tx_status = tx->status();
auto err = QString("Can't create transaction: ");
if(tx_status != PendingTransaction::Status_Ok){
auto tx_err = tx->errorString();
qCritical() << tx_err;
if (this->currentWallet->connectionStatus() == Wallet::ConnectionStatus_WrongVersion)
err = QString("%1 Wrong daemon version: %2").arg(err).arg(tx_err);
else
err = QString("%1 %2").arg(err).arg(tx_err);
qDebug() << Q_FUNC_INFO << err;
emit createTransactionError(err);
this->currentWallet->disposeTransaction(tx);
return;
} else if (tx->txCount() == 0) {
err = QString("%1 %2").arg(err).arg("No unmixable outputs to sweep.");
qDebug() << Q_FUNC_INFO << err;
emit createTransactionError(err);
this->currentWallet->disposeTransaction(tx);
return;
}
// tx created, but not sent yet. ask user to verify first.
emit createTransactionSuccess(tx, address);
if(this->autoCommitTx) {
this->currentWallet->commitTransactionAsync(tx);
}
}
QString AppContext::getAddress(quint32 accountIndex, quint32 addressIndex) {
return this->currentWallet->address(accountIndex, addressIndex);
}
void AppContext::onAskReceivingPIN() {
// request new receiving PIN from wowlet-backend
if(this->currentWallet == nullptr)
return;
auto address = this->currentWallet->address(0, 1);
QString signature = this->currentWallet->signMessage(address, false, address);
QJsonObject data;
data["signature"] = signature;
data["address"] = address;
QJsonObject obj;
obj["cmd"] = "requestPIN";
obj["data"] = data;
QJsonDocument doc = QJsonDocument(obj);
this->ws->sendMsg(doc.toJson(QJsonDocument::Compact));
}
void AppContext::onLookupReceivingPIN(QString pin) {
// lookup PIN -> address
if(this->currentWallet == nullptr)
return;
auto address = this->currentWallet->address(0, 1);
QString signature = this->currentWallet->signMessage(address, false, address);
QJsonObject data;
data["PIN"] = pin;
QJsonObject obj;
obj["cmd"] = "lookupPIN";
obj["data"] = data;
QJsonDocument doc = QJsonDocument(obj);
this->ws->sendMsg(doc.toJson(QJsonDocument::Compact));
}
void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, const QStringList& txid){
@ -784,12 +945,6 @@ void AppContext::onTransactionCommitted(bool status, PendingTransaction *tx, con
this->updateBalance();
emit transactionCommitted(status, tx, txid);
// this tx was a donation to Feather, stop our nagging
if(this->donationSending) {
this->donationSending = false;
config()->set(Config::donateBeg, -1);
}
}
void AppContext::storeWallet() {
@ -809,7 +964,13 @@ void AppContext::updateBalance() {
AppContext::balance = balance_u / globals::cdiv;
double spendable = this->currentWallet->unlockedBalance();
// formatted
QString fmt_str = QString("Balance: %1 WOW").arg(Utils::balanceFormat(spendable));
if (balance > spendable)
fmt_str += QString(" (+%1 WOW unconfirmed)").arg(Utils::balanceFormat(balance - spendable));
emit balanceUpdated(balance_u, spendable);
emit balanceUpdatedFormatted(fmt_str);
}
void AppContext::syncStatusUpdated(quint64 height, quint64 target) {
@ -831,3 +992,38 @@ void AppContext::refreshModels() {
this->currentWallet->coins()->refresh(this->currentWallet->currentSubaddressAccount());
// Todo: set timer for refreshes
}
void AppContext::setupPathsUnix() {
this->defaultWalletDir = QString("%1/Wownero/wallets").arg(this->configRoot);
this->defaultWalletDirRoot = QString("%1/Wownero").arg(this->configRoot);
}
void AppContext::setupPathsWindows() {
this->defaultWalletDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/Wownero";
this->defaultWalletDirRoot = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
void AppContext::setupPathsAndroid() {
this->defaultWalletDir = QString("%1/Wownero/wallets").arg(this->pathGenericData);
this->defaultWalletDirRoot = QString("%1/Wownero").arg(this->pathGenericData);
}
void AppContext::setupPathsTails() {
QString portablePath = []{
QString appImagePath = qgetenv("APPIMAGE");
if (appImagePath.isEmpty()) {
qDebug() << "Not an appimage, using currentPath()";
return QDir::currentPath() + "/.wowlet";
}
QFileInfo appImageDir(appImagePath);
return appImageDir.absoluteDir().path() + "/.wowlet";
}();
if (QDir().mkpath(portablePath)) {
this->configRoot = portablePath;
} else {
qCritical() << "Unable to create portable directory: " << portablePath;
}
}

@ -1,8 +1,8 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project.
#ifndef FEATHER_APPCONTEXT_H
#define FEATHER_APPCONTEXT_H
#ifndef WOWLET_APPCONTEXT_H
#define WOWLET_APPCONTEXT_H
#include <QObject>
#include <QProcess>
@ -17,8 +17,9 @@
#include "utils/xmrig.h"
#include "utils/wsclient.h"
#include "utils/txfiathistory.h"
#include "utils/FeatherSeed.h"
#include "utils/WowletSeed.h"
#include "widgets/RedditPost.h"
#include "widgets/ForumPost.h"
#include "widgets/CCSEntry.h"
#include "utils/RestoreHeightLookup.h"
#include "utils/nodes.h"
@ -39,30 +40,47 @@ public:
~AppContext() override;
bool isTails = false;
bool isWhonix = false;
bool isAndroid = false;
bool isLinux = false;
bool isMac = false;
bool isWindows = false;
bool isDebug = false;
static bool isQML;
bool androidDebug = false;
// Donation config
const QString donationAddress = "Wo3MWeKwtA918DU4c69hVSNgejdWFCRCuWjShRY66mJkU2Hv58eygJWDJS1MNa2Ge5M1WjUkGHuLqHkweDxwZZU42d16v94mP";
const int donationAmount = 25; // euro
bool donationSending = false;
QCommandLineParser *cmdargs;
QString coinName = "wownero";
bool isTorSocks = false;
QString pathGenericData;
QString homeDir;
QString accountName;
QString configRoot;
QString configDirectory;
QString configDirectoryVR;
QString defaultWalletDir;
QString defaultWalletDirRoot;
QString tmpTxDescription;
// https://git.wownero.com/wowlet/wowlet-backend/
QString backendHost = "l3hkasj5nnrh24yzj4acj5dgqlscq56o5xjvvqsftj55fkonqly5aiid.onion";
unsigned int backendPort = 80;
bool backendTLS = false;
QString backendWSUrl;
QString backendHTTPUrl;
QString walletPath;
QString walletPassword = "";
QString walletName;
bool walletViewOnly = false;
NetworkType::Type networkType;
Q_PROPERTY(QString walletName MEMBER walletName);
QString applicationPath;
static void createConfigDirectory(const QString &dir) ;
@ -73,6 +91,11 @@ public:
PendingTransaction::Priority tx_priority = PendingTransaction::Priority::Priority_Low;
quint32 tx_mixin = static_cast<const quint32 &>(10);
QString seedLanguage = "English"; // 14 word `monero-seed` only has English
// turn this on if you want to auto commit tx's after they have
// been created. Caution while using this setting is advised. This
// probably also breaks the default QtWidgets GUI. It is meant for
// alternative users of AppContext.
bool autoCommitTx = false;
QNetworkAccessManager *network;
QNetworkAccessManager *networkClearnet;
@ -88,30 +111,46 @@ public:
static QMap<QString, QString> txDescriptionCache;
static QMap<QString, QString> txCache;
static TxFiatHistory *txFiatHistory;
QJsonArray yellowPagesData;
QJsonObject versionPending;
// libwalletqt
bool refreshed = false;
WalletManager *walletManager;
Wallet *currentWallet = nullptr;
void createWallet(FeatherSeed seed, const QString &path, const QString &password);
void createWallet(WowletSeed seed, const QString &path, const QString &password);
Q_INVOKABLE void createWalletWithoutSpecifyingSeed(const QString &name, const QString &password);
void createWalletViewOnly(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight);
void createWalletFinish(const QString &password);
void syncStatusUpdated(quint64 height, quint64 target);
void updateBalance();
void initTor();
Q_INVOKABLE void initTor();
Q_INVOKABLE void initWS();
void initRestoreHeights();
void initWS();
void donateBeg();
void refreshModels();
void setWindowTitle(bool mining = false);
// Closes the currently opened wallet
void closeWallet(bool emitClosedSignal = true, bool storeWallet = false);
Q_INVOKABLE void closeWallet(bool emitClosedSignal = true, bool storeWallet = false);
void storeWallet();
Q_INVOKABLE QVariantList listWallets() {
m_walletKeysFilesModel->refresh();
QVariantList list;
for(const WalletKeysFiles &wallet: m_walletKeysFilesModel->listWallets())
list << wallet.toVariant();
return list;
}
Q_INVOKABLE QString displayAmount(quint64 amount) {
return Utils::balanceFormat(amount);
}
public slots:
void onOpenWallet(const QString& path, const QString &password);
void onCreateTransaction(const QString &address, quint64 amount, const QString &description, bool all);
Q_INVOKABLE void onOpenWallet(const QString& path, const QString &password);
void onCreateTransaction(QString address, quint64 amount, const QString description, bool all);
void onCreateTransactionMultiDest(const QVector<QString> &addresses, const QVector<quint64> &amounts, const QString &description);
void onCancelTransaction(PendingTransaction *tx, const QVector<QString> &address);
void onSweepOutput(const QString &keyImage, QString address, bool churn, int outputs) const;
@ -119,12 +158,16 @@ public slots:
void onOpenAliasResolve(const QString &openAlias);
void onSetRestoreHeight(quint64 height);
void onPreferredFiatCurrencyChanged(const QString &symbol);
Q_INVOKABLE void onAskReceivingPIN();
Q_INVOKABLE void onLookupReceivingPIN(QString pin);
Q_INVOKABLE QString getAddress(quint32 accountIndex, quint32 addressIndex);
private slots:
void onWSNodes(const QJsonArray &nodes);
void onWSMessage(const QJsonObject& msg);
void onWSCCS(const QJsonArray &ccs_data);
void onWSReddit(const QJsonArray& reddit_data);
void onWSForum(const QJsonArray& forum_data);
void onMoneySpent(const QString &txId, quint64 amount);
void onMoneyReceived(const QString &txId, quint64 amount);
@ -145,12 +188,13 @@ signals:
void walletClosed();
void balanceUpdated(quint64 balance, quint64 spendable);
void balanceUpdatedFormatted(QString fmt);
void blockchainSync(int height, int target);
void refreshSync(int height, int target);
void synchronized();
void blockHeightWSUpdated(QMap<QString, int> heights);
void walletRefreshed();
void walletOpened();
void walletOpened(Wallet *wallet);
void walletCreatedError(const QString &msg);
void walletCreated(Wallet *wallet);
void walletOpenedError(QString msg);
@ -159,13 +203,21 @@ signals:
void createTransactionError(QString message);
void createTransactionCancelled(const QVector<QString> &address, double amount);
void createTransactionSuccess(PendingTransaction *tx, const QVector<QString> &address);
void wsConnected();
void wsDisconnected();
void redditUpdated(QList<QSharedPointer<RedditPost>> &posts);
void nodesUpdated(QList<QSharedPointer<FeatherNode>> &nodes);
void forumUpdated(QList<QSharedPointer<ForumPost>> &posts);
void nodesUpdated(QList<QSharedPointer<WowletNode>> &nodes);
void ccsUpdated(QList<QSharedPointer<CCSEntry>> &entries);
void suchWowUpdated(const QJsonArray &such_data);
void yellowUpdated();
void nodeSourceChanged(NodeSource nodeSource);
void XMRigDownloads(const QJsonObject &data);
void setCustomNodes(QList<FeatherNode> nodes);
void WownerodDownloads(const QJsonObject &data);
void pinLookupReceived(QString address, QString pin);
void pinLookupErrorReceived();
void pinReceived(QString pin);
void setCustomNodes(QList<WowletNode> nodes);
void openAliasResolveError(const QString &msg);
void openAliasResolved(const QString &address, const QString &openAlias);
void setRestoreHeightError(const QString &msg);
@ -175,11 +227,20 @@ signals:
void initiateTransaction();
void endTransaction();
void setTitle(const QString &title); // set window title
void versionOutdated(QString version_string, QJsonObject data);
private:
WalletKeysFilesModel *m_walletKeysFilesModel;
const int m_donationBoundary = 15;
QTimer m_storeTimer;
QUrl m_wsUrl = QUrl(QStringLiteral("ws://feathercitimllbmdktu6cmjo3fizgmyfrntntqzu6xguqa2rlq5cgid.onion/ws"));
QTimer m_hibernateTimer;
std::chrono::seconds m_hibernateDetectInterval{300};
std::chrono::time_point<std::chrono::steady_clock> m_hibernatePreviousTime;
void setupPathsUnix();
void setupPathsWindows();
void setupPathsAndroid();
void setupPathsTails();
};
#endif //FEATHER_APPCONTEXT_H
#endif //WOWLET_APPCONTEXT_H

@ -3,7 +3,7 @@
<file>assets/about.txt</file>
<file>assets/ack.txt</file>
<file>assets/contributors.txt</file>
<file>assets/wowlet.desktop</file>
<file>assets/org.wowlet.wowlet.desktop</file>
<file>assets/nodes.json</file>
<file>assets/images/appicons/32x32.png</file>
<file>assets/images/appicons/48x48.png</file>
@ -12,7 +12,6 @@
<file>assets/images/appicons/128x128.png</file>
<file>assets/images/appicons/256x256.png</file>
<file>assets/images/arrow.svg</file>
<file>assets/images/banners/1.png</file>
<file>assets/images/bitcoin.png</file>
<file>assets/images/camera_dark.png</file>
<file>assets/images/camera_white.png</file>
@ -29,7 +28,8 @@
<file>assets/images/confirmed.svg</file>
<file>assets/images/connect.svg</file>
<file>assets/images/copy.png</file>
<file>assets/images/cutexmrfox.png</file>
<file>assets/images/dog_running.gif</file>
<file>assets/images/dog_sitting.gif</file>
<file>assets/images/edit.png</file>
<file>assets/images/exchange.png</file>
<file>assets/images/exchange_white.png</file>
@ -39,6 +39,7 @@
<file>assets/images/eye_blind.png</file>
<file>assets/images/wowlet.png</file>
<file>assets/images/file.png</file>
<file>assets/images/fire.png</file>
<file>assets/images/gnome-calc.png</file>
<file>assets/images/history.png</file>
<file>assets/images/info.png</file>
@ -54,56 +55,133 @@
<file>assets/images/network.png</file>
<file>assets/images/offline_tx.png</file>
<file>assets/images/person.svg</file>
<file>assets/images/photos/illiterate_illuminati.png</file>
<file>assets/images/photos/wow1.png</file>
<file>assets/images/photos/wow2.png</file>
<file>assets/images/photos/wow3.png</file>
<file>assets/images/photos/wow4.png</file>
<file>assets/images/photos/wow5.png</file>
<file>assets/images/photos/wow6.png</file>
<file>assets/images/photos/wow7.png</file>
<file>assets/images/photos/wow8.png</file>
<file>assets/images/photos/wow9.png</file>
<file>assets/images/photos/wow10.png</file>
<file>assets/images/photos/wow11.png</file>
<file>assets/images/photos/wow12.png</file>
<file>assets/images/photos/wow13.png</file>
<file>assets/images/photos/wow14.png</file>
<file>assets/images/photos/wow15.png</file>
<file>assets/images/photos/wow16.png</file>
<file>assets/images/photos/wow17.png</file>
<file>assets/images/photos/wow18.png</file>
<file>assets/images/photos/wow19.png</file>
<file>assets/images/photos/wow20.png</file>
<file>assets/images/photos/wow21.png</file>
<file>assets/images/photos/wow22.png</file>
<file>assets/images/photos/wow23.png</file>
<file>assets/images/photos/wow24.png</file>
<file>assets/images/photos/wow25.png</file>
<file>assets/images/photos/wow26.png</file>
<file>assets/images/photos/wow27.png</file>
<file>assets/images/photos/wow28.png</file>
<file>assets/images/photos/wow29.png</file>
<file>assets/images/photos/wow30.png</file>
<file>assets/images/photos/wow31.png</file>
<file>assets/images/photos/wow32.png</file>
<file>assets/images/photos/wow33.png</file>
<file>assets/images/photos/wow34.png</file>
<file>assets/images/photos/wow35.png</file>
<file>assets/images/photos/wow36.png</file>
<file>assets/images/photos/wow37.png</file>
<file>assets/images/photos/wow38.png</file>
<file>assets/images/photos/wow39.png</file>
<file>assets/images/photos/wow40.png</file>
<file>assets/images/photos/wow41.png</file>
<file>assets/images/photos/wow42.png</file>
<file>assets/images/photos/wow43.png</file>
<file>assets/images/photos/wow44.png</file>
<file>assets/images/photos/wow45.png</file>
<file>assets/images/photos/wow46.png</file>
<file>assets/images/photos/wow47.png</file>
<file>assets/images/illiterate_illuminati.png</file>
<file>assets/images/welcome/wow1.png</file>
<file>assets/images/welcome/wow2.png</file>
<file>assets/images/welcome/wow3.png</file>
<file>assets/images/welcome/wow4.png</file>
<file>assets/images/welcome/wow5.png</file>
<file>assets/images/welcome/wow6.png</file>
<file>assets/images/welcome/wow7.png</file>
<file>assets/images/welcome/wow8.png</file>
<file>assets/images/welcome/wow9.png</file>
<file>assets/images/welcome/wow10.png</file>
<file>assets/images/welcome/wow11.png</file>
<file>assets/images/welcome/wow12.png</file>
<file>assets/images/welcome/wow13.png</file>
<file>assets/images/welcome/wow14.png</file>
<file>assets/images/welcome/wow15.png</file>
<file>assets/images/welcome/wow16.png</file>
<file>assets/images/welcome/wow17.png</file>
<file>assets/images/welcome/wow18.png</file>
<file>assets/images/welcome/wow19.png</file>
<file>assets/images/welcome/wow20.png</file>
<file>assets/images/welcome/wow21.png</file>
<file>assets/images/welcome/wow22.png</file>
<file>assets/images/welcome/wow23.png</file>
<file>assets/images/welcome/wow24.png</file>
<file>assets/images/welcome/wow25.png</file>
<file>assets/images/welcome/wow26.png</file>
<file>assets/images/welcome/wow27.png</file>
<file>assets/images/welcome/wow28.png</file>
<file>assets/images/welcome/wow29.png</file>
<file>assets/images/welcome/wow30.png</file>
<file>assets/images/welcome/wow31.png</file>
<file>assets/images/welcome/wow32.png</file>
<file>assets/images/welcome/wow33.png</file>
<file>assets/images/welcome/wow34.png</file>
<file>assets/images/welcome/wow35.png</file>
<file>assets/images/welcome/wow36.png</file>
<file>assets/images/welcome/wow37.png</file>
<file>assets/images/welcome/wow38.png</file>
<file>assets/images/welcome/wow39.png</file>
<file>assets/images/welcome/wow40.png</file>
<file>assets/images/welcome/wow41.png</file>
<file>assets/images/welcome/wow42.png</file>
<file>assets/images/welcome/wow43.png</file>
<file>assets/images/welcome/wow44.png</file>
<file>assets/images/welcome/wow45.png</file>
<file>assets/images/welcome/wow46.png</file>
<file>assets/images/welcome/wow47.png</file>
<file>assets/images/welcome/wow48.png</file>
<file>assets/images/welcome/wow49.png</file>
<file>assets/images/welcome/wow50.png</file>
<file>assets/images/welcome/wow51.png</file>
<file>assets/images/welcome/wow52.png</file>
<file>assets/images/welcome/wow53.png</file>
<file>assets/images/welcome/wow54.png</file>
<file>assets/images/welcome/wow55.png</file>
<file>assets/images/welcome/wow56.png</file>
<file>assets/images/welcome/wow57.png</file>
<file>assets/images/welcome/wow58.png</file>
<file>assets/images/welcome/wow59.png</file>
<file>assets/images/welcome/wow60.png</file>
<file>assets/images/welcome/wow61.png</file>
<file>assets/images/welcome/wow62.png</file>
<file>assets/images/welcome/wow63.png</file>
<file>assets/images/welcome/wow64.png</file>
<file>assets/images/welcome/wow65.png</file>
<file>assets/images/welcome/wow66.png</file>
<file>assets/images/welcome/wow67.png</file>
<file>assets/images/welcome/wow68.png</file>
<file>assets/images/welcome/wow69.png</file>
<file>assets/images/welcome/wow70.png</file>
<file>assets/images/welcome/wow71.png</file>
<file>assets/images/welcome/wow72.png</file>
<file>assets/images/welcome/wow73.png</file>
<file>assets/images/welcome/wow74.png</file>
<file>assets/images/welcome/wow75.png</file>
<file>assets/images/welcome/wow76.png</file>
<file>assets/images/welcome/wow77.png</file>
<file>assets/images/welcome/wow78.png</file>
<file>assets/images/welcome/wow79.png</file>
<file>assets/images/welcome/wow80.png</file>
<file>assets/images/welcome/wow81.png</file>
<file>assets/images/welcome/wow82.png</file>
<file>assets/images/welcome/wow83.png</file>
<file>assets/images/welcome/wow84.png</file>
<file>assets/images/welcome/wow85.png</file>
<file>assets/images/welcome/wow86.png</file>
<file>assets/images/welcome/wow87.png</file>
<file>assets/images/welcome/wow88.png</file>
<file>assets/images/welcome/wow89.png</file>
<file>assets/images/welcome/wow90.png</file>
<file>assets/images/welcome/wow91.png</file>
<file>assets/images/welcome/wow92.png</file>
<file>assets/images/welcome/wow93.png</file>
<file>assets/images/welcome/wow94.png</file>
<file>assets/images/welcome/wow95.png</file>
<file>assets/images/welcome/wow96.png</file>
<file>assets/images/welcome/wow97.png</file>
<file>assets/images/welcome/wow98.png</file>
<file>assets/images/welcome/wow99.png</file>
<file>assets/images/welcome/wow100.png</file>
<file>assets/images/welcome/wow101.png</file>
<file>assets/images/welcome/wow102.png</file>
<file>assets/images/welcome/wow103.png</file>
<file>assets/images/welcome/wow104.png</file>
<file>assets/images/welcome/wow105.png</file>
<file>assets/images/welcome/wow106.png</file>
<file>assets/images/welcome/wow107.png</file>
<file>assets/images/welcome/wow108.png</file>
<file>assets/images/welcome/wow109.png</file>
<file>assets/images/welcome/wow110.png</file>
<file>assets/images/welcome/wow111.png</file>
<file>assets/images/welcome/wow112.png</file>
<file>assets/images/welcome/wow113.png</file>
<file>assets/images/welcome/wow114.png</file>
<file>assets/images/welcome/wow115.png</file>
<file>assets/images/welcome/wow116.png</file>
<file>assets/images/welcome/wow117.png</file>
<file>assets/images/welcome/wow118.png</file>
<file>assets/images/welcome/wow119.png</file>
<file>assets/images/welcome/wow120.png</file>
<file>assets/images/welcome/wow121.png</file>
<file>assets/images/welcome/wow122.png</file>
<file>assets/images/welcome/wow123.png</file>
<file>assets/images/preferences.png</file>
<file>assets/images/preferences.svg</file>
<file>assets/images/credits.jpg</file>
<file>assets/images/qrcode.png</file>
<file>assets/images/qrcode_white.png</file>
<file>assets/images/revealer_c.png</file>
@ -111,6 +189,7 @@
<file>assets/images/seal.png</file>
<file>assets/images/seed.png</file>
<file>assets/images/speaker.png</file>
<file>assets/images/pls_update.jpg</file>
<file>assets/images/status_connected_fork.png</file>
<file>assets/images/status_connected.png</file>
<file>assets/images/status_connected_proxy_fork.png</file>
@ -149,5 +228,34 @@
<file>assets/images/zoom.png</file>
<file>assets/mnemonic_25_english.txt</file>
<file>assets/restore_heights_wownero_mainnet.txt</file>
<file alias="mining/bottom_center_console.png">assets/images/mining/bottom_center_console.png</file>
<file alias="mining/intel.png">assets/images/mining/intel.png</file>
<file alias="mining/amd.png">assets/images/mining/amd.png</file>
<file alias="mining/overlay.png">assets/images/mining/overlay.png</file>
<file alias="mining/mining_gradient.png">assets/images/mining/mining_gradient.png</file>
<file alias="mining/bg1.gif">assets/images/mining/bg1.gif</file>
<file alias="mining/lowerleft_circle.png">assets/images/mining/lowerleft_circle.png</file>
<file alias="mining/lowerleft.png">assets/images/mining/lowerleft.png</file>
<file alias="mining/lower_repeat.png">assets/images/mining/lower_repeat.png</file>
<file alias="mining/lowerright.png">assets/images/mining/lowerright.png</file>
<file alias="mining/r_bottom.png">assets/images/mining/r_bottom.png</file>
<file alias="mining/r_left.png">assets/images/mining/r_left.png</file>
<file alias="mining/r_right.png">assets/images/mining/r_right.png</file>
<file alias="mining/topleft.png">assets/images/mining/topleft.png</file>
<file alias="mining/topright_bar.png">assets/images/mining/topright_bar.png</file>
<file alias="mining/topright_left.png">assets/images/mining/topright_left.png</file>
<file alias="mining/topright_middle.png">assets/images/mining/topright_middle.png</file>
<file alias="mining/topright_right.png">assets/images/mining/topright_right.png</file>
<file alias="mining/warning.png">assets/images/mining/warning.png</file>
<file alias="mining/axe.png">assets/images/mining/axe.png</file>
<file alias="mining/lowerleft_btn.png">assets/images/mining/lowerleft_btn.png</file>
<file alias="mining/elmo.gif">assets/images/mining/elmo.gif</file>
<file alias="mining/bubble.png">assets/images/mining/bubble.png</file>
<file alias="mining/mining.webp">assets/images/mining/mining.webp</file>
<file alias="fonts/ComicMono.ttf">assets/fonts/ComicMono.ttf</file>
<file alias="fonts/ComicMono-Bold.ttf">assets/fonts/ComicMono-Bold.ttf</file>
<file alias="mining.qml">ui/qml/mining.qml</file>
</qresource>
</RCC>

@ -1,4 +1,4 @@
WOWlet <feather_version> (<feather_git_head>)
WOWlet <wowlet_version> (<wowlet_git_head>)
Website: https://wownero.org
E-mail: dev@wownero.org

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 801 B

After

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1"
id="svg24" inkscape:version="1.0 (4035a4fb49, 2020-05-01)" sodipodi:docname="wownero.svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 600 600"
style="enable-background:new 0 0 600 600;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FF2AD4;}
.st1{fill:#FFCC00;}
.st2{fill:#FFFFFF;}
</style>
<g id="Identity" transform="matrix(0.92248086,0,0,0.92248086,-442.13585,-627.93272)">
<g id="g10">
<path id="path6" class="st0" d="M1103.8,879.4c-16.4-38.7-39.8-73.4-69.6-103.3c-29.9-29.9-64.5-53.2-103.3-69.6
c-40.1-17-82.6-25.6-126.5-25.6s-86.4,8.6-126.5,25.6c-38.7,16.4-73.4,39.8-103.3,69.6c-29.9,29.9-53.2,64.5-69.6,103.3
c-17,40.1-25.6,82.6-25.6,126.5s8.6,86.4,25.6,126.5c16.4,38.7,39.8,73.4,69.6,103.3c29.9,29.9,64.5,53.2,103.3,69.6
c40.1,17,82.6,25.6,126.5,25.6s86.4-8.6,126.5-25.6c38.7-16.4,73.4-39.8,103.3-69.6c29.9-29.9,53.2-64.5,69.6-103.3
c17-40.1,25.6-82.6,25.6-126.5S1120.7,919.5,1103.8,879.4L1103.8,879.4z"/>
<path id="path4" class="st1" d="M661,902.2l10.6,186.7l132.8-147.3L937,1089.2c0,0,9.6-157.9,11.3-186.7c47,0,98.1,0,150.8,0
c-42.7-121.8-158.5-208.9-294.7-208.9c-135.8,0-251.7,87.2-294.6,208.6C529.8,902.4,661,902.2,661,902.2z"/>
<path id="path8" class="st2" d="M1103.1,913.8H958.8l-11.7,203.9c-15-17.4-135-149.6-142.9-158.9
c-4.7,5.3-142.7,158.5-142.7,158.5l-11.6-203.5h-144c-8.9,29-13.7,59.7-13.7,91.5c0,172.2,140,312.3,312.3,312.3
s312.3-140.1,312.3-312.3C1116.8,973.7,1111.9,942.6,1103.1,913.8L1103.1,913.8z M804.5,1296c-158.5,0-287.1-128.6-287.1-287.1
c0-24.8,3-48.5,8.9-71.4h101.3c0.6,10.3,13.8,240.1,13.8,240.1l163.3-180.4l164.1,181.1c0,0,0,0.1,14-240.8h100.3
c5.8,22.8,8.5,46.8,8.5,71.4C1091.6,1167.4,963,1296,804.5,1296L804.5,1296z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

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

Loading…
Cancel
Save