# LWJGL 2.x

LWJGL 2 is an old version of LWJGL and doesn't have the NanoVG bindings. However, LWJGL 3 does have the bindings we need, and we'll go through a simple process to integrate LWJGL 3 bindings into LWJGL 2 projects.

Of course, any of the modern bindings can be used in the legacy versions, not just NanoVG!

Starting off, we need to have a base for our project where we can add NanoVG. I'm using Legacy Fabric, and this will only work with Legacy Fabric.

{% hint style="info" %}
Legacy Fabric is arguably better than other mod loaders in the case of Minecraft clients. Forge and other platforms are considered deprecated and no longer used, and no support will be provided for those. TLDR: You'll get bullied for using Forge.
{% endhint %}

We will need to add the LWJGL 3 dependencies to our project, obviously, there are a few conflicts that we need to rule out. We can just relocate these.

### Step 1: Configuring our buildscript

To start off, open up `gradle.properties` and append this snippet of code into it. This just defines the LWJGL 3 version you want to use, we're defining it here, so it's easier to change in the future. The version might be a little bit outdated by the time you're reading this, so always check on the [LWJGL website](https://lwjgl.org) and the [Legacy LWJGL3 repository](https://github.com/WaterwaveMC/Legacy-LWJGL3) to make sure you are always up to date.

{% code title="gradle.properties" %}

```properties
lwjgl_version = 3.3.4
```

{% endcode %}

Then, you need to append this at the start of the buildscript, right after the `plugins` block:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="build.gradle.kts" %}

```kotlin
val lwjglVersion = properties["lwjgl_version"] as String

val lwjglNatives = Pair(
	System.getProperty("os.name")!!,
	System.getProperty("os.arch")!!
).let { (name, arch) ->
	when {
		arrayOf("Linux", "SunOS", "Unit").any { name.startsWith(it) } ->
			if (arrayOf("arm", "aarch64").any { arch.startsWith(it) })
				"natives-linux${if (arch.contains("64") || arch.startsWith("armv8")) "-arm64" else "-arm32"}"
			else if (arch.startsWith("ppc"))
				"natives-linux-ppc64le"
			else if (arch.startsWith("riscv"))
				"natives-linux-riscv64"
			else
				"natives-linux"
		arrayOf("Mac OS X", "Darwin").any { name.startsWith(it) }     ->
			"natives-macos"
		arrayOf("Windows").any { name.startsWith(it) }                ->
			if (arch.contains("64"))
				"natives-windows${if (arch.startsWith("aarch64")) "-arm64" else ""}"
			else
				"natives-windows-x86"
		else                                                                            ->
			throw Error("Unrecognized or unsupported platform. Please set \"lwjglNatives\" manually")
	}
}
```

{% endcode %}
{% endtab %}

{% tab title="Groovy" %}
{% code title="build.gradle" %}

```groovy
project.ext.lwjglVersion = project.findProperty('lwjgl_version')

switch (OperatingSystem.current()) {
	case OperatingSystem.LINUX:
		project.ext.lwjglNatives = "natives-linux"
		def osArch = System.getProperty("os.arch")
		if (osArch.startsWith("arm") || osArch.startsWith("aarch64")) {
			project.ext.lwjglNatives += osArch.contains("64") || osArch.startsWith("armv8") ? "-arm64" : "-arm32"
		} else if  (osArch.startsWith("ppc")) {
			project.ext.lwjglNatives += "-ppc64le"
		} else if  (osArch.startsWith("riscv")) {
			project.ext.lwjglNatives += "-riscv64"
		}
		break
	case OperatingSystem.MAC_OS:
		project.ext.lwjglNatives = "natives-macos"
		break
	case OperatingSystem.WINDOWS:
		def osArch = System.getProperty("os.arch")
		project.ext.lwjglNatives = osArch.contains("64")
			? "natives-windows${osArch.startsWith("aarch64") ? "-arm64" : ""}"
			: "natives-windows-x86"
		break
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

This code defines what platform the dependency will use. For example, if you're on a x64 Windows machine, this code will infer that and tell Gradle to use the x64 Windows version of LWJGL.

### Step 2: Setting up dependencies

We can't forget the dependencies. These are what give you access to these libraries. We'll focus on setting up these dependencies, namely, Patched LWJGL and the natives, to work with LWJGL 2.x

First, we will start off with appending the Jitpack maven repository to our repositories block. This can be done by adding this entry into `repositories`

{% tabs %}
{% tab title="Kotlin" %}
{% code title="build.gradle.kts" %}

```kts
maven("https://jitpack.io")
```

{% endcode %}
{% endtab %}

{% tab title="Groovy" %}
{% code title="build.gradle" %}

```groovy
maven { url = 'https://jitpack.io' }
```

{% endcode %}
{% endtab %}
{% endtabs %}

Once you've done that, we can finally add in the real deal: our LWJGL dependencies!&#x20;

You can do that by appending these at the end of your `dependendencies` block:

{% tabs %}
{% tab title="Kotlin" %}
{% code title="build.gradle.kts" %}

```kotlin
implementation(platform("org.lwjgl:lwjgl-bom:$lwjglVersion"))

