Skip to content

Commit c5d3be2

Browse files
committed
Lambda Window Icon
1 parent f2a0a5c commit c5d3be2

File tree

10 files changed

+143
-1
lines changed

10 files changed

+143
-1
lines changed

src/main/kotlin/com/lambda/Lambda.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ import com.lambda.config.serializer.ItemStackSerializer
2727
import com.lambda.config.serializer.KeyCodeSerializer
2828
import com.lambda.config.serializer.OptionalSerializer
2929
import com.lambda.core.Loader
30+
import com.lambda.module.modules.client.ClickGui
3031
import com.lambda.threading.recordRenderCall
3132
import com.lambda.util.KeyCode
33+
import com.lambda.util.WindowIcons.setLambdaWindowIcon
3234
import com.mojang.authlib.GameProfile
3335
import net.fabricmc.api.ClientModInitializer
3436
import net.fabricmc.loader.api.FabricLoader
@@ -75,6 +77,7 @@ object Lambda : ClientModInitializer {
7577
override fun onInitializeClient() {
7678
recordRenderCall {
7779
LOG.info("$MOD_NAME $VERSION initialized in ${Loader.initialize()} ms\n")
80+
if (ClickGui.setLambdaWindowIcon) setLambdaWindowIcon()
7881
}
7982
}
8083
}

src/main/kotlin/com/lambda/module/modules/client/ClickGui.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,17 @@ import com.lambda.module.tag.ModuleTag
2323
import com.lambda.util.Describable
2424
import com.lambda.util.KeyCode
2525
import com.lambda.util.NamedEnum
26+
import com.lambda.util.WindowIcons.setLambdaWindowIcon
2627
import imgui.ImGui
2728
import imgui.flag.ImGuiCol
2829
import imgui.flag.ImGuiHoveredFlags
30+
import net.minecraft.SharedConstants
2931
import net.minecraft.client.gui.screen.ChatScreen
3032
import net.minecraft.client.gui.screen.Screen
3133
import net.minecraft.client.gui.screen.ingame.AnvilScreen
3234
import net.minecraft.client.gui.screen.ingame.CommandBlockScreen
3335
import net.minecraft.client.gui.screen.ingame.SignEditScreen
36+
import net.minecraft.client.util.Icons
3437
import java.awt.Color
3538

