mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-07-09 17:25:37 -06:00
emulator: Set window icon to game icon. (#4586)
* emulator: Set window icon to game icon. * window: Match system icon look-and-feel on macOS * window: Log SDL error on failure to set icon
This commit is contained in:
parent
c7956d066e
commit
0d5f7e83b7
@ -816,6 +816,10 @@ set(COMMON src/common/logging/classes.h
|
||||
src/common/key_manager.h
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND COMMON src/common/apple.h src/common/apple.mm)
|
||||
endif()
|
||||
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
list(APPEND COMMON src/common/discord_rpc_handler.cpp src/common/discord_rpc_handler.h)
|
||||
endif()
|
||||
|
||||
12
src/common/apple.h
Normal file
12
src/common/apple.h
Normal file
@ -0,0 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace Common {
|
||||
|
||||
void SetAppIcon(const std::filesystem::path& path);
|
||||
|
||||
} // namespace Common
|
||||
47
src/common/apple.mm
Normal file
47
src/common/apple.mm
Normal file
@ -0,0 +1,47 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <filesystem>
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include "common/apple.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void SetAppIcon(const std::filesystem::path& path) {
|
||||
@autoreleasepool {
|
||||
NSString* path_str = [NSString stringWithUTF8String:path.native().c_str()];
|
||||
NSImage* base_icon = [[[NSImage alloc] initWithContentsOfFile:path_str] autorelease];
|
||||
|
||||
// Transform the icon to match native look-and-feel.
|
||||
constexpr double ScaleFactor = 13.0 / 16.0;
|
||||
constexpr double CornerRadiusFactor = 22.0 / 100.0;
|
||||
|
||||
const double base_icon_width = base_icon.size.width;
|
||||
const double base_icon_height = base_icon.size.height;
|
||||
const double icon_width = base_icon_width * ScaleFactor;
|
||||
const double icon_height = base_icon_height * ScaleFactor;
|
||||
const double icon_x = (base_icon_width - icon_width) / 2.0;
|
||||
const double icon_y = (base_icon_height - icon_height) / 2.0;
|
||||
const double corner_radius_x = icon_width * CornerRadiusFactor;
|
||||
const double corner_radius_y = icon_height * CornerRadiusFactor;
|
||||
|
||||
NSRect bounds = NSMakeRect(icon_x, icon_y, icon_width, icon_height);
|
||||
NSBezierPath* mask_path = [NSBezierPath bezierPathWithRoundedRect:bounds
|
||||
xRadius:corner_radius_x
|
||||
yRadius:corner_radius_y];
|
||||
|
||||
NSImage* rounded_icon = [[[NSImage alloc] initWithSize:base_icon.size] autorelease];
|
||||
[rounded_icon lockFocus];
|
||||
[mask_path addClip];
|
||||
[base_icon drawInRect:bounds
|
||||
fromRect:NSZeroRect
|
||||
operation:NSCompositingOperationSourceOver
|
||||
fraction:1.0f];
|
||||
[rounded_icon unlockFocus];
|
||||
|
||||
[NSApp setApplicationIconImage:rounded_icon];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@ -430,6 +430,11 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
|
||||
g_window = window.get();
|
||||
|
||||
std::filesystem::path icon_path = mnt->GetHostPath("/app0/sce_sys/icon0.png");
|
||||
if (std::filesystem::exists(icon_path)) {
|
||||
window->SetIcon(icon_path);
|
||||
}
|
||||
|
||||
const auto& mount_data_dir = Common::FS::GetUserPath(Common::FS::PathType::GameDataDir);
|
||||
mnt->Mount(mount_data_dir, "/data");
|
||||
|
||||
|
||||
@ -1,14 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "SDL3/SDL_events.h"
|
||||
#include "SDL3/SDL_hints.h"
|
||||
#include "SDL3/SDL_init.h"
|
||||
#include "SDL3/SDL_properties.h"
|
||||
#include "SDL3/SDL_timer.h"
|
||||
#include "SDL3/SDL_video.h"
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_properties.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
#include <SDL3/SDL_video.h>
|
||||
#include <stb_image.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/logging/formatter.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/devtools/layer.h"
|
||||
#include "core/emulator_settings.h"
|
||||
@ -24,9 +29,9 @@
|
||||
#include "video_core/renderdoc.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "SDL3/SDL_metal.h"
|
||||
#include <SDL3/SDL_metal.h>
|
||||
#include "common/apple.h"
|
||||
#endif
|
||||
#include <core/emulator_settings.h>
|
||||
|
||||
namespace Frontend {
|
||||
|
||||
@ -172,6 +177,55 @@ WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameControllers* controller
|
||||
|
||||
WindowSDL::~WindowSDL() = default;
|
||||
|
||||
void WindowSDL::SetIcon(const std::filesystem::path& path) {
|
||||
#ifdef __APPLE__
|
||||
// Use native path which matches system icon look-and-feel.
|
||||
Common::SetAppIcon(path);
|
||||
#else
|
||||
Common::FS::IOFile file{path, Common::FS::FileAccessMode::Read,
|
||||
Common::FS::FileType::BinaryFile,
|
||||
Common::FS::FileShareFlag::ShareReadWrite};
|
||||
if (!file.IsOpen()) {
|
||||
LOG_ERROR(Core, "Failed to open window icon file '{}'.", fmt::UTF(path.u8string()));
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 file_size = file.GetSize();
|
||||
std::vector<u8> buf(file_size);
|
||||
const size_t bytes_read = file.ReadRaw<u8>(buf.data(), file_size);
|
||||
file.Close();
|
||||
if (bytes_read < file_size) {
|
||||
LOG_ERROR(Core, "Failed to read window icon file '{}'.", fmt::UTF(path.u8string()));
|
||||
return;
|
||||
}
|
||||
|
||||
int image_width = 0;
|
||||
int image_height = 0;
|
||||
constexpr int num_channels = 4;
|
||||
unsigned char* image_data =
|
||||
stbi_load_from_memory(buf.data(), static_cast<int>(buf.size()), &image_width, &image_height,
|
||||
nullptr, num_channels);
|
||||
if (image_data == nullptr) {
|
||||
LOG_ERROR(Core, "Failed to load window icon image '{}': {}", fmt::UTF(path.u8string()),
|
||||
stbi_failure_reason());
|
||||
return;
|
||||
}
|
||||
SCOPE_EXIT {
|
||||
stbi_image_free(image_data);
|
||||
};
|
||||
|
||||
SDL_Surface* surface = SDL_CreateSurfaceFrom(image_width, image_height, SDL_PIXELFORMAT_RGBA32,
|
||||
image_data, image_width * num_channels);
|
||||
if (surface == nullptr) {
|
||||
LOG_ERROR(Core, "Failed to create SDL surface for window icon: {}", SDL_GetError());
|
||||
}
|
||||
if (!SDL_SetWindowIcon(window, surface)) {
|
||||
LOG_ERROR(Core, "Failed to set SDL window icon: {}", SDL_GetError());
|
||||
}
|
||||
SDL_DestroySurface(surface);
|
||||
#endif
|
||||
}
|
||||
|
||||
void WindowSDL::WaitEvent() {
|
||||
// Called on main thread
|
||||
SDL_Event event;
|
||||
|
||||
@ -71,6 +71,8 @@ public:
|
||||
return window_info;
|
||||
}
|
||||
|
||||
void SetIcon(const std::filesystem::path& path);
|
||||
|
||||
void WaitEvent();
|
||||
void InitTimers();
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user