├── .gitignore ├── LICENSE.md ├── README.md ├── build.gradle ├── buildLinux.sh.example ├── gradle.properties.example ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── logo.png ├── start.png └── ui.png ├── settings.gradle ├── src ├── main │ ├── java │ │ └── xyz │ │ │ └── drop2deck │ │ │ ├── FXApplication.java │ │ │ ├── FXRunner.java │ │ │ ├── controller │ │ │ └── MainController.java │ │ │ ├── enums │ │ │ └── Platform.java │ │ │ ├── ftp │ │ │ └── FTPServer.java │ │ │ ├── jna │ │ │ ├── DwmAttribute.java │ │ │ └── StageOps.java │ │ │ ├── layout │ │ │ └── InputGroup.java │ │ │ ├── network │ │ │ └── StorLoggingFtplet.java │ │ │ ├── skin │ │ │ └── VisiblePasswordFieldSkin.java │ │ │ └── util │ │ │ ├── FXUtils.java │ │ │ ├── NetworkUtils.java │ │ │ ├── PlatformUtils.java │ │ │ └── QRCodeUtils.java │ └── resources │ │ ├── META-INF │ │ └── native-image │ │ │ ├── jni-config.json │ │ │ ├── native-image.properties │ │ │ ├── predefined-classes-config.json │ │ │ ├── proxy-config.json │ │ │ ├── reflect-config.json │ │ │ ├── resource-config.json │ │ │ └── serialization-config.json │ │ ├── css │ │ └── cupertino-dark.css │ │ ├── fxml │ │ └── Main.fxml │ │ ├── icons │ │ ├── icon.png │ │ ├── round_play_circle_48.png │ │ └── round_stop_circle_48.png │ │ └── logo.png └── windows │ └── assets │ └── icon.ico └── steam_images ├── grid.png ├── hero.png ├── home.png ├── icon.png └── logo.png /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store 43 | .idea/ 44 | 45 | gradle.properties 46 | 47 | tools/ 48 | 49 | users.properties 50 | 51 | buildLinux.sh 52 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2023 CrazyXacker 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Logo 3 |

4 | 5 | # Drop2Deck 6 | 7 | Q: *How the heck I can send files to my Steam Deck easily without headache?* 8 | A: *Drop2Deck!* 9 | 10 |

11 | UIStart 12 |

