stop my swiftui fork leaking...

This commit is contained in:
emiyl 2026-04-18 17:12:29 +01:00
parent 21e69d03b5
commit 37683f5e94
7 changed files with 10 additions and 677 deletions

View File

@ -244,7 +244,7 @@ Example usage: `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DENABLE_SDL=ON -
| ENABLE_SDL | | Enable SDLController controller API | ON | Currently required |
| ENABLE_VCPKG | | Use VCPKG package manager to obtain dependencies | ON | |
| ENABLE_VULKAN | | Enable the Vulkan graphics backend | ON | |
| ENABLE_WXWIDGETS | | Enable wxWidgets UI | ON | |
| ENABLE_WXWIDGETS | | Enable wxWidgets UI | ON | Currently required |
### Windows
| Flag | Description | Default | Note |
@ -264,5 +264,4 @@ Example usage: `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DENABLE_SDL=ON -
### macOS
| Flag | Description | Default |
|---------------------|------------------------------------------------------|---------|
| ENABLE_SWIFTUI_MACOS | Enable experimental native macOS SwiftUI GUI backend | OFF |
| MACOS_BUNDLE | MacOS executable will be an application bundle | OFF |
| MACOS_BUNDLE | macOS executable will be an application bundle | OFF |

View File

@ -17,7 +17,7 @@ execute_process(
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_compile_definitions($<$<COMPILE_LANGUAGE:C,CXX,OBJC,OBJCXX>:EMULATOR_HASH=${GIT_HASH}>)
add_definitions(-DEMULATOR_HASH=${GIT_HASH})
if (ENABLE_VCPKG)
# check if vcpkg is shallow and unshallow it if necessary
@ -66,9 +66,9 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_compile_definitions($<$<CONFIG:Debug>:CEMU_DEBUG_ASSERT>) # if build type is debug, set CEMU_DEBUG_ASSERT
add_compile_definitions($<$<COMPILE_LANGUAGE:C,CXX,OBJC,OBJCXX>:EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR}>)
add_compile_definitions($<$<COMPILE_LANGUAGE:C,CXX,OBJC,OBJCXX>:EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR}>)
add_compile_definitions($<$<COMPILE_LANGUAGE:C,CXX,OBJC,OBJCXX>:EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}>)
add_definitions(-DEMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR})
add_definitions(-DEMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR})
add_definitions(-DEMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH})
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
@ -194,7 +194,7 @@ endif()
if (ENABLE_METAL)
include_directories(${CMAKE_SOURCE_DIR}/dependencies/metal-cpp)
add_compile_definitions($<$<COMPILE_LANGUAGE:C,CXX,OBJC,OBJCXX>:ENABLE_METAL=1>)
add_definitions(-DENABLE_METAL=1)
endif()
if (ENABLE_DISCORD_RPC)
@ -232,7 +232,7 @@ if (ENABLE_CUBEB)
set_property(TARGET cubeb PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
add_library(cubeb::cubeb ALIAS cubeb)
endif()
add_compile_definitions($<$<COMPILE_LANGUAGE:C,CXX,OBJC,OBJCXX>:HAS_CUBEB=1>)
add_compile_definitions("HAS_CUBEB=1")
endif()
add_subdirectory("dependencies/ih264d" EXCLUDE_FROM_ALL)

View File

@ -32,13 +32,7 @@ elseif(UNIX)
add_compile_options(-Wno-ambiguous-reversed-operator)
endif()
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX,OBJC,OBJCXX>:-Wno-multichar>
$<$<COMPILE_LANGUAGE:C,CXX,OBJC,OBJCXX>:-Wno-invalid-offsetof>
$<$<COMPILE_LANGUAGE:C,CXX,OBJC,OBJCXX>:-Wno-switch>
$<$<COMPILE_LANGUAGE:C,CXX,OBJC,OBJCXX>:-Wno-ignored-attributes>
$<$<COMPILE_LANGUAGE:C,CXX,OBJC,OBJCXX>:-Wno-deprecated-enum-enum-conversion>
)
add_compile_options(-Wno-multichar -Wno-invalid-offsetof -Wno-switch -Wno-ignored-attributes -Wno-deprecated-enum-enum-conversion)
endif()
add_compile_definitions(VK_NO_PROTOTYPES)

