├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts ├── src ├── client │ ├── java │ │ └── net │ │ │ └── irisshaders │ │ │ └── imgui │ │ │ ├── ImGuiMC.java │ │ │ ├── mixin │ │ │ ├── DrawMixin.java │ │ │ ├── KeyboardHandlingMixin.java │ │ │ ├── MouseHandlingMixin.java │ │ │ ├── RendererInitMixin.java │ │ │ ├── ScreenMixin.java │ │ │ └── WindowMixin.java │ │ │ └── window │ │ │ ├── ImGuiImplGl3.java │ │ │ └── ImGuiImplGlfw.java │ └── resources │ │ ├── imgui-for-mc.client.mixins.json │ │ ├── imgui-java64.dll │ │ ├── libimgui-java64.dylib │ │ └── libimgui-java64.so └── main │ └── resources │ └── fabric.mod.json ├── stonecutter.gradle.kts └── versions ├── 1.20.1 └── gradle.properties ├── 1.20.4 └── gradle.properties ├── 1.20.6 └── gradle.properties ├── 1.21.1 └── gradle.properties └── 1.21.5 └── gradle.properties /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | 10 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Automatically build the project and run any configured tests for every push 2 | # and submitted pull request. This can help catch issues that only occur on 3 | # certain platforms or Java versions, and provides a first line of defence 4 | # against bad commits. 5 | 6 | name: build 7 | on: [pull_request, push] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | # Use these Java versions 14 | java: [21] 15 | runs-on: ubuntu-22.04 16 | steps: 17 | - name: checkout repository 18 | uses: actions/checkout@v4 19 | - name: validate gradle wrapper 20 | uses: gradle/wrapper-validation-action@v2 21 | - name: setup jdk ${{ matrix.java }} 22 | uses: actions/setup-java@v4 23 | with: 24 | java-version: ${{ matrix.java }} 25 | distribution: 'microsoft' 26 | - name: make gradle wrapper executable 27 | run: chmod +x ./gradlew 28 | - name: build 29 | run: ./gradlew chiseledBuild 30 | - name: capture build artifacts 31 | if: ${{ matrix.java == '21' }} # Only upload artifacts built from latest java on one OS 32 | uses: actions/upload-artifact@v4 33 | with: 34 | name: Artifacts 35 | path: build/libs/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | 35 | # java 36 | 37 | hs_err_*.log 38 | replay_*.log 39 | *.hprof 40 | *.jfr 41 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IrisShaders/imgui-mc/f22156e203aad54a9783f8a3b197a3ad4fd26766/CHANGELOG.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-present, Ilya "SpaiR" Prymshyts 4 | Copyright (c) 2025, IMS212 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 | # ImGui for MC 2 | 3 | This is a basic loader for our upgraded ImGui bindings that can be JiJ'd into any Fabric mod. No setup is needed, and ImGui can start being used immediately. 4 | 5 | All context handling, window creation, and events are handled by the mod, with multi-viewports being enabled by default. -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `maven-publish` 3 | id("fabric-loom") 4 | //id("dev.kikugie.j52j") 5 | //id("me.modmuss50.mod-publish-plugin") 6 | } 7 | 8 | class ModData { 9 | val id = property("mod.id").toString() 10 | val name = property("mod.name").toString() 11 | val version = property("mod.version").toString() 12 | val group = property("mod.group").toString() 13 | } 14 | 15 | class ModDependencies { 16 | operator fun get(name: String) = property("deps.$name").toString() 17 | } 18 | 19 | val mod = ModData() 20 | val deps = ModDependencies() 21 | val mcVersion = stonecutter.current.version 22 | val mcDep = property("mod.mc_dep").toString() 23 | 24 | version = "${mod.version}+$mcVersion" 25 | group = mod.group 26 | base { archivesName.set(mod.id) } 27 | 28 | loom { 29 | splitEnvironmentSourceSets() 30 | 31 | mods { 32 | create("template") { 33 | sourceSet(sourceSets["main"]) 34 | sourceSet(sourceSets["client"]) 35 | } 36 | } 37 | 38 | runs { 39 | getByName("client") { 40 | environmentVariable("__GL_THREADED_OPTIMIZATIONS", "0") 41 | } 42 | } 43 | } 44 | 45 | repositories { 46 | fun strictMaven(url: String, alias: String, vararg groups: String) = exclusiveContent { 47 | forRepository { maven(url) { name = alias } } 48 | filter { groups.forEach(::includeGroup) } 49 | } 50 | strictMaven("https://www.cursemaven.com", "CurseForge", "curse.maven") 51 | strictMaven("https://api.modrinth.com/maven", "Modrinth", "maven.modrinth") 52 | mavenLocal() 53 | } 54 | 55 | dependencies { 56 | minecraft("com.mojang:minecraft:$mcVersion") 57 | mappings(loom.officialMojangMappings()) 58 | modImplementation("net.fabricmc:fabric-loader:${deps["fabric_loader"]}") 59 | include("io.github.spair:imgui-java-binding:1.90.9") 60 | implementation("io.github.spair:imgui-java-binding:1.90.9") { 61 | isTransitive = false 62 | } 63 | } 64 | 65 | loom { 66 | decompilers { 67 | get("vineflower").apply { // Adds names to lambdas - useful for mixins 68 | options.put("mark-corresponding-synthetics", "1") 69 | } 70 | } 71 | 72 | runConfigs.all { 73 | ideConfigGenerated(true) 74 | vmArgs("-Dmixin.debug.export=true") 75 | runDir = "../../run" 76 | } 77 | } 78 | 79 | java { 80 | withSourcesJar() 81 | val java = if (stonecutter.eval(mcVersion, ">=1.20.6")) JavaVersion.VERSION_21 else JavaVersion.VERSION_17 82 | targetCompatibility = java 83 | sourceCompatibility = java 84 | } 85 | 86 | tasks.processResources { 87 | inputs.property("id", mod.id) 88 | inputs.property("name", mod.name) 89 | inputs.property("version", mod.version) 90 | inputs.property("mcdep", mcDep) 91 | 92 | val map = mapOf( 93 | "id" to mod.id, 94 | "name" to mod.name, 95 | "version" to mod.version, 96 | "mcdep" to mcDep 97 | ) 98 | 99 | filesMatching("fabric.mod.json") { expand(map) } 100 | } 101 | 102 | tasks.register("buildAndCollect") { 103 | group = "build" 104 | from(tasks.remapJar.get().archiveFile) 105 | into(rootProject.layout.buildDirectory.file("libs/${mod.version}")) 106 | dependsOn("build") 107 | } 108 | 109 | /* 110 | publishMods { 111 | file = tasks.remapJar.get().archiveFile 112 | additionalFiles.from(tasks.remapSourcesJar.get().archiveFile) 113 | displayName = "${mod.name} ${mod.version} for $mcVersion" 114 | version = mod.version 115 | changelog = rootProject.file("CHANGELOG.md").readText() 116 | type = STABLE 117 | modLoaders.add("fabric") 118 | 119 | dryRun = providers.environmentVariable("MODRINTH_TOKEN") 120 | .getOrNull() == null || providers.environmentVariable("CURSEFORGE_TOKEN").getOrNull() == null 121 | 122 | modrinth { 123 | projectId = property("publish.modrinth").toString() 124 | accessToken = providers.environmentVariable("MODRINTH_TOKEN") 125 | minecraftVersions.add(mcVersion) 126 | requires { 127 | slug = "fabric-api" 128 | } 129 | } 130 | 131 | curseforge { 132 | projectId = property("publish.curseforge").toString() 133 | accessToken = providers.environmentVariable("CURSEFORGE_TOKEN") 134 | minecraftVersions.add(mcVersion) 135 | requires { 136 | slug = "fabric-api" 137 | } 138 | } 139 | } 140 | */ 141 | /* 142 | publishing { 143 | repositories { 144 | maven("...") { 145 | name = "..." 146 | credentials(PasswordCredentials::class.java) 147 | authentication { 148 | create("basic") 149 | } 150 | } 151 | } 152 | 153 | publications { 154 | create("mavenJava") { 155 | groupId = "${property("mod.group")}.${mod.id}" 156 | artifactId = mod.version 157 | version = mcVersion 158 | 159 | from(components["java"]) 160 | } 161 | } 162 | } 163 | */ -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx4G 3 | org.gradle.parallel=true 4 | org.gradle.caching=true 5 | org.gradle.caching.debug=false 6 | org.gradle.configureondemand=true 7 | 8 | # Mod properties 9 | mod.version=0.1.0 10 | mod.group=net.irisshaders 11 | mod.id=imguimc 12 | mod.name=ImGui for MC 13 | 14 | # for fabric.mod.json 15 | mod.mc_dep=[VERSIONED] 16 | # for release title 17 | mod.mc_title=[VERSIONED] 18 | # for release metadata 19 | mod.mc_targets=[VERSIONED] 20 | 21 | # Global dependencies 22 | deps.fabric_loader=0.16.13 23 | 24 | 25 | # Publishing 26 | publish.modrinth=... 27 | publish.curseforge=... -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IrisShaders/imgui-mc/f22156e203aad54a9783f8a3b197a3ad4fd26766/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /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/HEAD/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 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | 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 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC2039,SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC2039,SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /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 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | maven("https://maven.fabricmc.net/") 6 | } 7 | } 8 | 9 | plugins { 10 | id("dev.kikugie.stonecutter") version "0.5" 11 | } 12 | 13 | stonecutter { 14 | kotlinController = true 15 | centralScript = "build.gradle.kts" 16 | 17 | shared { 18 | versions("1.20.1", "1.20.4", "1.20.6", "1.21.1", "1.21.5") 19 | } 20 | create(rootProject) 21 | } 22 | 23 | rootProject.name = "ImGui for MC" -------------------------------------------------------------------------------- /src/client/java/net/irisshaders/imgui/ImGuiMC.java: -------------------------------------------------------------------------------- 1 | package net.irisshaders.imgui; 2 | 3 | //? if =1.21.5 { 4 | import com.mojang.blaze3d.opengl.GlConst; 5 | import com.mojang.blaze3d.opengl.GlDevice; 6 | import com.mojang.blaze3d.opengl.GlStateManager; 7 | import com.mojang.blaze3d.opengl.GlTexture; 8 | //?} 9 | import com.mojang.blaze3d.systems.RenderSystem; 10 | import imgui.ImGui; 11 | import imgui.flag.ImGuiConfigFlags; 12 | import imgui.internal.ImGuiContext; 13 | import net.irisshaders.imgui.window.ImGuiImplGl3; 14 | import net.irisshaders.imgui.window.ImGuiImplGlfw; 15 | import net.minecraft.client.Minecraft; 16 | import org.jetbrains.annotations.ApiStatus; 17 | import org.lwjgl.glfw.GLFW; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.io.UncheckedIOException; 22 | import java.nio.file.*; 23 | 24 | public class ImGuiMC { 25 | private static final ImGuiMC INSTANCE = new ImGuiMC(); 26 | private static final boolean DEBUG = false; 27 | private ImGuiContext context; 28 | private ImGuiImplGl3 glAccessor; 29 | private ImGuiImplGlfw glWindow; 30 | private boolean drawing; 31 | 32 | public static ImGuiMC getInstance() { 33 | return INSTANCE; 34 | } 35 | 36 | /** 37 | * Force sets the context. Usually, you want {@link #startDrawing()} instead. 38 | */ 39 | public static void setContext() { 40 | ImGui.setCurrentContext(getInstance().context); 41 | } 42 | 43 | /** 44 | * If ready to draw, sets the current context. 45 | * 46 | * @return If drawing is allowed currently 47 | */ 48 | public static boolean startDrawing() { 49 | if (getInstance().context == null) return false; 50 | 51 | ImGui.setCurrentContext(getInstance().context); 52 | 53 | return getInstance().drawing; 54 | } 55 | 56 | private String tryLoadFromClasspath(final String fullLibName) { 57 | if (DEBUG) { 58 | System.out.println("Loading " + fullLibName); 59 | } 60 | 61 | try (InputStream is = ImGuiMC.class.getResourceAsStream("/" + fullLibName)) { 62 | if (is == null) { 63 | throw new IllegalStateException("Could not find library " + fullLibName + " in classpath"); 64 | } 65 | 66 | final Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir")).resolve("imguimc"); 67 | if (!Files.exists(tmpDir)) { 68 | Files.createDirectories(tmpDir); 69 | } 70 | 71 | final Path libBin = tmpDir.resolve(fullLibName); 72 | 73 | try { 74 | Files.copy(is, libBin, StandardCopyOption.REPLACE_EXISTING); 75 | } catch (AccessDeniedException e) { 76 | if (!Files.exists(libBin)) { 77 | throw e; 78 | } 79 | } 80 | 81 | return libBin.toAbsolutePath().toString(); 82 | } catch (IOException e) { 83 | throw new UncheckedIOException(e); 84 | } 85 | } 86 | 87 | private void loadLibrary() { 88 | final boolean isWin = System.getProperty("os.name").toLowerCase().contains("win"); 89 | final boolean isMac = System.getProperty("os.name").toLowerCase().contains("mac"); 90 | final String libPrefix; 91 | final String libSuffix; 92 | 93 | if (isWin) { 94 | libPrefix = ""; 95 | libSuffix = ".dll"; 96 | } else if (isMac) { 97 | libPrefix = "lib"; 98 | libSuffix = ".dylib"; 99 | } else { 100 | libPrefix = "lib"; 101 | libSuffix = ".so"; 102 | } 103 | 104 | String libName = libPrefix + "imgui-java64" + libSuffix; 105 | 106 | System.load(tryLoadFromClasspath(libName)); 107 | } 108 | 109 | @ApiStatus.Internal 110 | public void onRendererInit(long window) { 111 | loadLibrary(); 112 | 113 | this.context = ImGui.createContext(false); 114 | ImGui.getIO().addConfigFlags(ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.ViewportsEnable); 115 | this.glAccessor = new ImGuiImplGl3(); 116 | this.glWindow = new ImGuiImplGlfw(); 117 | this.glAccessor.init(); 118 | this.glWindow.init(window, false); 119 | } 120 | 121 | public void afterPollEvents(long l) { 122 | if (drawing) { 123 | // The last frame never correctly finished. The level may have changed. 124 | return; 125 | } 126 | 127 | glAccessor.newFrame(); 128 | glWindow.newFrame(); 129 | ImGui.newFrame(); 130 | 131 | drawing = true; 132 | } 133 | 134 | public void draw() { 135 | //? if =1.21.5 { 136 | GlStateManager._glBindFramebuffer(GlConst.GL_FRAMEBUFFER, ((GlTexture) Minecraft.getInstance().getMainRenderTarget().getColorTexture()).getFbo(((GlDevice) RenderSystem.getDevice()).directStateAccess(), null)); 137 | //?} else { 138 | /*Minecraft.getInstance().getMainRenderTarget().bindWrite(true); 139 | *///?} 140 | ImGui.render(); 141 | drawing = false; 142 | 143 | glAccessor.renderDrawData(ImGui.getDrawData()); 144 | 145 | ImGui.updatePlatformWindows(); 146 | ImGui.renderPlatformWindowsDefault(); 147 | GLFW.glfwMakeContextCurrent(Minecraft.getInstance().getWindow().getWindow()); 148 | } 149 | 150 | private static boolean isGone; 151 | 152 | public void shutdown() { 153 | if (isGone) { 154 | throw new IllegalStateException("Cannot shutdown twice!"); 155 | } 156 | isGone = true; 157 | setContext(); 158 | glAccessor.shutdown(); 159 | glWindow.shutdown(); 160 | ImGui.destroyContext(); 161 | context = null; 162 | } 163 | 164 | public void onMouseMove(long window, double mouseX, double mouseY) { 165 | if (isGone) return; 166 | 167 | setContext(); 168 | glWindow.cursorPosCallback(window, mouseX, mouseY); 169 | } 170 | 171 | public void onMouseScroll(long window, double scrollX, double scrollY) { 172 | if (isGone) return; 173 | 174 | setContext(); 175 | glWindow.scrollCallback(window, scrollX, scrollY); 176 | } 177 | 178 | public void onMouseButton(long window, int button, int action, int mods) { 179 | if (isGone) return; 180 | 181 | setContext(); 182 | glWindow.mouseButtonCallback(window, button, action, mods); 183 | } 184 | 185 | public void monitorCallback(long window, int event) { 186 | if (isGone) return; 187 | 188 | setContext(); 189 | glWindow.monitorCallback(window, event); 190 | } 191 | 192 | public void cursorEnterCallback(long window, boolean entered) { 193 | if (isGone) return; 194 | 195 | setContext(); 196 | glWindow.cursorEnterCallback(window, entered); 197 | } 198 | 199 | public void windowFocusCallback(long window, boolean focused) { 200 | if (isGone) return; 201 | 202 | setContext(); 203 | glWindow.windowFocusCallback(window, focused); 204 | } 205 | 206 | public void onKeyPress(long window, int keycode, int scancode, int action, int mods) { 207 | if (isGone) return; 208 | 209 | setContext(); 210 | glWindow.keyCallback(window, keycode, scancode, action, mods); 211 | } 212 | 213 | public void recreateFonts() { 214 | glAccessor.destroyFontsTexture(); 215 | glAccessor.createFontsTexture(); 216 | } 217 | 218 | public void onCharTyped(long window, int chara, int j) { 219 | glWindow.charCallback(window, chara); 220 | } 221 | } -------------------------------------------------------------------------------- /src/client/java/net/irisshaders/imgui/mixin/DrawMixin.java: -------------------------------------------------------------------------------- 1 | package net.irisshaders.imgui.mixin; 2 | 3 | import com.mojang.blaze3d.platform.Window; 4 | import imgui.ImGui; 5 | import net.irisshaders.imgui.ImGuiMC; 6 | import net.minecraft.client.Minecraft; 7 | import org.spongepowered.asm.mixin.Final; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | 14 | @Mixin(Minecraft.class) 15 | public class DrawMixin { 16 | @Shadow @Final private Window window; 17 | 18 | @Inject(at = @At("HEAD"), method = "runTick", remap = false) 19 | private void imgui$newFrame( 20 | boolean bl, CallbackInfo ci 21 | ) { 22 | ImGuiMC.getInstance().afterPollEvents(this.window.getWindow()); 23 | } 24 | 25 | @Inject(method = "runTick", at = @At(value = "INVOKE", target = /*? if >= 1.21.5 {*/"Lcom/mojang/blaze3d/platform/Window;isMinimized()Z"/*?} else {*//*"Lcom/mojang/blaze3d/pipeline/RenderTarget;unbindWrite()V"*//*?}*/)) 26 | private void imgui$draw(boolean bl, CallbackInfo ci) { 27 | if (ImGuiMC.startDrawing()) { 28 | ImGui.showDemoWindow(); 29 | ImGuiMC.getInstance().draw(); 30 | } 31 | } 32 | 33 | @Inject(method = "stop", at = @At("HEAD")) 34 | private void imgui$shutdown(CallbackInfo ci) { 35 | ImGuiMC.getInstance().shutdown(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/client/java/net/irisshaders/imgui/mixin/KeyboardHandlingMixin.java: -------------------------------------------------------------------------------- 1 | package net.irisshaders.imgui.mixin; 2 | 3 | import net.irisshaders.imgui.ImGuiMC; 4 | import net.minecraft.client.KeyboardHandler; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | @Mixin(KeyboardHandler.class) 11 | public class KeyboardHandlingMixin { 12 | @Inject(method = "keyPress", at = @At("HEAD")) 13 | private void imgui$keyPress(long l, int i, int j, int k, int m, CallbackInfo ci) { 14 | ImGuiMC.getInstance().onKeyPress(l, i, j, k, m); 15 | } 16 | 17 | @Inject(method = "charTyped", at = @At("HEAD")) 18 | private void imgui$charTyped(long l, int i, int j, CallbackInfo ci) { 19 | ImGuiMC.getInstance().onCharTyped(l, i, j); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/client/java/net/irisshaders/imgui/mixin/MouseHandlingMixin.java: -------------------------------------------------------------------------------- 1 | package net.irisshaders.imgui.mixin; 2 | 3 | import net.irisshaders.imgui.ImGuiMC; 4 | import net.minecraft.client.MouseHandler; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | @Mixin(MouseHandler.class) 11 | public class MouseHandlingMixin { 12 | @Inject(method = "onMove", at = @At("HEAD")) 13 | private void imgui$onMove(long window, double mouseX, double mouseY, CallbackInfo ci) { 14 | ImGuiMC.getInstance().onMouseMove(window, mouseX, mouseY); 15 | } 16 | 17 | @Inject(method = "onScroll", at = @At("HEAD")) 18 | private void imgui$onScroll(long window, double scrollX, double scrollY, CallbackInfo ci) { 19 | ImGuiMC.getInstance().onMouseScroll(window, scrollX, scrollY); 20 | } 21 | 22 | @Inject(method = "onPress", at = @At("HEAD")) 23 | private void imgui$onScroll(long window, int button, int action, int mods, CallbackInfo ci) { 24 | ImGuiMC.getInstance().onMouseButton(window, button, action, mods); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/client/java/net/irisshaders/imgui/mixin/RendererInitMixin.java: -------------------------------------------------------------------------------- 1 | package net.irisshaders.imgui.mixin; 2 | 3 | //? if >=1.21.5 { 4 | import com.mojang.blaze3d.TracyFrameCapture; 5 | import com.mojang.blaze3d.shaders.ShaderType; 6 | import com.mojang.blaze3d.systems.RenderSystem; 7 | //?} else { 8 | /*import com.mojang.blaze3d.systems.RenderSystem; 9 | import net.minecraft.client.Minecraft; 10 | *///?} 11 | import net.irisshaders.imgui.ImGuiMC; 12 | import net.minecraft.resources.ResourceLocation; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 | 18 | import java.util.function.BiFunction; 19 | 20 | @Mixin(RenderSystem.class) 21 | public class RendererInitMixin { 22 | @Inject(at = @At("RETURN"), method = "initRenderer", remap = false) 23 | //? if = 1.21.5 { 24 | private static void imgui$initRenderer(long window, int i, boolean bl, BiFunction biFunction, boolean bl2, CallbackInfo ci) { 25 | ImGuiMC.getInstance().onRendererInit(window); 26 | } 27 | //?} else { 28 | /*private static void imgui$initRenderer(int i, boolean bl, CallbackInfo ci) { 29 | ImGuiMC.getInstance().onRendererInit(Minecraft.getInstance().getWindow().getWindow()); 30 | } 31 | *///?} 32 | } -------------------------------------------------------------------------------- /src/client/java/net/irisshaders/imgui/mixin/ScreenMixin.java: -------------------------------------------------------------------------------- 1 | package net.irisshaders.imgui.mixin; 2 | 3 | import com.mojang.blaze3d.platform.ScreenManager; 4 | import net.irisshaders.imgui.ImGuiMC; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | @Mixin(ScreenManager.class) 11 | public class ScreenMixin { 12 | @Inject(method = "onMonitorChange", at = @At("HEAD")) 13 | private void imgui$onMonitorChange(long window, int event, CallbackInfo ci) { 14 | ImGuiMC.getInstance().monitorCallback(window, event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/client/java/net/irisshaders/imgui/mixin/WindowMixin.java: -------------------------------------------------------------------------------- 1 | package net.irisshaders.imgui.mixin; 2 | 3 | import com.mojang.blaze3d.platform.Window; 4 | import net.irisshaders.imgui.ImGuiMC; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Inject; 8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 9 | 10 | @Mixin(Window.class) 11 | public class WindowMixin { 12 | @Inject(method = "onEnter", at = @At("HEAD")) 13 | private void imgui$onEnter(long l, boolean bl, CallbackInfo ci) { 14 | ImGuiMC.getInstance().cursorEnterCallback(l, bl); 15 | } 16 | 17 | @Inject(method = "onFocus", at = @At("HEAD")) 18 | private void imgui$onFocus(long l, boolean bl, CallbackInfo ci) { 19 | ImGuiMC.getInstance().windowFocusCallback(l, bl); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/client/java/net/irisshaders/imgui/window/ImGuiImplGl3.java: -------------------------------------------------------------------------------- 1 | package net.irisshaders.imgui.window; 2 | 3 | import imgui.ImDrawData; 4 | import imgui.ImFontAtlas; 5 | import imgui.ImGui; 6 | import imgui.ImGuiIO; 7 | import imgui.ImGuiViewport; 8 | import imgui.ImVec4; 9 | import imgui.callback.ImPlatformFuncViewport; 10 | import imgui.flag.ImGuiBackendFlags; 11 | import imgui.flag.ImGuiConfigFlags; 12 | import imgui.flag.ImGuiViewportFlags; 13 | import imgui.type.ImInt; 14 | import org.lwjgl.opengl.GL; 15 | import org.lwjgl.opengl.GLCapabilities; 16 | 17 | import java.nio.ByteBuffer; 18 | import java.util.regex.Matcher; 19 | import java.util.regex.Pattern; 20 | 21 | import static org.lwjgl.opengl.GL20.glDeleteShader; 22 | import static org.lwjgl.opengl.GL20.glDetachShader; 23 | import static org.lwjgl.opengl.GL20.glGetAttribLocation; 24 | import static org.lwjgl.opengl.GL20.glGetProgramiv; 25 | import static org.lwjgl.opengl.GL20.glGetUniformLocation; 26 | import static org.lwjgl.opengl.GL21C.GL_PIXEL_UNPACK_BUFFER; 27 | import static org.lwjgl.opengl.GL21C.GL_PIXEL_UNPACK_BUFFER_BINDING; 28 | import static org.lwjgl.opengl.GL32.GL_ACTIVE_TEXTURE; 29 | import static org.lwjgl.opengl.GL32.GL_ARRAY_BUFFER; 30 | import static org.lwjgl.opengl.GL32.GL_ARRAY_BUFFER_BINDING; 31 | import static org.lwjgl.opengl.GL32.GL_BACK; 32 | import static org.lwjgl.opengl.GL32.GL_BLEND; 33 | import static org.lwjgl.opengl.GL32.GL_BLEND_DST_ALPHA; 34 | import static org.lwjgl.opengl.GL32.GL_BLEND_DST_RGB; 35 | import static org.lwjgl.opengl.GL32.GL_BLEND_EQUATION_ALPHA; 36 | import static org.lwjgl.opengl.GL32.GL_BLEND_EQUATION_RGB; 37 | import static org.lwjgl.opengl.GL32.GL_BLEND_SRC_ALPHA; 38 | import static org.lwjgl.opengl.GL32.GL_BLEND_SRC_RGB; 39 | import static org.lwjgl.opengl.GL32.GL_COLOR_BUFFER_BIT; 40 | import static org.lwjgl.opengl.GL32.GL_COMPILE_STATUS; 41 | import static org.lwjgl.opengl.GL32.GL_CONTEXT_COMPATIBILITY_PROFILE_BIT; 42 | import static org.lwjgl.opengl.GL32.GL_CONTEXT_PROFILE_MASK; 43 | import static org.lwjgl.opengl.GL32.GL_CULL_FACE; 44 | import static org.lwjgl.opengl.GL32.GL_CURRENT_PROGRAM; 45 | import static org.lwjgl.opengl.GL32.GL_DEPTH_TEST; 46 | import static org.lwjgl.opengl.GL32.GL_ELEMENT_ARRAY_BUFFER; 47 | import static org.lwjgl.opengl.GL32.GL_FALSE; 48 | import static org.lwjgl.opengl.GL32.GL_FILL; 49 | import static org.lwjgl.opengl.GL32.GL_FLOAT; 50 | import static org.lwjgl.opengl.GL32.GL_FRAGMENT_SHADER; 51 | import static org.lwjgl.opengl.GL32.GL_FRONT; 52 | import static org.lwjgl.opengl.GL32.GL_FRONT_AND_BACK; 53 | import static org.lwjgl.opengl.GL32.GL_FUNC_ADD; 54 | import static org.lwjgl.opengl.GL32.GL_INFO_LOG_LENGTH; 55 | import static org.lwjgl.opengl.GL32.GL_LINEAR; 56 | import static org.lwjgl.opengl.GL32.GL_LINK_STATUS; 57 | import static org.lwjgl.opengl.GL32.GL_MAJOR_VERSION; 58 | import static org.lwjgl.opengl.GL32.GL_MINOR_VERSION; 59 | import static org.lwjgl.opengl.GL32.GL_ONE; 60 | import static org.lwjgl.opengl.GL32.GL_ONE_MINUS_SRC_ALPHA; 61 | import static org.lwjgl.opengl.GL32.GL_POLYGON_MODE; 62 | import static org.lwjgl.opengl.GL32.GL_PRIMITIVE_RESTART; 63 | import static org.lwjgl.opengl.GL32.GL_RGBA; 64 | import static org.lwjgl.opengl.GL32.GL_SCISSOR_BOX; 65 | import static org.lwjgl.opengl.GL32.GL_SCISSOR_TEST; 66 | import static org.lwjgl.opengl.GL32.GL_SRC_ALPHA; 67 | import static org.lwjgl.opengl.GL32.GL_STENCIL_TEST; 68 | import static org.lwjgl.opengl.GL32.GL_STREAM_DRAW; 69 | import static org.lwjgl.opengl.GL32.GL_TEXTURE0; 70 | import static org.lwjgl.opengl.GL32.GL_TEXTURE_2D; 71 | import static org.lwjgl.opengl.GL32.GL_TEXTURE_BINDING_2D; 72 | import static org.lwjgl.opengl.GL32.GL_TEXTURE_MAG_FILTER; 73 | import static org.lwjgl.opengl.GL32.GL_TEXTURE_MIN_FILTER; 74 | import static org.lwjgl.opengl.GL32.GL_TRIANGLES; 75 | import static org.lwjgl.opengl.GL32.GL_TRUE; 76 | import static org.lwjgl.opengl.GL32.GL_UNPACK_ALIGNMENT; 77 | import static org.lwjgl.opengl.GL32.GL_UNPACK_ROW_LENGTH; 78 | import static org.lwjgl.opengl.GL32.GL_UNPACK_SKIP_PIXELS; 79 | import static org.lwjgl.opengl.GL32.GL_UNPACK_SKIP_ROWS; 80 | import static org.lwjgl.opengl.GL32.GL_UNSIGNED_BYTE; 81 | import static org.lwjgl.opengl.GL32.GL_UNSIGNED_INT; 82 | import static org.lwjgl.opengl.GL32.GL_UNSIGNED_SHORT; 83 | import static org.lwjgl.opengl.GL32.GL_UPPER_LEFT; 84 | import static org.lwjgl.opengl.GL32.GL_VERSION; 85 | import static org.lwjgl.opengl.GL32.GL_VERTEX_ARRAY_BINDING; 86 | import static org.lwjgl.opengl.GL32.GL_VERTEX_SHADER; 87 | import static org.lwjgl.opengl.GL32.GL_VIEWPORT; 88 | import static org.lwjgl.opengl.GL32.glActiveTexture; 89 | import static org.lwjgl.opengl.GL32.glAttachShader; 90 | import static org.lwjgl.opengl.GL32.glBindBuffer; 91 | import static org.lwjgl.opengl.GL32.glBindTexture; 92 | import static org.lwjgl.opengl.GL32.glBindVertexArray; 93 | import static org.lwjgl.opengl.GL32.glBlendEquation; 94 | import static org.lwjgl.opengl.GL32.glBlendEquationSeparate; 95 | import static org.lwjgl.opengl.GL32.glBlendFuncSeparate; 96 | import static org.lwjgl.opengl.GL32.glBufferData; 97 | import static org.lwjgl.opengl.GL32.glClear; 98 | import static org.lwjgl.opengl.GL32.glClearColor; 99 | import static org.lwjgl.opengl.GL32.glCompileShader; 100 | import static org.lwjgl.opengl.GL32.glCreateProgram; 101 | import static org.lwjgl.opengl.GL32.glCreateShader; 102 | import static org.lwjgl.opengl.GL32.glDeleteBuffers; 103 | import static org.lwjgl.opengl.GL32.glDeleteProgram; 104 | import static org.lwjgl.opengl.GL32.glDeleteTextures; 105 | import static org.lwjgl.opengl.GL32.glDeleteVertexArrays; 106 | import static org.lwjgl.opengl.GL32.glDisable; 107 | import static org.lwjgl.opengl.GL32.glDrawElements; 108 | import static org.lwjgl.opengl.GL32.glDrawElementsBaseVertex; 109 | import static org.lwjgl.opengl.GL32.glEnable; 110 | import static org.lwjgl.opengl.GL32.glEnableVertexAttribArray; 111 | import static org.lwjgl.opengl.GL32.glGenBuffers; 112 | import static org.lwjgl.opengl.GL32.glGenTextures; 113 | import static org.lwjgl.opengl.GL32.glGenVertexArrays; 114 | import static org.lwjgl.opengl.GL32.glGetInteger; 115 | import static org.lwjgl.opengl.GL32.glGetIntegerv; 116 | import static org.lwjgl.opengl.GL32.glGetProgramInfoLog; 117 | import static org.lwjgl.opengl.GL32.glGetShaderInfoLog; 118 | import static org.lwjgl.opengl.GL32.glGetShaderiv; 119 | import static org.lwjgl.opengl.GL32.glGetString; 120 | import static org.lwjgl.opengl.GL32.glIsEnabled; 121 | import static org.lwjgl.opengl.GL32.glIsProgram; 122 | import static org.lwjgl.opengl.GL32.glLinkProgram; 123 | import static org.lwjgl.opengl.GL32.glPixelStorei; 124 | import static org.lwjgl.opengl.GL32.glPolygonMode; 125 | import static org.lwjgl.opengl.GL32.glScissor; 126 | import static org.lwjgl.opengl.GL32.glShaderSource; 127 | import static org.lwjgl.opengl.GL32.glTexImage2D; 128 | import static org.lwjgl.opengl.GL32.glTexParameteri; 129 | import static org.lwjgl.opengl.GL32.glUniform1i; 130 | import static org.lwjgl.opengl.GL32.glUniformMatrix4fv; 131 | import static org.lwjgl.opengl.GL32.glUseProgram; 132 | import static org.lwjgl.opengl.GL32.glVertexAttribPointer; 133 | import static org.lwjgl.opengl.GL32.glViewport; 134 | import static org.lwjgl.opengl.GL33.GL_SAMPLER_BINDING; 135 | import static org.lwjgl.opengl.GL33.glBindSampler; 136 | import static org.lwjgl.opengl.GL45.GL_CLIP_ORIGIN; 137 | 138 | /** 139 | * This class is a straightforward port of the 140 | * imgui_impl_opengl3.cpp. 141 | *