13 | 14 | ### What it this? 15 | 16 | Very simple cross-platform `Java + JavaFX` application that creates `FTP servers` for both `Internal memory` and `MicroSD card` of your `Steam Deck` 17 | 18 | App can be used on `Windows`, `Linux` and `macOS`, but designed with `Steam Deck` and `SteamOS` in mind. Also, it can be used on any `Windows` handhelds (eg, `AyaNeo`, `Asus ROG Ally`, etc.) 19 | 20 | You even can add app into `Gaming Mode` on your `Steam Deck` and transfer files without moving into `Desktop mode`! 21 | 22 | ### Prerequisites 23 | 24 | *Note: `binary` version of app will work only on `x86_64` systems! `JAR` version works everywhere* 25 | 26 | #### Windows 27 | 28 | Install [Microsoft Visual C++ 2010 (x64)](https://www.microsoft.com/en-US/download/details.aspx?id=26999) and [Microsoft Visual C++ 2015 (x64)](https://www.microsoft.com/en-US/download/details.aspx?id=53840) 29 | 30 | #### Linux/macOS 31 | 32 | Additional libs are not required, but make sure to mark file as executable before use 33 | 34 | ### How to install and launch 35 | 36 | Just download latest binary file for your system from [Release](https://github.com/CrazyXacker/Drop2Deck/releases) section, put it in desired location and launch as normal app. After that you will see app UI 37 | 38 | ### How to configure 39 | 40 | App UI is very simple and consist of few configuration fields and `Start`/`Stop` button 41 | 42 | *Note: default settings with `deck` username, `deck` password and `9001`/`9002` ports should be fine for almost all users* 43 | 44 | Fields: 45 | `Username`: define `username` that you will use to connect 46 | `Password`: define `password` that you will use to connect 47 | `Port (Internal memory)`: define internal memory `port` that you will use to connect 48 | `Port (External memory)`: define external memory `port` that you will use to connect 49 | 50 | ### How to start 51 | 52 | Just hit `Start` button 53 | 54 | ### How to connect 55 | 56 | Select `FTP Client` that will be used for connection to `Deck2Drop`. I prefer `FileZilla`. Open client and type info from app: `IP`, `Username`, `Password` and `Port`. Hit `Connect` and you will see files and folders on your device. Start working with them as with regular file manager 57 | 58 | *Note: `Deck2Drop` starts 2 separate servers for `Internal` and `External` memory on separate ports* 59 | 60 | ### Notes 61 | 62 | #### Predefined paths 63 | 64 | Paths for supported systems are predefined: 65 | **Steam Deck**: `/home` and `/run/media/mmcblk0p1` 66 | **Windows**: `C:\` and `D:\` 67 | **Linux**: `/home` and `/mnt` 68 | **macOS**: `/Users` and `/Volumes` 69 | 70 | #### Hidden files and directories 71 | 72 | Hidden files and directories may be invisible in `FTP Client`, but you can always move to desired folder by name. For example: `/home/deck/.steam` 73 | 74 | #### FTP Server IP 75 | 76 | App will always select first Network interface with *lower IP*, get local `IP` and show it in UI. For example: `192.168.1.1`. In some cases, it may be not your actual `IP`. In that cases, check it with `ipconfig` commang on `Windows` and `ifconfig` on `Linux` 77 | 78 | #### FTP Users 79 | 80 | If you change `FTP Username` and want to delete old user, you will need to delete `users.properties` file that stored in app directory 81 | 82 | #### Build for macOS 83 | 84 | App is cross-platform and can be launched in `macOS` on regular `JDK 17+` with `JavaFX 20 SDK`, but it is designed to work as `Native app` that can be produced with `GraalVM Native image`. That neat feature requires some configuration before use 85 | 86 | There is no that configuration and build for `macOS`. But you can make it. Refer to `GraalVM Native Image` docs for info 87 | 88 | ## Building 89 | 90 | `Drop2Deck` is plain `Java` app that uses `JavaFX` for UI. If you want to build or develop app, clone this repo, make sure to rename `gradle.properties.example` into `gradle.properties`, fill `GraalVM` paths on it and open project in desired IDE or terminal 91 | 92 | ### Build JAR file 93 | 94 | Execute ```gradlew shadowJar``` task. This task will produce `fat` jar that can be launched with any `JRE/JDK 17+` with no additional dependencies 95 | 96 | *Note: is designed to work as `Native app` without any installed `JRE/JDK`* 97 | 98 | ### Build GraalVM Native Image 99 | 100 | ### Prerequisites 101 | 102 | Install latest [GraalVM JDK 17](https://www.graalvm.org/downloads/) for your system 103 | 104 | #### Windows 105 | 106 | Follow [Windows instructions](https://www.graalvm.org/latest/docs/getting-started/windows/) 107 | 108 | #### Linux 109 | 110 | Follow [Linux instructions](https://www.graalvm.org/latest/docs/getting-started/linux/) 111 | 112 | #### macOS 113 | 114 | Follow [macOS instructions](https://www.graalvm.org/latest/docs/getting-started/macos/) 115 | 116 | ### Building 117 | 118 | #### Windows 119 | 120 | Execute ```gradlew nativeBuild``` task. After success, binary file will be in `\build\gluonfx\x86_64-windows` folder 121 | 122 | #### Linux 123 | 124 | Rename `buildLinux.sh.example` into `buildLinux.sh` and fill required fields. Head into project dir and execute it. After success, binary file will be in `/build/gluonfx/x86_64-linux` folder 125 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform 2 | 3 | buildscript { 4 | repositories { 5 | maven { url "https://nexus.gluonhq.com/nexus/content/repositories/releases" } 6 | maven { url "https://plugins.gradle.org/m2/" } 7 | } 8 | dependencies { 9 | classpath "gradle.plugin.com.github.johnrengelman:shadow:7.1.2" 10 | classpath 'org.openjfx:javafx-plugin:0.1.0' 11 | classpath 'com.gluonhq:gluonfx-gradle-plugin:1.0.22' 12 | } 13 | } 14 | 15 | apply plugin: 'java' 16 | apply plugin: 'application' 17 | apply plugin: 'idea' 18 | 19 | apply plugin: 'com.github.johnrengelman.shadow' 20 | apply plugin: 'org.openjfx.javafxplugin' 21 | apply plugin: 'com.gluonhq.gluonfx-gradle-plugin' 22 | 23 | applicationName = 'DeckDrop' 24 | group = 'xyz.drop2deck' 25 | mainClassName = 'xyz.drop2deck.FXRunner' 26 | version = '1.1' 27 | 28 | compileJava.options.encoding = 'UTF-8' 29 | System.setProperty('file.encoding', 'UTF-8') 30 | 31 | repositories { 32 | mavenCentral() 33 | } 34 | 35 | OperatingSystem os = DefaultNativePlatform.currentOperatingSystem 36 | gluonfx { 37 | appIdentifier = applicationName 38 | 39 | if (os.isWindows()) { 40 | graalvmHome = GRAALVM_WINDOWS_HOME 41 | } else if (os.isLinux()) { 42 | graalvmHome = GRAALVM_LINUX_HOME 43 | } else if (os.isMacOsX()) { 44 | graalvmHome = GRAALVM_MACOSX_HOME 45 | } 46 | } 47 | 48 | nativeBuild { 49 | applicationDefaultJvmArgs = [ 50 | "-Djava.home=" + gluonfx.graalvmHome, 51 | "-Djava.awt.headless=true", 52 | "-Dfile.encoding=UTF-8", 53 | "-H:+AllowIncompleteClasspath", 54 | "-H:-IncludeMethodsData", 55 | "--no-fallback", 56 | "--enable-http", 57 | "--enable-https", 58 | "--allow-incomplete-classpath", 59 | "--static" 60 | ] 61 | } 62 | 63 | javafx { 64 | version = '22' 65 | modules = ['javafx.base', 'javafx.controls', 'javafx.fxml', 'javafx.graphics'] 66 | } 67 | 68 | dependencies { 69 | // SubstrateVM features 70 | compileOnly 'org.graalvm.nativeimage:svm:22.1.0' 71 | 72 | // SLF4j logging 73 | implementation 'org.slf4j:slf4j-api:2.0.7' 74 | implementation 'org.slf4j:slf4j-simple:2.0.7' 75 | 76 | // JNA 77 | implementation 'net.java.dev.jna:jna:5.13.0' 78 | implementation 'net.java.dev.jna:jna-platform:5.13.0' 79 | 80 | // Apache Mina 81 | implementation 'org.apache.mina:mina-core:2.2.2' 82 | 83 | // Apache FTP 84 | implementation 'org.apache.ftpserver:ftplet-api:1.2.0' 85 | implementation 'org.apache.ftpserver:ftpserver-core:1.2.0' 86 | 87 | // JavaFX 88 | implementation 'com.jfoenix:jfoenix:9.0.10' 89 | 90 | // Ikonli 91 | implementation 'org.kordamp.ikonli:ikonli-javafx:12.3.1' 92 | implementation 'org.kordamp.ikonli:ikonli-materialdesign2-pack:12.3.1' 93 | } 94 | 95 | jar { 96 | manifest { 97 | attributes 'Implementation-Title': applicationName, 98 | 'Implementation-Version': version, 99 | 'Main-Class': mainClassName, 100 | 'Built-By': 'CrazyXacker' 101 | } 102 | } 103 | 104 | run { 105 | new File(project.buildDir, "/resources/main/config.properties").delete() 106 | List args = new ArrayList() {{ 107 | add("--add-exports=javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED"); 108 | add("--add-opens=javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED"); 109 | }} 110 | 111 | applicationDefaultJvmArgs = args 112 | jvmArgs = args 113 | } -------------------------------------------------------------------------------- /buildLinux.sh.example: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | export JAVA_HOME={path_to_graalvm} 4 | export GRAALVM_HOME={path_to_graalvm} 5 | export USE_NATIVE_IMAGE_JAVA_PLATFORM_MODULE_SYSTEM=false 6 | 7 | ./gradlew nativeBuild -------------------------------------------------------------------------------- /gradle.properties.example: -------------------------------------------------------------------------------- 1 | GRAALVM_WINDOWS_HOME = 2 | GRAALVM_LINUX_HOME = 3 | GRAALVM_MACOSX_HOME = -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Sep 02 13:43:31 EEST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/images/logo.png -------------------------------------------------------------------------------- /images/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/images/start.png -------------------------------------------------------------------------------- /images/ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/images/ui.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'Drop2Deck' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/FXApplication.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck; 2 | 3 | import xyz.drop2deck.enums.Platform; 4 | import xyz.drop2deck.jna.DwmAttribute; 5 | import xyz.drop2deck.jna.StageOps; 6 | import xyz.drop2deck.util.FXUtils; 7 | import xyz.drop2deck.util.PlatformUtils; 8 | import javafx.application.Application; 9 | import javafx.application.HostServices; 10 | import javafx.scene.Parent; 11 | import javafx.scene.Scene; 12 | import javafx.stage.Stage; 13 | 14 | import java.util.prefs.Preferences; 15 | 16 | public class FXApplication extends Application { 17 | private static final String APP_NAME = "Drop2Deck"; 18 | public static Platform CURRENT_PLATFORM; 19 | public static Preferences PREFERENCES; 20 | public static HostServices HOST_SERVICES; 21 | 22 | @Override 23 | public void init() { 24 | // Detect on which OS app is launching 25 | CURRENT_PLATFORM = PlatformUtils.detectPlatform(); 26 | 27 | // Load preferences 28 | PREFERENCES = Preferences.userRoot(); 29 | 30 | // Get host services 31 | HOST_SERVICES = getHostServices(); 32 | } 33 | 34 | @Override 35 | public void start(Stage primaryStage) { 36 | // Load main FXML file 37 | Parent parent = FXUtils.loadFXMLAndGetParent("/fxml/Main.fxml"); 38 | 39 | // Create scene 40 | Scene scene = new Scene(parent); 41 | FXUtils.addDefaultStylesheet(scene); 42 | 43 | // Configure stage 44 | FXUtils.setTaskbarAppIcon(primaryStage, FXUtils.createAppIconImage()); 45 | primaryStage.setTitle(APP_NAME); 46 | primaryStage.setWidth(1280); 47 | primaryStage.setHeight(800); 48 | primaryStage.setMinWidth(600); 49 | primaryStage.setMinHeight(400); 50 | primaryStage.setScene(scene); 51 | primaryStage.show(); 52 | 53 | // Enable dark window style and Mica material on Windows 54 | manipulateWithNativeWindow(); 55 | } 56 | 57 | private void manipulateWithNativeWindow() { 58 | if (CURRENT_PLATFORM == Platform.WINDOWS) { 59 | // Enable Mica material and dark mode 60 | StageOps.WindowHandle handle = new StageOps.WindowHandle(APP_NAME); 61 | 62 | // Enable the dark mode 63 | StageOps.dwmSetBooleanValue(handle, DwmAttribute.DWMWA_USE_IMMERSIVE_DARK_MODE, true); 64 | 65 | // Enable the Mica material 66 | if (!StageOps.dwmSetIntValue(handle, DwmAttribute.DWMWA_SYSTEMBACKDROP_TYPE, DwmAttribute.DWMSBT_MAINWINDOW.value)) { 67 | StageOps.dwmSetBooleanValue(handle, DwmAttribute.DWMWA_MICA_EFFECT, true); // This is the "old" way 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/FXRunner.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck; 2 | 3 | import javafx.application.Application; 4 | 5 | public class FXRunner { 6 | 7 | public static void main(String[] args) { 8 | Application.launch(FXApplication.class, args); 9 | System.exit(0); 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/controller/MainController.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck.controller; 2 | 3 | import xyz.drop2deck.FXApplication; 4 | import xyz.drop2deck.enums.Platform; 5 | import xyz.drop2deck.ftp.FTPServer; 6 | import xyz.drop2deck.network.StorLoggingFtplet; 7 | import xyz.drop2deck.skin.VisiblePasswordFieldSkin; 8 | import xyz.drop2deck.util.FXUtils; 9 | import xyz.drop2deck.util.NetworkUtils; 10 | import xyz.drop2deck.util.QRCodeUtils; 11 | import com.jfoenix.controls.JFXButton; 12 | import com.jfoenix.controls.JFXDialog; 13 | import com.jfoenix.controls.JFXDialogLayout; 14 | import javafx.beans.property.BooleanProperty; 15 | import javafx.beans.property.SimpleBooleanProperty; 16 | import javafx.fxml.FXML; 17 | import javafx.scene.Node; 18 | import javafx.scene.Scene; 19 | import javafx.scene.control.Button; 20 | import javafx.scene.control.Label; 21 | import javafx.scene.control.PasswordField; 22 | import javafx.scene.control.TextField; 23 | import javafx.scene.image.Image; 24 | import javafx.scene.image.ImageView; 25 | import javafx.scene.layout.GridPane; 26 | import javafx.scene.layout.StackPane; 27 | import javafx.scene.layout.VBox; 28 | import javafx.stage.Stage; 29 | import org.kordamp.ikonli.javafx.FontIcon; 30 | 31 | import java.io.IOException; 32 | import java.util.HashMap; 33 | import java.util.Optional; 34 | import java.util.function.Function; 35 | 36 | import static xyz.drop2deck.FXApplication.PREFERENCES; 37 | 38 | public class MainController { 39 | private static final String DEFAULT_BUTTON_STYLE = "-fx-background-radius: 60; -fx-font-weight: bold; -fx-text-fill: white; -fx-background-color: "; 40 | private static final String STARTED_COLOR = "#c0392b;"; 41 | private static final String STOPPED_COLOR = "#0b82fb;"; 42 | 43 | private static final String STARTED_ICON = "/icons/round_stop_circle_48.png"; 44 | private static final String STOPPED_ICON = "/icons/round_play_circle_48.png"; 45 | 46 | private static final String NO_DATA = "No data transfer..."; 47 | 48 | private static final String PREF_USERNAME = "username"; 49 | private static final String PREF_PASSWORD = "password"; 50 | private static final String PREF_PORT_INTERNAL = "port_internal"; 51 | private static final String PREF_PORT_EXTERNAL = "port_external"; 52 | 53 | private static final String FTP_URL_TEMPLATE = "ftp://%s:%s@%s:%s"; 54 | 55 | private static final String DEFAULT_USERNAME = "deck"; 56 | private static final String DEFAULT_PASSWORD = "deck"; 57 | private static final int DEFAULT_PORT_INTERNAL = 9001; 58 | private static final int DEFAULT_PORT_EXTERNAL = 9002; 59 | 60 | private static final String GIT_URL = "https://github.com/CrazyXacker/Drop2Deck"; 61 | 62 | // Root node 63 | @FXML 64 | private StackPane root; 65 | 66 | // Server config 67 | @FXML 68 | private VBox vbConfig; 69 | @FXML 70 | private TextField tfUsername; 71 | @FXML 72 | private PasswordField tfPassword; 73 | @FXML 74 | private Button btnShowHidePassword; 75 | @FXML 76 | private FontIcon fiShowHidePassword; 77 | @FXML 78 | private TextField tfPortInternal; 79 | @FXML 80 | private TextField tfPortExternal; 81 | 82 | // Start/Stop server button 83 | @FXML 84 | private JFXButton btnStartStop; 85 | @FXML 86 | public ImageView ivStartStop; 87 | 88 | // Started server details 89 | @FXML 90 | private GridPane gpDetails; 91 | 92 | // QR 93 | @FXML 94 | private ImageView ivQRInternal; 95 | @FXML 96 | private ImageView ivQRExternal; 97 | 98 | // FTP urls 99 | @FXML 100 | private TextField tfInternalUrl; 101 | @FXML 102 | private TextField tfExternalUrl; 103 | 104 | // Logs 105 | @FXML 106 | private Label lblInternalLogs; 107 | @FXML 108 | private Label lblExternalLogs; 109 | 110 | // Other buttons 111 | @FXML 112 | private Button btnUsername; 113 | @FXML 114 | private Button btnPassword; 115 | @FXML 116 | private Button btnInternalMemory; 117 | @FXML 118 | private Button btnExternalMemory; 119 | @FXML 120 | private Button btnCopyInternal; 121 | @FXML 122 | private Button btnCopyExternal; 123 | 124 | private FTPServer ftpServerInternal; 125 | private FTPServer ftpServerExternal; 126 | 127 | private final BooleanProperty serverStartedProperty = new SimpleBooleanProperty(); 128 | 129 | private final Function storCallbackFunction = label -> new StorLoggingFtplet.StorCallback() { 130 | @Override 131 | public void onStart(String fileName) { 132 | javafx.application.Platform.runLater(() -> lblInternalLogs.setText("Saving: " + fileName)); 133 | } 134 | 135 | @Override 136 | public void onEnd(String fileName) { 137 | javafx.application.Platform.runLater(() -> lblInternalLogs.setText("Saved: " + fileName)); 138 | } 139 | }; 140 | 141 | @FXML 142 | public void initialize() { 143 | FXUtils.numericOnlyTextField(tfPortInternal); 144 | FXUtils.numericOnlyTextField(tfPortExternal); 145 | 146 | setPasswordFieldSkin(); 147 | bindVisibility(); 148 | bindTextFieldsInputChange(); 149 | roundQRCorners(); 150 | createOnServerStateChangeListener(); 151 | fixButtonHeightOnWindows(); 152 | 153 | loadDataFromPreferences(); 154 | } 155 | 156 | private void loadDataFromPreferences() { 157 | tfUsername.setText(PREFERENCES.get(PREF_USERNAME, DEFAULT_USERNAME)); 158 | tfPassword.setText(PREFERENCES.get(PREF_PASSWORD, DEFAULT_PASSWORD)); 159 | tfPortInternal.setText(String.valueOf(PREFERENCES.getInt(PREF_PORT_INTERNAL, DEFAULT_PORT_INTERNAL))); 160 | tfPortExternal.setText(String.valueOf(PREFERENCES.getInt(PREF_PORT_EXTERNAL, DEFAULT_PORT_EXTERNAL))); 161 | } 162 | 163 | private void setPasswordFieldSkin() { 164 | tfPassword.setSkin(new VisiblePasswordFieldSkin(tfPassword, btnShowHidePassword, fiShowHidePassword)); 165 | } 166 | 167 | private void bindVisibility() { 168 | vbConfig.visibleProperty().bind(serverStartedProperty.not()); 169 | vbConfig.managedProperty().bind(vbConfig.visibleProperty()); 170 | 171 | gpDetails.visibleProperty().bind(serverStartedProperty); 172 | gpDetails.managedProperty().bind(gpDetails.visibleProperty()); 173 | } 174 | 175 | private void bindTextFieldsInputChange() { 176 | tfUsername.textProperty().addListener((observable, oldValue, newValue) -> PREFERENCES.put(PREF_USERNAME, newValue)); 177 | tfPassword.textProperty().addListener((observable, oldValue, newValue) -> PREFERENCES.put(PREF_PASSWORD, newValue)); 178 | tfPortInternal.textProperty().addListener((observable, oldValue, newValue) -> PREFERENCES.putInt(PREF_PORT_INTERNAL, Integer.parseInt(newValue))); 179 | tfPortExternal.textProperty().addListener((observable, oldValue, newValue) -> PREFERENCES.putInt(PREF_PORT_EXTERNAL, Integer.parseInt(newValue))); 180 | } 181 | 182 | private void roundQRCorners() { 183 | FXUtils.clipRoundedCorners(ivQRInternal, 300, 300, 22, 22); 184 | FXUtils.clipRoundedCorners(ivQRExternal, 300, 300, 22, 22); 185 | } 186 | 187 | private void createOnServerStateChangeListener() { 188 | serverStartedProperty.addListener((observable, oldValue, started) -> { 189 | ivStartStop.setImage(new Image(started ? STARTED_ICON : STOPPED_ICON)); 190 | btnStartStop.setStyle(DEFAULT_BUTTON_STYLE + (started ? STARTED_COLOR : STOPPED_COLOR)); 191 | lblInternalLogs.setText(NO_DATA); 192 | lblExternalLogs.setText(NO_DATA); 193 | 194 | fillFtpServerInfo(); 195 | }); 196 | } 197 | 198 | private void fixButtonHeightOnWindows() { 199 | if (FXApplication.CURRENT_PLATFORM == Platform.WINDOWS) { 200 | btnUsername.setMinHeight(32); 201 | btnPassword.setMinHeight(32); 202 | btnInternalMemory.setMinHeight(32); 203 | btnExternalMemory.setMinHeight(32); 204 | btnCopyInternal.setMinHeight(32); 205 | btnCopyExternal.setMinHeight(32); 206 | btnShowHidePassword.setMinHeight(32); 207 | } 208 | } 209 | 210 | private void fillFtpServerInfo() { 211 | Optional.of(NetworkUtils.getLocalIPs()) 212 | .filter(list -> !list.isEmpty()) 213 | .map(list -> list.get(0)) 214 | .ifPresent(ip -> { 215 | String internalUrl = String.format(FTP_URL_TEMPLATE, tfUsername.getText(), tfPassword.getText(), ip, tfPortInternal.getText()); 216 | String externalUrl = String.format(FTP_URL_TEMPLATE, tfUsername.getText(), tfPassword.getText(), ip, tfPortExternal.getText()); 217 | 218 | tfInternalUrl.setText(internalUrl); 219 | tfExternalUrl.setText(externalUrl); 220 | 221 | ivQRInternal.setImage(null); 222 | ivQRExternal.setImage(null); 223 | 224 | ivQRInternal.setImage(new Image(QRCodeUtils.createQRCodeUrl(internalUrl), true)); 225 | ivQRExternal.setImage(new Image(QRCodeUtils.createQRCodeUrl(externalUrl), true)); 226 | }); 227 | } 228 | 229 | @FXML 230 | private void startStopServer() { 231 | try { 232 | if (ftpServerInternal == null && ftpServerExternal == null) { 233 | ftpServerInternal = createServer( 234 | lblInternalLogs, 235 | FXApplication.CURRENT_PLATFORM.getInternalPath(), 236 | tfPortInternal.getText() 237 | ); 238 | ftpServerExternal = createServer( 239 | lblExternalLogs, 240 | FXApplication.CURRENT_PLATFORM.getExternalPath(), 241 | tfPortExternal.getText() 242 | ); 243 | 244 | ftpServerInternal.start(); 245 | ftpServerExternal.start(); 246 | } else { 247 | destroyServers(); 248 | } 249 | } catch (Exception ex) { 250 | ex.printStackTrace(); 251 | showErrorDialog(ex.getMessage()); 252 | } 253 | 254 | serverStartedProperty.set(ftpServerInternal != null && ftpServerExternal != null); 255 | } 256 | 257 | private FTPServer createServer(Label lblLogs, String path, String port) throws IOException { 258 | return FTPServer.create( 259 | new HashMap<>(){{ 260 | put(lblLogs.getId(), new StorLoggingFtplet(storCallbackFunction.apply(lblLogs))); 261 | }}, 262 | path, 263 | tfUsername.getText(), 264 | tfPassword.getText(), 265 | Integer.parseInt(port) 266 | ); 267 | } 268 | 269 | @FXML 270 | private void copyInternalUrl() { 271 | FXUtils.copyToClipboard(tfInternalUrl.getText()); 272 | } 273 | 274 | @FXML 275 | private void copyExternalUrl() { 276 | FXUtils.copyToClipboard(tfExternalUrl.getText()); 277 | } 278 | 279 | @FXML 280 | private void showGit() { 281 | FXApplication.HOST_SERVICES.showDocument(GIT_URL); 282 | } 283 | 284 | @FXML 285 | private void exit() { 286 | Optional.of(root) 287 | .map(Node::getScene) 288 | .map(Scene::getWindow) 289 | .map(Stage.class::cast) 290 | .ifPresent(Stage::close); 291 | } 292 | 293 | private void showErrorDialog(String reason) { 294 | JFXDialogLayout layout = new JFXDialogLayout(); 295 | layout.setStyle("-fx-background-color: #28282e"); 296 | layout.setHeading(new Label("Error")); 297 | layout.setBody(new Label("Unable to start server!\n\nReason: " + reason)); 298 | 299 | new JFXDialog(root, layout, JFXDialog.DialogTransition.CENTER).show(); 300 | } 301 | 302 | private void destroyServers() { 303 | if (ftpServerInternal != null) { 304 | ftpServerInternal.stop(); 305 | } 306 | if (ftpServerExternal != null) { 307 | ftpServerExternal.stop(); 308 | } 309 | 310 | ftpServerInternal = null; 311 | ftpServerExternal = null; 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/enums/Platform.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck.enums; 2 | 3 | public enum Platform { 4 | STEAM_DECK("/home", "/run/media/mmcblk0p1"), 5 | WINDOWS("C:\\", "D:\\"), 6 | LINUX("/home", "/mnt"), 7 | MAC_OSX("/Users", "/Volumes"); 8 | 9 | private final String internalPath; 10 | private final String externalPath; 11 | 12 | Platform(String internalPath, String externalPath) { 13 | this.internalPath = internalPath; 14 | this.externalPath = externalPath; 15 | } 16 | 17 | public String getInternalPath() { 18 | return internalPath; 19 | } 20 | 21 | public String getExternalPath() { 22 | return externalPath; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/ftp/FTPServer.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck.ftp; 2 | 3 | import org.apache.ftpserver.FtpServer; 4 | import org.apache.ftpserver.FtpServerFactory; 5 | import org.apache.ftpserver.ftplet.*; 6 | import org.apache.ftpserver.listener.Listener; 7 | import org.apache.ftpserver.listener.ListenerFactory; 8 | import org.apache.ftpserver.usermanager.PasswordEncryptor; 9 | import org.apache.ftpserver.usermanager.PropertiesUserManagerFactory; 10 | import org.apache.ftpserver.usermanager.impl.BaseUser; 11 | import org.apache.ftpserver.usermanager.impl.WritePermission; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | public class FTPServer { 20 | private final FtpServer server; 21 | 22 | private FTPServer(Map ftpletsMap, String homePath, String userName, String userPassword, int port) throws IOException { 23 | // Create FtpServer factory 24 | FtpServerFactory serverFactory = new FtpServerFactory(); 25 | 26 | // Add Ftplets 27 | if (ftpletsMap != null) { 28 | serverFactory.setFtplets(ftpletsMap); 29 | } 30 | 31 | // Replace default listener 32 | serverFactory.addListener("default", createListener(port)); 33 | 34 | // Create user manager 35 | serverFactory.setUserManager(createUserManager(homePath, userName, userPassword)); 36 | 37 | // Create server 38 | server = serverFactory.createServer(); 39 | } 40 | 41 | /** 42 | * Create {@link FtpServer} instance for use with {@link #start()} and {@link #stop()} methods 43 | * @param ftpletsMap {@link Map} with custom {@link Ftplet} implementations 44 | * @param homePath {@link String} path to home directory 45 | * @param userName {@link User} username 46 | * @param userPassword {@link User} password 47 | * @param port {@link FtpServer} port 48 | * @throws IOException if properties {@link File} doesn't exists and can't be created 49 | */ 50 | public static FTPServer create(Map ftpletsMap, String homePath, String userName, String userPassword, int port) throws IOException { 51 | return new FTPServer(ftpletsMap, homePath, userName, userPassword, port); 52 | } 53 | 54 | /** 55 | * Check if {@link FtpServer} is stopped 56 | */ 57 | public boolean isStopped() { 58 | return server.isStopped(); 59 | } 60 | 61 | /** 62 | * Start {@link FtpServer} 63 | */ 64 | public void start() throws FtpException { 65 | server.start(); 66 | } 67 | 68 | /** 69 | * Stop launched {@link FtpServer} 70 | */ 71 | public void stop() { 72 | server.stop(); 73 | } 74 | 75 | /** 76 | * Create {@link Listener} with custom {@link FtpServer} port 77 | * @param port {@link FtpServer} port 78 | * @return {@link Listener} {@link FtpServer} listener 79 | */ 80 | private static Listener createListener(int port) { 81 | ListenerFactory listenerFactory = new ListenerFactory(); 82 | listenerFactory.setPort(port); 83 | return listenerFactory.createListener(); 84 | } 85 | 86 | /** 87 | * Create {@link UserManager} instance for {@link FtpServer} 88 | * @param homePath {@link String} path to home directory 89 | * @param userName {@link User} username 90 | * @param userPassword {@link User} password 91 | * @return {@link UserManager} instance 92 | * @throws IOException if properties {@link File} doesn't exists and can't be created 93 | */ 94 | private static UserManager createUserManager(String homePath, String userName, String userPassword) throws IOException { 95 | UserManager userManager = createUserManagerFactory().createUserManager(); 96 | try { 97 | userManager.save(createUser(homePath, userName, userPassword)); 98 | } catch (FtpException ex) { 99 | ex.printStackTrace(); 100 | } 101 | return userManager; 102 | } 103 | 104 | /** 105 | * Create {@link PropertiesUserManagerFactory} factory for file user properties and plain passwords 106 | * @return {@link PropertiesUserManagerFactory} factory 107 | * @throws IOException if properties {@link File} doesn't exists and can't be created 108 | */ 109 | private static PropertiesUserManagerFactory createUserManagerFactory() throws IOException { 110 | PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory(); 111 | userManagerFactory.setFile(getUserPropertiesFile()); 112 | userManagerFactory.setPasswordEncryptor(new PasswordEncryptor() { 113 | @Override 114 | public String encrypt(String password) { 115 | return password; 116 | } 117 | 118 | @Override 119 | public boolean matches(String passwordToCheck, String storedPassword) { 120 | return passwordToCheck.equals(storedPassword); 121 | } 122 | }); 123 | return userManagerFactory; 124 | } 125 | 126 | /** 127 | * Create R/W access {@link User} with provided username and password 128 | * @param homePath {@link String} path to home directory 129 | * @param userName {@link User} username 130 | * @param userPassword {@link User} password 131 | * @return {@link User} object with credentials and R/W access 132 | */ 133 | private static User createUser(String homePath, String userName, String userPassword) { 134 | BaseUser user = new BaseUser(); 135 | user.setName(userName); 136 | user.setPassword(userPassword); 137 | user.setHomeDirectory(homePath); 138 | 139 | List authorities = new ArrayList<>(); 140 | authorities.add(new WritePermission()); 141 | user.setAuthorities(authorities); 142 | 143 | return user; 144 | } 145 | 146 | /** 147 | * Create properties {@link File} where {@link User}s will be stored 148 | * @return properties {@link File} 149 | * @throws IOException if properties {@link File} doesn't exists and can't be created 150 | */ 151 | private static File getUserPropertiesFile() throws IOException { 152 | File file = new File("./users.properties"); 153 | if (!file.exists()) { 154 | file.createNewFile(); 155 | } 156 | return file; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/jna/DwmAttribute.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck.jna; 2 | 3 | /** Some DWM enum variants and constants. */ 4 | public enum DwmAttribute { 5 | /** A possible value for DWMWA_SYSTEMBACKDROP_TYPE: default. */ 6 | DWMSBT_DISABLE(1), 7 | 8 | /** A possible value for DWMWA_SYSTEMBACKDROP_TYPE: mica. */ 9 | DWMSBT_MAINWINDOW(2), 10 | 11 | /** A possible value for DWMWA_SYSTEMBACKDROP_TYPE: tabbed. */ 12 | DWMSBT_TABBEDWINDOW(4), 13 | 14 | /** A possible value for DWMWA_SYSTEMBACKDROP_TYPE: acrylic. */ 15 | DWMSBT_TRANSIENTWINDOW(3), 16 | 17 | /** 18 | * Window border color. Accepts a COLORREF or a DWORD reference for pvAttribute, 19 | * 0x00_bb_gg_rr. 20 | */ 21 | DWMWA_BORDER_COLOR(34), 22 | 23 | /** 24 | * Title bar color. Accepts a COLORREF or a DWORD reference for pvAttribute, 25 | * 0x00_bb_gg_rr. 26 | */ 27 | DWMWA_CAPTION_COLOR(35), 28 | 29 | /** 30 | * The old way of enabling the mica effect. Takes a boolean reference for 31 | * pvAttribute. 32 | */ 33 | DWMWA_MICA_EFFECT(1029), 34 | 35 | /** 36 | * The new way of enabling the mica effect. Takes a DWMSBT_* for pvAttribute 37 | */ 38 | DWMWA_SYSTEMBACKDROP_TYPE(38), 39 | 40 | /** 41 | * Title text color. Accepts a COLORREF or a DWORD reference for pvAttribute, 42 | * 0x00_bb_gg_rr. 43 | */ 44 | DWMWA_TEXT_COLOR(36), 45 | 46 | /** Set dark (or light) mode. Accepts a BOOLRef for pvAttribute. */ 47 | DWMWA_USE_IMMERSIVE_DARK_MODE(20); 48 | 49 | /** Corresponding DMW constant value */ 50 | public final int value; 51 | 52 | DwmAttribute(int value) { 53 | this.value = value; 54 | } 55 | } -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/jna/StageOps.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck.jna; 2 | 3 | import com.sun.jna.Library; 4 | import com.sun.jna.Native; 5 | import com.sun.jna.PointerType; 6 | import com.sun.jna.platform.win32.User32; 7 | import com.sun.jna.platform.win32.W32Errors; 8 | import com.sun.jna.platform.win32.WinDef; 9 | import com.sun.jna.platform.win32.WinNT; 10 | import javafx.scene.paint.Color; 11 | 12 | /** 13 | * A small collection of utility methods to customize a window. 14 | * Targets Windows 11+, won't show any effect on unsupported OSes. 15 | */ 16 | @SuppressWarnings("UnusedReturnValue") 17 | public class StageOps { 18 | /** 19 | * A wrapper for HWND type. 20 | */ 21 | public static class WindowHandle { 22 | private final WinDef.HWND value; 23 | 24 | public WindowHandle(String stageName) { 25 | value = getHwnd(stageName); 26 | } 27 | } 28 | 29 | private static WinDef.HWND getHwnd(String stageName) { 30 | return User32.INSTANCE.FindWindow(null, stageName); 31 | } 32 | 33 | private interface DwmSupport extends Library { 34 | DwmSupport INSTANCE = Native.load("dwmapi", DwmSupport.class); 35 | 36 | WinNT.HRESULT DwmSetWindowAttribute( 37 | WinDef.HWND hwnd, 38 | int dwAttribute, 39 | PointerType pvAttribute, 40 | int cbAttribute 41 | ); 42 | } 43 | 44 | /** 45 | * A wrapper for DwmSetWindowAttribute. 46 | * 47 | * @param handle WindowHandle for the window. Can be obtained by using findWindowHandle method. Can be null. 48 | * @param attribute dwAttribute 49 | * @param value pvAttribute 50 | * @return True if it was successful, false if it wasn't. 51 | */ 52 | public static boolean dwmSetBooleanValue(final WindowHandle handle, final DwmAttribute attribute, final boolean value) { 53 | if (handle == null) { 54 | return false; 55 | } 56 | return isOk( 57 | DwmSupport.INSTANCE.DwmSetWindowAttribute( 58 | handle.value, 59 | attribute.value, 60 | new WinDef.BOOLByReference(new WinDef.BOOL(value)), 61 | WinDef.BOOL.SIZE 62 | ) 63 | ); 64 | } 65 | 66 | /** 67 | * A wrapper for DwmSetWindowAttribute. 68 | * 69 | * @param handle WindowHandle for the window. Can be obtained by using findWindowHandle method. Can be null. 70 | * @param attribute dwAttribute 71 | * @param value pvAttribute 72 | * @return True if it was successful, false if it wasn't. 73 | */ 74 | public static boolean dwmSetIntValue(final WindowHandle handle, final DwmAttribute attribute, final int value) { 75 | if (handle == null) { 76 | return false; 77 | } 78 | return isOk( 79 | DwmSupport.INSTANCE.DwmSetWindowAttribute( 80 | handle.value, 81 | attribute.value, 82 | new WinDef.DWORDByReference(new WinDef.DWORD(value)), 83 | WinDef.DWORD.SIZE 84 | ) 85 | ); 86 | } 87 | 88 | /** 89 | * Sets the border color of a window. 90 | * 91 | * @param handle WindowHandle for the window. Can be obtained by using findWindowHandle method. Can be null. 92 | * @param color Border color 93 | * @return True if it was successful, false if it wasn't. 94 | */ 95 | public static boolean setBorderColor(final WindowHandle handle, final Color color) { 96 | return dwmSetIntValue(handle, DwmAttribute.DWMWA_BORDER_COLOR, RGB(color)); 97 | } 98 | 99 | /** 100 | * Sets the title bar background color of a window. 101 | * 102 | * @param handle WindowHandle for the window. Can be obtained by using findWindowHandle method. Can be null. 103 | * @param color Caption color 104 | * @return True if it was successful, false if it wasn't. 105 | */ 106 | public static boolean setCaptionColor(final WindowHandle handle, final Color color) { 107 | return dwmSetIntValue(handle, DwmAttribute.DWMWA_CAPTION_COLOR, RGB(color)); 108 | } 109 | 110 | /** 111 | * Sets the title text color of a window. 112 | * 113 | * @param handle WindowHandle for the window. Can be obtained by using findWindowHandle method. Can be null. 114 | * @param color Caption color 115 | * @return True if it was successful, false if it wasn't. 116 | */ 117 | public static boolean setTextColor(final WindowHandle handle, final Color color) { 118 | return dwmSetIntValue(handle, DwmAttribute.DWMWA_TEXT_COLOR, RGB(color)); 119 | } 120 | 121 | private static int floatingTo8Bit(final double n) { 122 | return (int) Math.min(255.0, Math.max(n * 255.0, 0.0)); 123 | } 124 | 125 | private static boolean isOk(final WinNT.HRESULT result) { 126 | return WinNT.HRESULT.compare(result, W32Errors.S_OK) == 0; 127 | } 128 | 129 | private static int RGB(final Color color) { 130 | return (floatingTo8Bit(color.getBlue()) << 16) 131 | | (floatingTo8Bit(color.getGreen()) << 8) 132 | | floatingTo8Bit(color.getRed()); 133 | } 134 | 135 | private StageOps() { 136 | } 137 | } -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/layout/InputGroup.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck.layout; 2 | 3 | import javafx.beans.InvalidationListener; 4 | import javafx.geometry.Pos; 5 | import javafx.scene.Node; 6 | import javafx.scene.layout.HBox; 7 | 8 | /** 9 | * A layout that helps combine multiple controls into a group that looks 10 | * like a single control. 11 | * 12 | *