View File

@ -1,12 +1,7 @@
add_library(CemuGui INTERFACE)
target_include_directories(CemuGui INTERFACE "interface")
if(APPLE AND ENABLE_SWIFTUI_MACOS)
add_subdirectory(swiftui)
target_link_libraries(CemuGui INTERFACE CemuSwiftUiGui)
elseif(ENABLE_WXWIDGETS)
if(ENABLE_WXWIDGETS)
add_subdirectory(wxgui)
target_link_libraries(CemuGui INTERFACE CemuWxGui)
else()
message(FATAL_ERROR "No GUI backend selected. Enable ENABLE_WXWIDGETS or ENABLE_SWIFTUI_MACOS on macOS.")
endif()

View File

@ -1,29 +0,0 @@
enable_language(Swift)
add_library(CemuSwiftUiGui STATIC
WindowSystemSwiftUI.mm
ContentView.swift
)
set_target_properties(CemuSwiftUiGui PROPERTIES
Swift_LANGUAGE_VERSION 5
)
set_property(TARGET CemuSwiftUiGui PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
target_include_directories(CemuSwiftUiGui PRIVATE "../../")
target_include_directories(CemuSwiftUiGui PUBLIC "../")
target_link_libraries(CemuSwiftUiGui PRIVATE
CemuCommon
CemuConfig
CemuCafe
CemuResource
)
find_library(COCOA_FRAMEWORK Cocoa REQUIRED)
target_link_libraries(CemuSwiftUiGui PRIVATE ${COCOA_FRAMEWORK})
if(ALLOW_PORTABLE)
target_compile_definitions(CemuSwiftUiGui PRIVATE CEMU_ALLOW_PORTABLE)
endif()

View File

@ -1,237 +0,0 @@
import AppKit
import SwiftUI
@objc(CemuSwiftUIRootViewController)
final class CemuSwiftUIRootViewController: NSViewController {
override func loadView() {
self.view = NSHostingView(rootView: ContentView())
}
}
@_cdecl("CemuCreateSwiftUIRootViewController")
public func CemuCreateSwiftUIRootViewController() -> UnsafeMutableRawPointer {
let controller = CemuSwiftUIRootViewController()
return Unmanaged.passRetained(controller).autorelease().toOpaque()
}
struct ContentView: View {
@State private var selectedTitleID: UInt64?
@State private var showUpdatingBanner = false
@State private var games: [GameItem] = [
GameItem(
titleID: 0x0005_0000_101C_9400,
name: "The Legend of Zelda: Breath of the Wild",
version: "208",
dlc: "80",
played: "37 hours 42 minutes",
lastPlayed: "4/12/26",
region: "EUR"
),
GameItem(
titleID: 0x0005_0000_1010_EC00,
name: "Mario Kart 8",
version: "64",
dlc: "48",
played: "12 hours 5 minutes",
lastPlayed: "3/28/26",
region: "USA"
),
GameItem(
titleID: 0x0005_0000_1017_6A00,
name: "Splatoon",
version: "80",
dlc: "",
played: "1 hour 12 minutes",
lastPlayed: "never",
region: "JPN"
),
GameItem(
titleID: 0x0005_0000_1014_4F00,
name: "Super Smash Bros. for Wii U",
version: "288",
dlc: "192",
played: "",
lastPlayed: "never",
region: "USA"
),
]
var body: some View {
VStack(spacing: 0) {
GameListHeaderView()
Divider()
ScrollView {
LazyVStack(spacing: 0) {
ForEach(Array(games.enumerated()), id: \.element.id) { index, game in
GameListRowView(
game: game,
isSelected: selectedTitleID == game.titleID,
isAlternateRow: index.isMultiple(of: 2)
)
.contentShape(Rectangle())
.onTapGesture {
selectedTitleID = game.titleID
}
Divider()
}
}
}
.background(Color(nsColor: .controlBackgroundColor))
if showUpdatingBanner {
GameListInfoBarView(message: "Updating game list...") {
withAnimation(.easeInOut(duration: 0.2)) {
showUpdatingBanner = false
}
}
.transition(.move(edge: .bottom).combined(with: .opacity))
}
}
.toolbar {
ToolbarItemGroup(placement: .automatic) {
Button(action: refreshGameList) {
Image(systemName: "arrow.clockwise")
}
.help("Refresh game list")
Button(action: openSettings) {
Image(systemName: "gear")
}
.help("Settings")
}
}
.frame(minWidth: 900, minHeight: 480)
}
private func refreshGameList() {
withAnimation(.easeInOut(duration: 0.2)) {
showUpdatingBanner = true
}
}
private func openSettings() {
// SwiftUI migration placeholder: the menu action exists in WindowSystemSwiftUI.
}
}
struct GameListHeaderView: View {
var body: some View {
HStack(spacing: 0) {
headerCell("Icon", width: 66, alignment: .center)
headerCell("Game", width: 340, alignment: .leading)
headerCell("Version", width: 84, alignment: .leading)
headerCell("DLC", width: 68, alignment: .leading)
headerCell("You've played", width: 170, alignment: .leading)
headerCell("Last played", width: 136, alignment: .leading)
headerCell("Region", width: 88, alignment: .leading)
headerCell("Title ID", width: 170, alignment: .leading)
}
.padding(.vertical, 4)
.background(Color(nsColor: .windowBackgroundColor))
}
private func headerCell(_ title: String, width: CGFloat, alignment: Alignment) -> some View {
Text(title)
.font(.system(size: 12, weight: .semibold))
.foregroundStyle(.primary)
.lineLimit(1)
.frame(width: width, alignment: alignment)
.padding(.horizontal, 8)
}
}
struct GameListRowView: View {
let game: GameItem
let isSelected: Bool
let isAlternateRow: Bool
var body: some View {
HStack(spacing: 0) {
iconCell
.frame(width: 66, alignment: .center)
textCell(game.name, width: 340)
textCell(game.version, width: 84)
textCell(game.dlc, width: 68)
textCell(game.played, width: 170)
textCell(game.lastPlayed, width: 136)
textCell(game.region, width: 88)
textCell(String(format: "%016llx", game.titleID), width: 170)
}
.frame(maxWidth: .infinity, minHeight: 40, alignment: .leading)
.background(backgroundColor)
}
private var iconCell: some View {
RoundedRectangle(cornerRadius: 4)
.fill(Color.accentColor.opacity(0.20))
.frame(width: 40, height: 24)
.overlay(
Image(systemName: "photo")
.font(.system(size: 11, weight: .medium))
.foregroundStyle(.secondary)
)
.padding(.horizontal, 8)
}
private func textCell(_ value: String, width: CGFloat) -> some View {
Text(value)
.font(.system(size: 12))
.lineLimit(1)
.truncationMode(.tail)
.frame(width: width, alignment: .leading)
.padding(.horizontal, 8)
}
private var backgroundColor: Color {
if isSelected {
return Color(nsColor: .selectedContentBackgroundColor)
}
if isAlternateRow {
return Color(nsColor: .controlBackgroundColor)
}
return Color(nsColor: .windowBackgroundColor)
}
}
struct GameListInfoBarView: View {
let message: String
let onDismiss: () -> Void
var body: some View {
HStack(spacing: 10) {
Image(systemName: "arrow.triangle.2.circlepath")
.font(.system(size: 12, weight: .semibold))
.foregroundStyle(.secondary)
Text(message)
.font(.system(size: 12))
Spacer()
Button("Dismiss", action: onDismiss)
.buttonStyle(.plain)
.font(.system(size: 12, weight: .medium))
}
.padding(.horizontal, 10)
.padding(.vertical, 7)
.background(Color(nsColor: .unemphasizedSelectedContentBackgroundColor))
}
}
struct GameItem: Identifiable {
let titleID: UInt64
let name: String
let version: String
let dlc: String
let played: String
let lastPlayed: String
let region: String
var id: UInt64 { titleID }
}

View File

@ -1,389 +0,0 @@
#include "Common/precompiled.h"
#include "interface/WindowSystem.h"
#include "Cafe/CafeSystem.h"
#include "Cafe/TitleList/TitleInfo.h"
#include "Cafe/TitleList/TitleList.h"
#include "config/ActiveSettings.h"
#import <Cocoa/Cocoa.h>
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
extern "C" void *CemuCreateSwiftUIRootViewController(void);
@interface CemuAppDelegate : NSObject <NSApplicationDelegate>
- (void)setupMenuBar;
- (void)quitApp:(id)sender;
- (void)openGame:(id)sender;
- (void)openPreferences:(id)sender;
- (void)toggleFullscreen:(id)sender;
- (void)showHelp:(id)sender;
- (void)showAbout:(id)sender;
@end
namespace {
extern WindowSystem::WindowInfo g_window_info;
extern NSWindow *g_main_window;
extern CemuAppDelegate *g_app_delegate;
bool PrepareLaunchPath(const fs::path &launchPath, std::string &errorOut);
}
@implementation CemuAppDelegate
- (void)setupMenuBar {
NSMenu *mainMenu = [[NSMenu alloc] init];
// File menu
NSMenu *fileMenu = [[NSMenu alloc] initWithTitle:@"File"];
NSMenuItem *fileMenuItem = [mainMenu addItemWithTitle:@"File" action:nil keyEquivalent:@""];
[mainMenu setSubmenu:fileMenu forItem:fileMenuItem];
NSMenuItem *openGameItem = [fileMenu addItemWithTitle:@"Open Game..." action:@selector(openGame:) keyEquivalent:@"o"];
[openGameItem setTarget:self];
[fileMenu addItem:[NSMenuItem separatorItem]];
NSMenuItem *quitItem = [fileMenu addItemWithTitle:@"Quit Cemu" action:@selector(quitApp:) keyEquivalent:@"q"];
[quitItem setTarget:self];
// Edit menu
NSMenu *editMenu = [[NSMenu alloc] initWithTitle:@"Edit"];
NSMenuItem *editMenuItem = [mainMenu addItemWithTitle:@"Edit" action:nil keyEquivalent:@""];
[mainMenu setSubmenu:editMenu forItem:editMenuItem];
NSMenuItem *preferencesItem = [editMenu addItemWithTitle:@"Preferences..." action:@selector(openPreferences:) keyEquivalent:@","];
[preferencesItem setTarget:self];
// View menu
NSMenu *viewMenu = [[NSMenu alloc] initWithTitle:@"View"];
NSMenuItem *viewMenuItem = [mainMenu addItemWithTitle:@"View" action:nil keyEquivalent:@""];
[mainMenu setSubmenu:viewMenu forItem:viewMenuItem];
NSMenuItem *fullscreenItem = [viewMenu addItemWithTitle:@"Toggle Fullscreen" action:@selector(toggleFullscreen:) keyEquivalent:@"f"];
[fullscreenItem setTarget:self];
// Help menu
NSMenu *helpMenu = [[NSMenu alloc] initWithTitle:@"Help"];
NSMenuItem *helpMenuItem = [mainMenu addItemWithTitle:@"Help" action:nil keyEquivalent:@""];
[mainMenu setSubmenu:helpMenu forItem:helpMenuItem];
NSMenuItem *helpItem = [helpMenu addItemWithTitle:@"Cemu Help" action:@selector(showHelp:) keyEquivalent:@""];
[helpItem setTarget:self];
NSMenuItem *aboutItem = [helpMenu addItemWithTitle:@"About Cemu" action:@selector(showAbout:) keyEquivalent:@""];
[aboutItem setTarget:self];
[NSApp setMainMenu:mainMenu];
}
- (void)quitApp:(id)sender {
[NSApp terminate:sender];
}
- (void)openGame:(id)sender {
if (CafeSystem::IsTitleRunning()) {
WindowSystem::ShowErrorDialog("A title is already running.",
"Launch blocked");
return;
}
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setCanChooseFiles:YES];
[panel setCanChooseDirectories:NO];
[panel setAllowsMultipleSelection:NO];
[panel setAllowedContentTypes:@[
[UTType typeWithFilenameExtension:@"wud"],
[UTType typeWithFilenameExtension:@"wux"],
[UTType typeWithFilenameExtension:@"wua"],
[UTType typeWithFilenameExtension:@"wuhb"],
[UTType typeWithFilenameExtension:@"iso"],
[UTType typeWithFilenameExtension:@"rpx"],
[UTType typeWithFilenameExtension:@"elf"],
[UTType typeWithFilenameExtension:@"tmd"]
]];
if ([panel runModal] != NSModalResponseOK || panel.URL == nil)
return;
NSString *pathString = panel.URL.path;
if (pathString.length == 0)
return;
fs::path launchPath = _utf8ToPath(std::string(pathString.UTF8String));
std::string errorMessage;
if (!PrepareLaunchPath(launchPath, errorMessage)) {
WindowSystem::ShowErrorDialog(errorMessage, "Failed to launch game");
return;
}
WindowSystem::UpdateWindowTitles(false, true, 0.0);
CafeSystem::LaunchForegroundTitle();
WindowSystem::NotifyGameLoaded();
const std::string titleName = CafeSystem::GetForegroundTitleName();
if (!titleName.empty() && g_main_window) {
[g_main_window setTitle:[NSString stringWithUTF8String:titleName.c_str()]];
}
}
- (void)openPreferences:(id)sender {
fs::path configPath = ActiveSettings::GetConfigPath();
std::string configPathUtf8 = _pathToUtf8(configPath);
NSString *configNSString =
[NSString stringWithUTF8String:configPathUtf8.c_str()];
if (configNSString.length == 0)
return;
NSURL *configURL = [NSURL fileURLWithPath:configNSString isDirectory:YES];
[[NSWorkspace sharedWorkspace] openURL:configURL];
}
- (void)toggleFullscreen:(id)sender {
if (!g_main_window)
return;
[g_main_window toggleFullScreen:nil];
g_window_info.is_fullscreen = !g_window_info.is_fullscreen.load();
}
- (void)showHelp:(id)sender {
NSURL *helpURL = [NSURL URLWithString:@"https://wiki.cemu.info"];
if (helpURL)
[[NSWorkspace sharedWorkspace] openURL:helpURL];
}
- (void)showAbout:(id)sender {
NSAlert *about = [[NSAlert alloc] init];
[about setAlertStyle:NSAlertStyleInformational];
[about setMessageText:@"About Cemu"];
[about setInformativeText:@"Cemu - Wii U Emulator\nSwiftUI macOS GUI"];
[about addButtonWithTitle:@"OK"];
[about runModal];
}
@end
namespace {
WindowSystem::WindowInfo g_window_info{};
NSWindow *g_main_window = nil;
CemuAppDelegate *g_app_delegate = nil;
bool PrepareLaunchPath(const fs::path &launchPath, std::string &errorOut) {
TitleInfo launchTitle{launchPath};
if (launchTitle.IsValid()) {
CafeTitleList::AddTitleFromPath(launchPath);
TitleId baseTitleId;
if (!CafeTitleList::FindBaseTitleId(launchTitle.GetAppTitleId(),
baseTitleId)) {
errorOut =
"Unable to launch game because the base files were not found.";
return false;
}
CafeSystem::PREPARE_STATUS_CODE status =
CafeSystem::PrepareForegroundTitle(baseTitleId);
if (status == CafeSystem::PREPARE_STATUS_CODE::UNABLE_TO_MOUNT) {
errorOut =
"Unable to mount title. Make sure your game paths are valid and "
"refresh the game list.";
return false;
}
if (status != CafeSystem::PREPARE_STATUS_CODE::SUCCESS) {
errorOut = "Failed to prepare the selected game for launch.";
return false;
}
return true;
}
CafeTitleFileType fileType = DetermineCafeSystemFileType(launchPath);
if (fileType == CafeTitleFileType::RPX || fileType == CafeTitleFileType::ELF) {
CafeSystem::PREPARE_STATUS_CODE status =
CafeSystem::PrepareForegroundTitleFromStandaloneRPX(launchPath);
if (status != CafeSystem::PREPARE_STATUS_CODE::SUCCESS) {
errorOut = "Failed to prepare standalone RPX/ELF executable.";
return false;
}
return true;
}
errorOut = "Unsupported or invalid Wii U title path.";
return false;
}
} // namespace
void WindowSystem::ShowErrorDialog(
std::string_view message, std::string_view title,
std::optional<WindowSystem::ErrorCategory> /*errorCategory*/) {
@autoreleasepool {
NSAlert *alert = [[NSAlert alloc] init];
std::string titleCopy(title);
NSString *alertTitle =
titleCopy.empty() ? @"Error"
: [NSString stringWithUTF8String:titleCopy.c_str()];
NSString *alertMessage =
[NSString stringWithUTF8String:std::string(message).c_str()];
[alert setAlertStyle:NSAlertStyleCritical];
[alert setMessageText:alertTitle];
[alert setInformativeText:alertMessage ?: @""];
[alert addButtonWithTitle:@"OK"];
[alert runModal];
}
}
void WindowSystem::Create() {
@autoreleasepool {
NSApplication *app = [NSApplication sharedApplication];
[app setActivationPolicy:NSApplicationActivationPolicyRegular];
// Setup application delegate with menu bar
g_app_delegate = [[CemuAppDelegate alloc] init];
[app setDelegate:g_app_delegate];
[g_app_delegate setupMenuBar];
const NSRect frame = NSMakeRect(120.0, 120.0, 1280.0, 720.0);
const NSWindowStyleMask style =
NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable |
NSWindowStyleMaskUnifiedTitleAndToolbar;
g_main_window = [[NSWindow alloc] initWithContentRect:frame
styleMask:style
backing:NSBackingStoreBuffered
defer:NO];
[g_main_window setTitle:@"Cemu"];
[g_main_window setTitlebarAppearsTransparent:NO];
// Instantiate SwiftUI-backed root controller via explicit Swift C symbol.
NSViewController *rootViewController = nil;
if (void *swiftControllerPtr = CemuCreateSwiftUIRootViewController()) {
id swiftControllerObj = (__bridge id)swiftControllerPtr;
if ([swiftControllerObj isKindOfClass:[NSViewController class]]) {
rootViewController = (NSViewController *)swiftControllerObj;
}
}
if (!rootViewController) {
rootViewController = [[NSViewController alloc] init];
NSView *contentView = [[NSView alloc] initWithFrame:frame];
[contentView setWantsLayer:YES];
contentView.layer.backgroundColor = NSColor.windowBackgroundColor.CGColor;
NSTextField *fallbackLabel = [NSTextField labelWithString:
@"SwiftUI root view not found.\nUsing AppKit fallback view."];
[fallbackLabel setFont:[NSFont systemFontOfSize:16 weight:NSFontWeightMedium]];
[fallbackLabel setTextColor:NSColor.secondaryLabelColor];
[fallbackLabel setAlignment:NSTextAlignmentCenter];
[fallbackLabel setFrame:NSMakeRect(40, frame.size.height / 2 - 20,
frame.size.width - 80, 60)];
[fallbackLabel setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin | NSViewMaxYMargin];
[contentView addSubview:fallbackLabel];
rootViewController.view = contentView;
}
[g_main_window setContentViewController:rootViewController];
[g_main_window makeKeyAndOrderFront:nil];
[app activateIgnoringOtherApps:YES];
g_window_info.app_active = true;
g_window_info.width = static_cast<int32_t>(frame.size.width);
g_window_info.height = static_cast<int32_t>(frame.size.height);
g_window_info.phys_width = g_window_info.width.load();
g_window_info.phys_height = g_window_info.height.load();
g_window_info.dpi_scale = 1.0;
g_window_info.pad_open = false;
g_window_info.pad_width = 0;
g_window_info.pad_height = 0;
g_window_info.phys_pad_width = 0;
g_window_info.phys_pad_height = 0;
g_window_info.pad_dpi_scale = 1.0;
g_window_info.is_fullscreen = false;
g_window_info.debugger_focused = false;
g_window_info.window_main.backend =
WindowSystem::WindowHandleInfo::Backend::Cocoa;
g_window_info.window_main.display = nullptr;
g_window_info.window_main.surface = (__bridge void *)g_main_window;
[NSApp run];
}
}
WindowSystem::WindowInfo &WindowSystem::GetWindowInfo() {
return g_window_info;
}
void WindowSystem::UpdateWindowTitles(bool isIdle, bool isLoading, double fps) {
if (!g_main_window)
return;
NSString *title = nil;
if (isIdle)
title = @"Cemu";
else if (isLoading)
title = @"Cemu - Loading...";
else
title = [NSString stringWithFormat:@"Cemu - FPS: %.2f", fps];
[g_main_window setTitle:title];
}
void WindowSystem::GetWindowSize(int &w, int &h) {
w = g_window_info.width;
h = g_window_info.height;
}
void WindowSystem::GetPadWindowSize(int &w, int &h) {
w = 0;
h = 0;
}
void WindowSystem::GetWindowPhysSize(int &w, int &h) {
w = g_window_info.phys_width;
h = g_window_info.phys_height;
}
void WindowSystem::GetPadWindowPhysSize(int &w, int &h) {
w = 0;
h = 0;
}
double WindowSystem::GetWindowDPIScale() { return g_window_info.dpi_scale; }
double WindowSystem::GetPadDPIScale() { return 1.0; }
bool WindowSystem::IsPadWindowOpen() { return false; }
bool WindowSystem::IsKeyDown(uint32 key) {
return g_window_info.get_keystate(key);
}
bool WindowSystem::IsKeyDown(PlatformKeyCodes key) {
switch (key) {
case PlatformKeyCodes::LCONTROL:
return IsKeyDown(0x3B);
case PlatformKeyCodes::RCONTROL:
return IsKeyDown(0x3E);
case PlatformKeyCodes::TAB:
return IsKeyDown(0x30);
case PlatformKeyCodes::ESCAPE:
return IsKeyDown(0x35);
default:
return false;
}
}
std::string WindowSystem::GetKeyCodeName(uint32 key) {
return fmt::format("key_{}", key);
}
bool WindowSystem::InputConfigWindowHasFocus() { return false; }
void WindowSystem::NotifyGameLoaded() {}
void WindowSystem::NotifyGameExited() {}
void WindowSystem::RefreshGameList() { CafeTitleList::Refresh(); }
void WindowSystem::CaptureInput(const ControllerState & /*currentState*/,
const ControllerState & /*lastState*/) {}
bool WindowSystem::IsFullScreen() { return g_window_info.is_fullscreen; }