Skip to content

Commit 1d7a736

Browse files
authored
Merge pull request #11 from SteamClientHomebrew/next
Next
2 parents 40fd2d7 + 326a9f9 commit 1d7a736

48 files changed

Lines changed: 3779 additions & 113 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ name: Build Millennium
22

33
on:
44
workflow_dispatch:
5+
inputs:
6+
skip_release:
7+
description: 'Build artifacts without creating a GitHub release or signing'
8+
type: boolean
9+
default: false
510
push:
611
branches:
712
- main
@@ -72,6 +77,7 @@ jobs:
7277

7378
- name: Sign Installer Build
7479
id: sign_artifact
80+
if: ${{ !inputs.skip_release }}
7581
uses: signpath/github-action-submit-signing-request@v1.1
7682
with:
7783
api-token: "${{ secrets.SIGNPATH_API_TOKEN }}"
@@ -85,6 +91,7 @@ jobs:
8591

8692
- name: Upload Signed Build Release
8793
id: upload-signed-artifact
94+
if: ${{ !inputs.skip_release }}
8895
uses: actions/upload-artifact@v4
8996
with:
9097
include-hidden-files: true
@@ -102,6 +109,7 @@ jobs:
102109
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
103110

104111
- name: Create GitHub Release
112+
if: ${{ !inputs.skip_release }}
105113
run: cd resources/versioning && npx semantic-release
106114
env:
107115
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

CMakeLists.txt

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,38 @@ endif()
3939

4040
include(${CMAKE_CURRENT_SOURCE_DIR}/resources/cmake/bootstrap_deps.cmake)
4141

42+
# ── Embed locale JSON files as C++ raw string literals ────────────────────────
43+
# Each src/locales/<lang>.json is read at configure time and appended into
44+
# build/generated/locale_data.h as inline const char* kLocaleData_<lang>.
45+
# Re-run cmake automatically when any JSON changes (CMAKE_CONFIGURE_DEPENDS).
46+
47+
set(_LOCALE_LANGS
48+
english russian ukrainian german french spanish italian polish turkish
49+
swedish dutch brazilian hungarian indonesian vietnamese
50+
schinese tchinese japanese koreana latam
51+
bulgarian danish
52+
)
53+
set(_LOCALE_HEADER "${CMAKE_BINARY_DIR}/generated/locale_data.h")
54+
55+
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/generated")
56+
57+
# Use file(WRITE/APPEND) directly — avoids CMake list/semicolon expansion bugs
58+
# that occur when building large strings with string(APPEND) + file(WRITE).
59+
file(WRITE "${_LOCALE_HEADER}" "// Auto-generated by CMake — do not edit. Re-run cmake to refresh.\n#pragma once\n\n")
60+
61+
foreach(_LANG ${_LOCALE_LANGS})
62+
set(_JSON_PATH "${CMAKE_SOURCE_DIR}/src/locales/${_LANG}.json")
63+
# Track for automatic re-configure when any JSON file changes
64+
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_JSON_PATH}")
65+
file(READ "${_JSON_PATH}" _JSON_CONTENT)
66+
# Write open, content, close as separate appends so no CMake string
67+
# processing can swallow the semicolon in )__LD__";
68+
file(APPEND "${_LOCALE_HEADER}" "inline const char* kLocaleData_${_LANG} = R\"__LD__(\n")
69+
file(APPEND "${_LOCALE_HEADER}" "${_JSON_CONTENT}")
70+
file(APPEND "${_LOCALE_HEADER}" "\n)__LD__\";\n\n")
71+
endforeach()
72+
# ──────────────────────────────────────────────────────────────────────────────
73+
4274
add_library(imgui_lib STATIC
4375
${imgui_SOURCE_DIR}/imgui.cpp
4476
${imgui_SOURCE_DIR}/imgui_draw.cpp
@@ -62,6 +94,7 @@ set(SOURCES
6294
src/window/wndproc.cc
6395
src/window/renderer.cc
6496
src/util/updater.cc
97+
src/util/locale.cc
6598
src/routes/router.cc
6699
src/routes/home.cc
67100
src/routes/install_prompt.cc
@@ -97,14 +130,29 @@ include_directories(SYSTEM ${stb_SOURCE_DIR})
97130
include_directories(SYSTEM ${zlib_SOURCE_DIR} ${zlib_BINARY_DIR})
98131

99132
target_link_libraries(${PROJECT_NAME} PRIVATE imgui_lib)
100-
target_link_options(${PROJECT_NAME} PRIVATE /FORCE:MULTIPLE)
101133

102-
if(UNIX AND NOT APPLE)
103-
find_package(PkgConfig REQUIRED)
104-
pkg_check_modules(GLFW REQUIRED glfw3)
134+
target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_BINARY_DIR}/generated")
135+
if(WIN32)
136+
target_link_options(${PROJECT_NAME} PRIVATE /FORCE:MULTIPLE)
137+
elseif(UNIX)
138+
target_link_options(${PROJECT_NAME} PRIVATE -Wl,--allow-multiple-definition)
139+
endif()
105140

106-
target_compile_options(${PROJECT_NAME} PRIVATE ${GLFW_CFLAGS})
107-
target_link_libraries(${PROJECT_NAME} PRIVATE ${GLFW_LDFLAGS} GL)
141+
if(UNIX AND NOT APPLE)
142+
find_package(GLEW REQUIRED)
143+
find_package(OpenSSL REQUIRED)
144+
target_include_directories(${PROJECT_NAME}
145+
SYSTEM PRIVATE ${minizip_ng_SOURCE_DIR}
146+
)
147+
target_link_libraries(${PROJECT_NAME} PRIVATE
148+
freetype
149+
glfw
150+
GL
151+
GLEW::GLEW
152+
CURL::libcurl
153+
minizip
154+
OpenSSL::Crypto
155+
)
108156
elseif(APPLE)
109157
find_library(COCOA_LIBRARY Cocoa REQUIRED)
110158
find_library(IOKIT_LIBRARY IOKit REQUIRED)
@@ -137,3 +185,4 @@ elseif(WIN32)
137185
bcrypt
138186
)
139187
endif()
188+