Without it, you would have to manually add the ".left-pill", ".center-pill" 13 | * and ".right-pill" styles classes to each control in such combination. 14 | * The InputGroup removes this ceremony. Since it inherits from HBox, you can use 15 | * the same API. 16 | */ 17 | public class InputGroup extends HBox { 18 | public static final String LEFT_PILL = "left-pill"; 19 | public static final String CENTER_PILL = "center-pill"; 20 | public static final String RIGHT_PILL = "right-pill"; 21 | 22 | /** 23 | * Creates a new empty InputGroup. 24 | */ 25 | public InputGroup() { 26 | super(); 27 | init(); 28 | } 29 | 30 | /** 31 | * Creates an InputGroup with the given children. 32 | * 33 | * @param children The initial set of children for this pane. 34 | */ 35 | public InputGroup(Node... children) { 36 | super(children); 37 | init(); 38 | } 39 | 40 | protected void init() { 41 | setAlignment(Pos.CENTER_LEFT); 42 | getStyleClass().add("input-group"); 43 | 44 | updateStyles(); 45 | getChildren().addListener((InvalidationListener) o -> updateStyles()); 46 | } 47 | 48 | // We don't clean up style classes if a control is removed from the input group. 49 | // However, they will be fixed if the same control is added to the input group again. 50 | protected void updateStyles() { 51 | for (int i = 0; i < getChildren().size(); i++) { 52 | Node n = getChildren().get(i); 53 | 54 | n.getStyleClass().removeAll( 55 | LEFT_PILL, CENTER_PILL, RIGHT_PILL 56 | ); 57 | 58 | if (i == getChildren().size() - 1) { 59 | if (i != 0) { 60 | n.getStyleClass().add(RIGHT_PILL); 61 | } 62 | } else if (i == 0) { 63 | n.getStyleClass().add(LEFT_PILL); 64 | } else { 65 | n.getStyleClass().add(CENTER_PILL); 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/network/StorLoggingFtplet.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck.network; 2 | 3 | import org.apache.ftpserver.ftplet.FtpReply; 4 | import org.apache.ftpserver.ftplet.FtpRequest; 5 | import org.apache.ftpserver.ftplet.FtpSession; 6 | import org.apache.ftpserver.ftplet.Ftplet; 7 | import org.apache.ftpserver.ftplet.FtpletContext; 8 | import org.apache.ftpserver.ftplet.FtpletResult; 9 | 10 | public class StorLoggingFtplet implements Ftplet { 11 | private static final String STOR_COMMAND = "STOR"; 12 | private final StorCallback callback; 13 | 14 | public StorLoggingFtplet(StorCallback callback) { 15 | this.callback = callback; 16 | } 17 | 18 | @Override 19 | public void init(FtpletContext ftpletContext) { 20 | } 21 | 22 | @Override 23 | public void destroy() { 24 | } 25 | 26 | @Override 27 | public FtpletResult beforeCommand(FtpSession session, FtpRequest request) { 28 | System.out.println(request.getRequestLine()); 29 | String data = getStorCommandData(request); 30 | if (data != null) { 31 | callback.onStart(data); 32 | } 33 | return null; 34 | } 35 | 36 | @Override 37 | public FtpletResult afterCommand(FtpSession session, FtpRequest request, FtpReply reply) { 38 | System.out.println(request.getRequestLine()); 39 | String data = getStorCommandData(request); 40 | if (data != null) { 41 | callback.onEnd(data); 42 | } 43 | return null; 44 | } 45 | 46 | @Override 47 | public FtpletResult onConnect(FtpSession session) { 48 | return null; 49 | } 50 | 51 | @Override 52 | public FtpletResult onDisconnect(FtpSession session) { 53 | return null; 54 | } 55 | 56 | private String getStorCommandData(FtpRequest request) { 57 | String requestLine = request.getRequestLine(); 58 | if (requestLine.startsWith(STOR_COMMAND)) { 59 | return requestLine.replace(STOR_COMMAND, "").trim(); 60 | } 61 | return null; 62 | } 63 | 64 | public interface StorCallback { 65 | void onStart(String fileName); 66 | void onEnd(String fileName); 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/skin/VisiblePasswordFieldSkin.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck.skin; 2 | 3 | import javafx.scene.Cursor; 4 | import javafx.scene.control.Button; 5 | import javafx.scene.control.PasswordField; 6 | import javafx.scene.control.skin.TextFieldSkin; 7 | import org.kordamp.ikonli.javafx.FontIcon; 8 | 9 | public class VisiblePasswordFieldSkin extends TextFieldSkin { 10 | private static final String BULLET = "•"; 11 | 12 | private static final String EYE = "mdi2e-eye"; 13 | private static final String EYE_OFF = "mdi2e-eye-off"; 14 | 15 | private boolean mask = true; 16 | 17 | public VisiblePasswordFieldSkin(PasswordField textField, Button showHideButton, FontIcon showHideIcon) { 18 | super(textField); 19 | 20 | showHideButton.setCursor(Cursor.HAND); 21 | showHideButton.setVisible(false); 22 | 23 | showHideButton.setOnMouseClicked(event -> { 24 | showHideIcon.setIconLiteral(mask ? EYE_OFF : EYE); 25 | mask = !mask; 26 | 27 | textField.setText(textField.getText()); 28 | textField.end(); 29 | 30 | }); 31 | 32 | textField.textProperty().addListener((observable, oldValue, newValue) -> showHideButton.setVisible(!newValue.isEmpty())); 33 | } 34 | 35 | @Override 36 | protected String maskText(String txt) { 37 | if (getSkinnable() instanceof PasswordField && mask) { 38 | return BULLET.repeat(txt.length()); 39 | } else { 40 | return txt; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/util/FXUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck.util; 2 | 3 | import xyz.drop2deck.FXApplication; 4 | import javafx.fxml.FXMLLoader; 5 | import javafx.scene.Node; 6 | import javafx.scene.Parent; 7 | import javafx.scene.Scene; 8 | import javafx.scene.control.TextField; 9 | import javafx.scene.image.Image; 10 | import javafx.scene.input.Clipboard; 11 | import javafx.scene.input.ClipboardContent; 12 | import javafx.scene.shape.Rectangle; 13 | import javafx.stage.Stage; 14 | import javafx.util.Pair; 15 | 16 | import java.io.IOException; 17 | 18 | public class FXUtils { 19 | 20 | public static void setTaskbarAppIcon(Stage primaryStage, Image appIconImage) { 21 | primaryStage.getIcons().add(appIconImage); 22 | } 23 | 24 | public static Image createAppIconImage() { 25 | return new Image("/icons/icon.png"); 26 | } 27 | 28 | public static void addDefaultStylesheet(Scene scene) { 29 | scene.getStylesheets().addAll( 30 | FXApplication.class.getResource("/css/cupertino-dark.css").toExternalForm() 31 | ); 32 | } 33 | 34 | public static FXMLLoader getLoader(String fxmlPath) { 35 | return new FXMLLoader(FXApplication.class.getResource(fxmlPath), null); 36 | } 37 | 38 | public static FXMLLoader loadFXMLAndGetLoader(String fxmlPath) { 39 | FXMLLoader loader = getLoader(fxmlPath); 40 | try { 41 | return loader.load(); 42 | } catch (IOException e) { 43 | throw new RuntimeException(e); 44 | } 45 | } 46 | 47 | public static Pair loadFXML(String fxmlPath) { 48 | FXMLLoader loader = getLoader(fxmlPath); 49 | try { 50 | return new Pair<>(loader.load(), loader.getController()); 51 | } catch (IOException e) { 52 | throw new RuntimeException(e); 53 | } 54 | } 55 | 56 | public static Parent loadFXMLAndGetParent(String fxmlPath) { 57 | FXMLLoader loader = getLoader(fxmlPath); 58 | try { 59 | loader.load(); 60 | } catch (IOException e) { 61 | throw new RuntimeException(e); 62 | } 63 | return loader.getRoot(); 64 | } 65 | 66 | public static T loadFXMLAndGetController(String fxmlPath) { 67 | FXMLLoader loader = getLoader(fxmlPath); 68 | try { 69 | loader.load(); 70 | } catch (IOException e) { 71 | throw new RuntimeException(e); 72 | } 73 | return loader.getController(); 74 | } 75 | 76 | public static void clipRoundedCorners(Node node, double width, double height, int arcWidth, int arcHeight) { 77 | Rectangle rectangle = new Rectangle(); 78 | rectangle.setWidth(width); 79 | rectangle.setHeight(height); 80 | rectangle.setArcWidth(arcWidth); 81 | rectangle.setArcHeight(arcHeight); 82 | node.setClip(rectangle); 83 | } 84 | 85 | public static void copyToClipboard(String clip) { 86 | ClipboardContent content = new ClipboardContent(); 87 | content.putString(clip); 88 | 89 | Clipboard.getSystemClipboard().setContent(content); 90 | } 91 | 92 | public static void numericOnlyTextField(TextField field) { 93 | field.textProperty().addListener((observable, oldValue, newValue) -> { 94 | if (!newValue.matches("\\d*")) { 95 | field.setText(newValue.replaceAll("\\D", "")); 96 | } 97 | }); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/util/NetworkUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck.util; 2 | 3 | import java.net.*; 4 | import java.util.*; 5 | 6 | public class NetworkUtils { 7 | private static final Comparator IP_COMPARATOR = (a, b) -> { 8 | int[] aOct = Arrays.stream(a.split("\\.")).mapToInt(Integer::parseInt).toArray(); 9 | int[] bOct = Arrays.stream(b.split("\\.")).mapToInt(Integer::parseInt).toArray(); 10 | int r = 0; 11 | for (int i = 0; i < aOct.length && i < bOct.length; i++) { 12 | r = Integer.compare(aOct[i], bOct[i]); 13 | if (r != 0) { 14 | return r; 15 | } 16 | } 17 | return r; 18 | }; 19 | 20 | public static List getLocalIPs() { 21 | List addresses = new ArrayList<>(); 22 | try { 23 | for (Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements(); ) { 24 | final NetworkInterface cur = interfaces.nextElement(); 25 | 26 | if (cur.isLoopback() || !cur.isUp()) { 27 | continue; 28 | } 29 | 30 | for (InterfaceAddress addr : cur.getInterfaceAddresses()) { 31 | InetAddress inetAddress = addr.getAddress(); 32 | 33 | if (!(inetAddress instanceof Inet4Address)) { 34 | continue; 35 | } 36 | 37 | String address = inetAddress.getHostAddress(); 38 | if (!address.startsWith("192.168.")) { 39 | continue; 40 | } 41 | 42 | addresses.add(inetAddress.getHostAddress()); 43 | addresses.sort(IP_COMPARATOR); 44 | } 45 | } 46 | 47 | return addresses; 48 | } catch (SocketException ex) { 49 | ex.printStackTrace(); 50 | return new ArrayList<>(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/util/PlatformUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck.util; 2 | 3 | import xyz.drop2deck.enums.Platform; 4 | import org.graalvm.nativeimage.ImageInfo; 5 | import org.graalvm.nativeimage.ImageSingletons; 6 | 7 | import java.nio.file.Files; 8 | import java.nio.file.Paths; 9 | 10 | public class PlatformUtils { 11 | private static final String LINUX_BOARD_VENDOR_PATH = "/sys/devices/virtual/dmi/id/board_vendor"; 12 | private static final String VALVE_VENDOR = "valve"; 13 | private static final String STEAM_DECK_CODENAME = "jupiter"; 14 | 15 | public static Platform detectPlatform() { 16 | String osName = ImageInfo.inImageCode() 17 | ? ImageSingletons.lookup(org.graalvm.nativeimage.Platform.class).getOS() 18 | : System.getProperty("os.name"); 19 | 20 | if (isOnSteamDeck()) { 21 | return Platform.STEAM_DECK; 22 | } else if (isWindows(osName)) { 23 | return Platform.WINDOWS; 24 | } else if (isUnix(osName)) { 25 | return Platform.LINUX; 26 | } else if (isMac(osName)) { 27 | return Platform.MAC_OSX; 28 | } 29 | throw new RuntimeException("Unsupported platform!"); 30 | } 31 | 32 | public static boolean isOnSteamDeck() { 33 | try { 34 | return Files.readAllLines(Paths.get(LINUX_BOARD_VENDOR_PATH)) 35 | .stream() 36 | .map(String::toLowerCase) 37 | .anyMatch(value -> value.contains(VALVE_VENDOR) || value.contains(STEAM_DECK_CODENAME)); 38 | } catch (Exception e) { 39 | return false; 40 | } 41 | } 42 | 43 | public static boolean isWindows(String osName) { 44 | String os = osName.toLowerCase(); 45 | return os.contains("win"); 46 | } 47 | 48 | public static boolean isMac(String osName) { 49 | String os = osName.toLowerCase(); 50 | return os.contains("mac") || os.contains("darwin"); 51 | } 52 | 53 | public static boolean isUnix(String osName) { 54 | String os = osName.toLowerCase(); 55 | return os.contains("nix") || os.contains("nux"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/xyz/drop2deck/util/QRCodeUtils.java: -------------------------------------------------------------------------------- 1 | package xyz.drop2deck.util; 2 | 3 | public class QRCodeUtils { 4 | private static final String QR_GEN_URL = "https://quickchart.io/chart?cht=qr&chs=300&choe=UTF-8&chl="; 5 | 6 | public static String createQRCodeUrl(String data) { 7 | return QR_GEN_URL + data; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/jni-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name":"[Lcom.sun.glass.ui.Screen;" 4 | }, 5 | { 6 | "name":"[Lcom.sun.javafx.font.FontConfigManager$FontConfigFont;" 7 | }, 8 | { 9 | "name":"com.sun.glass.ui.Application", 10 | "methods":[ 11 | {"name":"GetApplication","parameterTypes":[] }, 12 | {"name":"getName","parameterTypes":[] }, 13 | {"name":"reportException","parameterTypes":["java.lang.Throwable"] } 14 | ] 15 | }, 16 | { 17 | "name":"com.sun.glass.ui.Clipboard", 18 | "methods":[{"name":"contentChanged","parameterTypes":[] }] 19 | }, 20 | { 21 | "name":"com.sun.glass.ui.Cursor", 22 | "fields":[{"name":"ptr"}] 23 | }, 24 | { 25 | "name":"com.sun.glass.ui.Pixels", 26 | "methods":[{"name":"attachData","parameterTypes":["long"] }] 27 | }, 28 | { 29 | "name":"com.sun.glass.ui.Screen", 30 | "methods":[ 31 | {"name":"","parameterTypes":["long","int","int","int","int","int","int","int","int","int","int","int","int","int","int","int","float","float","float","float"] }, 32 | {"name":"notifySettingsChanged","parameterTypes":[] } 33 | ] 34 | }, 35 | { 36 | "name":"com.sun.glass.ui.Size", 37 | "methods":[{"name":"","parameterTypes":["int","int"] }] 38 | }, 39 | { 40 | "name":"com.sun.glass.ui.View", 41 | "fields":[{"name":"ptr"}], 42 | "methods":[ 43 | {"name":"notifyDragDrop","parameterTypes":["int","int","int","int","int"] }, 44 | {"name":"notifyDragEnter","parameterTypes":["int","int","int","int","int"] }, 45 | {"name":"notifyDragLeave","parameterTypes":[] }, 46 | {"name":"notifyDragOver","parameterTypes":["int","int","int","int","int"] }, 47 | {"name":"notifyInputMethod","parameterTypes":["java.lang.String","int[]","int[]","byte[]","int","int","int"] }, 48 | {"name":"notifyKey","parameterTypes":["int","int","char[]","int"] }, 49 | {"name":"notifyMenu","parameterTypes":["int","int","int","int","boolean"] }, 50 | {"name":"notifyMouse","parameterTypes":["int","int","int","int","int","int","int","boolean","boolean"] }, 51 | {"name":"notifyRepaint","parameterTypes":["int","int","int","int"] }, 52 | {"name":"notifyResize","parameterTypes":["int","int"] }, 53 | {"name":"notifyScroll","parameterTypes":["int","int","int","int","double","double","int","int","int","int","int","double","double"] }, 54 | {"name":"notifyView","parameterTypes":["int"] } 55 | ] 56 | }, 57 | { 58 | "name":"com.sun.glass.ui.Window", 59 | "fields":[{"name":"ptr"}], 60 | "methods":[ 61 | {"name":"isEnabled","parameterTypes":[] }, 62 | {"name":"notifyClose","parameterTypes":[] }, 63 | {"name":"notifyDelegatePtr","parameterTypes":["long"] }, 64 | {"name":"notifyDestroy","parameterTypes":[] }, 65 | {"name":"notifyFocus","parameterTypes":["int"] }, 66 | {"name":"notifyFocusDisabled","parameterTypes":[] }, 67 | {"name":"notifyFocusUngrab","parameterTypes":[] }, 68 | {"name":"notifyLevelChanged","parameterTypes":["int"] }, 69 | {"name":"notifyMove","parameterTypes":["int","int"] }, 70 | {"name":"notifyMoveToAnotherScreen","parameterTypes":["com.sun.glass.ui.Screen"] }, 71 | {"name":"notifyResize","parameterTypes":["int","int","int"] } 72 | ] 73 | }, 74 | { 75 | "name":"com.sun.glass.ui.Window", 76 | "methods":[ 77 | {"name":"notifyClose","parameterTypes":[] }, 78 | {"name":"notifyDelegatePtr","parameterTypes":["long"] }, 79 | {"name":"notifyDestroy","parameterTypes":[] }, 80 | {"name":"notifyFocus","parameterTypes":["int"] }, 81 | {"name":"notifyFocusDisabled","parameterTypes":[] }, 82 | {"name":"notifyFocusUngrab","parameterTypes":[] }, 83 | {"name":"notifyMove","parameterTypes":["int","int"] }, 84 | {"name":"notifyMoveToAnotherScreen","parameterTypes":["com.sun.glass.ui.Screen"] }, 85 | {"name":"notifyScaleChanged","parameterTypes":["float","float","float","float"] } 86 | ] 87 | }, 88 | { 89 | "name":"com.sun.glass.ui.win.WinApplication" 90 | }, 91 | { 92 | "name":"com.sun.glass.ui.win.WinDnDClipboard", 93 | "methods":[ 94 | {"name":"getInstance","parameterTypes":[] }, 95 | {"name":"setDragButton","parameterTypes":["int"] } 96 | ] 97 | }, 98 | { 99 | "name":"com.sun.glass.ui.win.WinGestureSupport", 100 | "methods":[ 101 | {"name":"gesturePerformed","parameterTypes":["com.sun.glass.ui.View","int","boolean","boolean","int","int","int","int","float","float","float","float","float","float","float"] }, 102 | {"name":"inertiaGestureFinished","parameterTypes":["com.sun.glass.ui.View"] }, 103 | {"name":"notifyBeginTouchEvent","parameterTypes":["com.sun.glass.ui.View","int","boolean","int"] }, 104 | {"name":"notifyEndTouchEvent","parameterTypes":["com.sun.glass.ui.View"] }, 105 | {"name":"notifyNextTouchEvent","parameterTypes":["com.sun.glass.ui.View","int","long","int","int","int","int"] } 106 | ] 107 | }, 108 | { 109 | "name":"com.sun.glass.ui.win.WinPixels" 110 | }, 111 | { 112 | "name":"com.sun.glass.ui.win.WinSystemClipboard", 113 | "fields":[{"name":"ptr"}], 114 | "methods":[{"name":"fosSerialize","parameterTypes":["java.lang.String","long"] }] 115 | }, 116 | { 117 | "name":"com.sun.glass.ui.win.WinView", 118 | "methods":[{"name":"notifyResize","parameterTypes":["int","int"] }] 119 | }, 120 | { 121 | "name":"com.sun.glass.ui.win.WinWindow", 122 | "methods":[ 123 | {"name":"notifyMoving","parameterTypes":["int","int","int","int","float","float","int","int","int","int","int","int","int"] }, 124 | {"name":"notifyResize","parameterTypes":["int","int","int"] } 125 | ] 126 | }, 127 | { 128 | "name":"com.sun.glass.ui.gtk.GtkApplication", 129 | "fields":[ 130 | {"name":"display"}, 131 | {"name":"screen"}, 132 | {"name":"visualID"} 133 | ] 134 | }, 135 | { 136 | "name":"com.sun.glass.ui.gtk.GtkPixels", 137 | "methods":[{"name":"","parameterTypes":["int","int","java.nio.ByteBuffer"] }] 138 | }, 139 | { 140 | "name":"com.sun.glass.ui.gtk.GtkView", 141 | "methods":[ 142 | {"name":"notifyInputMethodCaret","parameterTypes":["int","int","int"] }, 143 | {"name":"notifyInputMethodDraw","parameterTypes":["java.lang.String","int","int","int","byte[]"] }, 144 | {"name":"notifyPreeditMode","parameterTypes":["boolean"] } 145 | ] 146 | }, 147 | { 148 | "name":"com.sun.glass.ui.gtk.GtkWindow", 149 | "methods":[{"name":"notifyStateChanged","parameterTypes":["int"] }] 150 | }, 151 | { 152 | "name":"com.sun.javafx.font.FontConfigManager$FcCompFont", 153 | "fields":[ 154 | {"name":"allFonts"}, 155 | {"name":"fcName"}, 156 | {"name":"firstFont"} 157 | ] 158 | }, 159 | { 160 | "name":"com.sun.javafx.font.FontConfigManager$FontConfigFont", 161 | "fields":[ 162 | {"name":"familyName"}, 163 | {"name":"fontFile"}, 164 | {"name":"fullName"}, 165 | {"name":"styleStr"} 166 | ], 167 | "methods":[{"name":"","parameterTypes":[] }] 168 | }, 169 | { 170 | "name":"com.sun.javafx.font.freetype.FT_Bitmap", 171 | "fields":[ 172 | {"name":"buffer"}, 173 | {"name":"num_grays"}, 174 | {"name":"palette"}, 175 | {"name":"palette_mode"}, 176 | {"name":"pitch"}, 177 | {"name":"pixel_mode"}, 178 | {"name":"rows"}, 179 | {"name":"width"} 180 | ] 181 | }, 182 | { 183 | "name":"com.sun.javafx.font.freetype.FT_GlyphSlotRec", 184 | "fields":[ 185 | {"name":"advance_x"}, 186 | {"name":"advance_y"}, 187 | {"name":"bitmap"}, 188 | {"name":"bitmap_left"}, 189 | {"name":"bitmap_top"}, 190 | {"name":"format"}, 191 | {"name":"linearHoriAdvance"}, 192 | {"name":"linearVertAdvance"}, 193 | {"name":"metrics"} 194 | ], 195 | "methods":[{"name":"","parameterTypes":[] }] 196 | }, 197 | { 198 | "name":"com.sun.javafx.font.freetype.FT_Glyph_Metrics", 199 | "fields":[ 200 | {"name":"height"}, 201 | {"name":"horiAdvance"}, 202 | {"name":"horiBearingX"}, 203 | {"name":"horiBearingY"}, 204 | {"name":"vertAdvance"}, 205 | {"name":"vertBearingX"}, 206 | {"name":"vertBearingY"}, 207 | {"name":"width"} 208 | ] 209 | }, 210 | { 211 | "name":"com.sun.javafx.font.freetype.FT_Matrix", 212 | "fields":[ 213 | {"name":"xx"}, 214 | {"name":"xy"}, 215 | {"name":"yx"}, 216 | {"name":"yy"} 217 | ] 218 | }, 219 | { 220 | "name":"java.lang.Boolean", 221 | "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] 222 | }, 223 | { 224 | "name":"java.lang.ClassLoader", 225 | "methods":[ 226 | {"name":"getPlatformClassLoader","parameterTypes":[] }, 227 | {"name":"loadClass","parameterTypes":["java.lang.String"] } 228 | ] 229 | }, 230 | { 231 | "name":"java.lang.Iterable", 232 | "methods":[{"name":"iterator","parameterTypes":[] }] 233 | }, 234 | { 235 | "name":"java.lang.Runnable", 236 | "methods":[{"name":"run","parameterTypes":[] }] 237 | }, 238 | { 239 | "name":"java.lang.String", 240 | "methods":[ 241 | {"name":"","parameterTypes":["byte[]","java.lang.String"] }, 242 | {"name":"getBytes","parameterTypes":["java.lang.String"] }, 243 | {"name":"toLowerCase","parameterTypes":["java.util.Locale"] } 244 | ] 245 | }, 246 | { 247 | "name":"java.nio.ByteBuffer", 248 | "methods":[ 249 | {"name":"array","parameterTypes":[] }, 250 | {"name":"wrap","parameterTypes":["byte[]"] } 251 | ] 252 | }, 253 | { 254 | "name":"java.util.ArrayList", 255 | "methods":[ 256 | {"name":"","parameterTypes":[] }, 257 | {"name":"","parameterTypes":["int"] }, 258 | {"name":"add","parameterTypes":["java.lang.Object"] }, 259 | {"name":"get","parameterTypes":["int"] } 260 | ] 261 | }, 262 | { 263 | "name":"java.util.HashMap", 264 | "methods":[ 265 | {"name":"containsKey","parameterTypes":["java.lang.Object"] }, 266 | {"name":"get","parameterTypes":["java.lang.Object"] }, 267 | {"name":"put","parameterTypes":["java.lang.Object","java.lang.Object"] } 268 | ] 269 | }, 270 | { 271 | "name":"java.util.HashSet", 272 | "methods":[{"name":"","parameterTypes":[] }] 273 | }, 274 | { 275 | "name":"java.util.Iterator", 276 | "methods":[ 277 | {"name":"hasNext","parameterTypes":[] }, 278 | {"name":"next","parameterTypes":[] } 279 | ] 280 | }, 281 | { 282 | "name":"java.util.Map", 283 | "methods":[ 284 | {"name":"containsKey","parameterTypes":["java.lang.Object"] }, 285 | {"name":"get","parameterTypes":["java.lang.Object"] }, 286 | {"name":"keySet","parameterTypes":[] } 287 | ] 288 | }, 289 | { 290 | "name":"java.util.Set", 291 | "methods":[ 292 | {"name":"add","parameterTypes":["java.lang.Object"] }, 293 | {"name":"size","parameterTypes":[] }, 294 | {"name":"toArray","parameterTypes":["java.lang.Object[]"] } 295 | ] 296 | }, 297 | { 298 | "name":"jdk.internal.loader.ClassLoaders$PlatformClassLoader" 299 | }, 300 | { 301 | "name":"org.graalvm.jniutils.JNIExceptionWrapperEntryPoints", 302 | "methods":[{"name":"getClassName","parameterTypes":["java.lang.Class"] }] 303 | }, 304 | { 305 | "name":"com.sun.jna.Callback" 306 | }, 307 | { 308 | "name":"com.sun.jna.CallbackReference", 309 | "methods":[ 310 | {"name":"getCallback","parameterTypes":["java.lang.Class","com.sun.jna.Pointer","boolean"] }, 311 | {"name":"getFunctionPointer","parameterTypes":["com.sun.jna.Callback","boolean"] }, 312 | {"name":"getNativeString","parameterTypes":["java.lang.Object","boolean"] }, 313 | {"name":"initializeThread","parameterTypes":["com.sun.jna.Callback","com.sun.jna.CallbackReference$AttachOptions"] } 314 | ] 315 | }, 316 | { 317 | "name":"com.sun.jna.CallbackReference$AttachOptions" 318 | }, 319 | { 320 | "name":"com.sun.jna.FromNativeConverter", 321 | "methods":[{"name":"nativeType","parameterTypes":[] }] 322 | }, 323 | { 324 | "name":"com.sun.jna.IntegerType", 325 | "fields":[{"name":"value"}] 326 | }, 327 | { 328 | "name":"com.sun.jna.JNIEnv" 329 | }, 330 | { 331 | "name":"com.sun.jna.Native", 332 | "methods":[ 333 | {"name":"dispose","parameterTypes":[] }, 334 | {"name":"fromNative","parameterTypes":["com.sun.jna.FromNativeConverter","java.lang.Object","java.lang.reflect.Method"] }, 335 | {"name":"fromNative","parameterTypes":["java.lang.Class","java.lang.Object"] }, 336 | {"name":"fromNative","parameterTypes":["java.lang.reflect.Method","java.lang.Object"] }, 337 | {"name":"nativeType","parameterTypes":["java.lang.Class"] }, 338 | {"name":"toNative","parameterTypes":["com.sun.jna.ToNativeConverter","java.lang.Object"] } 339 | ] 340 | }, 341 | { 342 | "name":"com.sun.jna.Native$ffi_callback", 343 | "methods":[{"name":"invoke","parameterTypes":["long","long","long"] }] 344 | }, 345 | { 346 | "name":"com.sun.jna.NativeMapped", 347 | "methods":[{"name":"toNative","parameterTypes":[] }] 348 | }, 349 | { 350 | "name":"com.sun.jna.Pointer", 351 | "fields":[{"name":"peer"}], 352 | "methods":[{"name":"","parameterTypes":["long"] }] 353 | }, 354 | { 355 | "name":"com.sun.jna.PointerType", 356 | "fields":[{"name":"pointer"}] 357 | }, 358 | { 359 | "name":"com.sun.jna.Structure", 360 | "fields":[ 361 | {"name":"memory"}, 362 | {"name":"typeInfo"} 363 | ], 364 | "methods":[ 365 | {"name":"autoRead","parameterTypes":[] }, 366 | {"name":"autoWrite","parameterTypes":[] }, 367 | {"name":"getTypeInfo","parameterTypes":[] }, 368 | {"name":"newInstance","parameterTypes":["java.lang.Class","long"] } 369 | ] 370 | }, 371 | { 372 | "name":"com.sun.jna.Structure$ByValue" 373 | }, 374 | { 375 | "name":"com.sun.jna.Structure$FFIType$FFITypes", 376 | "fields":[ 377 | {"name":"ffi_type_double"}, 378 | {"name":"ffi_type_float"}, 379 | {"name":"ffi_type_longdouble"}, 380 | {"name":"ffi_type_pointer"}, 381 | {"name":"ffi_type_sint16"}, 382 | {"name":"ffi_type_sint32"}, 383 | {"name":"ffi_type_sint64"}, 384 | {"name":"ffi_type_sint8"}, 385 | {"name":"ffi_type_uint16"}, 386 | {"name":"ffi_type_uint32"}, 387 | {"name":"ffi_type_uint64"}, 388 | {"name":"ffi_type_uint8"}, 389 | {"name":"ffi_type_void"} 390 | ] 391 | }, 392 | { 393 | "name":"com.sun.jna.WString", 394 | "methods":[{"name":"","parameterTypes":["java.lang.String"] }] 395 | }, 396 | { 397 | "name":"java.nio.Buffer", 398 | "methods":[{"name":"position","parameterTypes":[] }] 399 | }, 400 | { 401 | "name":"java.nio.ByteBuffer", 402 | "methods":[ 403 | {"name":"array","parameterTypes":[] }, 404 | {"name":"arrayOffset","parameterTypes":[] } 405 | ] 406 | }, 407 | { 408 | "name":"java.nio.CharBuffer", 409 | "methods":[ 410 | {"name":"array","parameterTypes":[] }, 411 | {"name":"arrayOffset","parameterTypes":[] } 412 | ] 413 | }, 414 | { 415 | "name":"java.nio.DoubleBuffer", 416 | "methods":[ 417 | {"name":"array","parameterTypes":[] }, 418 | {"name":"arrayOffset","parameterTypes":[] } 419 | ] 420 | }, 421 | { 422 | "name":"java.nio.FloatBuffer", 423 | "methods":[ 424 | {"name":"array","parameterTypes":[] }, 425 | {"name":"arrayOffset","parameterTypes":[] } 426 | ] 427 | }, 428 | { 429 | "name":"java.nio.IntBuffer", 430 | "methods":[ 431 | {"name":"array","parameterTypes":[] }, 432 | {"name":"arrayOffset","parameterTypes":[] } 433 | ] 434 | }, 435 | { 436 | "name":"java.nio.LongBuffer", 437 | "methods":[ 438 | {"name":"array","parameterTypes":[] }, 439 | {"name":"arrayOffset","parameterTypes":[] } 440 | ] 441 | }, 442 | { 443 | "name":"java.nio.ShortBuffer", 444 | "methods":[ 445 | {"name":"array","parameterTypes":[] }, 446 | {"name":"arrayOffset","parameterTypes":[] } 447 | ] 448 | }, 449 | { 450 | "name":"com.sun.prism.impl.PrismSettings", 451 | "fields":[ 452 | {"name":"disableD3D9Ex"}, 453 | {"name":"forceGPU"}, 454 | {"name":"isVsyncEnabled"}, 455 | {"name":"verbose"} 456 | ] 457 | }, 458 | { 459 | "name":"java.lang.Boolean", 460 | "fields":[ 461 | {"name":"TYPE"}, 462 | {"name":"value"} 463 | ], 464 | "methods":[{"name":"","parameterTypes":["boolean"] }] 465 | }, 466 | { 467 | "name":"java.lang.Byte", 468 | "fields":[ 469 | {"name":"TYPE"}, 470 | {"name":"value"} 471 | ], 472 | "methods":[{"name":"","parameterTypes":["byte"] }] 473 | }, 474 | { 475 | "name":"java.lang.Character", 476 | "fields":[ 477 | {"name":"TYPE"}, 478 | {"name":"value"} 479 | ], 480 | "methods":[{"name":"","parameterTypes":["char"] }] 481 | }, 482 | { 483 | "name":"java.lang.Class", 484 | "methods":[ 485 | {"name":"forName","parameterTypes":["java.lang.String","boolean","java.lang.ClassLoader"] }, 486 | {"name":"getComponentType","parameterTypes":[] } 487 | ] 488 | }, 489 | { 490 | "name":"java.lang.ClassLoader", 491 | "methods":[ 492 | {"name":"getPlatformClassLoader","parameterTypes":[] }, 493 | {"name":"loadClass","parameterTypes":["java.lang.String"] } 494 | ] 495 | }, 496 | { 497 | "name":"java.lang.Double", 498 | "fields":[ 499 | {"name":"TYPE"}, 500 | {"name":"value"} 501 | ], 502 | "methods":[{"name":"","parameterTypes":["double"] }] 503 | }, 504 | { 505 | "name":"java.lang.Float", 506 | "fields":[ 507 | {"name":"TYPE"}, 508 | {"name":"value"} 509 | ], 510 | "methods":[{"name":"","parameterTypes":["float"] }] 511 | }, 512 | { 513 | "name":"java.lang.Integer", 514 | "fields":[ 515 | {"name":"TYPE"}, 516 | {"name":"value"} 517 | ], 518 | "methods":[{"name":"","parameterTypes":["int"] }] 519 | }, 520 | { 521 | "name":"java.lang.Long", 522 | "fields":[ 523 | {"name":"TYPE"}, 524 | {"name":"value"} 525 | ], 526 | "methods":[{"name":"","parameterTypes":["long"] }] 527 | }, 528 | { 529 | "name":"java.lang.Object", 530 | "methods":[{"name":"toString","parameterTypes":[] }] 531 | }, 532 | { 533 | "name":"java.lang.Runnable", 534 | "methods":[{"name":"run","parameterTypes":[] }] 535 | }, 536 | { 537 | "name":"java.lang.Short", 538 | "fields":[ 539 | {"name":"TYPE"}, 540 | {"name":"value"} 541 | ], 542 | "methods":[{"name":"","parameterTypes":["short"] }] 543 | }, 544 | { 545 | "name":"java.lang.String", 546 | "methods":[ 547 | {"name":"","parameterTypes":["byte[]"] }, 548 | {"name":"","parameterTypes":["byte[]","java.lang.String"] }, 549 | {"name":"getBytes","parameterTypes":[] }, 550 | {"name":"getBytes","parameterTypes":["java.lang.String"] }, 551 | {"name":"toCharArray","parameterTypes":[] }, 552 | {"name":"toLowerCase","parameterTypes":["java.util.Locale"] } 553 | ] 554 | }, 555 | { 556 | "name":"java.lang.System", 557 | "methods":[{"name":"getProperty","parameterTypes":["java.lang.String"] }] 558 | }, 559 | { 560 | "name":"java.lang.Void", 561 | "fields":[{"name":"TYPE"}] 562 | }, 563 | { 564 | "name":"java.lang.reflect.Method", 565 | "methods":[ 566 | {"name":"getParameterTypes","parameterTypes":[] }, 567 | {"name":"getReturnType","parameterTypes":[] } 568 | ] 569 | } 570 | ] 571 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/native-image.properties: -------------------------------------------------------------------------------- 1 | Args=-Djava.awt.headless=true\ 2 | \ -Duser.country=US\ 3 | \ -Duser.language=en\ 4 | \ -H:IncludeLocales=en\ 5 | \ -H:IncludeResources=static/.*|templates/.*|.*.yml|.*.xml\ 6 | \ -march=compatibility\ 7 | \ --no-fallback\ 8 | \ --enable-http\ 9 | \ --enable-https -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/predefined-classes-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type":"agent-extracted", 4 | "classes":[ 5 | ] 6 | } 7 | ] 8 | 9 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/proxy-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "interfaces":["xyz.drop2deck.jna.StageOps$DwmSupport"]} 4 | , 5 | { 6 | "interfaces":["com.sun.jna.platform.win32.User32"]} 7 | 8 | ] 9 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/reflect-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name":"[B" 4 | }, 5 | { 6 | "name":"[Ljava.lang.String;" 7 | }, 8 | { 9 | "name":"[Lsun.security.pkcs.SignerInfo;" 10 | }, 11 | { 12 | "name":"xyz.drop2deck.FXApplication", 13 | "methods":[{"name":"","parameterTypes":[] }] 14 | }, 15 | { 16 | "name":"xyz.drop2deck.controller.MainController", 17 | "allDeclaredFields":true, 18 | "queryAllDeclaredMethods":true, 19 | "methods":[ 20 | {"name":"","parameterTypes":[] }, 21 | {"name":"copyInternalUrl","parameterTypes":[] }, 22 | {"name":"copyExternalUrl","parameterTypes":[] }, 23 | {"name":"exit","parameterTypes":[] }, 24 | {"name":"initialize","parameterTypes":[] }, 25 | {"name":"showGit","parameterTypes":[] }, 26 | {"name":"startStopServer","parameterTypes":[] } 27 | ] 28 | }, 29 | { 30 | "name":"xyz.drop2deck.layout.InputGroup", 31 | "queryAllDeclaredMethods":true, 32 | "queryAllPublicConstructors":true, 33 | "methods":[{"name":"","parameterTypes":[] }] 34 | }, 35 | { 36 | "name":"com.jfoenix.controls.JFXButton", 37 | "queryAllDeclaredMethods":true, 38 | "queryAllPublicConstructors":true, 39 | "methods":[ 40 | {"name":"","parameterTypes":[] }, 41 | {"name":"setRipplerFill","parameterTypes":["javafx.scene.paint.Paint"] } 42 | ] 43 | }, 44 | { 45 | "name":"com.jfoenix.controls.JFXSpinner", 46 | "queryAllDeclaredMethods":true, 47 | "queryAllPublicConstructors":true, 48 | "methods":[{"name":"","parameterTypes":[] }] 49 | }, 50 | { 51 | "name":"com.sun.crypto.provider.AESCipher$General", 52 | "methods":[{"name":"","parameterTypes":[] }] 53 | }, 54 | { 55 | "name":"com.sun.crypto.provider.DHParameters", 56 | "methods":[{"name":"","parameterTypes":[] }] 57 | }, 58 | { 59 | "name":"com.sun.crypto.provider.HmacCore$HmacSHA384", 60 | "methods":[{"name":"","parameterTypes":[] }] 61 | }, 62 | { 63 | "name":"com.sun.crypto.provider.TlsMasterSecretGenerator", 64 | "methods":[{"name":"","parameterTypes":[] }] 65 | }, 66 | { 67 | "name":"com.sun.glass.ui.win.WinDnDClipboard" 68 | }, 69 | { 70 | "name":"com.sun.glass.ui.win.WinGestureSupport" 71 | }, 72 | { 73 | "name":"com.sun.glass.ui.win.WinPlatformFactory", 74 | "methods":[{"name":"","parameterTypes":[] }] 75 | }, 76 | { 77 | "name":"com.sun.jna.platform.win32.WinDef$BOOLByReference", 78 | "methods":[{"name":"","parameterTypes":[] }] 79 | }, 80 | { 81 | "name":"com.sun.jna.platform.win32.WinDef$DWORDByReference", 82 | "methods":[{"name":"","parameterTypes":[] }] 83 | }, 84 | { 85 | "name":"com.sun.jna.platform.win32.WinDef$HWND", 86 | "methods":[{"name":"","parameterTypes":[] }] 87 | }, 88 | { 89 | "name":"com.sun.jna.platform.win32.WinNT$HRESULT", 90 | "methods":[{"name":"","parameterTypes":[] }] 91 | }, 92 | { 93 | "name":"com.sun.jna.win32.DLLCallback" 94 | }, 95 | { 96 | "name":"com.sun.glass.ui.gtk.GtkPlatformFactory", 97 | "methods":[{"name":"","parameterTypes":[] }] 98 | }, 99 | { 100 | "name":"com.sun.javafx.font.freetype.FTFactory", 101 | "methods":[{"name":"getFactory","parameterTypes":[] }] 102 | }, 103 | { 104 | "name":"com.sun.javafx.logging.PrintLogger", 105 | "methods":[{"name":"createInstance","parameterTypes":[] }] 106 | }, 107 | { 108 | "name":"com.sun.javafx.logging.jfr.JFRPulseLogger", 109 | "methods":[{"name":"createInstance","parameterTypes":[] }] 110 | }, 111 | { 112 | "name":"com.sun.javafx.reflect.Trampoline", 113 | "methods":[{"name":"invoke","parameterTypes":["java.lang.reflect.Method","java.lang.Object","java.lang.Object[]"] }] 114 | }, 115 | { 116 | "name":"com.sun.javafx.scene.control.skin.Utils", 117 | "methods":[{"name":"getResource","parameterTypes":["java.lang.String"] }] 118 | }, 119 | { 120 | "name":"com.sun.javafx.tk.quantum.QuantumToolkit", 121 | "methods":[{"name":"","parameterTypes":[] }] 122 | }, 123 | { 124 | "name":"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl", 125 | "methods":[{"name":"","parameterTypes":[] }] 126 | }, 127 | { 128 | "name":"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", 129 | "methods":[{"name":"","parameterTypes":[] }] 130 | }, 131 | { 132 | "name":"com.sun.prism.GraphicsPipeline", 133 | "methods":[ 134 | {"name":"getFontFactory","parameterTypes":[] }, 135 | {"name":"getPipeline","parameterTypes":[] } 136 | ] 137 | }, 138 | { 139 | "name":"com.sun.prism.es2.ES2Pipeline", 140 | "methods":[{"name":"getInstance","parameterTypes":[] }] 141 | }, 142 | { 143 | "name":"com.sun.prism.es2.X11GLFactory", 144 | "methods":[{"name":"","parameterTypes":[] }] 145 | }, 146 | { 147 | "name":"com.sun.prism.shader.DrawCircle_Color_Loader", 148 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 149 | }, 150 | { 151 | "name":"com.sun.prism.shader.DrawRoundRect_Color_Loader", 152 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 153 | }, 154 | { 155 | "name":"com.sun.prism.shader.FillCircle_Color_Loader", 156 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 157 | }, 158 | { 159 | "name":"com.sun.prism.shader.FillPgram_Color_Loader", 160 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 161 | }, 162 | { 163 | "name":"com.sun.prism.shader.FillRoundRect_Color_Loader", 164 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 165 | }, 166 | { 167 | "name":"com.sun.prism.shader.Solid_Color_Loader", 168 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 169 | }, 170 | { 171 | "name":"com.sun.prism.shader.Solid_TextureRGB_Loader", 172 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 173 | }, 174 | { 175 | "name":"com.sun.prism.shader.Texture_Color_Loader", 176 | "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] 177 | }, 178 | { 179 | "name":"com.sun.scenario.effect.impl.es2.ES2ShaderSource", 180 | "methods":[{"name":"","parameterTypes":[] }] 181 | }, 182 | { 183 | "name":"com.sun.scenario.effect.impl.prism.PrRenderer", 184 | "methods":[{"name":"createRenderer","parameterTypes":["com.sun.scenario.effect.FilterContext"] }] 185 | }, 186 | { 187 | "name":"com.sun.scenario.effect.impl.prism.ps.PPSBlend_SRC_INPeer", 188 | "methods":[{"name":"","parameterTypes":["com.sun.scenario.effect.FilterContext","com.sun.scenario.effect.impl.Renderer","java.lang.String"] }] 189 | }, 190 | { 191 | "name":"com.sun.scenario.effect.impl.prism.ps.PPSLinearConvolveShadowPeer", 192 | "methods":[{"name":"","parameterTypes":["com.sun.scenario.effect.FilterContext","com.sun.scenario.effect.impl.Renderer","java.lang.String"] }] 193 | }, 194 | { 195 | "name":"com.sun.scenario.effect.impl.prism.ps.PPSRenderer", 196 | "methods":[{"name":"createRenderer","parameterTypes":["com.sun.scenario.effect.FilterContext"] }] 197 | }, 198 | { 199 | "name":"com.sun.xml.internal.stream.XMLInputFactoryImpl", 200 | "methods":[{"name":"","parameterTypes":[] }] 201 | }, 202 | { 203 | "name":"java.nio.Buffer" 204 | }, 205 | { 206 | "name":"java.nio.ByteBuffer", 207 | "methods":[{"name":"order","parameterTypes":["java.nio.ByteOrder"] }] 208 | }, 209 | { 210 | "name":"java.nio.ByteOrder", 211 | "methods":[{"name":"nativeOrder","parameterTypes":[] }] 212 | }, 213 | { 214 | "name":"java.lang.Character", 215 | "methods":[{"name":"isIdeographic","parameterTypes":["int"] }] 216 | }, 217 | { 218 | "name":"java.lang.Class", 219 | "methods":[{"name":"getModule","parameterTypes":[] }] 220 | }, 221 | { 222 | "name":"java.lang.Module", 223 | "queriedMethods":[{"name":"getResourceAsStream","parameterTypes":["java.lang.String"] }] 224 | }, 225 | { 226 | "name":"java.lang.String" 227 | }, 228 | { 229 | "name":"java.nio.ByteBuffer", 230 | "methods":[{"name":"order","parameterTypes":["java.nio.ByteOrder"] }] 231 | }, 232 | { 233 | "name":"java.nio.ByteOrder", 234 | "methods":[{"name":"nativeOrder","parameterTypes":[] }] 235 | }, 236 | { 237 | "name":"java.security.AccessController" 238 | }, 239 | { 240 | "name":"java.security.AlgorithmParametersSpi" 241 | }, 242 | { 243 | "name":"java.security.KeyStoreSpi" 244 | }, 245 | { 246 | "name":"java.security.MessageDigestSpi" 247 | }, 248 | { 249 | "name":"java.security.SecureRandomParameters" 250 | }, 251 | { 252 | "name":"java.security.interfaces.ECPrivateKey" 253 | }, 254 | { 255 | "name":"java.security.interfaces.ECPublicKey" 256 | }, 257 | { 258 | "name":"java.security.interfaces.RSAPrivateKey" 259 | }, 260 | { 261 | "name":"java.security.interfaces.RSAPublicKey" 262 | }, 263 | { 264 | "name":"java.util.Date" 265 | }, 266 | { 267 | "name":"javafx.animation.KeyValue" 268 | }, 269 | { 270 | "name":"javafx.fxml.FXMLLoader" 271 | }, 272 | { 273 | "name":"javafx.geometry.HPos", 274 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] 275 | }, 276 | { 277 | "name":"javafx.geometry.Insets", 278 | "queryAllPublicMethods":true, 279 | "queryAllPublicConstructors":true, 280 | "methods":[{"name":"","parameterTypes":["double","double","double","double"] }] 281 | }, 282 | { 283 | "name":"javafx.geometry.Pos", 284 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] 285 | }, 286 | { 287 | "name":"javafx.scene.Camera" 288 | }, 289 | { 290 | "name":"javafx.scene.Group" 291 | }, 292 | { 293 | "name":"javafx.scene.Node", 294 | "queryAllDeclaredMethods":true, 295 | "methods":[ 296 | {"name":"getId","parameterTypes":[] }, 297 | {"name":"getStyleClass","parameterTypes":[] }, 298 | {"name":"setId","parameterTypes":["java.lang.String"] }, 299 | {"name":"setLayoutX","parameterTypes":["double"] }, 300 | {"name":"setLayoutY","parameterTypes":["double"] }, 301 | {"name":"setPickOnBounds","parameterTypes":["boolean"] }, 302 | {"name":"setScaleX","parameterTypes":["double"] }, 303 | {"name":"setScaleY","parameterTypes":["double"] }, 304 | {"name":"setStyle","parameterTypes":["java.lang.String"] } 305 | ] 306 | }, 307 | { 308 | "name":"javafx.scene.ParallelCamera" 309 | }, 310 | { 311 | "name":"javafx.scene.Parent", 312 | "queryAllDeclaredMethods":true 313 | }, 314 | { 315 | "name":"javafx.scene.Scene" 316 | }, 317 | { 318 | "name":"javafx.scene.control.Button", 319 | "queryAllDeclaredMethods":true, 320 | "queryAllPublicConstructors":true, 321 | "methods":[{"name":"","parameterTypes":[] }] 322 | }, 323 | { 324 | "name":"javafx.scene.control.ButtonBase", 325 | "queryAllDeclaredMethods":true, 326 | "methods":[{"name":"setOnAction","parameterTypes":["javafx.event.EventHandler"] }] 327 | }, 328 | { 329 | "name":"javafx.scene.control.ContentDisplay", 330 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] 331 | }, 332 | { 333 | "name":"javafx.scene.control.Control", 334 | "queryAllDeclaredMethods":true 335 | }, 336 | { 337 | "name":"javafx.scene.control.Label", 338 | "queryAllDeclaredMethods":true, 339 | "queryAllPublicConstructors":true, 340 | "methods":[{"name":"","parameterTypes":[] }] 341 | }, 342 | { 343 | "name":"javafx.scene.control.Labeled", 344 | "queryAllDeclaredMethods":true, 345 | "methods":[ 346 | {"name":"setAlignment","parameterTypes":["javafx.geometry.Pos"] }, 347 | {"name":"setContentDisplay","parameterTypes":["javafx.scene.control.ContentDisplay"] }, 348 | {"name":"setGraphic","parameterTypes":["javafx.scene.Node"] }, 349 | {"name":"setText","parameterTypes":["java.lang.String"] }, 350 | {"name":"setTextAlignment","parameterTypes":["javafx.scene.text.TextAlignment"] } 351 | ] 352 | }, 353 | { 354 | "name":"javafx.scene.control.PasswordField", 355 | "queryAllDeclaredMethods":true, 356 | "queryAllPublicConstructors":true, 357 | "methods":[{"name":"","parameterTypes":[] }] 358 | }, 359 | { 360 | "name":"javafx.scene.control.ProgressIndicator", 361 | "queryAllDeclaredMethods":true 362 | }, 363 | { 364 | "name":"javafx.scene.control.TextField", 365 | "queryAllDeclaredMethods":true, 366 | "queryAllPublicConstructors":true, 367 | "methods":[{"name":"","parameterTypes":[] }] 368 | }, 369 | { 370 | "name":"javafx.scene.control.TextInputControl", 371 | "queryAllDeclaredMethods":true, 372 | "methods":[{"name":"setPromptText","parameterTypes":["java.lang.String"] }] 373 | }, 374 | { 375 | "name":"javafx.scene.effect.Effect" 376 | }, 377 | { 378 | "name":"javafx.scene.image.Image" 379 | }, 380 | { 381 | "name":"javafx.scene.image.ImageView", 382 | "queryAllDeclaredMethods":true, 383 | "queryAllPublicConstructors":true, 384 | "methods":[ 385 | {"name":"","parameterTypes":[] }, 386 | {"name":"setFitHeight","parameterTypes":["double"] }, 387 | {"name":"setFitWidth","parameterTypes":["double"] }, 388 | {"name":"setImage","parameterTypes":["javafx.scene.image.Image"] }, 389 | {"name":"setPreserveRatio","parameterTypes":["boolean"] } 390 | ] 391 | }, 392 | { 393 | "name":"javafx.scene.input.Clipboard" 394 | }, 395 | { 396 | "name":"javafx.scene.layout.BorderPane", 397 | "queryAllDeclaredMethods":true, 398 | "queryAllPublicConstructors":true, 399 | "methods":[ 400 | {"name":"","parameterTypes":[] }, 401 | {"name":"setAlignment","parameterTypes":["javafx.scene.Node","javafx.geometry.Pos"] }, 402 | {"name":"setBottom","parameterTypes":["javafx.scene.Node"] }, 403 | {"name":"setCenter","parameterTypes":["javafx.scene.Node"] }, 404 | {"name":"setMargin","parameterTypes":["javafx.scene.Node","javafx.geometry.Insets"] }, 405 | {"name":"setTop","parameterTypes":["javafx.scene.Node"] } 406 | ], 407 | "queriedMethods":[{"name":"getAlignment","parameterTypes":["javafx.scene.Node"] }] 408 | }, 409 | { 410 | "name":"javafx.scene.layout.ColumnConstraints", 411 | "queryAllDeclaredMethods":true, 412 | "queryAllPublicConstructors":true, 413 | "methods":[ 414 | {"name":"","parameterTypes":[] }, 415 | {"name":"setHalignment","parameterTypes":["javafx.geometry.HPos"] }, 416 | {"name":"setHgrow","parameterTypes":["javafx.scene.layout.Priority"] }, 417 | {"name":"setMinWidth","parameterTypes":["double"] }, 418 | {"name":"setPrefWidth","parameterTypes":["double"] } 419 | ] 420 | }, 421 | { 422 | "name":"javafx.scene.layout.ConstraintsBase", 423 | "queryAllDeclaredMethods":true 424 | }, 425 | { 426 | "name":"javafx.scene.layout.GridPane", 427 | "queryAllDeclaredMethods":true, 428 | "queryAllPublicConstructors":true, 429 | "methods":[ 430 | {"name":"","parameterTypes":[] }, 431 | {"name":"getColumnConstraints","parameterTypes":[] }, 432 | {"name":"getRowConstraints","parameterTypes":[] }, 433 | {"name":"setColumnIndex","parameterTypes":["javafx.scene.Node","java.lang.Integer"] }, 434 | {"name":"setRowIndex","parameterTypes":["javafx.scene.Node","java.lang.Integer"] } 435 | ], 436 | "queriedMethods":[ 437 | {"name":"getColumnIndex","parameterTypes":["javafx.scene.Node"] }, 438 | {"name":"getRowIndex","parameterTypes":["javafx.scene.Node"] } 439 | ] 440 | }, 441 | { 442 | "name":"javafx.scene.layout.HBox", 443 | "queryAllDeclaredMethods":true, 444 | "queryAllPublicConstructors":true, 445 | "methods":[ 446 | {"name":"","parameterTypes":[] }, 447 | {"name":"setAlignment","parameterTypes":["javafx.geometry.Pos"] }, 448 | {"name":"setSpacing","parameterTypes":["double"] } 449 | ] 450 | }, 451 | { 452 | "name":"javafx.scene.layout.Pane", 453 | "queryAllDeclaredMethods":true, 454 | "methods":[{"name":"getChildren","parameterTypes":[] }] 455 | }, 456 | { 457 | "name":"javafx.scene.layout.Priority", 458 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] 459 | }, 460 | { 461 | "name":"javafx.scene.layout.Region", 462 | "queryAllDeclaredMethods":true, 463 | "methods":[ 464 | {"name":"setMaxHeight","parameterTypes":["double"] }, 465 | {"name":"setMaxWidth","parameterTypes":["double"] }, 466 | {"name":"setMinHeight","parameterTypes":["double"] }, 467 | {"name":"setMinWidth","parameterTypes":["double"] }, 468 | {"name":"setPadding","parameterTypes":["javafx.geometry.Insets"] }, 469 | {"name":"setPrefHeight","parameterTypes":["double"] }, 470 | {"name":"setPrefWidth","parameterTypes":["double"] } 471 | ] 472 | }, 473 | { 474 | "name":"javafx.scene.layout.RowConstraints", 475 | "queryAllDeclaredMethods":true, 476 | "queryAllPublicConstructors":true, 477 | "methods":[ 478 | {"name":"","parameterTypes":[] }, 479 | {"name":"setMaxHeight","parameterTypes":["double"] }, 480 | {"name":"setMinHeight","parameterTypes":["double"] }, 481 | {"name":"setPrefHeight","parameterTypes":["double"] }, 482 | {"name":"setVgrow","parameterTypes":["javafx.scene.layout.Priority"] } 483 | ] 484 | }, 485 | { 486 | "name":"javafx.scene.layout.StackPane", 487 | "queryAllDeclaredMethods":true, 488 | "queryAllPublicConstructors":true, 489 | "methods":[ 490 | {"name":"","parameterTypes":[] }, 491 | {"name":"setAlignment","parameterTypes":["javafx.scene.Node","javafx.geometry.Pos"] } 492 | ], 493 | "queriedMethods":[{"name":"getAlignment","parameterTypes":["javafx.scene.Node"] }] 494 | }, 495 | { 496 | "name":"javafx.scene.layout.VBox", 497 | "queryAllDeclaredMethods":true, 498 | "queryAllPublicConstructors":true, 499 | "methods":[ 500 | {"name":"","parameterTypes":[] }, 501 | {"name":"setAlignment","parameterTypes":["javafx.geometry.Pos"] }, 502 | {"name":"setSpacing","parameterTypes":["double"] } 503 | ] 504 | }, 505 | { 506 | "name":"javafx.scene.paint.Paint", 507 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] 508 | }, 509 | { 510 | "name":"javafx.scene.shape.Arc" 511 | }, 512 | { 513 | "name":"javafx.scene.shape.Circle" 514 | }, 515 | { 516 | "name":"javafx.scene.shape.LineTo" 517 | }, 518 | { 519 | "name":"javafx.scene.shape.MoveTo" 520 | }, 521 | { 522 | "name":"javafx.scene.shape.Path" 523 | }, 524 | { 525 | "name":"javafx.scene.shape.PathElement" 526 | }, 527 | { 528 | "name":"javafx.scene.shape.Rectangle" 529 | }, 530 | { 531 | "name":"javafx.scene.shape.Shape", 532 | "queryAllDeclaredMethods":true 533 | }, 534 | { 535 | "name":"javafx.scene.text.Font" 536 | }, 537 | { 538 | "name":"javafx.scene.text.Text", 539 | "queryAllDeclaredMethods":true 540 | }, 541 | { 542 | "name":"javafx.scene.text.TextAlignment", 543 | "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] 544 | }, 545 | { 546 | "name":"javafx.scene.transform.Transform" 547 | }, 548 | { 549 | "name":"javafx.stage.PopupWindow" 550 | }, 551 | { 552 | "name":"javafx.stage.Stage" 553 | }, 554 | { 555 | "name":"javafx.stage.Window" 556 | }, 557 | { 558 | "name":"javax.security.auth.x500.X500Principal", 559 | "fields":[{"name":"thisX500Name"}], 560 | "methods":[{"name":"","parameterTypes":["sun.security.x509.X500Name"] }] 561 | }, 562 | { 563 | "name":"org.apache.mina.transport.socket.nio.NioProcessor", 564 | "methods":[{"name":"","parameterTypes":["java.util.concurrent.Executor"] }] 565 | }, 566 | { 567 | "name":"org.kordamp.ikonli.javafx.FontIcon", 568 | "queryAllDeclaredMethods":true, 569 | "queryAllPublicConstructors":true, 570 | "methods":[ 571 | {"name":"","parameterTypes":[] }, 572 | {"name":"setIconLiteral","parameterTypes":["java.lang.String"] }, 573 | {"name":"setIconSize","parameterTypes":["int"] } 574 | ] 575 | }, 576 | { 577 | "name":"sun.misc.Unsafe", 578 | "fields":[{"name":"theUnsafe"}] 579 | }, 580 | { 581 | "name":"sun.security.pkcs12.PKCS12KeyStore", 582 | "methods":[{"name":"","parameterTypes":[] }] 583 | }, 584 | { 585 | "name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12", 586 | "methods":[{"name":"","parameterTypes":[] }] 587 | }, 588 | { 589 | "name":"sun.security.provider.DSA$SHA224withDSA", 590 | "methods":[{"name":"","parameterTypes":[] }] 591 | }, 592 | { 593 | "name":"sun.security.provider.DSA$SHA256withDSA", 594 | "methods":[{"name":"","parameterTypes":[] }] 595 | }, 596 | { 597 | "name":"sun.security.provider.JavaKeyStore$DualFormatJKS", 598 | "methods":[{"name":"","parameterTypes":[] }] 599 | }, 600 | { 601 | "name":"sun.security.provider.JavaKeyStore$JKS", 602 | "methods":[{"name":"","parameterTypes":[] }] 603 | }, 604 | { 605 | "name":"sun.security.provider.NativePRNG", 606 | "methods":[{"name":"","parameterTypes":[] }] 607 | }, 608 | { 609 | "name":"sun.security.provider.SHA", 610 | "methods":[{"name":"","parameterTypes":[] }] 611 | }, 612 | { 613 | "name":"sun.security.provider.SHA2$SHA224", 614 | "methods":[{"name":"","parameterTypes":[] }] 615 | }, 616 | { 617 | "name":"sun.security.provider.SHA2$SHA256", 618 | "methods":[{"name":"","parameterTypes":[] }] 619 | }, 620 | { 621 | "name":"sun.security.provider.SHA5$SHA384", 622 | "methods":[{"name":"","parameterTypes":[] }] 623 | }, 624 | { 625 | "name":"sun.security.provider.SHA5$SHA512", 626 | "methods":[{"name":"","parameterTypes":[] }] 627 | }, 628 | { 629 | "name":"sun.security.provider.X509Factory", 630 | "methods":[{"name":"","parameterTypes":[] }] 631 | }, 632 | { 633 | "name":"sun.security.provider.certpath.PKIXCertPathValidator", 634 | "methods":[{"name":"","parameterTypes":[] }] 635 | }, 636 | { 637 | "name":"sun.security.rsa.PSSParameters", 638 | "methods":[{"name":"","parameterTypes":[] }] 639 | }, 640 | { 641 | "name":"sun.security.rsa.RSAKeyFactory$Legacy", 642 | "methods":[{"name":"","parameterTypes":[] }] 643 | }, 644 | { 645 | "name":"sun.security.rsa.RSAPSSSignature", 646 | "methods":[{"name":"","parameterTypes":[] }] 647 | }, 648 | { 649 | "name":"sun.security.rsa.RSASignature$SHA224withRSA", 650 | "methods":[{"name":"","parameterTypes":[] }] 651 | }, 652 | { 653 | "name":"sun.security.rsa.RSASignature$SHA256withRSA", 654 | "methods":[{"name":"","parameterTypes":[] }] 655 | }, 656 | { 657 | "name":"sun.security.ssl.KeyManagerFactoryImpl$SunX509", 658 | "methods":[{"name":"","parameterTypes":[] }] 659 | }, 660 | { 661 | "name":"sun.security.ssl.SSLContextImpl$DefaultSSLContext", 662 | "methods":[{"name":"","parameterTypes":[] }] 663 | }, 664 | { 665 | "name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", 666 | "methods":[{"name":"","parameterTypes":[] }] 667 | }, 668 | { 669 | "name":"sun.security.util.ObjectIdentifier" 670 | }, 671 | { 672 | "name":"sun.security.x509.AuthorityInfoAccessExtension", 673 | "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] 674 | }, 675 | { 676 | "name":"sun.security.x509.AuthorityKeyIdentifierExtension", 677 | "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] 678 | }, 679 | { 680 | "name":"sun.security.x509.BasicConstraintsExtension", 681 | "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] 682 | }, 683 | { 684 | "name":"sun.security.x509.CRLDistributionPointsExtension", 685 | "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] 686 | }, 687 | { 688 | "name":"sun.security.x509.CertificateExtensions" 689 | }, 690 | { 691 | "name":"sun.security.x509.CertificatePoliciesExtension", 692 | "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] 693 | }, 694 | { 695 | "name":"sun.security.x509.ExtendedKeyUsageExtension", 696 | "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] 697 | }, 698 | { 699 | "name":"sun.security.x509.IssuerAlternativeNameExtension", 700 | "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] 701 | }, 702 | { 703 | "name":"sun.security.x509.KeyUsageExtension", 704 | "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] 705 | }, 706 | { 707 | "name":"sun.security.x509.NetscapeCertTypeExtension", 708 | "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] 709 | }, 710 | { 711 | "name":"sun.security.x509.PrivateKeyUsageExtension", 712 | "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] 713 | }, 714 | { 715 | "name":"sun.security.x509.SubjectAlternativeNameExtension", 716 | "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] 717 | }, 718 | { 719 | "name":"sun.security.x509.SubjectKeyIdentifierExtension", 720 | "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] 721 | } 722 | ] 723 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/resource-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources":{ 3 | "includes":[ 4 | { 5 | "pattern":"\\QMETA-INF/resources/materialdesignicons2/5.8.55/fonts/materialdesignicons-webfont.ttf\\E" 6 | }, 7 | { 8 | "pattern":"\\QMETA-INF/services/org.kordamp.ikonli.IkonHandler\\E" 9 | }, 10 | { 11 | "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" 12 | }, 13 | { 14 | "pattern":"\\Qcom/jfoenix/assets/css/controls/jfx-button.css\\E" 15 | }, 16 | { 17 | "pattern":"\\Qcom/jfoenix/assets/css/controls/jfx-spinner.css\\E" 18 | }, 19 | { 20 | "pattern":"\\Qcss/cupertino-dark.css\\E" 21 | }, 22 | { 23 | "pattern":"\\Qfxml/Main.fxml\\E" 24 | }, 25 | { 26 | "pattern":"\\Qicons/icon.png\\E" 27 | }, 28 | { 29 | "pattern":"\\Qicons/round_play_circle_48.png\\E" 30 | }, 31 | { 32 | "pattern":"\\Qicons/round_stop_circle_48.png\\E" 33 | }, 34 | { 35 | "pattern":"\\Qorg/apache/ftpserver/message/FtpStatus.properties\\E" 36 | }, 37 | { 38 | "pattern":"\\Qcom/sun/jna/win32-x86-64/jnidispatch.dll\\E" 39 | } 40 | ]}, 41 | "bundles":[ 42 | { 43 | "name":"com.sun.javafx.tk.quantum.QuantumMessagesBundle", 44 | "locales":[""] 45 | }, 46 | { 47 | "name":"com.sun.org.apache.xml.internal.serializer.XMLEntities", 48 | "locales":[""] 49 | }, 50 | { 51 | "name":"com/sun/glass/ui/win/themes", 52 | "locales":[""] 53 | }, 54 | { 55 | "name":"com/sun/javafx/scene/control/skin/resources/controls", 56 | "locales":[""] 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/serialization-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "types":[ 3 | ], 4 | "lambdaCapturingTypes":[ 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/fxml/Main.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |

38 | 39 | 40 | 41 | 42 | 43 | 48 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 64 | 65 | 66 | 67 | 68 | 69 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 135 | 136 | 137 | 138 | 139 | 140 | 142 | 143 | 144 | 145 | 146 | 148 | 149 | 150 | 151 | 152 |
153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /src/main/resources/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/src/main/resources/icons/icon.png -------------------------------------------------------------------------------- /src/main/resources/icons/round_play_circle_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/src/main/resources/icons/round_play_circle_48.png -------------------------------------------------------------------------------- /src/main/resources/icons/round_stop_circle_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/src/main/resources/icons/round_stop_circle_48.png -------------------------------------------------------------------------------- /src/main/resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/src/main/resources/logo.png -------------------------------------------------------------------------------- /src/windows/assets/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/src/windows/assets/icon.ico -------------------------------------------------------------------------------- /steam_images/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/steam_images/grid.png -------------------------------------------------------------------------------- /steam_images/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/steam_images/hero.png -------------------------------------------------------------------------------- /steam_images/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/steam_images/home.png -------------------------------------------------------------------------------- /steam_images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/steam_images/icon.png -------------------------------------------------------------------------------- /steam_images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AniLabXTeam/Drop2Deck/5e0208f5c80dc12c0c95e71bb49f30c244a528d8/steam_images/logo.png --------------------------------------------------------------------------------