implementation("io.waterwave.Legacy-LWJGL3:lwjgl:$lwjglVersion-5")
implementation("io.waterwave.Legacy-LWJGL3:lwjgl-stb:$lwjglVersion-5")
implementation("io.waterwave.Legacy-LWJGL3:lwjgl-nanovg:$lwjglVersion-5")

runtimeOnly("org.lwjgl:lwjgl::$lwjglNatives")
runtimeOnly("org.lwjgl:lwjgl-nanovg::$lwjglNatives")
runtimeOnly("org.lwjgl:lwjgl-stb::$lwjglNatives")
```

{% endcode %}
{% endtab %}

{% tab title="Groovy" %}

<pre class="language-groovy"><code class="lang-groovy">implementation platform("org.lwjgl:lwjgl-bom:${lwjglVersion}")

<strong>implementation "io.waterwave.Legacy-LWJGL3:lwjgl:${lwjglVersion}-5"
</strong>implementation "io.waterwave.Legacy-LWJGL3:lwjgl-stb:${lwjglVersion}-5"
implementation "io.waterwave.Legacy-LWJGL3:lwjgl-nanovg:${lwjglVersion}-5"

runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-nanovg::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives" 
</code></pre>

{% endtab %}
{% endtabs %}

Once you've configured dependencies, you can refresh the project and wait for Gradle to sync everything.

### Step 3: Patching NanoVG at runtime

NanoVG needs a little bit of patching to work with LWJGL. We can patch NanoVG's function provider with our own, compatible with LWJGL 2, so it works well.

First, add this class into your mod:

```java
package dev.lunasa.prov;

import org.lwjgl.opengl.GLContext;
import org.lwjgl.system.FunctionProvider;

import java.lang.reflect.Method;
import java.nio.ByteBuffer;

/**
 * This class is only required in legacy versions which use LWJGL 2.
 *
 * @author DJ The Redstoner
 */
public class Lwjgl2FunctionProvider implements FunctionProvider {

    private final Method m_getFunctionAddress;

    public Lwjgl2FunctionProvider() {
        try {
            m_getFunctionAddress = GLContext.class.getDeclaredMethod("getFunctionAddress", String.class);
            m_getFunctionAddress.setAccessible(true);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public long getFunctionAddress(CharSequence functionName) {
        try {
            return (long) m_getFunctionAddress.invoke(null, functionName.toString());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public long getFunctionAddress(ByteBuffer byteBuffer) {
        throw new UnsupportedOperationException();
    }
}
```

You can place the class anywhere, that's up to you.

We'll make NanoVG use this function provider using SpongePowered Mixins, Fabric has that built-in!

{% hint style="info" %}
The Mixins method only works with Fabric. For other mod loaders, you have to use ASM to patch the classes.
{% endhint %}

Here are the sources of the mixins. It's quite easy to grasp:

```java
import dev.lunasa.prov.Lwjgl2FunctionProvider;
import org.spongepowered.asm.mixin.Mixin;

import org.lwjgl.system.*;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.*;

/**
 * This class is only required in legacy versions which use LWJGL 2.
 *
 * @author Lunasa
 */
@Mixin(targets = "org.lwjgl.nanovg.NanoVGGLConfig")
public abstract class MixinNanoVGGLConfig {
    @Inject(method = "getFunctionProvider", at = @At("HEAD"),
            cancellable = true, remap = false)
    private static void getFunctionProvider(String className, CallbackInfoReturnable<FunctionProvider> cir) {
        cir.setReturnValue(new Lwjgl2FunctionProvider());
    }
}
```

This just forces NanoVG to use our function provider instead of their default one.

Now, you will have NanoVG in your classpath and you will be able to use it inside LWJGL 2!


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wiki.lunasa.dev/rendering-libraries/nanovg/lwjgl-2.x.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