CMakePresets.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,36 @@
5151
"CMAKE_C_FLAGS_DEBUG": "/Z7 -fstandalone-debug"
5252
}
5353
},
54+
{
55+
"inherits": "default",
56+
"name": "linux",
57+
"hidden": true,
58+
"cacheVariables": {
59+
"CMAKE_C_COMPILER": "gcc",
60+
"CMAKE_CXX_COMPILER": "g++"
61+
},
62+
"displayName": "linux"
63+
},
64+
{
65+
"inherits": "linux",
66+
"name": "linux-release",
67+
"displayName": "linux-release",
68+
"description": "Linux release build.",
69+
"binaryDir": "${sourceDir}/build/linux-release",
70+
"cacheVariables": {
71+
"CMAKE_BUILD_TYPE": "Release"
72+
}
73+
},
74+
{
75+
"inherits": "linux",
76+
"name": "linux-debug",
77+
"displayName": "linux-debug",
78+
"description": "Linux debug build.",
79+
"binaryDir": "${sourceDir}/build/linux-debug",
80+
"cacheVariables": {
81+
"CMAKE_BUILD_TYPE": "Debug"
82+
}
83+
},
5484
{
5585
"inherits": "default",
5686
"name": "osx-release",

resources/cmake/bootstrap_deps.cmake

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,11 @@ FetchContent_Declare(imspinner URL "file://${THIRDPARTY_DIR}/imspinner-b50e1c8.t
101101
# ============================================================================
102102
# Build independent dependencies
103103
# ============================================================================
104-
set(INDEPENDENT_DEPS zlib glfw glew nlohmann_json mini)
104+
if(WIN32)
105+
set(INDEPENDENT_DEPS zlib glfw glew nlohmann_json mini)
106+
else()
107+
set(INDEPENDENT_DEPS zlib glfw nlohmann_json mini)
108+
endif()
105109

106110
foreach(dep ${INDEPENDENT_DEPS})
107111
string(TIMESTAMP start_time "%s")
@@ -112,7 +116,7 @@ foreach(dep ${INDEPENDENT_DEPS})
112116
endforeach()
113117

114118
# Fix non-ASCII copyright symbol in GLEW .rc that breaks llvm-rc on CI
115-
if(EXISTS "${glew_SOURCE_DIR}/build/glew.rc")
119+
if(WIN32 AND EXISTS "${glew_SOURCE_DIR}/build/glew.rc")
116120
file(WRITE "${glew_SOURCE_DIR}/build/glew.rc" "// intentionally blank\n")
117121
endif()
118122

src/components/bottombar.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@
3333
#include <dpi.h>
3434
#include <texture.hh>
3535
#include <animate.h>
36+
#include <i18n.h>
3637
#ifdef _WIN32
3738
#endif
39+
#include <format>
3840
#include <util.h>
3941

4042
using namespace ImGui;
@@ -66,10 +68,10 @@ const void RenderBottomNavBar(const char* identifier, float xPos, std::function<
6668
const float cursorPosSave = GetCursorPosX();
6769

6870
SetCursorPosY(GetCursorPosY() - ScaleX(12));
69-
TextColored(ImVec4(0.322f, 0.325f, 0.341f, 1.0f), "Steam Homebrew & Millennium are not affiliated with");
71+
TextColored(ImVec4(0.322f, 0.325f, 0.341f, 1.0f), "%s", Locale::Get("installerDisclaimer1"));
7072

7173
SetCursorPos({ cursorPosSave, GetCursorPosY() - ScaleY(20) });
72-
TextColored(ImVec4(0.322f, 0.325f, 0.341f, 1.0f), "Steam®, Valve, or any of their partners.");
74+
TextColored(ImVec4(0.322f, 0.325f, 0.341f, 1.0f), "%s", Locale::Get("installerDisclaimer2"));
7375

7476
SameLine(0);
7577
SetCursorPosY(GetCursorPosY() - ScaleY(25));
@@ -95,7 +97,7 @@ const void RenderBottomNavBar(const char* identifier, float xPos, std::function<
9597
PushStyleVar(ImGuiStyleVar_WindowRounding, 6);
9698
PushStyleVar(ImGuiStyleVar_Alpha, discordIconHoverTransparency);
9799
PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.098f, 0.102f, 0.11f, 1.0f));
98-
SetTooltip("Join Discord Server");
100+
SetTooltip("%s", Locale::Get("tooltipDiscord"));
99101

100102
if (IsItemClicked()) {
101103
OpenUrl(discordInviteLink);
@@ -126,7 +128,7 @@ const void RenderBottomNavBar(const char* identifier, float xPos, std::function<
126128
PushStyleVar(ImGuiStyleVar_WindowRounding, 6);
127129
PushStyleVar(ImGuiStyleVar_Alpha, githubIconHoverTransparency);
128130
PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.098f, 0.102f, 0.11f, 1.0f));
129-
SetTooltip("View Source Code");
131+
SetTooltip("%s", Locale::Get("tooltipGithub"));
130132

131133
if (IsItemClicked()) {
132134
OpenUrl(githubRepositoryUrl);

src/components/titlebar.cc

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,21 @@
2828
* SOFTWARE.
2929
*/
3030

31+
#include <exception>
3132
#include <imgui.h>
3233
#include <texture.hh>
3334
#include <dpi.h>
3435
#include <components.h>
3536
#include <animate.h>
37+
#include <i18n.h>
3638
#include <math.h>
3739
#include <worker.h>
40+
#include <renderer.h>
41+
#include <cstdlib>
42+
#include <format>
43+
#ifndef _WIN32
44+
#include <unistd.h>
45+
#endif
3846

3947
using namespace ImGui;
4048

@@ -44,8 +52,7 @@ using namespace ImGui;
4452
*/
4553
bool RenderTitleBarComponent(std::shared_ptr<RouterNav> router)
4654
{
47-
ImGuiIO& io = GetIO();
48-
const std::string strTitleText = std::format("Steam Homebrew", io.Framerate);
55+
const std::string strTitleText = Locale::Get("titlebarTitle");
4956

5057
ImGuiViewport* viewport = GetMainViewport();
5158
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(ScaleX(15), ScaleY(15)));
@@ -88,13 +95,109 @@ bool RenderTitleBarComponent(std::shared_ptr<RouterNav> router)
8895
Text("%s", strTitleText.c_str());
8996
SameLine();
9097

98+
ImVec2 closeButtonDimensions = { ceil(ScaleX(70)), ceil(ScaleY(43)) };
99+
91100
static bool isCloseButtonHovered = false;
92101

93102
if (isCloseButtonHovered) {
94103
PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.769f, 0.169f, 0.11f, 1.0f));
95104
}
96105

97-
ImVec2 closeButtonDimensions = { ceil(ScaleX(70)), ceil(ScaleY(43)) };
106+
static bool langPopupOpen = false;
107+
108+
ImVec2 langButtonDimensions = { ceil(ScaleX(50)), ceil(ScaleY(43)) };
109+
SetCursorPos({ viewport->Size.x - closeButtonDimensions.x - langButtonDimensions.x, 0 });
110+
111+
PushStyleVar(ImGuiStyleVar_ChildRounding, 0);
112+
PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.f, 0.f, 0.f, 0.f));
113+
static bool isLangButtonHovered = false;
114+
if (isLangButtonHovered)
115+
PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.f, 1.f, 1.f, 0.06f));
116+
117+
BeginChild("##LangButton", { langButtonDimensions.x, langButtonDimensions.y }, false, ImGuiWindowFlags_NoScrollbar);
118+
{
119+
SetCursorPos({ (langButtonDimensions.x - ScaleX(20)) * 0.5f, ScaleY(12) });
120+
Image((ImTextureID)(intptr_t)languageIconTexture, { ScaleX(20), ScaleY(20) });
121+
}
122+
EndChild();
123+
124+
ImVec2 langBtnMin = GetItemRectMin();
125+
ImVec2 langBtnMax = GetItemRectMax();
126+
127+
if (isLangButtonHovered)
128+
PopStyleColor();
129+
PopStyleColor();
130+
PopStyleVar();
131+
132+
if (IsItemClicked(ImGuiMouseButton_Left))
133+
OpenPopup("##LangPopup");
134+
135+
if (IsItemHovered())
136+
SetMouseCursor(ImGuiMouseCursor_Hand);
137+
138+
isLangButtonHovered = IsItemHovered();
139+
140+
ImGuiIO& io = GetIO();
141+
ImFont* vietItemFont = (io.Fonts->Fonts.Size > 2) ? io.Fonts->Fonts[2] : nullptr;
142+
ImFont* dropdownFont = (io.Fonts->Fonts.Size > 3) ? io.Fonts->Fonts[3] : nullptr;
143+
144+
const auto& langs = Locale::GetAvailableLanguages();
145+
const std::string& currentLangId = Locale::GetCurrentLanguageId();
146+
147+
const float popupWidth = ScaleX(400);
148+
float anim = EaseInOutFloat("##LangPopupAnim", 0.f, 1.f, IsPopupOpen("##LangPopup"), 0.35f);
149+
150+
float popupY = langBtnMax.y - ScaleY(6) * (1.f - anim);
151+
SetNextWindowPos({ langBtnMax.x - popupWidth, popupY });
152+
SetNextWindowSize({ popupWidth, ScaleY(500) });
153+
SetNextWindowBgAlpha(anim);
154+
155+
PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.10f, 0.10f, 0.11f, 1.0f));
156+
PushStyleColor(ImGuiCol_Header, ImVec4(0.20f, 0.21f, 0.22f, 1.0f));
157+
PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.26f, 0.27f, 0.28f, 1.0f));
158+
PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.23f, 0.25f, 1.0f));
159+
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(ScaleX(6), ScaleY(6)));
160+
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ScaleX(8), ScaleY(6)));
161+
PushStyleVar(ImGuiStyleVar_PopupRounding, ScaleX(4));
162+
163+
if (dropdownFont)
164+
PushFont(dropdownFont);
165+
166+
if (BeginPopup("##LangPopup", ImGuiWindowFlags_NoMove)) {
167+
if (!::IsWindowFocused())
168+
CloseCurrentPopup();
169+
PushStyleVar(ImGuiStyleVar_Alpha, anim);
170+
for (const auto& lang : langs) {
171+
bool isSelected = (lang.id == currentLangId);
172+
bool useVietFont = (lang.id == "vietnamese") && (vietItemFont != nullptr);
173+
if (useVietFont) {
174+
if (dropdownFont)
175+
PopFont();
176+
PushFont(vietItemFont);
177+
}
178+
if (Selectable(lang.displayName.c_str(), isSelected, 0, ImVec2(popupWidth, 0))) {
179+
Locale::SetLanguage(lang.id);
180+
RequestFontRebuild();
181+
CloseCurrentPopup();
182+
}
183+
if (isSelected)
184+
SetItemDefaultFocus();
185+
if (useVietFont) {
186+
PopFont();
187+
if (dropdownFont)
188+
PushFont(dropdownFont);
189+
}
190+
}
191+
PopStyleVar();
192+
EndPopup();
193+
}
194+
195+
if (dropdownFont)
196+
PopFont();
197+
198+
PopStyleVar(3);
199+
PopStyleColor(4);
200+
98201
SetCursorPos({ viewport->Size.x - closeButtonDimensions.x, 0 });
99202

100203
PushStyleVar(ImGuiStyleVar_ChildRounding, 0);
@@ -108,7 +211,11 @@ bool RenderTitleBarComponent(std::shared_ptr<RouterNav> router)
108211

109212
if (IsItemClicked(ImGuiMouseButton_Left) && !IsWorkerBusy()) {
110213
JoinWorker();
111-
ExitProcess(0);
214+
#ifdef _WIN32
215+
std::exit(0);
216+
#else
217+
_exit(0);
218+
#endif
112219
}
113220

114221
if (isCloseButtonHovered) {

0 commit comments

Comments
 (0)