3639
object ClickGui : Module(
@@ -58,11 +61,19 @@ object ClickGui : Module(
5861
LongDelay("Long Delay", "Show tooltip after a longer delay (~0.40s), and only after the mouse has been still briefly on the item.", ImGuiHoveredFlags.DelayNormal)
5962
}
6063

61-
6264
// General
6365
val alpha by setting("Alpha", 1.0f, 0.0f..1.0f, 0.01f).group(Group.General)
6466
val disabledAlpha by setting("Disabled Alpha", 0.6f, 0.0f..1.0f, 0.01f).group(Group.General)
6567
val tooltipType by setting("Tooltip Type", TooltipType.Stationary, description = "When to show the tooltip.").group(Group.General)
68+
val setLambdaWindowIcon by setting("Set Lambda Window Icon", true).group(Group.General).onValueChange { _, to ->
69+
if (to) {
70+
setLambdaWindowIcon()
71+
} else {
72+
val icon = if (SharedConstants.getGameVersion().isStable) Icons.RELEASE else Icons.SNAPSHOT
73+
mc.window.setIcon(mc.defaultResourcePack, icon)
74+
}
75+
}
76+
val setLambdaWindowTitle by setting("Set Lambda Window Title", true).group(Group.General)
6677

6778
// Sizing
6879
val windowPaddingX by setting("Window Padding X", 8.0f, 0.0f..20.0f, 0.1f).group(Group.Sizing)
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.util
19+
20+
import com.lambda.Lambda.mc
21+
import org.lwjgl.glfw.GLFW
22+
import org.lwjgl.glfw.GLFWImage
23+
import org.lwjgl.system.MemoryStack
24+
import org.lwjgl.system.MemoryUtil
25+
import java.awt.image.BufferedImage
26+
import java.nio.ByteBuffer
27+
28+
object WindowIcons {
29+
/**
30+
* Sets the window icon for the application using a predefined set of icon sizes.
31+
*
32+
* This method constructs a list of image resource paths corresponding to different icon sizes
33+
* (16x16, 24x24, 32x32, 48x48, 64x64, 128x128, and 256x256) and applies them as the window icon.
34+
* The function utilizes the `setWindowIcon` function to handle the underlying platform-specific logic.
35+
*/
36+
fun setLambdaWindowIcon() {
37+
val icons = listOf(16, 24, 32, 48, 64, 128, 256).map { "textures/icon/logo_$it.png" }
38+
setWindowIcon(*icons.toTypedArray())
39+
}
40+
41+
/**
42+
* Sets the window icon using the provided list of image resource paths.
43+
* - On Windows/X11: uses glfwSetWindowIcon with all given sizes.
44+
* - On macOS: attempts to set the application Dock icon from the largest image (glfwSetWindowIcon is ignored).
45+
*
46+
* Paths are resolved via your readImage() extension (same as your texture system).
47+
*
48+
* Example:
49+
* WindowIcons.setWindowIcon(
50+
* "textures/icon16.png",
51+
* "textures/icon32.png",
52+
* "textures/icon48.png",
53+
* "textures/icon128.png",
54+
* )
55+
*/
56+
@JvmStatic
57+
fun setWindowIcon(vararg iconPaths: String) {
58+
if (iconPaths.isEmpty()) return
59+
val windowHandle = mc.window?.handle ?: return
60+
61+
val platform = GLFW.glfwGetPlatform()
62+
when (platform) {
63+
GLFW.GLFW_PLATFORM_WIN32,
64+
GLFW.GLFW_PLATFORM_X11,
65+
GLFW.GLFW_PLATFORM_WAYLAND,
66+
GLFW.GLFW_PLATFORM_NULL -> {
67+
val buffers = mutableListOf<ByteBuffer>()
68+
try {
69+
MemoryStack.stackPush().use { stack ->
70+
val images = GLFWImage.malloc(iconPaths.size, stack)
71+
72+
iconPaths.forEachIndexed { index, path ->
73+
val img = path.readImage()
74+
val pixels = toRGBA(img).also { buffers.add(it) }
75+
images.position(index)
76+
images.width(img.width)
77+
images.height(img.height)
78+
images.pixels(pixels)
79+
}
80+
81+
GLFW.glfwSetWindowIcon(windowHandle, images.position(0))
82+
}
83+
} finally {
84+
buffers.forEach(MemoryUtil::memFree)
85+
}
86+
}
87+
88+
GLFW.GLFW_PLATFORM_COCOA -> {
89+
// On macOS glfwSetWindowIcon is ignored; set dock icon instead if possible.
90+
try {
91+
val largest = iconPaths
92+
.mapNotNull { runCatching { it.readImage() }.getOrNull() }
93+
.maxByOrNull { it.width * it.height }
94+
?: return
95+
96+
// Try Minecraft's MacWindowUtil if present
97+
val klass = Class.forName("net.minecraft.client.util.MacWindowUtil")
98+
val method = klass.getMethod("setApplicationIconImage", BufferedImage::class.java)
99+
method.invoke(null, largest)
100+
} catch (_: Throwable) {
101+
// Silently ignore if class isn't available; no safe fallback on macOS via GLFW.
102+
}
103+
}
104+
105+
else -> {
106+
// Unrecognized platform, ignore
107+
}
108+
}
109+
}
110+
111+
private fun toRGBA(image: BufferedImage): ByteBuffer {
112+
val width = image.width
113+
val height = image.height
114+
val argb = IntArray(width * height)
115+
image.getRGB(0, 0, width, height, argb, 0, width)
116+
117+
val buf = MemoryUtil.memAlloc(width * height * 4)
118+
for (p in argb) {
119+
val a = (p ushr 24) and 0xFF
120+
val r = (p ushr 16) and 0xFF
121+
val g = (p ushr 8) and 0xFF
122+
val b = p and 0xFF
123+
buf.put(r.toByte()).put(g.toByte()).put(b.toByte()).put(a.toByte())
124+
}
125+
buf.flip()
126+
return buf
127+
}
128+
}
5.07 KB
Loading
921 Bytes
Loading
1.22 KB
Loading
11 KB
Loading
1.56 KB
Loading
1.83 KB
Loading
2.43 KB
Loading

0 commit comments

Comments
 (0)