142 | * It does support a backup and restoring of the GL state in the same way the original Dear ImGui code does. 143 | * Some of the very specific OpenGL variables may be ignored here, 144 | * yet you can copy-paste this class in your codebase and modify the rendering routine in the way you'd like. 145 | *

146 | * This implementation has an ability to use a GLSL version provided during the initialization. 147 | * Please read the documentation for the {@link #init(String)}. 148 | */ 149 | @SuppressWarnings({"checkstyle:DesignForExtension", "checkstyle:NeedBraces", "checkstyle:LocalVariableName", "checkstyle:FinalLocalVariable", "checkstyle:ParameterName", "checkstyle:EmptyBlock", "checkstyle:AvoidNestedBlocks"}) 150 | public class ImGuiImplGl3 { 151 | protected static final String OS = System.getProperty("os.name", "generic").toLowerCase(); 152 | protected static final boolean IS_APPLE = OS.contains("mac") || OS.contains("darwin"); 153 | 154 | /** 155 | * Data class to store implementation specific fields. 156 | * Same as {@code ImGui_ImplOpenGL3_Data}. 157 | */ 158 | protected static class Data { 159 | protected int glVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2) 160 | // protected boolean glProfileIsES2; 161 | // protected boolean glProfileIsES3; 162 | protected boolean glProfileIsCompat; 163 | protected int glProfileMask; 164 | protected GLCapabilities glCapabilities = null; 165 | protected String glslVersion = ""; 166 | protected int fontTexture = 0; 167 | protected int shaderHandle = 0; 168 | protected int attribLocationTex = 0; // Uniforms location 169 | protected int attribLocationProjMtx = 0; 170 | protected int attribLocationVtxPos = 0; // Vertex attributes location 171 | protected int attribLocationVtxUV = 0; 172 | protected int attribLocationVtxColor = 0; 173 | protected int vboHandle = 0; 174 | protected int elementsHandle = 0; 175 | // protected int vertexBufferSize; 176 | // protected int indexBufferSize; 177 | protected boolean hasClipOrigin; 178 | } 179 | 180 | /** 181 | * Internal class to store containers for frequently used arrays. 182 | * This class helps minimize the number of object allocations on the JVM side, 183 | * thereby improving performance and reducing garbage collection overhead. 184 | */ 185 | private static final class Properties { 186 | private final ImVec4 clipRect = new ImVec4(); 187 | private final float[] orthoProjMatrix = new float[4 * 4]; 188 | private final int[] lastActiveTexture = new int[1]; 189 | private final int[] lastPixelUnpackBuffer = new int[1]; 190 | private final int[] lastProgram = new int[1]; 191 | private final int[] lastTexture = new int[1]; 192 | private final int[] lastSampler = new int[1]; 193 | private final int[] lastArrayBuffer = new int[1]; 194 | private final int[] lastVertexArrayObject = new int[1]; 195 | private final int[] lastPolygonMode = new int[2]; 196 | private final int[] lastViewport = new int[4]; 197 | private final int[] lastScissorBox = new int[4]; 198 | private final int[] lastBlendSrcRgb = new int[1]; 199 | private final int[] lastBlendDstRgb = new int[1]; 200 | private final int[] lastBlendSrcAlpha = new int[1]; 201 | private final int[] lastBlendDstAlpha = new int[1]; 202 | private final int[] lastBlendEquationRgb = new int[1]; 203 | private final int[] lastBlendEquationAlpha = new int[1]; 204 | private boolean lastEnableBlend = false; 205 | private boolean lastEnableCullFace = false; 206 | private boolean lastEnableDepthTest = false; 207 | private boolean lastEnableStencilTest = false; 208 | private boolean lastEnableScissorTest = false; 209 | private boolean lastEnablePrimitiveRestart = false; 210 | } 211 | 212 | protected Data data = null; 213 | private final Properties props = new Properties(); 214 | 215 | protected Data newData() { 216 | return new Data(); 217 | } 218 | 219 | /** 220 | * Method to do an initialization of the {@link ImGuiImplGl3} state. 221 | * It SHOULD be called before calling of the {@link ImGuiImplGl3#renderDrawData(ImDrawData)} method. 222 | *

223 | * Unlike in the {@link #init(String)} method, here the glslVersion argument is omitted. 224 | * Thus, a "#version 130" string will be used instead. 225 | * 226 | * @return true when initialized 227 | */ 228 | public boolean init() { 229 | return init(null); 230 | } 231 | 232 | /** 233 | * Method to do an initialization of the {@link ImGuiImplGl3} state. 234 | * It SHOULD be called before calling of the {@link ImGuiImplGl3#renderDrawData(ImDrawData)} method. 235 | *

236 | * Method takes an argument, which should be a valid GLSL string with the version to use. 237 | *

238 |      * ----------------------------------------
239 |      * OpenGL    GLSL      GLSL
240 |      * version   version   string
241 |      * ---------------------------------------
242 |      *  2.0       110       "#version 110"
243 |      *  2.1       120       "#version 120"
244 |      *  3.0       130       "#version 130"
245 |      *  3.1       140       "#version 140"
246 |      *  3.2       150       "#version 150"
247 |      *  3.3       330       "#version 330 core"
248 |      *  4.0       400       "#version 400 core"
249 |      *  4.1       410       "#version 410 core"
250 |      *  4.2       420       "#version 410 core"
251 |      *  4.3       430       "#version 430 core"
252 |      *  ES 3.0    300       "#version 300 es"   = WebGL 2.0
253 |      * ---------------------------------------
254 |      * 
255 | *

256 | * If the argument is null, then a "#version 130" (150 for APPLE) string will be used by default. 257 | * 258 | * @param glslVersion string with the version of the GLSL 259 | * @return true when initialized 260 | */ 261 | public boolean init(final String glslVersion) { 262 | data = newData(); 263 | 264 | final ImGuiIO io = ImGui.getIO(); 265 | io.setBackendRendererName("imgui-java_impl_opengl3"); 266 | 267 | { // Desktop or GLES 3 268 | int major = glGetInteger(GL_MAJOR_VERSION); 269 | int minor = glGetInteger(GL_MINOR_VERSION); 270 | if (major == 0 && minor == 0) { 271 | // Query GL_VERSION in desktop GL 2.x, the string will start with "." 272 | final String glVersion = glGetString(GL_VERSION); 273 | if (glVersion != null) { 274 | final String[] glVersions = glVersion.split("\\."); 275 | major = Integer.parseInt(glVersions[0]); 276 | minor = Integer.parseInt(glVersions[1]); 277 | } 278 | } 279 | data.glVersion = major * 100 + minor * 10; 280 | data.glProfileMask = glGetInteger(GL_CONTEXT_PROFILE_MASK); 281 | data.glProfileIsCompat = (data.glProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0; 282 | if (data.glVersion < 330) { // Ignore in higher GL versions since they support sampler objects anyway 283 | try { 284 | data.glCapabilities = GL.getCapabilities(); 285 | } catch (IllegalStateException ignored) { 286 | // IllegalStateException – if setCapabilities has never been called in the current thread or was last called with a null value 287 | // This exception can be safely ignored as it does not impact the initialization process. 288 | // GL_ARB_sampler_objects will be unavailable (and therefore not supported) if the GL version is less than 3.3. 289 | } 290 | } 291 | } 292 | 293 | // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. 294 | if (data.glVersion >= 320) { 295 | io.addBackendFlags(ImGuiBackendFlags.RendererHasVtxOffset); 296 | } 297 | 298 | // We can create multi-viewports on the Renderer side (optional) 299 | io.addBackendFlags(ImGuiBackendFlags.RendererHasViewports); 300 | 301 | if (glslVersion == null) { 302 | if (IS_APPLE) { 303 | data.glslVersion = "#version 150"; 304 | } else { 305 | data.glslVersion = "#version 130"; 306 | } 307 | } else { 308 | data.glslVersion = glslVersion; 309 | } 310 | 311 | // Make an arbitrary GL call (we don't actually need the result) 312 | // IF YOU GET A CRASH HERE: it probably means the OpenGL function loader didn't do its job. Let us know! 313 | { 314 | final int[] currentTexture = new int[1]; 315 | glGetIntegerv(GL_TEXTURE_BINDING_2D, currentTexture); 316 | } 317 | 318 | data.hasClipOrigin = data.glVersion >= 450; 319 | 320 | 321 | if (ImGui.getIO().hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) { 322 | initPlatformInterface(); 323 | } 324 | 325 | return true; 326 | } 327 | 328 | public void shutdown() { 329 | final ImGuiIO io = ImGui.getIO(); 330 | 331 | shutdownPlatformInterface(); 332 | destroyDeviceObjects(); 333 | 334 | io.setBackendRendererName(null); 335 | io.removeBackendFlags(ImGuiBackendFlags.RendererHasVtxOffset | ImGuiBackendFlags.RendererHasViewports); 336 | data = null; 337 | } 338 | 339 | public void newFrame() { 340 | if (data.shaderHandle == 0) { 341 | createDeviceObjects(); 342 | } 343 | 344 | if (data.fontTexture == 0) { 345 | createFontsTexture(); 346 | } 347 | } 348 | 349 | protected void setupRenderState(final ImDrawData drawData, final int fbWidth, final int fbHeight, final int gVertexArrayObject) { 350 | // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill 351 | glEnable(GL_BLEND); 352 | glBlendEquation(GL_FUNC_ADD); 353 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 354 | glDisable(GL_CULL_FACE); 355 | glDisable(GL_DEPTH_TEST); 356 | glDisable(GL_STENCIL_TEST); 357 | glEnable(GL_SCISSOR_TEST); 358 | 359 | if (data.glVersion >= 310) { 360 | glDisable(GL_PRIMITIVE_RESTART); 361 | } 362 | if (data.glVersion >= 200) { 363 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 364 | } 365 | 366 | // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT) 367 | boolean clipOriginLowerLeft = true; 368 | if (data.hasClipOrigin) { 369 | final int[] currentClipOrigin = new int[1]; 370 | glGetIntegerv(GL_CLIP_ORIGIN, currentClipOrigin); 371 | if (currentClipOrigin[0] == GL_UPPER_LEFT) { 372 | clipOriginLowerLeft = false; 373 | } 374 | } 375 | 376 | // Setup viewport, orthographic projection matrix 377 | // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). 378 | // DisplayPos is (0,0) for single viewport apps. 379 | glViewport(0, 0, fbWidth, fbHeight); 380 | float L = drawData.getDisplayPosX(); 381 | float R = drawData.getDisplayPosX() + drawData.getDisplaySizeX(); 382 | float T = drawData.getDisplayPosY(); 383 | float B = drawData.getDisplayPosY() + drawData.getDisplaySizeY(); 384 | 385 | // Swap top and bottom if origin is upper left 386 | if (data.hasClipOrigin && !clipOriginLowerLeft) { 387 | float tmp = T; 388 | T = B; 389 | B = tmp; 390 | } 391 | 392 | props.orthoProjMatrix[0] = 2.0f / (R - L); 393 | props.orthoProjMatrix[5] = 2.0f / (T - B); 394 | props.orthoProjMatrix[10] = -1.0f; 395 | props.orthoProjMatrix[12] = (R + L) / (L - R); 396 | props.orthoProjMatrix[13] = (T + B) / (B - T); 397 | props.orthoProjMatrix[15] = 1.0f; 398 | 399 | glUseProgram(data.shaderHandle); 400 | glUniform1i(data.attribLocationTex, 0); 401 | glUniformMatrix4fv(data.attribLocationProjMtx, false, props.orthoProjMatrix); 402 | 403 | if (data.glVersion >= 330 || (data.glCapabilities != null && data.glCapabilities.GL_ARB_sampler_objects)) { 404 | glBindSampler(0, 0); 405 | } 406 | 407 | glBindVertexArray(gVertexArrayObject); 408 | 409 | // Bind vertex/index buffers and setup attributes for ImDrawVert 410 | glBindBuffer(GL_ARRAY_BUFFER, data.vboHandle); 411 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.elementsHandle); 412 | glEnableVertexAttribArray(data.attribLocationVtxPos); 413 | glEnableVertexAttribArray(data.attribLocationVtxUV); 414 | glEnableVertexAttribArray(data.attribLocationVtxColor); 415 | glVertexAttribPointer(data.attribLocationVtxPos, 2, GL_FLOAT, false, ImDrawData.sizeOfImDrawVert(), 0); 416 | glVertexAttribPointer(data.attribLocationVtxUV, 2, GL_FLOAT, false, ImDrawData.sizeOfImDrawVert(), 8); 417 | glVertexAttribPointer(data.attribLocationVtxColor, 4, GL_UNSIGNED_BYTE, true, ImDrawData.sizeOfImDrawVert(), 16); 418 | } 419 | 420 | /** 421 | * OpenGL3 Render function. 422 | * Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly. 423 | * This is in order to be able to run within an OpenGL engine that doesn't do so. 424 | * 425 | * @param drawData draw data to render 426 | */ 427 | public void renderDrawData(final ImDrawData drawData) { 428 | // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) 429 | final int fbWidth = (int) (drawData.getDisplaySizeX() * drawData.getFramebufferScaleX()); 430 | final int fbHeight = (int) (drawData.getDisplaySizeY() * drawData.getFramebufferScaleY()); 431 | if (fbWidth <= 0 || fbHeight <= 0) { 432 | return; 433 | } 434 | 435 | if (drawData.getCmdListsCount() <= 0) { 436 | return; 437 | } 438 | 439 | glGetIntegerv(GL_ACTIVE_TEXTURE, props.lastActiveTexture); 440 | glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, props.lastPixelUnpackBuffer); 441 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); 442 | glActiveTexture(GL_TEXTURE0); 443 | glGetIntegerv(GL_CURRENT_PROGRAM, props.lastProgram); 444 | glGetIntegerv(GL_TEXTURE_BINDING_2D, props.lastTexture); 445 | if (data.glVersion >= 330 || (data.glCapabilities != null && data.glCapabilities.GL_ARB_sampler_objects)) { 446 | glGetIntegerv(GL_SAMPLER_BINDING, props.lastSampler); 447 | } 448 | glGetIntegerv(GL_ARRAY_BUFFER_BINDING, props.lastArrayBuffer); 449 | glGetIntegerv(GL_VERTEX_ARRAY_BINDING, props.lastVertexArrayObject); 450 | if (data.glVersion >= 200) { 451 | glGetIntegerv(GL_POLYGON_MODE, props.lastPolygonMode); 452 | } 453 | glGetIntegerv(GL_VIEWPORT, props.lastViewport); 454 | glGetIntegerv(GL_SCISSOR_BOX, props.lastScissorBox); 455 | glGetIntegerv(GL_BLEND_SRC_RGB, props.lastBlendSrcRgb); 456 | glGetIntegerv(GL_BLEND_DST_RGB, props.lastBlendDstRgb); 457 | glGetIntegerv(GL_BLEND_SRC_ALPHA, props.lastBlendSrcAlpha); 458 | glGetIntegerv(GL_BLEND_DST_ALPHA, props.lastBlendDstAlpha); 459 | glGetIntegerv(GL_BLEND_EQUATION_RGB, props.lastBlendEquationRgb); 460 | glGetIntegerv(GL_BLEND_EQUATION_ALPHA, props.lastBlendEquationAlpha); 461 | props.lastEnableBlend = glIsEnabled(GL_BLEND); 462 | props.lastEnableCullFace = glIsEnabled(GL_CULL_FACE); 463 | props.lastEnableDepthTest = glIsEnabled(GL_DEPTH_TEST); 464 | props.lastEnableStencilTest = glIsEnabled(GL_STENCIL_TEST); 465 | props.lastEnableScissorTest = glIsEnabled(GL_SCISSOR_TEST); 466 | if (data.glVersion >= 310) { 467 | props.lastEnablePrimitiveRestart = glIsEnabled(GL_PRIMITIVE_RESTART); 468 | } 469 | 470 | // Setup desired GL state 471 | // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts) 472 | // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. 473 | final int vertexArrayObject = glGenVertexArrays(); 474 | setupRenderState(drawData, fbWidth, fbHeight, vertexArrayObject); 475 | 476 | // Will project scissor/clipping rectangles into framebuffer space 477 | final float clipOffX = drawData.getDisplayPosX(); // (0,0) unless using multi-viewports 478 | final float clipOffY = drawData.getDisplayPosY(); // (0,0) unless using multi-viewports 479 | final float clipScaleX = drawData.getFramebufferScaleX(); // (1,1) unless using retina display which are often (2,2) 480 | final float clipScaleY = drawData.getFramebufferScaleY(); // (1,1) unless using retina display which are often (2,2) 481 | 482 | // Render command lists 483 | for (int n = 0; n < drawData.getCmdListsCount(); n++) { 484 | // FIXME: this is a straightforward port from Dear ImGui and it doesn't work with multi-viewports. 485 | // So we keep solution we used before. 486 | // Upload vertex/index buffers 487 | // final int vtxBufferSize = drawData.getCmdListVtxBufferSize(n) * ImDrawData.sizeOfImDrawVert(); 488 | // final int idxBufferSize = drawData.getCmdListIdxBufferSize(n) * ImDrawData.sizeOfImDrawIdx(); 489 | // if (data.vertexBufferSize < vtxBufferSize) { 490 | // data.vertexBufferSize = vtxBufferSize; 491 | // glBufferData(GL_ARRAY_BUFFER, data.vertexBufferSize, GL_STREAM_DRAW); 492 | // } 493 | // if (data.indexBufferSize < idxBufferSize) { 494 | // data.indexBufferSize = idxBufferSize; 495 | // glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.indexBufferSize, GL_STREAM_DRAW); 496 | // } 497 | // glBufferSubData(GL_ARRAY_BUFFER, 0, drawData.getCmdListVtxBufferData(n)); 498 | // glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, drawData.getCmdListIdxBufferData(n)); 499 | 500 | glBufferData(GL_ARRAY_BUFFER, drawData.getCmdListVtxBufferData(n), GL_STREAM_DRAW); 501 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, drawData.getCmdListIdxBufferData(n), GL_STREAM_DRAW); 502 | 503 | for (int cmdIdx = 0; cmdIdx < drawData.getCmdListCmdBufferSize(n); cmdIdx++) { 504 | // TODO: 505 | // if userCallback 506 | // else 507 | 508 | drawData.getCmdListCmdBufferClipRect(props.clipRect, n, cmdIdx); 509 | 510 | final float clipMinX = (props.clipRect.x - clipOffX) * clipScaleX; 511 | final float clipMinY = (props.clipRect.y - clipOffY) * clipScaleY; 512 | final float clipMaxX = (props.clipRect.z - clipOffX) * clipScaleX; 513 | final float clipMaxY = (props.clipRect.w - clipOffY) * clipScaleY; 514 | 515 | if (clipMaxX <= clipMinX || clipMaxY <= clipMinY) { 516 | continue; 517 | } 518 | 519 | // Apply scissor/clipping rectangle (Y is inverted in OpenGL) 520 | glScissor((int) clipMinX, (int) (fbHeight - clipMaxY), (int) (clipMaxX - clipMinX), (int) (clipMaxY - clipMinY)); 521 | 522 | // Bind texture, Draw 523 | final long textureId = drawData.getCmdListCmdBufferTextureId(n, cmdIdx); 524 | final int elemCount = drawData.getCmdListCmdBufferElemCount(n, cmdIdx); 525 | final int idxOffset = drawData.getCmdListCmdBufferIdxOffset(n, cmdIdx); 526 | final int vtxOffset = drawData.getCmdListCmdBufferVtxOffset(n, cmdIdx); 527 | final long indices = idxOffset * (long) ImDrawData.sizeOfImDrawIdx(); 528 | final int type = ImDrawData.sizeOfImDrawIdx() == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; 529 | 530 | glBindTexture(GL_TEXTURE_2D, (int) textureId); 531 | 532 | if (data.glVersion >= 320) { 533 | glDrawElementsBaseVertex(GL_TRIANGLES, elemCount, type, indices, vtxOffset); 534 | } else { 535 | glDrawElements(GL_TRIANGLES, elemCount, type, indices); 536 | } 537 | } 538 | } 539 | 540 | // Destroy the temporary VAO 541 | glDeleteVertexArrays(vertexArrayObject); 542 | 543 | // Restore modified GL state 544 | // This "glIsProgram()" check is required because if the program is "pending deletion" at the time of binding backup, it will have been deleted by now and will cause an OpenGL error. See #6220. 545 | if (props.lastProgram[0] == 0 || glIsProgram(props.lastProgram[0])) { 546 | glUseProgram(props.lastProgram[0]); 547 | } 548 | glBindTexture(GL_TEXTURE_2D, props.lastTexture[0]); 549 | if (data.glVersion >= 330 || (data.glCapabilities != null && data.glCapabilities.GL_ARB_sampler_objects)) { 550 | glBindSampler(0, props.lastSampler[0]); 551 | } 552 | glActiveTexture(props.lastActiveTexture[0]); 553 | glBindVertexArray(props.lastVertexArrayObject[0]); 554 | glBindBuffer(GL_ARRAY_BUFFER, props.lastArrayBuffer[0]); 555 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, props.lastPixelUnpackBuffer[0]); 556 | glBlendEquationSeparate(props.lastBlendEquationRgb[0], props.lastBlendEquationAlpha[0]); 557 | glBlendFuncSeparate(props.lastBlendSrcRgb[0], props.lastBlendDstRgb[0], props.lastBlendSrcAlpha[0], props.lastBlendDstAlpha[0]); 558 | if (props.lastEnableBlend) glEnable(GL_BLEND); 559 | else glDisable(GL_BLEND); 560 | if (props.lastEnableCullFace) glEnable(GL_CULL_FACE); 561 | else glDisable(GL_CULL_FACE); 562 | if (props.lastEnableDepthTest) glEnable(GL_DEPTH_TEST); 563 | else glDisable(GL_DEPTH_TEST); 564 | if (props.lastEnableStencilTest) glEnable(GL_STENCIL_TEST); 565 | else glDisable(GL_STENCIL_TEST); 566 | if (props.lastEnableScissorTest) glEnable(GL_SCISSOR_TEST); 567 | else glDisable(GL_SCISSOR_TEST); 568 | if (data.glVersion >= 310) { 569 | if (props.lastEnablePrimitiveRestart) { 570 | glEnable(GL_PRIMITIVE_RESTART); 571 | } else { 572 | glDisable(GL_PRIMITIVE_RESTART); 573 | } 574 | } 575 | if (data.glVersion <= 310 || data.glProfileIsCompat) { 576 | glPolygonMode(GL_FRONT, props.lastPolygonMode[0]); 577 | glPolygonMode(GL_BACK, props.lastPolygonMode[1]); 578 | } else { 579 | glPolygonMode(GL_FRONT_AND_BACK, props.lastPolygonMode[0]); 580 | } 581 | glViewport(props.lastViewport[0], props.lastViewport[1], props.lastViewport[2], props.lastViewport[3]); 582 | glScissor(props.lastScissorBox[0], props.lastScissorBox[1], props.lastScissorBox[2], props.lastScissorBox[3]); 583 | } 584 | 585 | public boolean createFontsTexture() { 586 | final ImFontAtlas fontAtlas = ImGui.getIO().getFonts(); 587 | 588 | // Build texture atlas 589 | // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. 590 | // If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. 591 | final ImInt width = new ImInt(); 592 | final ImInt height = new ImInt(); 593 | final ByteBuffer pixels = fontAtlas.getTexDataAsRGBA32(width, height); 594 | 595 | final int[] lastTexture = new int[1]; 596 | glGetIntegerv(GL_TEXTURE_BINDING_2D, lastTexture); 597 | data.fontTexture = glGenTextures(); 598 | glBindTexture(GL_TEXTURE_2D, data.fontTexture); 599 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 600 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 601 | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // Not on WebGL/ES 602 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); // Not on WebGL/ES 603 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); // Not on WebGL/ES 604 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); // Not on WebGL/ES 605 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width.get(), height.get(), 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 606 | 607 | // Store our identifier 608 | fontAtlas.setTexID(data.fontTexture); 609 | 610 | glBindTexture(GL_TEXTURE_2D, lastTexture[0]); 611 | 612 | return true; 613 | } 614 | 615 | public void destroyFontsTexture() { 616 | final ImGuiIO io = ImGui.getIO(); 617 | if (data.fontTexture != 0) { 618 | glDeleteTextures(data.fontTexture); 619 | io.getFonts().setTexID(0); 620 | data.fontTexture = 0; 621 | } 622 | } 623 | 624 | protected boolean checkShader(final int handle, final String desc) { 625 | final int[] status = new int[1]; 626 | final int[] logLength = new int[1]; 627 | glGetShaderiv(handle, GL_COMPILE_STATUS, status); 628 | glGetShaderiv(handle, GL_INFO_LOG_LENGTH, logLength); 629 | if (status[0] == GL_FALSE) { 630 | System.err.printf("%s: failed to compile %s! With GLSL: %s\n", this, desc, data.glslVersion); 631 | } 632 | if (logLength[0] > 1) { 633 | final String log = glGetShaderInfoLog(handle); 634 | System.err.println(log); 635 | } 636 | return status[0] == GL_TRUE; 637 | } 638 | 639 | protected boolean checkProgram(final int handle, final String desc) { 640 | final int[] status = new int[1]; 641 | final int[] logLength = new int[1]; 642 | glGetProgramiv(handle, GL_LINK_STATUS, status); 643 | glGetProgramiv(handle, GL_INFO_LOG_LENGTH, logLength); 644 | if (status[0] == GL_FALSE) { 645 | System.err.printf("%s: failed to link %s! With GLSL: %s\n", this, desc, data.glslVersion); 646 | } 647 | if (logLength[0] > 1) { 648 | final String log = glGetProgramInfoLog(handle); 649 | System.err.println(log); 650 | } 651 | return status[0] == GL_TRUE; 652 | } 653 | 654 | protected int parseGlslVersionString(final String glslVersion) { 655 | final Pattern p = Pattern.compile("\\d+"); 656 | final Matcher m = p.matcher(glslVersion); 657 | 658 | if (m.find()) { 659 | return Integer.parseInt(m.group()); 660 | } 661 | 662 | return 130; 663 | } 664 | 665 | protected boolean createDeviceObjects() { 666 | // Backup GL state 667 | final int[] lastTexture = new int[1]; 668 | final int[] lastArrayBuffer = new int[1]; 669 | final int[] lastVertexArray = new int[1]; 670 | glGetIntegerv(GL_TEXTURE_BINDING_2D, lastTexture); 671 | glGetIntegerv(GL_ARRAY_BUFFER_BINDING, lastArrayBuffer); 672 | glGetIntegerv(GL_VERTEX_ARRAY_BINDING, lastVertexArray); 673 | 674 | final int glslVersionValue = parseGlslVersionString(data.glslVersion); 675 | 676 | // Select shaders matching our GLSL versions 677 | final CharSequence vertexShader; 678 | final CharSequence fragmentShader; 679 | 680 | if (glslVersionValue < 130) { 681 | vertexShader = vertexShaderGlsl120(); 682 | fragmentShader = fragmentShaderGlsl120(); 683 | } else if (glslVersionValue >= 410) { 684 | vertexShader = vertexShaderGlsl410Core(); 685 | fragmentShader = fragmentShaderGlsl410Core(); 686 | } else if (glslVersionValue == 300) { 687 | vertexShader = vertexShaderGlsl300es(); 688 | fragmentShader = fragmentShaderGlsl300es(); 689 | } else { 690 | vertexShader = vertexShaderGlsl130(); 691 | fragmentShader = fragmentShaderGlsl130(); 692 | } 693 | 694 | // Create shaders 695 | final int vertHandle = glCreateShader(GL_VERTEX_SHADER); 696 | glShaderSource(vertHandle, vertexShader); 697 | glCompileShader(vertHandle); 698 | checkShader(vertHandle, "vertex shader"); 699 | 700 | final int fragHandle = glCreateShader(GL_FRAGMENT_SHADER); 701 | glShaderSource(fragHandle, fragmentShader); 702 | glCompileShader(fragHandle); 703 | checkShader(fragHandle, "fragment shader"); 704 | 705 | // Link 706 | data.shaderHandle = glCreateProgram(); 707 | glAttachShader(data.shaderHandle, vertHandle); 708 | glAttachShader(data.shaderHandle, fragHandle); 709 | glLinkProgram(data.shaderHandle); 710 | checkProgram(data.shaderHandle, "shader program"); 711 | 712 | glDetachShader(data.shaderHandle, vertHandle); 713 | glDetachShader(data.shaderHandle, fragHandle); 714 | glDeleteShader(vertHandle); 715 | glDeleteShader(fragHandle); 716 | 717 | data.attribLocationTex = glGetUniformLocation(data.shaderHandle, "Texture"); 718 | data.attribLocationProjMtx = glGetUniformLocation(data.shaderHandle, "ProjMtx"); 719 | data.attribLocationVtxPos = glGetAttribLocation(data.shaderHandle, "Position"); 720 | data.attribLocationVtxUV = glGetAttribLocation(data.shaderHandle, "UV"); 721 | data.attribLocationVtxColor = glGetAttribLocation(data.shaderHandle, "Color"); 722 | 723 | // Create buffers 724 | data.vboHandle = glGenBuffers(); 725 | data.elementsHandle = glGenBuffers(); 726 | 727 | createFontsTexture(); 728 | 729 | // Restore modified GL state 730 | glBindTexture(GL_TEXTURE_2D, lastTexture[0]); 731 | glBindBuffer(GL_ARRAY_BUFFER, lastArrayBuffer[0]); 732 | glBindVertexArray(lastVertexArray[0]); 733 | 734 | return true; 735 | } 736 | 737 | public void destroyDeviceObjects() { 738 | if (data.vboHandle != 0) { 739 | glDeleteBuffers(data.vboHandle); 740 | data.vboHandle = 0; 741 | } 742 | if (data.elementsHandle != 0) { 743 | glDeleteBuffers(data.elementsHandle); 744 | data.elementsHandle = 0; 745 | } 746 | if (data.shaderHandle != 0) { 747 | glDeleteProgram(data.shaderHandle); 748 | data.shaderHandle = 0; 749 | } 750 | destroyFontsTexture(); 751 | } 752 | 753 | //-------------------------------------------------------------------------------------------------------- 754 | // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT 755 | // This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. 756 | // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. 757 | //-------------------------------------------------------------------------------------------------------- 758 | 759 | private final class RendererRenderWindowFunction extends ImPlatformFuncViewport { 760 | @Override 761 | public void accept(final ImGuiViewport vp) { 762 | if (!vp.hasFlags(ImGuiViewportFlags.NoRendererClear)) { 763 | glClearColor(0, 0, 0, 0); 764 | glClear(GL_COLOR_BUFFER_BIT); 765 | } 766 | renderDrawData(vp.getDrawData()); 767 | } 768 | } 769 | 770 | protected void initPlatformInterface() { 771 | ImGui.getPlatformIO().setRendererRenderWindow(new RendererRenderWindowFunction()); 772 | } 773 | 774 | protected void shutdownPlatformInterface() { 775 | ImGui.destroyPlatformWindows(); 776 | } 777 | 778 | protected String vertexShaderGlsl120() { 779 | return data.glslVersion + "\n" 780 | + "uniform mat4 ProjMtx;\n" 781 | + "attribute vec2 Position;\n" 782 | + "attribute vec2 UV;\n" 783 | + "attribute vec4 Color;\n" 784 | + "varying vec2 Frag_UV;\n" 785 | + "varying vec4 Frag_Color;\n" 786 | + "void main()\n" 787 | + "{\n" 788 | + " Frag_UV = UV;\n" 789 | + " Frag_Color = Color;\n" 790 | + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 791 | + "}\n"; 792 | } 793 | 794 | protected String vertexShaderGlsl130() { 795 | return data.glslVersion + "\n" 796 | + "uniform mat4 ProjMtx;\n" 797 | + "in vec2 Position;\n" 798 | + "in vec2 UV;\n" 799 | + "in vec4 Color;\n" 800 | + "out vec2 Frag_UV;\n" 801 | + "out vec4 Frag_Color;\n" 802 | + "void main()\n" 803 | + "{\n" 804 | + " Frag_UV = UV;\n" 805 | + " Frag_Color = Color;\n" 806 | + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 807 | + "}\n"; 808 | } 809 | 810 | private String vertexShaderGlsl300es() { 811 | return data.glslVersion + "\n" 812 | + "precision highp float;\n" 813 | + "layout (location = 0) in vec2 Position;\n" 814 | + "layout (location = 1) in vec2 UV;\n" 815 | + "layout (location = 2) in vec4 Color;\n" 816 | + "uniform mat4 ProjMtx;\n" 817 | + "out vec2 Frag_UV;\n" 818 | + "out vec4 Frag_Color;\n" 819 | + "void main()\n" 820 | + "{\n" 821 | + " Frag_UV = UV;\n" 822 | + " Frag_Color = Color;\n" 823 | + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 824 | + "}\n"; 825 | } 826 | 827 | protected String vertexShaderGlsl410Core() { 828 | return data.glslVersion + "\n" 829 | + "layout (location = 0) in vec2 Position;\n" 830 | + "layout (location = 1) in vec2 UV;\n" 831 | + "layout (location = 2) in vec4 Color;\n" 832 | + "uniform mat4 ProjMtx;\n" 833 | + "out vec2 Frag_UV;\n" 834 | + "out vec4 Frag_Color;\n" 835 | + "void main()\n" 836 | + "{\n" 837 | + " Frag_UV = UV;\n" 838 | + " Frag_Color = Color;\n" 839 | + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 840 | + "}\n"; 841 | } 842 | 843 | protected String fragmentShaderGlsl120() { 844 | return data.glslVersion + "\n" 845 | + "#ifdef GL_ES\n" 846 | + " precision mediump float;\n" 847 | + "#endif\n" 848 | + "uniform sampler2D Texture;\n" 849 | + "varying vec2 Frag_UV;\n" 850 | + "varying vec4 Frag_Color;\n" 851 | + "void main()\n" 852 | + "{\n" 853 | + " gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n" 854 | + "}\n"; 855 | } 856 | 857 | protected String fragmentShaderGlsl130() { 858 | return data.glslVersion + "\n" 859 | + "uniform sampler2D Texture;\n" 860 | + "in vec2 Frag_UV;\n" 861 | + "in vec4 Frag_Color;\n" 862 | + "out vec4 Out_Color;\n" 863 | + "void main()\n" 864 | + "{\n" 865 | + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" 866 | + "}\n"; 867 | } 868 | 869 | protected String fragmentShaderGlsl300es() { 870 | return data.glslVersion + "\n" 871 | + "precision mediump float;\n" 872 | + "uniform sampler2D Texture;\n" 873 | + "in vec2 Frag_UV;\n" 874 | + "in vec4 Frag_Color;\n" 875 | + "layout (location = 0) out vec4 Out_Color;\n" 876 | + "void main()\n" 877 | + "{\n" 878 | + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" 879 | + "}\n"; 880 | } 881 | 882 | protected String fragmentShaderGlsl410Core() { 883 | return data.glslVersion + "\n" 884 | + "in vec2 Frag_UV;\n" 885 | + "in vec4 Frag_Color;\n" 886 | + "uniform sampler2D Texture;\n" 887 | + "layout (location = 0) out vec4 Out_Color;\n" 888 | + "void main()\n" 889 | + "{\n" 890 | + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" 891 | + "}\n"; 892 | } 893 | } 894 | -------------------------------------------------------------------------------- /src/client/java/net/irisshaders/imgui/window/ImGuiImplGlfw.java: -------------------------------------------------------------------------------- 1 | package net.irisshaders.imgui.window; 2 | 3 | import imgui.ImGui; 4 | import imgui.ImGuiIO; 5 | import imgui.ImGuiPlatformIO; 6 | import imgui.ImGuiViewport; 7 | import imgui.ImVec2; 8 | import imgui.callback.ImPlatformFuncViewport; 9 | import imgui.callback.ImPlatformFuncViewportFloat; 10 | import imgui.callback.ImPlatformFuncViewportImVec2; 11 | import imgui.callback.ImPlatformFuncViewportString; 12 | import imgui.callback.ImPlatformFuncViewportSuppBoolean; 13 | import imgui.callback.ImPlatformFuncViewportSuppImVec2; 14 | import imgui.callback.ImStrConsumer; 15 | import imgui.callback.ImStrSupplier; 16 | import imgui.flag.ImGuiBackendFlags; 17 | import imgui.flag.ImGuiConfigFlags; 18 | import imgui.flag.ImGuiKey; 19 | import imgui.flag.ImGuiMouseButton; 20 | import imgui.flag.ImGuiMouseCursor; 21 | import imgui.flag.ImGuiViewportFlags; 22 | import imgui.lwjgl3.glfw.ImGuiImplGlfwNative; 23 | import org.lwjgl.PointerBuffer; 24 | import org.lwjgl.glfw.Callbacks; 25 | import org.lwjgl.glfw.GLFWCharCallback; 26 | import org.lwjgl.glfw.GLFWCursorEnterCallback; 27 | import org.lwjgl.glfw.GLFWCursorPosCallback; 28 | import org.lwjgl.glfw.GLFWErrorCallback; 29 | import org.lwjgl.glfw.GLFWGamepadState; 30 | import org.lwjgl.glfw.GLFWKeyCallback; 31 | import org.lwjgl.glfw.GLFWMonitorCallback; 32 | import org.lwjgl.glfw.GLFWMouseButtonCallback; 33 | import org.lwjgl.glfw.GLFWNativeCocoa; 34 | import org.lwjgl.glfw.GLFWNativeWin32; 35 | import org.lwjgl.glfw.GLFWScrollCallback; 36 | import org.lwjgl.glfw.GLFWVidMode; 37 | import org.lwjgl.glfw.GLFWWindowFocusCallback; 38 | import org.lwjgl.system.Callback; 39 | import org.lwjgl.system.MemoryUtil; 40 | import org.lwjgl.system.Platform; 41 | 42 | import java.nio.ByteBuffer; 43 | import java.nio.FloatBuffer; 44 | 45 | import static org.lwjgl.glfw.GLFW.GLFW_ARROW_CURSOR; 46 | import static org.lwjgl.glfw.GLFW.GLFW_CURSOR; 47 | import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_DISABLED; 48 | import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_HIDDEN; 49 | import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_NORMAL; 50 | import static org.lwjgl.glfw.GLFW.GLFW_DECORATED; 51 | import static org.lwjgl.glfw.GLFW.GLFW_FALSE; 52 | import static org.lwjgl.glfw.GLFW.GLFW_FLOATING; 53 | import static org.lwjgl.glfw.GLFW.GLFW_FOCUSED; 54 | import static org.lwjgl.glfw.GLFW.GLFW_FOCUS_ON_SHOW; 55 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER; 56 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_LEFT_X; 57 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_LEFT_Y; 58 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER; 59 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_X; 60 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_AXIS_RIGHT_Y; 61 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_A; 62 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_B; 63 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_BACK; 64 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN; 65 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT; 66 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT; 67 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP; 68 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER; 69 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_LEFT_THUMB; 70 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER; 71 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB; 72 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_START; 73 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_X; 74 | import static org.lwjgl.glfw.GLFW.GLFW_GAMEPAD_BUTTON_Y; 75 | import static org.lwjgl.glfw.GLFW.GLFW_HAND_CURSOR; 76 | import static org.lwjgl.glfw.GLFW.GLFW_HOVERED; 77 | import static org.lwjgl.glfw.GLFW.GLFW_HRESIZE_CURSOR; 78 | import static org.lwjgl.glfw.GLFW.GLFW_IBEAM_CURSOR; 79 | import static org.lwjgl.glfw.GLFW.GLFW_ICONIFIED; 80 | import static org.lwjgl.glfw.GLFW.GLFW_JOYSTICK_1; 81 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_0; 82 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_1; 83 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_2; 84 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_3; 85 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_4; 86 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_5; 87 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_6; 88 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_7; 89 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_8; 90 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_9; 91 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_A; 92 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_APOSTROPHE; 93 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_B; 94 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_BACKSLASH; 95 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_BACKSPACE; 96 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_C; 97 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_CAPS_LOCK; 98 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_COMMA; 99 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_D; 100 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_DELETE; 101 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_DOWN; 102 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_E; 103 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_END; 104 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_ENTER; 105 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_EQUAL; 106 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_ESCAPE; 107 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F; 108 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F1; 109 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F10; 110 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F11; 111 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F12; 112 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F2; 113 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F3; 114 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F4; 115 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F5; 116 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F6; 117 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F7; 118 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F8; 119 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_F9; 120 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_G; 121 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_GRAVE_ACCENT; 122 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_H; 123 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_HOME; 124 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_I; 125 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_INSERT; 126 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_J; 127 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_K; 128 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_0; 129 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_1; 130 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_2; 131 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_3; 132 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_4; 133 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_5; 134 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_6; 135 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_7; 136 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_8; 137 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_9; 138 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_ADD; 139 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_DECIMAL; 140 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_DIVIDE; 141 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_ENTER; 142 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_EQUAL; 143 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_MULTIPLY; 144 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_KP_SUBTRACT; 145 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_L; 146 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_LAST; 147 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT; 148 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_ALT; 149 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_BRACKET; 150 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_CONTROL; 151 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_SHIFT; 152 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_SUPER; 153 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_M; 154 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_MENU; 155 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_MINUS; 156 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_N; 157 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_NUM_LOCK; 158 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_O; 159 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_P; 160 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_PAGE_DOWN; 161 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_PAGE_UP; 162 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_PAUSE; 163 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_PERIOD; 164 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_PRINT_SCREEN; 165 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_Q; 166 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_R; 167 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT; 168 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_ALT; 169 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_BRACKET; 170 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_CONTROL; 171 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_SHIFT; 172 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_SUPER; 173 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_S; 174 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_SCROLL_LOCK; 175 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_SEMICOLON; 176 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_SLASH; 177 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_SPACE; 178 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_T; 179 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_TAB; 180 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_U; 181 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_UP; 182 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_V; 183 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_W; 184 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_X; 185 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_Y; 186 | import static org.lwjgl.glfw.GLFW.GLFW_KEY_Z; 187 | import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_PASSTHROUGH; 188 | import static org.lwjgl.glfw.GLFW.GLFW_NOT_ALLOWED_CURSOR; 189 | import static org.lwjgl.glfw.GLFW.GLFW_PRESS; 190 | import static org.lwjgl.glfw.GLFW.GLFW_RELEASE; 191 | import static org.lwjgl.glfw.GLFW.GLFW_RESIZE_ALL_CURSOR; 192 | import static org.lwjgl.glfw.GLFW.GLFW_RESIZE_NESW_CURSOR; 193 | import static org.lwjgl.glfw.GLFW.GLFW_RESIZE_NWSE_CURSOR; 194 | import static org.lwjgl.glfw.GLFW.GLFW_TRUE; 195 | import static org.lwjgl.glfw.GLFW.GLFW_VERSION_MAJOR; 196 | import static org.lwjgl.glfw.GLFW.GLFW_VERSION_MINOR; 197 | import static org.lwjgl.glfw.GLFW.GLFW_VERSION_REVISION; 198 | import static org.lwjgl.glfw.GLFW.GLFW_VISIBLE; 199 | import static org.lwjgl.glfw.GLFW.GLFW_VRESIZE_CURSOR; 200 | import static org.lwjgl.glfw.GLFW.glfwCreateStandardCursor; 201 | import static org.lwjgl.glfw.GLFW.glfwCreateWindow; 202 | import static org.lwjgl.glfw.GLFW.glfwDestroyCursor; 203 | import static org.lwjgl.glfw.GLFW.glfwDestroyWindow; 204 | import static org.lwjgl.glfw.GLFW.glfwFocusWindow; 205 | import static org.lwjgl.glfw.GLFW.glfwGetClipboardString; 206 | import static org.lwjgl.glfw.GLFW.glfwGetCursorPos; 207 | import static org.lwjgl.glfw.GLFW.glfwGetError; 208 | import static org.lwjgl.glfw.GLFW.glfwGetFramebufferSize; 209 | import static org.lwjgl.glfw.GLFW.glfwGetGamepadState; 210 | import static org.lwjgl.glfw.GLFW.glfwGetInputMode; 211 | import static org.lwjgl.glfw.GLFW.glfwGetJoystickAxes; 212 | import static org.lwjgl.glfw.GLFW.glfwGetJoystickButtons; 213 | import static org.lwjgl.glfw.GLFW.glfwGetKey; 214 | import static org.lwjgl.glfw.GLFW.glfwGetKeyName; 215 | import static org.lwjgl.glfw.GLFW.glfwGetMonitorContentScale; 216 | import static org.lwjgl.glfw.GLFW.glfwGetMonitorPos; 217 | import static org.lwjgl.glfw.GLFW.glfwGetMonitorWorkarea; 218 | import static org.lwjgl.glfw.GLFW.glfwGetMonitors; 219 | import static org.lwjgl.glfw.GLFW.glfwGetTime; 220 | import static org.lwjgl.glfw.GLFW.glfwGetVideoMode; 221 | import static org.lwjgl.glfw.GLFW.glfwGetWindowAttrib; 222 | import static org.lwjgl.glfw.GLFW.glfwGetWindowPos; 223 | import static org.lwjgl.glfw.GLFW.glfwGetWindowSize; 224 | import static org.lwjgl.glfw.GLFW.glfwMakeContextCurrent; 225 | import static org.lwjgl.glfw.GLFW.glfwSetCharCallback; 226 | import static org.lwjgl.glfw.GLFW.glfwSetClipboardString; 227 | import static org.lwjgl.glfw.GLFW.glfwSetCursor; 228 | import static org.lwjgl.glfw.GLFW.glfwSetCursorEnterCallback; 229 | import static org.lwjgl.glfw.GLFW.glfwSetCursorPos; 230 | import static org.lwjgl.glfw.GLFW.glfwSetCursorPosCallback; 231 | import static org.lwjgl.glfw.GLFW.glfwSetErrorCallback; 232 | import static org.lwjgl.glfw.GLFW.glfwSetInputMode; 233 | import static org.lwjgl.glfw.GLFW.glfwSetKeyCallback; 234 | import static org.lwjgl.glfw.GLFW.glfwSetMonitorCallback; 235 | import static org.lwjgl.glfw.GLFW.glfwSetMouseButtonCallback; 236 | import static org.lwjgl.glfw.GLFW.glfwSetScrollCallback; 237 | import static org.lwjgl.glfw.GLFW.glfwSetWindowAttrib; 238 | import static org.lwjgl.glfw.GLFW.glfwSetWindowCloseCallback; 239 | import static org.lwjgl.glfw.GLFW.glfwSetWindowFocusCallback; 240 | import static org.lwjgl.glfw.GLFW.glfwSetWindowOpacity; 241 | import static org.lwjgl.glfw.GLFW.glfwSetWindowPos; 242 | import static org.lwjgl.glfw.GLFW.glfwSetWindowPosCallback; 243 | import static org.lwjgl.glfw.GLFW.glfwSetWindowSize; 244 | import static org.lwjgl.glfw.GLFW.glfwSetWindowSizeCallback; 245 | import static org.lwjgl.glfw.GLFW.glfwSetWindowTitle; 246 | import static org.lwjgl.glfw.GLFW.glfwShowWindow; 247 | import static org.lwjgl.glfw.GLFW.glfwSwapBuffers; 248 | import static org.lwjgl.glfw.GLFW.glfwSwapInterval; 249 | import static org.lwjgl.glfw.GLFW.glfwWindowHint; 250 | import static org.lwjgl.system.MemoryUtil.NULL; 251 | 252 | /** 253 | * This class is a straightforward port of the 254 | * imgui_impl_glfw.cpp. 255 | *

256 | * It supports clipboard, gamepad, mouse and keyboard in the same way the original Dear ImGui code does. You can copy-paste this class in your codebase and 257 | * modify the rendering routine in the way you'd like. 258 | */ 259 | @SuppressWarnings({"checkstyle:DesignForExtension", "checkstyle:NeedBraces", "checkstyle:LocalVariableName", "checkstyle:FinalLocalVariable", "checkstyle:ParameterName", "checkstyle:EmptyBlock", "checkstyle:AvoidNestedBlocks"}) 260 | public class ImGuiImplGlfw { 261 | protected static final String OS = System.getProperty("os.name", "generic").toLowerCase(); 262 | protected static final boolean IS_WINDOWS = OS.contains("win"); 263 | protected static final boolean IS_APPLE = OS.contains("mac") || OS.contains("darwin"); 264 | 265 | /** 266 | * Data class to store implementation specific fields. 267 | * Same as {@code ImGui_ImplGlfw_Data}. 268 | */ 269 | protected static class Data { 270 | protected long window = -1; 271 | protected double time = 0.0; 272 | protected long mouseWindow = -1; 273 | boolean MouseIgnoreButtonUpWaitForFocusLoss; 274 | boolean MouseIgnoreButtonUp; 275 | protected long[] mouseCursors = new long[ImGuiMouseCursor.COUNT]; 276 | protected ImVec2 lastValidMousePos = new ImVec2(); 277 | protected long[] keyOwnerWindows = new long[GLFW_KEY_LAST]; 278 | protected boolean installedCallbacks = false; 279 | protected boolean callbacksChainForAllWindows = false; 280 | protected boolean wantUpdateMonitors = true; 281 | 282 | // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. 283 | protected GLFWWindowFocusCallback prevUserCallbackWindowFocus = null; 284 | protected GLFWCursorPosCallback prevUserCallbackCursorPos = null; 285 | protected GLFWCursorEnterCallback prevUserCallbackCursorEnter = null; 286 | protected GLFWMouseButtonCallback prevUserCallbackMousebutton = null; 287 | protected GLFWScrollCallback prevUserCallbackScroll = null; 288 | protected GLFWKeyCallback prevUserCallbackKey = null; 289 | protected GLFWCharCallback prevUserCallbackChar = null; 290 | protected GLFWMonitorCallback prevUserCallbackMonitor = null; 291 | 292 | // This field is required to use GLFW with touch screens on Windows. 293 | // For compatibility reasons it was added here as a comment. But we don't use somewhere in the binding implementation. 294 | // protected long glfwWndProc; 295 | } 296 | 297 | /** 298 | * Internal class to store containers for frequently used arrays. 299 | * This class helps minimize the number of object allocations on the JVM side, 300 | * thereby improving performance and reducing garbage collection overhead. 301 | */ 302 | private static final class Properties { 303 | private final int[] windowW = new int[1]; 304 | private final int[] windowH = new int[1]; 305 | private final int[] windowX = new int[1]; 306 | private final int[] windowY = new int[1]; 307 | private final int[] displayW = new int[1]; 308 | private final int[] displayH = new int[1]; 309 | 310 | // For mouse tracking 311 | private final ImVec2 mousePosPrev = new ImVec2(); 312 | private final double[] mouseX = new double[1]; 313 | private final double[] mouseY = new double[1]; 314 | 315 | // Monitor properties 316 | private final int[] monitorX = new int[1]; 317 | private final int[] monitorY = new int[1]; 318 | private final int[] monitorWorkAreaX = new int[1]; 319 | private final int[] monitorWorkAreaY = new int[1]; 320 | private final int[] monitorWorkAreaWidth = new int[1]; 321 | private final int[] monitorWorkAreaHeight = new int[1]; 322 | private final float[] monitorContentScaleX = new float[1]; 323 | private final float[] monitorContentScaleY = new float[1]; 324 | 325 | // For char translation 326 | private final String charNames = "`-=[]\\,;'./"; 327 | private final int[] charKeys = { 328 | GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, 329 | GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, 330 | GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH 331 | }; 332 | } 333 | 334 | protected Data data = null; 335 | private final Properties props = new Properties(); 336 | 337 | // We gather version tests as define in order to easily see which features are version-dependent. 338 | protected static final int glfwVersionCombined = GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION; 339 | protected static final boolean glfwHawWindowTopmost = glfwVersionCombined >= 3200; // 3.2+ GLFW_FLOATING 340 | protected static final boolean glfwHasWindowHovered = glfwVersionCombined >= 3300; // 3.3+ GLFW_HOVERED 341 | protected static final boolean glfwHasWindowAlpha = glfwVersionCombined >= 3300; // 3.3+ glfwSetWindowOpacity 342 | protected static final boolean glfwHasPerMonitorDpi = glfwVersionCombined >= 3300; // 3.3+ glfwGetMonitorContentScale 343 | // protected boolean glfwHasVulkan; TODO: I want to believe... 344 | protected static final boolean glfwHasFocusWindow = glfwVersionCombined >= 3200; // 3.2+ glfwFocusWindow 345 | protected static final boolean glfwHasFocusOnShow = glfwVersionCombined >= 3300; // 3.3+ GLFW_FOCUS_ON_SHOW 346 | protected static final boolean glfwHasMonitorWorkArea = glfwVersionCombined >= 3300; // 3.3+ glfwGetMonitorWorkarea 347 | protected static final boolean glfwHasOsxWindowPosFix = glfwVersionCombined >= 3301; // 3.3.1+ Fixed: Resizing window repositions it on MacOS #1553 348 | protected static final boolean glfwHasNewCursors = glfwVersionCombined >= 3400; // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR 349 | protected static final boolean glfwHasMousePassthrough = glfwVersionCombined >= 3400; // 3.4+ GLFW_MOUSE_PASSTHROUGH 350 | protected static final boolean glfwHasGamepadApi = glfwVersionCombined >= 3300; // 3.3+ glfwGetGamepadState() new api 351 | protected static final boolean glfwHasGetKeyName = glfwVersionCombined >= 3200; // 3.2+ glfwGetKeyName() 352 | protected static final boolean glfwHasGetError = glfwVersionCombined >= 3300; // 3.3+ glfwGetError() 353 | 354 | protected ImStrSupplier getClipboardTextFn() { 355 | return new ImStrSupplier() { 356 | @Override 357 | public String get() { 358 | final String clipboardString = glfwGetClipboardString(data.window); 359 | return clipboardString != null ? clipboardString : ""; 360 | } 361 | }; 362 | } 363 | 364 | protected ImStrConsumer setClipboardTextFn() { 365 | return new ImStrConsumer() { 366 | @Override 367 | public void accept(final String text) { 368 | glfwSetClipboardString(data.window, text); 369 | } 370 | }; 371 | } 372 | 373 | protected int glfwKeyToImGuiKey(final int glfwKey) { 374 | switch (glfwKey) { 375 | case GLFW_KEY_TAB: 376 | return ImGuiKey.Tab; 377 | case GLFW_KEY_LEFT: 378 | return ImGuiKey.LeftArrow; 379 | case GLFW_KEY_RIGHT: 380 | return ImGuiKey.RightArrow; 381 | case GLFW_KEY_UP: 382 | return ImGuiKey.UpArrow; 383 | case GLFW_KEY_DOWN: 384 | return ImGuiKey.DownArrow; 385 | case GLFW_KEY_PAGE_UP: 386 | return ImGuiKey.PageUp; 387 | case GLFW_KEY_PAGE_DOWN: 388 | return ImGuiKey.PageDown; 389 | case GLFW_KEY_HOME: 390 | return ImGuiKey.Home; 391 | case GLFW_KEY_END: 392 | return ImGuiKey.End; 393 | case GLFW_KEY_INSERT: 394 | return ImGuiKey.Insert; 395 | case GLFW_KEY_DELETE: 396 | return ImGuiKey.Delete; 397 | case GLFW_KEY_BACKSPACE: 398 | return ImGuiKey.Backspace; 399 | case GLFW_KEY_SPACE: 400 | return ImGuiKey.Space; 401 | case GLFW_KEY_ENTER: 402 | return ImGuiKey.Enter; 403 | case GLFW_KEY_ESCAPE: 404 | return ImGuiKey.Escape; 405 | case GLFW_KEY_APOSTROPHE: 406 | return ImGuiKey.Apostrophe; 407 | case GLFW_KEY_COMMA: 408 | return ImGuiKey.Comma; 409 | case GLFW_KEY_MINUS: 410 | return ImGuiKey.Minus; 411 | case GLFW_KEY_PERIOD: 412 | return ImGuiKey.Period; 413 | case GLFW_KEY_SLASH: 414 | return ImGuiKey.Slash; 415 | case GLFW_KEY_SEMICOLON: 416 | return ImGuiKey.Semicolon; 417 | case GLFW_KEY_EQUAL: 418 | return ImGuiKey.Equal; 419 | case GLFW_KEY_LEFT_BRACKET: 420 | return ImGuiKey.LeftBracket; 421 | case GLFW_KEY_BACKSLASH: 422 | return ImGuiKey.Backslash; 423 | case GLFW_KEY_RIGHT_BRACKET: 424 | return ImGuiKey.RightBracket; 425 | case GLFW_KEY_GRAVE_ACCENT: 426 | return ImGuiKey.GraveAccent; 427 | case GLFW_KEY_CAPS_LOCK: 428 | return ImGuiKey.CapsLock; 429 | case GLFW_KEY_SCROLL_LOCK: 430 | return ImGuiKey.ScrollLock; 431 | case GLFW_KEY_NUM_LOCK: 432 | return ImGuiKey.NumLock; 433 | case GLFW_KEY_PRINT_SCREEN: 434 | return ImGuiKey.PrintScreen; 435 | case GLFW_KEY_PAUSE: 436 | return ImGuiKey.Pause; 437 | case GLFW_KEY_KP_0: 438 | return ImGuiKey.Keypad0; 439 | case GLFW_KEY_KP_1: 440 | return ImGuiKey.Keypad1; 441 | case GLFW_KEY_KP_2: 442 | return ImGuiKey.Keypad2; 443 | case GLFW_KEY_KP_3: 444 | return ImGuiKey.Keypad3; 445 | case GLFW_KEY_KP_4: 446 | return ImGuiKey.Keypad4; 447 | case GLFW_KEY_KP_5: 448 | return ImGuiKey.Keypad5; 449 | case GLFW_KEY_KP_6: 450 | return ImGuiKey.Keypad6; 451 | case GLFW_KEY_KP_7: 452 | return ImGuiKey.Keypad7; 453 | case GLFW_KEY_KP_8: 454 | return ImGuiKey.Keypad8; 455 | case GLFW_KEY_KP_9: 456 | return ImGuiKey.Keypad9; 457 | case GLFW_KEY_KP_DECIMAL: 458 | return ImGuiKey.KeypadDecimal; 459 | case GLFW_KEY_KP_DIVIDE: 460 | return ImGuiKey.KeypadDivide; 461 | case GLFW_KEY_KP_MULTIPLY: 462 | return ImGuiKey.KeypadMultiply; 463 | case GLFW_KEY_KP_SUBTRACT: 464 | return ImGuiKey.KeypadSubtract; 465 | case GLFW_KEY_KP_ADD: 466 | return ImGuiKey.KeypadAdd; 467 | case GLFW_KEY_KP_ENTER: 468 | return ImGuiKey.KeypadEnter; 469 | case GLFW_KEY_KP_EQUAL: 470 | return ImGuiKey.KeypadEqual; 471 | case GLFW_KEY_LEFT_SHIFT: 472 | return ImGuiKey.LeftShift; 473 | case GLFW_KEY_LEFT_CONTROL: 474 | return ImGuiKey.LeftCtrl; 475 | case GLFW_KEY_LEFT_ALT: 476 | return ImGuiKey.LeftAlt; 477 | case GLFW_KEY_LEFT_SUPER: 478 | return ImGuiKey.LeftSuper; 479 | case GLFW_KEY_RIGHT_SHIFT: 480 | return ImGuiKey.RightShift; 481 | case GLFW_KEY_RIGHT_CONTROL: 482 | return ImGuiKey.RightCtrl; 483 | case GLFW_KEY_RIGHT_ALT: 484 | return ImGuiKey.RightAlt; 485 | case GLFW_KEY_RIGHT_SUPER: 486 | return ImGuiKey.RightSuper; 487 | case GLFW_KEY_MENU: 488 | return ImGuiKey.Menu; 489 | case GLFW_KEY_0: 490 | return ImGuiKey._0; 491 | case GLFW_KEY_1: 492 | return ImGuiKey._1; 493 | case GLFW_KEY_2: 494 | return ImGuiKey._2; 495 | case GLFW_KEY_3: 496 | return ImGuiKey._3; 497 | case GLFW_KEY_4: 498 | return ImGuiKey._4; 499 | case GLFW_KEY_5: 500 | return ImGuiKey._5; 501 | case GLFW_KEY_6: 502 | return ImGuiKey._6; 503 | case GLFW_KEY_7: 504 | return ImGuiKey._7; 505 | case GLFW_KEY_8: 506 | return ImGuiKey._8; 507 | case GLFW_KEY_9: 508 | return ImGuiKey._9; 509 | case GLFW_KEY_A: 510 | return ImGuiKey.A; 511 | case GLFW_KEY_B: 512 | return ImGuiKey.B; 513 | case GLFW_KEY_C: 514 | return ImGuiKey.C; 515 | case GLFW_KEY_D: 516 | return ImGuiKey.D; 517 | case GLFW_KEY_E: 518 | return ImGuiKey.E; 519 | case GLFW_KEY_F: 520 | return ImGuiKey.F; 521 | case GLFW_KEY_G: 522 | return ImGuiKey.G; 523 | case GLFW_KEY_H: 524 | return ImGuiKey.H; 525 | case GLFW_KEY_I: 526 | return ImGuiKey.I; 527 | case GLFW_KEY_J: 528 | return ImGuiKey.J; 529 | case GLFW_KEY_K: 530 | return ImGuiKey.K; 531 | case GLFW_KEY_L: 532 | return ImGuiKey.L; 533 | case GLFW_KEY_M: 534 | return ImGuiKey.M; 535 | case GLFW_KEY_N: 536 | return ImGuiKey.N; 537 | case GLFW_KEY_O: 538 | return ImGuiKey.O; 539 | case GLFW_KEY_P: 540 | return ImGuiKey.P; 541 | case GLFW_KEY_Q: 542 | return ImGuiKey.Q; 543 | case GLFW_KEY_R: 544 | return ImGuiKey.R; 545 | case GLFW_KEY_S: 546 | return ImGuiKey.S; 547 | case GLFW_KEY_T: 548 | return ImGuiKey.T; 549 | case GLFW_KEY_U: 550 | return ImGuiKey.U; 551 | case GLFW_KEY_V: 552 | return ImGuiKey.V; 553 | case GLFW_KEY_W: 554 | return ImGuiKey.W; 555 | case GLFW_KEY_X: 556 | return ImGuiKey.X; 557 | case GLFW_KEY_Y: 558 | return ImGuiKey.Y; 559 | case GLFW_KEY_Z: 560 | return ImGuiKey.Z; 561 | case GLFW_KEY_F1: 562 | return ImGuiKey.F1; 563 | case GLFW_KEY_F2: 564 | return ImGuiKey.F2; 565 | case GLFW_KEY_F3: 566 | return ImGuiKey.F3; 567 | case GLFW_KEY_F4: 568 | return ImGuiKey.F4; 569 | case GLFW_KEY_F5: 570 | return ImGuiKey.F5; 571 | case GLFW_KEY_F6: 572 | return ImGuiKey.F6; 573 | case GLFW_KEY_F7: 574 | return ImGuiKey.F7; 575 | case GLFW_KEY_F8: 576 | return ImGuiKey.F8; 577 | case GLFW_KEY_F9: 578 | return ImGuiKey.F9; 579 | case GLFW_KEY_F10: 580 | return ImGuiKey.F10; 581 | case GLFW_KEY_F11: 582 | return ImGuiKey.F11; 583 | case GLFW_KEY_F12: 584 | return ImGuiKey.F12; 585 | default: 586 | return ImGuiKey.None; 587 | } 588 | } 589 | 590 | // X11 does not include current pressed/released modifier key in 'mods' flags submitted by GLFW 591 | // See https://github.com/ocornut/imgui/issues/6034 and https://github.com/glfw/glfw/issues/1630 592 | protected void updateKeyModifiers(final long window) { 593 | final ImGuiIO io = ImGui.getIO(); 594 | io.addKeyEvent(ImGuiKey.ModCtrl, (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)); 595 | io.addKeyEvent(ImGuiKey.ModShift, (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)); 596 | io.addKeyEvent(ImGuiKey.ModAlt, (glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)); 597 | io.addKeyEvent(ImGuiKey.ModSuper, (glfwGetKey(window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)); 598 | } 599 | 600 | protected boolean shouldChainCallback(final long window) { 601 | return data.callbacksChainForAllWindows ? true : (window == data.window); 602 | } 603 | 604 | public void mouseButtonCallback(final long window, final int button, final int action, final int mods) { 605 | if (data.prevUserCallbackMousebutton != null && shouldChainCallback(window)) { 606 | data.prevUserCallbackMousebutton.invoke(window, button, action, mods); 607 | } 608 | 609 | // Workaround for Linux: ignore mouse up events which are following an focus loss following a viewport creation 610 | if (data.MouseIgnoreButtonUp && action == GLFW_RELEASE) 611 | return; 612 | 613 | updateKeyModifiers(window); 614 | 615 | final ImGuiIO io = ImGui.getIO(); 616 | if (button >= 0 && button < ImGuiMouseButton.COUNT) { 617 | io.addMouseButtonEvent(button, action == GLFW_PRESS); 618 | } 619 | } 620 | 621 | public void scrollCallback(final long window, final double xOffset, final double yOffset) { 622 | if (data.prevUserCallbackScroll != null && shouldChainCallback(window)) { 623 | data.prevUserCallbackScroll.invoke(window, xOffset, yOffset); 624 | } 625 | 626 | final ImGuiIO io = ImGui.getIO(); 627 | io.addMouseWheelEvent((float) xOffset, (float) yOffset); 628 | } 629 | 630 | protected int translateUntranslatedKey(final int key, final int scancode) { 631 | if (!glfwHasGetKeyName) { 632 | return key; 633 | } 634 | 635 | // GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult. 636 | // (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently) 637 | // See https://github.com/glfw/glfw/issues/1502 for details. 638 | // Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process). 639 | // This won't cover edge cases but this is at least going to cover common cases. 640 | if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL) { 641 | return key; 642 | } 643 | 644 | int resultKey = key; 645 | final String keyName = glfwGetKeyName(key, scancode); 646 | eatErrors(); 647 | if (keyName != null && keyName.length() > 2 && keyName.charAt(0) != 0 && keyName.charAt(1) == 0) { 648 | if (keyName.charAt(0) >= '0' && keyName.charAt(0) <= '9') { 649 | resultKey = GLFW_KEY_0 + (keyName.charAt(0) - '0'); 650 | } else if (keyName.charAt(0) >= 'A' && keyName.charAt(0) <= 'Z') { 651 | resultKey = GLFW_KEY_A + (keyName.charAt(0) - 'A'); 652 | } else if (keyName.charAt(0) >= 'a' && keyName.charAt(0) <= 'z') { 653 | resultKey = GLFW_KEY_A + (keyName.charAt(0) - 'a'); 654 | } else { 655 | final int index = props.charNames.indexOf(keyName.charAt(0)); 656 | if (index != -1) { 657 | resultKey = props.charKeys[index]; 658 | } 659 | } 660 | } 661 | 662 | return resultKey; 663 | } 664 | 665 | protected void eatErrors() { 666 | if (glfwHasGetError) { // Eat errors (see #5908) 667 | final PointerBuffer pb = MemoryUtil.memAllocPointer(1); 668 | glfwGetError(pb); 669 | MemoryUtil.memFree(pb); 670 | } 671 | } 672 | 673 | public void keyCallback(final long window, final int keycode, final int scancode, final int action, final int mods) { 674 | if (data.prevUserCallbackKey != null && shouldChainCallback(window)) { 675 | data.prevUserCallbackKey.invoke(window, keycode, scancode, action, mods); 676 | } 677 | 678 | if (action != GLFW_PRESS && action != GLFW_RELEASE) { 679 | return; 680 | } 681 | 682 | updateKeyModifiers(window); 683 | 684 | if (keycode >= 0 && keycode < data.keyOwnerWindows.length) { 685 | data.keyOwnerWindows[keycode] = (action == GLFW_PRESS) ? window : -1; 686 | } 687 | 688 | final int key = translateUntranslatedKey(keycode, scancode); 689 | 690 | final ImGuiIO io = ImGui.getIO(); 691 | final int imguiKey = glfwKeyToImGuiKey(key); 692 | io.addKeyEvent(imguiKey, (action == GLFW_PRESS)); 693 | io.setKeyEventNativeData(imguiKey, key, scancode); // To support legacy indexing (<1.87 user code) 694 | } 695 | 696 | public void windowFocusCallback(final long window, final boolean focused) { 697 | if (data.prevUserCallbackWindowFocus != null && shouldChainCallback(window)) { 698 | data.prevUserCallbackWindowFocus.invoke(window, focused); 699 | } 700 | 701 | // Workaround for Linux: when losing focus with MouseIgnoreButtonUpWaitForFocusLoss set, we will temporarily ignore subsequent Mouse Up events 702 | data.MouseIgnoreButtonUp = (data.MouseIgnoreButtonUpWaitForFocusLoss && !focused); 703 | data.MouseIgnoreButtonUpWaitForFocusLoss = false; 704 | 705 | ImGui.getIO().addFocusEvent(focused); 706 | } 707 | 708 | public void cursorPosCallback(final long window, final double x, final double y) { 709 | if (data.prevUserCallbackCursorPos != null && shouldChainCallback(window)) { 710 | data.prevUserCallbackCursorPos.invoke(window, x, y); 711 | } 712 | 713 | float posX = (float) x; 714 | float posY = (float) y; 715 | 716 | final ImGuiIO io = ImGui.getIO(); 717 | 718 | if (io.hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) { 719 | glfwGetWindowPos(window, props.windowX, props.windowY); 720 | posX += props.windowX[0]; 721 | posY += props.windowY[0]; 722 | } 723 | 724 | io.addMousePosEvent(posX, posY); 725 | data.lastValidMousePos.set(posX, posY); 726 | } 727 | 728 | // Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position, 729 | // so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984) 730 | public void cursorEnterCallback(final long window, final boolean entered) { 731 | if (data.prevUserCallbackCursorEnter != null && shouldChainCallback(window)) { 732 | data.prevUserCallbackCursorEnter.invoke(window, entered); 733 | } 734 | 735 | final ImGuiIO io = ImGui.getIO(); 736 | 737 | if (entered) { 738 | data.mouseWindow = window; 739 | io.addMousePosEvent(data.lastValidMousePos.x, data.lastValidMousePos.y); 740 | } else if (data.mouseWindow == window) { 741 | io.getMousePos(data.lastValidMousePos); 742 | data.mouseWindow = -1; 743 | io.addMousePosEvent(Float.MIN_VALUE, Float.MIN_VALUE); 744 | } 745 | } 746 | 747 | public void charCallback(final long window, final int c) { 748 | if (data.prevUserCallbackChar != null && shouldChainCallback(window)) { 749 | data.prevUserCallbackChar.invoke(window, c); 750 | } 751 | 752 | ImGui.getIO().addInputCharacter(c); 753 | } 754 | 755 | public void monitorCallback(final long window, final int event) { 756 | data.wantUpdateMonitors = true; 757 | } 758 | 759 | public void installCallbacks(final long window) { 760 | data.prevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, this::windowFocusCallback); 761 | data.prevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, this::cursorEnterCallback); 762 | data.prevUserCallbackCursorPos = glfwSetCursorPosCallback(window, this::cursorPosCallback); 763 | data.prevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, this::mouseButtonCallback); 764 | data.prevUserCallbackScroll = glfwSetScrollCallback(window, this::scrollCallback); 765 | data.prevUserCallbackKey = glfwSetKeyCallback(window, this::keyCallback); 766 | data.prevUserCallbackChar = glfwSetCharCallback(window, this::charCallback); 767 | data.prevUserCallbackMonitor = glfwSetMonitorCallback(this::monitorCallback); 768 | data.installedCallbacks = true; 769 | } 770 | 771 | protected void freeCallback(final Callback cb) { 772 | if (cb != null) { 773 | cb.free(); 774 | } 775 | } 776 | 777 | public void restoreCallbacks(final long window) { 778 | freeCallback(glfwSetWindowFocusCallback(window, data.prevUserCallbackWindowFocus)); 779 | freeCallback(glfwSetCursorEnterCallback(window, data.prevUserCallbackCursorEnter)); 780 | freeCallback(glfwSetCursorPosCallback(window, data.prevUserCallbackCursorPos)); 781 | freeCallback(glfwSetMouseButtonCallback(window, data.prevUserCallbackMousebutton)); 782 | freeCallback(glfwSetScrollCallback(window, data.prevUserCallbackScroll)); 783 | freeCallback(glfwSetKeyCallback(window, data.prevUserCallbackKey)); 784 | freeCallback(glfwSetCharCallback(window, data.prevUserCallbackChar)); 785 | freeCallback(glfwSetMonitorCallback(data.prevUserCallbackMonitor)); 786 | data.installedCallbacks = false; 787 | data.prevUserCallbackWindowFocus = null; 788 | data.prevUserCallbackCursorEnter = null; 789 | data.prevUserCallbackCursorPos = null; 790 | data.prevUserCallbackMousebutton = null; 791 | data.prevUserCallbackScroll = null; 792 | data.prevUserCallbackKey = null; 793 | data.prevUserCallbackChar = null; 794 | data.prevUserCallbackMonitor = null; 795 | } 796 | 797 | /** 798 | * Set to 'true' to enable chaining installed callbacks for all windows (including secondary viewports created by backends or by user. 799 | * This is 'false' by default meaning we only chain callbacks for the main viewport. 800 | * We cannot set this to 'true' by default because user callbacks code may be not testing the 'window' parameter of their callback. 801 | * If you set this to 'true' your user callback code will need to make sure you are testing the 'window' parameter. 802 | */ 803 | public void setCallbacksChainForAllWindows(final boolean chainForAllWindows) { 804 | data.callbacksChainForAllWindows = chainForAllWindows; 805 | } 806 | 807 | protected Data newData() { 808 | return new Data(); 809 | } 810 | 811 | public boolean init(final long window, final boolean installCallbacks) { 812 | final ImGuiIO io = ImGui.getIO(); 813 | 814 | io.setBackendPlatformName("imgui-java_impl_glfw"); 815 | io.addBackendFlags(ImGuiBackendFlags.HasMouseCursors | ImGuiBackendFlags.HasSetMousePos | ImGuiBackendFlags.PlatformHasViewports); 816 | if (glfwHasMousePassthrough || (glfwHasWindowHovered && IS_WINDOWS)) { 817 | io.addBackendFlags(ImGuiBackendFlags.HasMouseHoveredViewport); 818 | } 819 | 820 | data = newData(); 821 | data.window = window; 822 | data.time = 0.0; 823 | data.wantUpdateMonitors = true; 824 | 825 | ImGui.getPlatformIO().setGetClipboardTextFn(getClipboardTextFn()); 826 | ImGui.getPlatformIO().setSetClipboardTextFn(setClipboardTextFn()); 827 | 828 | // Create mouse cursors 829 | // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, 830 | // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. 831 | // Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.) 832 | final GLFWErrorCallback prevErrorCallback = glfwSetErrorCallback(null); 833 | data.mouseCursors[ImGuiMouseCursor.Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 834 | data.mouseCursors[ImGuiMouseCursor.TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); 835 | data.mouseCursors[ImGuiMouseCursor.ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); 836 | data.mouseCursors[ImGuiMouseCursor.ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); 837 | data.mouseCursors[ImGuiMouseCursor.Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); 838 | if (glfwHasNewCursors) { 839 | data.mouseCursors[ImGuiMouseCursor.ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); 840 | data.mouseCursors[ImGuiMouseCursor.ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); 841 | data.mouseCursors[ImGuiMouseCursor.ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); 842 | data.mouseCursors[ImGuiMouseCursor.NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); 843 | } else { 844 | data.mouseCursors[ImGuiMouseCursor.ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 845 | data.mouseCursors[ImGuiMouseCursor.ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 846 | data.mouseCursors[ImGuiMouseCursor.ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 847 | data.mouseCursors[ImGuiMouseCursor.NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 848 | } 849 | glfwSetErrorCallback(prevErrorCallback); 850 | eatErrors(); 851 | 852 | // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. 853 | if (installCallbacks) { 854 | installCallbacks(window); 855 | } 856 | 857 | // Update monitors the first time (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784) 858 | updateMonitors(); 859 | glfwSetMonitorCallback(this::monitorCallback); 860 | 861 | // Our mouse update function expect PlatformHandle to be filled for the main viewport 862 | final ImGuiViewport mainViewport = ImGui.getMainViewport(); 863 | mainViewport.setPlatformHandle(window); 864 | if (IS_WINDOWS) { 865 | mainViewport.setPlatformHandleRaw(GLFWNativeWin32.glfwGetWin32Window(window)); 866 | } 867 | if (IS_APPLE) { 868 | mainViewport.setPlatformHandleRaw(GLFWNativeCocoa.glfwGetCocoaWindow(window)); 869 | } 870 | if (io.hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) { 871 | initPlatformInterface(); 872 | } 873 | 874 | return true; 875 | } 876 | 877 | public void shutdown() { 878 | final ImGuiIO io = ImGui.getIO(); 879 | 880 | shutdownPlatformInterface(); 881 | 882 | if (data.installedCallbacks) { 883 | restoreCallbacks(data.window); 884 | } 885 | 886 | for (int cursorN = 0; cursorN < ImGuiMouseCursor.COUNT; cursorN++) { 887 | if (data.mouseCursors[cursorN] != 0) glfwDestroyCursor(data.mouseCursors[cursorN]); 888 | } 889 | 890 | io.setBackendPlatformName(null); 891 | data = null; 892 | io.removeBackendFlags(ImGuiBackendFlags.HasMouseCursors | ImGuiBackendFlags.HasSetMousePos | ImGuiBackendFlags.HasGamepad 893 | | ImGuiBackendFlags.PlatformHasViewports | ImGuiBackendFlags.HasMouseHoveredViewport); 894 | } 895 | 896 | protected void updateMouseData() { 897 | final ImGuiIO io = ImGui.getIO(); 898 | final ImGuiPlatformIO platformIO = ImGui.getPlatformIO(); 899 | 900 | int mouseViewportId = 0; 901 | io.getMousePos(props.mousePosPrev); 902 | 903 | for (int n = 0; n < platformIO.getViewportsSize(); n++) { 904 | final ImGuiViewport viewport = platformIO.getViewports(n); 905 | final long window = viewport.getPlatformHandle(); 906 | final boolean isWindowFocused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; 907 | 908 | if (isWindowFocused) { 909 | // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) 910 | // When multi-viewports are enabled, all Dear ImGui positions are same as OS positions. 911 | if (io.getWantSetMousePos()) { 912 | glfwSetCursorPos(window, props.mousePosPrev.x - viewport.getPosX(), props.mousePosPrev.y - viewport.getPosY()); 913 | } 914 | 915 | // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured) 916 | if (data.mouseWindow == -1) { 917 | glfwGetCursorPos(window, props.mouseX, props.mouseY); 918 | double mouseX = props.mouseX[0]; 919 | double mouseY = props.mouseY[0]; 920 | if (io.hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) { 921 | // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) 922 | // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) 923 | glfwGetWindowPos(window, props.windowX, props.windowY); 924 | mouseX += props.windowX[0]; 925 | mouseY += props.windowY[0]; 926 | } 927 | data.lastValidMousePos.set((float) mouseX, (float) mouseY); 928 | io.addMousePosEvent((float) mouseX, (float) mouseY); 929 | } 930 | } 931 | 932 | // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. 933 | // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. 934 | // - [X] GLFW >= 3.3 backend ON WINDOWS ONLY does correctly ignore viewports with the _NoInputs flag. 935 | // - [!] GLFW <= 3.2 backend CANNOT correctly ignore viewports with the _NoInputs flag, and CANNOT reported Hovered Viewport because of mouse capture. 936 | // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window 937 | // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported 938 | // by the backend, and use its flawed heuristic to guess the viewport behind. 939 | // - [X] GLFW backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). 940 | // FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems. 941 | // See https://github.com/glfw/glfw/issues/1236 if you want to help in making this a GLFW feature. 942 | 943 | if (glfwHasMousePassthrough || (glfwHasWindowHovered && IS_WINDOWS)) { 944 | boolean windowNoInput = viewport.hasFlags(ImGuiViewportFlags.NoInputs); 945 | if (glfwHasMousePassthrough) { 946 | glfwSetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH, windowNoInput ? GLFW_TRUE : GLFW_FALSE); 947 | } 948 | if (glfwGetWindowAttrib(window, GLFW_HOVERED) == GLFW_TRUE && !windowNoInput) { 949 | mouseViewportId = viewport.getID(); 950 | } 951 | } 952 | // else 953 | // We cannot use bd->MouseWindow maintained from CursorEnter/Leave callbacks, because it is locked to the window capturing mouse. 954 | } 955 | 956 | if (io.hasBackendFlags(ImGuiBackendFlags.HasMouseHoveredViewport)) { 957 | io.addMouseViewportEvent(mouseViewportId); 958 | } 959 | } 960 | 961 | protected void updateMouseCursor() { 962 | final ImGuiIO io = ImGui.getIO(); 963 | 964 | if (io.hasConfigFlags(ImGuiConfigFlags.NoMouseCursorChange) || glfwGetInputMode(data.window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) { 965 | return; 966 | } 967 | 968 | final int imguiCursor = ImGui.getMouseCursor(); 969 | final ImGuiPlatformIO platformIO = ImGui.getPlatformIO(); 970 | 971 | for (int n = 0; n < platformIO.getViewportsSize(); n++) { 972 | final long windowPtr = platformIO.getViewports(n).getPlatformHandle(); 973 | 974 | if (imguiCursor == ImGuiMouseCursor.None || io.getMouseDrawCursor()) { 975 | // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor 976 | glfwSetInputMode(windowPtr, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); 977 | } else { 978 | // Show OS mouse cursor 979 | // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. 980 | glfwSetCursor(windowPtr, data.mouseCursors[imguiCursor] != 0 ? data.mouseCursors[imguiCursor] : data.mouseCursors[ImGuiMouseCursor.Arrow]); 981 | glfwSetInputMode(windowPtr, GLFW_CURSOR, GLFW_CURSOR_NORMAL); 982 | } 983 | } 984 | } 985 | 986 | @FunctionalInterface 987 | private interface MapButton { 988 | void run(int keyNo, int buttonNo, int _unused); 989 | } 990 | 991 | @FunctionalInterface 992 | private interface MapAnalog { 993 | void run(int keyNo, int axisNo, int _unused, float v0, float v1); 994 | } 995 | 996 | @SuppressWarnings("ManualMinMaxCalculation") 997 | private float saturate(final float v) { 998 | return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; 999 | } 1000 | 1001 | protected void updateGamepads() { 1002 | final ImGuiIO io = ImGui.getIO(); 1003 | 1004 | if (!io.hasConfigFlags(ImGuiConfigFlags.NavEnableGamepad)) { 1005 | return; 1006 | } 1007 | 1008 | io.removeBackendFlags(ImGuiBackendFlags.HasGamepad); 1009 | 1010 | final MapButton mapButton; 1011 | final MapAnalog mapAnalog; 1012 | 1013 | if (glfwHasGamepadApi) { 1014 | try (GLFWGamepadState gamepad = GLFWGamepadState.create()) { 1015 | if (!glfwGetGamepadState(GLFW_JOYSTICK_1, gamepad)) { 1016 | return; 1017 | } 1018 | mapButton = (keyNo, buttonNo, _unused) -> io.addKeyEvent(keyNo, gamepad.buttons(buttonNo) != 0); 1019 | mapAnalog = (keyNo, axisNo, _unused, v0, v1) -> { 1020 | float v = gamepad.axes(axisNo); 1021 | v = (v - v0) / (v1 - v0); 1022 | io.addKeyAnalogEvent(keyNo, v > 0.10f, saturate(v)); 1023 | }; 1024 | } 1025 | } else { 1026 | final FloatBuffer axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1); 1027 | final ByteBuffer buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1); 1028 | if (axes == null || axes.limit() == 0 || buttons == null || buttons.limit() == 0) { 1029 | return; 1030 | } 1031 | mapButton = (keyNo, buttonNo, _unused) -> io.addKeyEvent(keyNo, (buttons.limit() > buttonNo && buttons.get(buttonNo) == GLFW_PRESS)); 1032 | mapAnalog = (keyNo, axisNo, _unused, v0, v1) -> { 1033 | float v = (axes.limit() > axisNo) ? axes.get(axisNo) : v0; 1034 | v = (v - v0) / (v1 - v0); 1035 | io.addKeyAnalogEvent(keyNo, v > 0.10f, saturate(v)); 1036 | }; 1037 | } 1038 | 1039 | io.addBackendFlags(ImGuiBackendFlags.HasGamepad); 1040 | mapButton.run(ImGuiKey.GamepadStart, GLFW_GAMEPAD_BUTTON_START, 7); 1041 | mapButton.run(ImGuiKey.GamepadBack, GLFW_GAMEPAD_BUTTON_BACK, 6); 1042 | mapButton.run(ImGuiKey.GamepadFaceLeft, GLFW_GAMEPAD_BUTTON_X, 2); // Xbox X, PS Square 1043 | mapButton.run(ImGuiKey.GamepadFaceRight, GLFW_GAMEPAD_BUTTON_B, 1); // Xbox B, PS Circle 1044 | mapButton.run(ImGuiKey.GamepadFaceUp, GLFW_GAMEPAD_BUTTON_Y, 3); // Xbox Y, PS Triangle 1045 | mapButton.run(ImGuiKey.GamepadFaceDown, GLFW_GAMEPAD_BUTTON_A, 0); // Xbox A, PS Cross 1046 | mapButton.run(ImGuiKey.GamepadDpadLeft, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, 13); 1047 | mapButton.run(ImGuiKey.GamepadDpadRight, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, 11); 1048 | mapButton.run(ImGuiKey.GamepadDpadUp, GLFW_GAMEPAD_BUTTON_DPAD_UP, 10); 1049 | mapButton.run(ImGuiKey.GamepadDpadDown, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, 12); 1050 | mapButton.run(ImGuiKey.GamepadL1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, 4); 1051 | mapButton.run(ImGuiKey.GamepadR1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, 5); 1052 | mapAnalog.run(ImGuiKey.GamepadL2, GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, 4, -0.75f, +1.0f); 1053 | mapAnalog.run(ImGuiKey.GamepadR2, GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, 5, -0.75f, +1.0f); 1054 | mapButton.run(ImGuiKey.GamepadL3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, 8); 1055 | mapButton.run(ImGuiKey.GamepadR3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, 9); 1056 | mapAnalog.run(ImGuiKey.GamepadLStickLeft, GLFW_GAMEPAD_AXIS_LEFT_X, 0, -0.25f, -1.0f); 1057 | mapAnalog.run(ImGuiKey.GamepadLStickRight, GLFW_GAMEPAD_AXIS_LEFT_X, 0, +0.25f, +1.0f); 1058 | mapAnalog.run(ImGuiKey.GamepadLStickUp, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, -0.25f, -1.0f); 1059 | mapAnalog.run(ImGuiKey.GamepadLStickDown, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, +0.25f, +1.0f); 1060 | mapAnalog.run(ImGuiKey.GamepadRStickLeft, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, -0.25f, -1.0f); 1061 | mapAnalog.run(ImGuiKey.GamepadRStickRight, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, +0.25f, +1.0f); 1062 | mapAnalog.run(ImGuiKey.GamepadRStickUp, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, -0.25f, -1.0f); 1063 | mapAnalog.run(ImGuiKey.GamepadRStickDown, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, +0.25f, +1.0f); 1064 | } 1065 | 1066 | protected void updateMonitors() { 1067 | final ImGuiPlatformIO platformIO = ImGui.getPlatformIO(); 1068 | data.wantUpdateMonitors = false; 1069 | 1070 | final PointerBuffer monitors = glfwGetMonitors(); 1071 | if (monitors == null) { 1072 | System.err.println("Unable to get monitors!"); 1073 | return; 1074 | } 1075 | if (monitors.limit() == 0) { // Preserve existing monitor list if there are none. Happens on macOS sleeping (#5683) 1076 | return; 1077 | } 1078 | 1079 | platformIO.resizeMonitors(0); 1080 | 1081 | for (int n = 0; n < monitors.limit(); n++) { 1082 | final long monitor = monitors.get(n); 1083 | 1084 | final GLFWVidMode vidMode = glfwGetVideoMode(monitor); 1085 | if (vidMode == null) { 1086 | continue; 1087 | } 1088 | 1089 | glfwGetMonitorPos(monitor, props.monitorX, props.monitorY); 1090 | 1091 | final float mainPosX = props.monitorX[0]; 1092 | final float mainPosY = props.monitorY[0]; 1093 | final float mainSizeX = vidMode.width(); 1094 | final float mainSizeY = vidMode.height(); 1095 | 1096 | float workPosX = 0; 1097 | float workPosY = 0; 1098 | float workSizeX = 0; 1099 | float workSizeY = 0; 1100 | 1101 | // Workaround a small GLFW issue reporting zero on monitor changes: https://github.com/glfw/glfw/pull/1761 1102 | if (glfwHasMonitorWorkArea) { 1103 | glfwGetMonitorWorkarea(monitor, props.monitorWorkAreaX, props.monitorWorkAreaY, props.monitorWorkAreaWidth, props.monitorWorkAreaHeight); 1104 | if (props.monitorWorkAreaWidth[0] > 0 && props.monitorWorkAreaHeight[0] > 0) { 1105 | workPosX = props.monitorWorkAreaX[0]; 1106 | workPosY = props.monitorWorkAreaY[0]; 1107 | workSizeX = props.monitorWorkAreaWidth[0]; 1108 | workSizeY = props.monitorWorkAreaHeight[0]; 1109 | } 1110 | } 1111 | 1112 | float dpiScale = 0; 1113 | 1114 | // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, 1115 | // which generally needs to be set in the manifest or at runtime. 1116 | if (glfwHasPerMonitorDpi) { 1117 | glfwGetMonitorContentScale(monitor, props.monitorContentScaleX, props.monitorContentScaleY); 1118 | dpiScale = props.monitorContentScaleX[0]; 1119 | } 1120 | 1121 | platformIO.pushMonitors(monitor, mainPosX, mainPosY, mainSizeX, mainSizeY, workPosX, workPosY, workSizeX, workSizeY, dpiScale); 1122 | } 1123 | } 1124 | 1125 | public void newFrame() { 1126 | final ImGuiIO io = ImGui.getIO(); 1127 | 1128 | // Setup display size (every frame to accommodate for window resizing) 1129 | glfwGetWindowSize(data.window, props.windowW, props.windowH); 1130 | glfwGetFramebufferSize(data.window, props.displayW, props.displayH); 1131 | io.setDisplaySize((float) props.windowW[0], (float) props.windowH[0]); 1132 | if (props.windowW[0] > 0 && props.windowH[0] > 0) { 1133 | final float scaleX = (float) props.displayW[0] / props.windowW[0]; 1134 | final float scaleY = (float) props.displayH[0] / props.windowH[0]; 1135 | io.setDisplayFramebufferScale(scaleX, scaleY); 1136 | } 1137 | 1138 | if (data.wantUpdateMonitors) { 1139 | updateMonitors(); 1140 | } 1141 | 1142 | // Setup time step 1143 | // (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644) 1144 | double currentTime = glfwGetTime(); 1145 | if (currentTime <= data.time) { 1146 | currentTime = data.time + 0.00001f; 1147 | } 1148 | io.setDeltaTime(data.time > 0.0 ? (float) (currentTime - data.time) : 1.0f / 60.0f); 1149 | data.time = currentTime; 1150 | 1151 | data.MouseIgnoreButtonUp = false; 1152 | updateMouseData(); 1153 | updateMouseCursor(); 1154 | 1155 | // Update game controllers (if enabled and available) 1156 | updateGamepads(); 1157 | } 1158 | 1159 | //-------------------------------------------------------------------------------------------------------- 1160 | // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT 1161 | // This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. 1162 | // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. 1163 | //-------------------------------------------------------------------------------------------------------- 1164 | 1165 | private static final class ViewportData { 1166 | long window = -1; 1167 | boolean windowOwned = false; 1168 | int ignoreWindowPosEventFrame = -1; 1169 | int ignoreWindowSizeEventFrame = -1; 1170 | } 1171 | 1172 | private void windowCloseCallback(final long windowId) { 1173 | final ImGuiViewport vp = ImGui.findViewportByPlatformHandle(windowId); 1174 | if (vp.isValidPtr()) { 1175 | vp.setPlatformRequestClose(true); 1176 | } 1177 | } 1178 | 1179 | // GLFW may dispatch window pos/size events after calling glfwSetWindowPos()/glfwSetWindowSize(). 1180 | // However: depending on the platform the callback may be invoked at different time: 1181 | // - on Windows it appears to be called within the glfwSetWindowPos()/glfwSetWindowSize() call 1182 | // - on Linux it is queued and invoked during glfwPollEvents() 1183 | // Because the event doesn't always fire on glfwSetWindowXXX() we use a frame counter tag to only 1184 | // ignore recent glfwSetWindowXXX() calls. 1185 | private void windowPosCallback(final long windowId, final int xPos, final int yPos) { 1186 | final ImGuiViewport vp = ImGui.findViewportByPlatformHandle(windowId); 1187 | if (vp.isNotValidPtr()) { 1188 | return; 1189 | } 1190 | 1191 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1192 | if (vd != null) { 1193 | final boolean ignoreEvent = (ImGui.getFrameCount() <= vd.ignoreWindowPosEventFrame + 1); 1194 | if (ignoreEvent) { 1195 | return; 1196 | } 1197 | } 1198 | 1199 | vp.setPlatformRequestMove(true); 1200 | } 1201 | 1202 | private void windowSizeCallback(final long windowId, final int width, final int height) { 1203 | final ImGuiViewport vp = ImGui.findViewportByPlatformHandle(windowId); 1204 | if (vp.isNotValidPtr()) { 1205 | return; 1206 | } 1207 | 1208 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1209 | if (vd != null) { 1210 | final boolean ignoreEvent = (ImGui.getFrameCount() <= vd.ignoreWindowSizeEventFrame + 1); 1211 | if (ignoreEvent) { 1212 | return; 1213 | } 1214 | } 1215 | 1216 | vp.setPlatformRequestResize(true); 1217 | } 1218 | 1219 | private final class CreateWindowFunction extends ImPlatformFuncViewport { 1220 | @Override 1221 | public void accept(final ImGuiViewport vp) { 1222 | final ViewportData vd = new ViewportData(); 1223 | vp.setPlatformUserData(vd); 1224 | 1225 | // Workaround for Linux: ignore mouse up events corresponding to losing focus of the previously focused window (#7733, #3158, #7922) 1226 | // #ifdef __linux__ 1227 | if (Platform.get() == Platform.LINUX) { 1228 | data.MouseIgnoreButtonUpWaitForFocusLoss = true; 1229 | } 1230 | // #endif 1231 | 1232 | // GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED 1233 | // With GLFW 3.3, the hint GLFW_FOCUS_ON_SHOW fixes this problem 1234 | glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); 1235 | glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); 1236 | if (glfwHasFocusOnShow) { 1237 | glfwWindowHint(GLFW_FOCUS_ON_SHOW, GLFW_FALSE); 1238 | } 1239 | glfwWindowHint(GLFW_DECORATED, vp.hasFlags(ImGuiViewportFlags.NoDecoration) ? GLFW_FALSE : GLFW_TRUE); 1240 | if (glfwHawWindowTopmost) { 1241 | glfwWindowHint(GLFW_FLOATING, vp.hasFlags(ImGuiViewportFlags.TopMost) ? GLFW_TRUE : GLFW_FALSE); 1242 | } 1243 | 1244 | vd.window = glfwCreateWindow((int) vp.getSizeX(), (int) vp.getSizeY(), "No Title Yet", NULL, data.window); 1245 | vd.windowOwned = true; 1246 | 1247 | vp.setPlatformHandle(vd.window); 1248 | 1249 | if (IS_WINDOWS) { 1250 | vp.setPlatformHandleRaw(GLFWNativeWin32.glfwGetWin32Window(vd.window)); 1251 | } else if (IS_APPLE) { 1252 | vp.setPlatformHandleRaw(GLFWNativeCocoa.glfwGetCocoaWindow(vd.window)); 1253 | } 1254 | 1255 | glfwSetWindowPos(vd.window, (int) vp.getPosX(), (int) vp.getPosY()); 1256 | 1257 | // Install GLFW callbacks for secondary viewports 1258 | glfwSetWindowFocusCallback(vd.window, ImGuiImplGlfw.this::windowFocusCallback); 1259 | glfwSetCursorEnterCallback(vd.window, ImGuiImplGlfw.this::cursorEnterCallback); 1260 | glfwSetCursorPosCallback(vd.window, ImGuiImplGlfw.this::cursorPosCallback); 1261 | glfwSetMouseButtonCallback(vd.window, ImGuiImplGlfw.this::mouseButtonCallback); 1262 | glfwSetScrollCallback(vd.window, ImGuiImplGlfw.this::scrollCallback); 1263 | glfwSetKeyCallback(vd.window, ImGuiImplGlfw.this::keyCallback); 1264 | glfwSetCharCallback(vd.window, ImGuiImplGlfw.this::charCallback); 1265 | glfwSetWindowCloseCallback(vd.window, ImGuiImplGlfw.this::windowCloseCallback); 1266 | glfwSetWindowPosCallback(vd.window, ImGuiImplGlfw.this::windowPosCallback); 1267 | glfwSetWindowSizeCallback(vd.window, ImGuiImplGlfw.this::windowSizeCallback); 1268 | 1269 | glfwMakeContextCurrent(vd.window); 1270 | glfwSwapInterval(0); 1271 | } 1272 | } 1273 | 1274 | private final class DestroyWindowFunction extends ImPlatformFuncViewport { 1275 | @Override 1276 | public void accept(final ImGuiViewport vp) { 1277 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1278 | 1279 | if (vd != null && vd.windowOwned) { 1280 | if (!glfwHasMousePassthrough && glfwHasWindowHovered && IS_WINDOWS) { 1281 | // TODO: RemovePropA 1282 | } 1283 | 1284 | // Release any keys that were pressed in the window being destroyed and are still held down, 1285 | // because we will not receive any release events after window is destroyed. 1286 | for (int i = 0; i < data.keyOwnerWindows.length; i++) { 1287 | if (data.keyOwnerWindows[i] == vd.window) { 1288 | keyCallback(vd.window, i, 0, GLFW_RELEASE, 0); // Later params are only used for main viewport, on which this function is never called. 1289 | } 1290 | } 1291 | 1292 | Callbacks.glfwFreeCallbacks(vd.window); 1293 | glfwDestroyWindow(vd.window); 1294 | 1295 | vd.window = -1; 1296 | } 1297 | 1298 | 1299 | vp.setPlatformHandle(-1); 1300 | vp.setPlatformUserData(null); 1301 | } 1302 | } 1303 | 1304 | private static final class ShowWindowFunction extends ImPlatformFuncViewport { 1305 | @Override 1306 | public void accept(final ImGuiViewport vp) { 1307 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1308 | if (vd == null) { 1309 | return; 1310 | } 1311 | 1312 | if (IS_WINDOWS && vp.hasFlags(ImGuiViewportFlags.NoTaskBarIcon)) { 1313 | ImGuiImplGlfwNative.win32hideFromTaskBar(vp.getPlatformHandleRaw()); 1314 | } 1315 | 1316 | glfwShowWindow(vd.window); 1317 | } 1318 | } 1319 | 1320 | private static final class GetWindowPosFunction extends ImPlatformFuncViewportSuppImVec2 { 1321 | private final int[] posX = new int[1]; 1322 | private final int[] posY = new int[1]; 1323 | 1324 | @Override 1325 | public void get(final ImGuiViewport vp, final ImVec2 dst) { 1326 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1327 | if (vd == null) { 1328 | return; 1329 | } 1330 | posX[0] = 0; 1331 | posY[0] = 0; 1332 | glfwGetWindowPos(vd.window, posX, posY); 1333 | dst.set(posX[0], posY[0]); 1334 | } 1335 | } 1336 | 1337 | private static final class SetWindowPosFunction extends ImPlatformFuncViewportImVec2 { 1338 | @Override 1339 | public void accept(final ImGuiViewport vp, final ImVec2 value) { 1340 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1341 | if (vd == null) { 1342 | return; 1343 | } 1344 | vd.ignoreWindowPosEventFrame = ImGui.getFrameCount(); 1345 | glfwSetWindowPos(vd.window, (int) value.x, (int) value.y); 1346 | } 1347 | } 1348 | 1349 | private static final class GetWindowSizeFunction extends ImPlatformFuncViewportSuppImVec2 { 1350 | private final int[] width = new int[1]; 1351 | private final int[] height = new int[1]; 1352 | 1353 | @Override 1354 | public void get(final ImGuiViewport vp, final ImVec2 dst) { 1355 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1356 | if (vd == null) { 1357 | return; 1358 | } 1359 | width[0] = 0; 1360 | height[0] = 0; 1361 | glfwGetWindowSize(vd.window, width, height); 1362 | dst.x = width[0]; 1363 | dst.y = height[0]; 1364 | } 1365 | } 1366 | 1367 | private final class SetWindowSizeFunction extends ImPlatformFuncViewportImVec2 { 1368 | private final int[] x = new int[1]; 1369 | private final int[] y = new int[1]; 1370 | private final int[] width = new int[1]; 1371 | private final int[] height = new int[1]; 1372 | 1373 | @Override 1374 | public void accept(final ImGuiViewport vp, final ImVec2 value) { 1375 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1376 | if (vd == null) { 1377 | return; 1378 | } 1379 | if (IS_APPLE && !glfwHasOsxWindowPosFix) { 1380 | // Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are 1381 | // positioned from the upper-left corner. GLFW makes an effort to convert macOS style coordinates, however it 1382 | // doesn't handle it when changing size. We are manually moving the window in order for changes of size to be based 1383 | // on the upper-left corner. 1384 | x[0] = 0; 1385 | y[0] = 0; 1386 | width[0] = 0; 1387 | height[0] = 0; 1388 | glfwGetWindowPos(vd.window, x, y); 1389 | glfwGetWindowSize(vd.window, width, height); 1390 | glfwSetWindowPos(vd.window, x[0], y[0] - height[0] + (int) value.y); 1391 | } 1392 | vd.ignoreWindowSizeEventFrame = ImGui.getFrameCount(); 1393 | glfwSetWindowSize(vd.window, (int) value.x, (int) value.y); 1394 | } 1395 | } 1396 | 1397 | private static final class SetWindowTitleFunction extends ImPlatformFuncViewportString { 1398 | @Override 1399 | public void accept(final ImGuiViewport vp, final String value) { 1400 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1401 | if (vd != null) { 1402 | glfwSetWindowTitle(vd.window, value); 1403 | } 1404 | } 1405 | } 1406 | 1407 | private final class SetWindowFocusFunction extends ImPlatformFuncViewport { 1408 | @Override 1409 | public void accept(final ImGuiViewport vp) { 1410 | if (glfwHasFocusWindow) { 1411 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1412 | if (vd != null) { 1413 | glfwFocusWindow(vd.window); 1414 | } 1415 | } 1416 | } 1417 | } 1418 | 1419 | private static final class GetWindowFocusFunction extends ImPlatformFuncViewportSuppBoolean { 1420 | @Override 1421 | public boolean get(final ImGuiViewport vp) { 1422 | final ViewportData data = (ViewportData) vp.getPlatformUserData(); 1423 | return glfwGetWindowAttrib(data.window, GLFW_FOCUSED) != 0; 1424 | } 1425 | } 1426 | 1427 | private static final class GetWindowMinimizedFunction extends ImPlatformFuncViewportSuppBoolean { 1428 | @Override 1429 | public boolean get(final ImGuiViewport vp) { 1430 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1431 | if (vd != null) { 1432 | return glfwGetWindowAttrib(vd.window, GLFW_ICONIFIED) != GLFW_FALSE; 1433 | } 1434 | return false; 1435 | } 1436 | } 1437 | 1438 | private final class SetWindowAlphaFunction extends ImPlatformFuncViewportFloat { 1439 | @Override 1440 | public void accept(final ImGuiViewport vp, final float value) { 1441 | if (glfwHasWindowAlpha) { 1442 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1443 | if (vd != null) { 1444 | glfwSetWindowOpacity(vd.window, value); 1445 | } 1446 | } 1447 | } 1448 | } 1449 | 1450 | private static final class RenderWindowFunction extends ImPlatformFuncViewport { 1451 | @Override 1452 | public void accept(final ImGuiViewport vp) { 1453 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1454 | if (vd != null) { 1455 | glfwMakeContextCurrent(vd.window); 1456 | } 1457 | } 1458 | } 1459 | 1460 | private static final class SwapBuffersFunction extends ImPlatformFuncViewport { 1461 | @Override 1462 | public void accept(final ImGuiViewport vp) { 1463 | final ViewportData vd = (ViewportData) vp.getPlatformUserData(); 1464 | if (vd != null) { 1465 | glfwMakeContextCurrent(vd.window); 1466 | glfwSwapBuffers(vd.window); 1467 | } 1468 | } 1469 | } 1470 | 1471 | protected void initPlatformInterface() { 1472 | final ImGuiPlatformIO platformIO = ImGui.getPlatformIO(); 1473 | 1474 | // Register platform interface (will be coupled with a renderer interface) 1475 | platformIO.setPlatformCreateWindow(new CreateWindowFunction()); 1476 | platformIO.setPlatformDestroyWindow(new DestroyWindowFunction()); 1477 | platformIO.setPlatformShowWindow(new ShowWindowFunction()); 1478 | platformIO.setPlatformGetWindowPos(new GetWindowPosFunction()); 1479 | platformIO.setPlatformSetWindowPos(new SetWindowPosFunction()); 1480 | platformIO.setPlatformGetWindowSize(new GetWindowSizeFunction()); 1481 | platformIO.setPlatformSetWindowSize(new SetWindowSizeFunction()); 1482 | platformIO.setPlatformSetWindowTitle(new SetWindowTitleFunction()); 1483 | platformIO.setPlatformSetWindowFocus(new SetWindowFocusFunction()); 1484 | platformIO.setPlatformGetWindowFocus(new GetWindowFocusFunction()); 1485 | platformIO.setPlatformGetWindowMinimized(new GetWindowMinimizedFunction()); 1486 | platformIO.setPlatformSetWindowAlpha(new SetWindowAlphaFunction()); 1487 | platformIO.setPlatformRenderWindow(new RenderWindowFunction()); 1488 | platformIO.setPlatformSwapBuffers(new SwapBuffersFunction()); 1489 | 1490 | // Register main window handle (which is owned by the main application, not by us) 1491 | // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. 1492 | final ImGuiViewport mainViewport = ImGui.getMainViewport(); 1493 | final ViewportData vd = new ViewportData(); 1494 | vd.window = data.window; 1495 | vd.windowOwned = false; 1496 | mainViewport.setPlatformUserData(vd); 1497 | mainViewport.setPlatformHandle(data.window); 1498 | } 1499 | 1500 | protected void shutdownPlatformInterface() { 1501 | ImGui.destroyPlatformWindows(); 1502 | } 1503 | } 1504 | -------------------------------------------------------------------------------- /src/client/resources/imgui-for-mc.client.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "net.irisshaders.imgui.mixin", 4 | "compatibilityLevel": "JAVA_21", 5 | "client": [ 6 | "RendererInitMixin", 7 | "MouseHandlingMixin", 8 | "DrawMixin", 9 | "KeyboardHandlingMixin", 10 | "ScreenMixin", 11 | "WindowMixin" 12 | ], 13 | "injectors": { 14 | "defaultRequire": 1 15 | } 16 | } -------------------------------------------------------------------------------- /src/client/resources/imgui-java64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IrisShaders/imgui-mc/f22156e203aad54a9783f8a3b197a3ad4fd26766/src/client/resources/imgui-java64.dll -------------------------------------------------------------------------------- /src/client/resources/libimgui-java64.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IrisShaders/imgui-mc/f22156e203aad54a9783f8a3b197a3ad4fd26766/src/client/resources/libimgui-java64.dylib -------------------------------------------------------------------------------- /src/client/resources/libimgui-java64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IrisShaders/imgui-mc/f22156e203aad54a9783f8a3b197a3ad4fd26766/src/client/resources/libimgui-java64.so -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "${id}", 4 | "version": "${version}", 5 | "name": "${name}", 6 | "description": "This library adds support for the latest ImGui in a compatible way, while managing all the details of contexts for you.", 7 | "authors": [ 8 | "IMS212" 9 | ], 10 | "contact": { 11 | "sources": "https://github.com/IrisShaders/imgui-mc" 12 | }, 13 | "license": "MIT", 14 | "icon": "assets/template/icon.png", 15 | "environment": "*", 16 | "custom": { 17 | "modmenu": { 18 | "badges": [ 19 | "library" 20 | ] 21 | } 22 | }, 23 | "mixins": [ 24 | { 25 | "config": "imgui-for-mc.client.mixins.json", 26 | "environment": "client" 27 | } 28 | ], 29 | "depends": { 30 | "fabricloader": ">=0.15", 31 | "minecraft": "${mcdep}" 32 | } 33 | } -------------------------------------------------------------------------------- /stonecutter.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("dev.kikugie.stonecutter") 3 | id("fabric-loom") version "1.9-SNAPSHOT" apply false 4 | //id("dev.kikugie.j52j") version "1.0.2" apply false // Enables asset processing by writing json5 files 5 | //id("me.modmuss50.mod-publish-plugin") version "0.7.+" apply false // Publishes builds to hosting websites 6 | } 7 | stonecutter active "1.21.5" /* [SC] DO NOT EDIT */ 8 | 9 | // Builds every version into `build/libs/{mod.version}/` 10 | stonecutter registerChiseled tasks.register("chiseledBuild", stonecutter.chiseled) { 11 | group = "project" 12 | ofTask("buildAndCollect") 13 | } 14 | 15 | /* 16 | // Publishes every version 17 | stonecutter registerChiseled tasks.register("chiseledPublishMods", stonecutter.chiseled) { 18 | group = "project" 19 | ofTask("publishMods") 20 | } 21 | */ 22 | 23 | stonecutter parameters { 24 | /* 25 | See src/main/java/com/example/TemplateMod.java 26 | and https://stonecutter.kikugie.dev/ 27 | */ 28 | // Swaps replace the scope with a predefined value 29 | swap("mod_version", "\"${property("mod.version")}\";") 30 | // Constants add variables available in conditions 31 | const("release", property("mod.id") != "template") 32 | // Dependencies add targets to check versions against 33 | } 34 | -------------------------------------------------------------------------------- /versions/1.20.1/gradle.properties: -------------------------------------------------------------------------------- 1 | deps.yarn_build=10 2 | deps.fabric_api=0.92.2+1.20.1 3 | 4 | mod.mc_dep=>=1.20 <=1.20.1 5 | mod.mc_title=1.20.1 6 | mod.mc_targets=1.20 1.20.1 -------------------------------------------------------------------------------- /versions/1.20.4/gradle.properties: -------------------------------------------------------------------------------- 1 | deps.yarn_build=3 2 | deps.fabric_api=0.97.1+1.20.4 3 | 4 | mod.mc_dep=>=1.20.3 <=1.20.4 5 | mod.mc_title=1.20.4 6 | mod.mc_targets=1.20.3 1.20.4 -------------------------------------------------------------------------------- /versions/1.20.6/gradle.properties: -------------------------------------------------------------------------------- 1 | deps.yarn_build=3 2 | deps.fabric_api=0.100.8+1.20.6 3 | 4 | mod.mc_dep=>=1.20.5 <=1.20.6 5 | mod.mc_title=1.20.6 6 | mod.mc_targets=1.20.5 1.20.6 -------------------------------------------------------------------------------- /versions/1.21.1/gradle.properties: -------------------------------------------------------------------------------- 1 | deps.yarn_build=3 2 | deps.fabric_api=0.110.0+1.21.1 3 | 4 | mod.mc_dep=>=1.21 <=1.21.1 5 | mod.mc_title=1.21.1 6 | mod.mc_targets=1.21 1.21.1 -------------------------------------------------------------------------------- /versions/1.21.5/gradle.properties: -------------------------------------------------------------------------------- 1 | deps.yarn_build=3 2 | deps.fabric_api=0.110.0+1.21.6 3 | 4 | mod.mc_dep=>=1.21.4 <=1.21.5 5 | mod.mc_title=1.21.5 6 | mod.mc_targets=1.21.5 --------------------------------------------------------------------------------