123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- // Copyright 2023 Dolphin Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include "DolphinNoGUI/Platform.h"
- #include "Common/MsgHandler.h"
- #include "Core/Config/MainSettings.h"
- #include "Core/Core.h"
- #include "Core/State.h"
- #include "Core/System.h"
- #include "VideoCommon/Present.h"
- #include "VideoCommon/RenderBase.h"
- #include <AppKit/AppKit.h>
- #include <Carbon/Carbon.h>
- #include <Foundation/Foundation.h>
- #include <array>
- #include <chrono>
- #include <climits>
- #include <cstdio>
- #include <cstring>
- #include <thread>
- @interface Application : NSApplication
- @property Platform* platform;
- - (void)shutdown;
- - (void)togglePause;
- - (void)saveScreenShot;
- - (void)loadLastSaved;
- - (void)undoLoadState;
- - (void)undoSaveState;
- - (void)loadState:(id)sender;
- - (void)saveState:(id)sender;
- @end
- @implementation Application
- - (void)shutdown;
- {
- [self platform]->RequestShutdown();
- [self stop:nil];
- }
- - (void)togglePause
- {
- auto& system = Core::System::GetInstance();
- if (Core::GetState(system) == Core::State::Running)
- Core::SetState(system, Core::State::Paused);
- else
- Core::SetState(system, Core::State::Running);
- }
- - (void)saveScreenShot
- {
- Core::SaveScreenShot();
- }
- - (void)loadLastSaved
- {
- State::LoadLastSaved(Core::System::GetInstance());
- }
- - (void)undoLoadState
- {
- State::UndoLoadState(Core::System::GetInstance());
- }
- - (void)undoSaveState
- {
- State::UndoSaveState(Core::System::GetInstance());
- }
- - (void)loadState:(id)sender
- {
- State::Load(Core::System::GetInstance(), [sender tag]);
- }
- - (void)saveState:(id)sender
- {
- State::Save(Core::System::GetInstance(), [sender tag]);
- }
- @end
- @interface AppDelegate : NSObject <NSApplicationDelegate>
- @property(readonly) Platform* platform;
- - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender;
- - (id)initWithPlatform:(Platform*)platform;
- @end
- @implementation AppDelegate
- - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender
- {
- return YES;
- }
- - (id)initWithPlatform:(Platform*)platform
- {
- self = [super init];
- if (self)
- {
- _platform = platform;
- }
- return self;
- }
- @end
- @interface WindowDelegate : NSObject <NSWindowDelegate>
- - (void)windowDidResize:(NSNotification*)notification;
- @end
- @implementation WindowDelegate
- - (void)windowDidResize:(NSNotification*)notification
- {
- if (g_presenter)
- g_presenter->ResizeSurface();
- }
- @end
- namespace
- {
- class PlatformMacOS : public Platform
- {
- public:
- ~PlatformMacOS() override;
- bool Init() override;
- void SetTitle(const std::string& title) override;
- void MainLoop() override;
- WindowSystemInfo GetWindowSystemInfo() const override;
- private:
- void ProcessEvents();
- void UpdateWindowPosition();
- void HandleSaveStates(NSUInteger key, NSUInteger flags);
- void SetupMenu();
- NSRect m_window_rect;
- NSWindow* m_window;
- NSMenu* menuBar;
- AppDelegate* m_app_delegate;
- WindowDelegate* m_window_delegate;
- int m_window_x = Config::Get(Config::MAIN_RENDER_WINDOW_XPOS);
- int m_window_y = Config::Get(Config::MAIN_RENDER_WINDOW_YPOS);
- unsigned int m_window_width = Config::Get(Config::MAIN_RENDER_WINDOW_WIDTH);
- unsigned int m_window_height = Config::Get(Config::MAIN_RENDER_WINDOW_HEIGHT);
- bool m_window_fullscreen = Config::Get(Config::MAIN_FULLSCREEN);
- };
- PlatformMacOS::~PlatformMacOS()
- {
- [m_window close];
- }
- bool PlatformMacOS::Init()
- {
- [Application sharedApplication];
- m_app_delegate = [[AppDelegate alloc] initWithPlatform:this];
- [NSApp setDelegate:m_app_delegate];
- [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
- [NSApp setPlatform:this];
- [Application.sharedApplication finishLaunching];
- unsigned long styleMask =
- NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable;
- m_window_rect = CGRectMake(m_window_x, m_window_y, m_window_width, m_window_height);
- m_window = [NSWindow alloc];
- m_window = [m_window initWithContentRect:m_window_rect
- styleMask:styleMask
- backing:NSBackingStoreBuffered
- defer:NO];
- m_window_delegate = [[WindowDelegate alloc] init];
- [m_window setDelegate:m_window_delegate];
- NSNotificationCenter* c = [NSNotificationCenter defaultCenter];
- [c addObserver:NSApp
- selector:@selector(shutdown)
- name:NSWindowWillCloseNotification
- object:m_window];
- if (m_window == nil)
- {
- NSLog(@"Window is %@\n", m_window);
- return false;
- }
- if (Config::Get(Config::MAIN_SHOW_CURSOR) == Config::ShowCursor::Never)
- [NSCursor hide];
- if (Config::Get(Config::MAIN_FULLSCREEN))
- {
- m_window_fullscreen = true;
- [m_window toggleFullScreen:m_window];
- }
- [m_window makeKeyAndOrderFront:NSApp];
- [m_window makeMainWindow];
- [NSApp activateIgnoringOtherApps:YES];
- [m_window setTitle:@"Dolphin-emu-nogui"];
- SetupMenu();
- return true;
- }
- void PlatformMacOS::SetTitle(const std::string& title)
- {
- @autoreleasepool
- {
- NSWindow* window = m_window;
- NSString* str = [NSString stringWithUTF8String:title.c_str()];
- dispatch_async(dispatch_get_main_queue(), ^{
- [window setTitle:str];
- });
- }
- }
- void PlatformMacOS::MainLoop()
- {
- while (IsRunning())
- {
- UpdateRunningFlag();
- Core::HostDispatchJobs(Core::System::GetInstance());
- ProcessEvents();
- UpdateWindowPosition();
- }
- }
- WindowSystemInfo PlatformMacOS::GetWindowSystemInfo() const
- {
- @autoreleasepool
- {
- WindowSystemInfo wsi;
- wsi.type = WindowSystemType::MacOS;
- wsi.render_window = (void*)CFBridgingRetain([m_window contentView]);
- wsi.render_surface = wsi.render_window;
- return wsi;
- }
- }
- void PlatformMacOS::ProcessEvents()
- {
- @autoreleasepool
- {
- NSDate* expiration = [NSDate dateWithTimeIntervalSinceNow:1];
- NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
- untilDate:expiration
- inMode:NSDefaultRunLoopMode
- dequeue:YES];
- [NSApp sendEvent:event];
- // Need to update if m_window becomes fullscreen
- m_window_fullscreen = [m_window styleMask] & NSWindowStyleMaskFullScreen;
- if ([m_window isMainWindow])
- {
- m_window_focus = true;
- if (Config::Get(Config::MAIN_SHOW_CURSOR) == Config::ShowCursor::Never &&
- Core::GetState(Core::System::GetInstance()) != Core::State::Paused)
- {
- [NSCursor unhide];
- }
- }
- else
- {
- m_window_focus = false;
- if (Config::Get(Config::MAIN_SHOW_CURSOR) == Config::ShowCursor::Never)
- [NSCursor hide];
- }
- }
- }
- void PlatformMacOS::UpdateWindowPosition()
- {
- if (m_window_fullscreen)
- return;
- NSRect win = [m_window frame];
- m_window_x = win.origin.x;
- m_window_y = win.origin.y;
- m_window_width = win.size.width;
- m_window_height = win.size.height;
- }
- void PlatformMacOS::SetupMenu()
- {
- @autoreleasepool
- {
- menuBar = [NSMenu new];
- NSMenu* appMenu = [NSMenu new];
- NSMenu* stateMenu = [[NSMenu alloc] initWithTitle:@"States"];
- NSMenu* loadStateMenu = [[NSMenu alloc] initWithTitle:@"Load"];
- NSMenu* saveStateMenu = [[NSMenu alloc] initWithTitle:@"Save"];
- NSMenu* miscMenu = [[NSMenu alloc] initWithTitle:@"Misc"];
- NSMenuItem* appMenuItem = [NSMenuItem new];
- NSMenuItem* miscMenuItem = [NSMenuItem new];
- NSMenuItem* stateMenuItem = [NSMenuItem new];
- NSMenuItem* loadStateItem = [[NSMenuItem alloc] initWithTitle:@"Load"
- action:nil
- keyEquivalent:@""];
- NSMenuItem* saveStateItem = [[NSMenuItem alloc] initWithTitle:@"Save"
- action:nil
- keyEquivalent:@""];
- [menuBar addItem:appMenuItem];
- [menuBar addItem:stateMenuItem];
- [menuBar addItem:miscMenuItem];
- // Quit
- NSString* quitTitle = [@"Quit " stringByAppendingString:@"dolphin-emu-nogui"];
- NSMenuItem* quitMenuItem = [[NSMenuItem alloc] initWithTitle:quitTitle
- action:@selector(shutdown)
- keyEquivalent:@"q"];
- // Fullscreen
- NSString* fullScreenItemTitle = @"Toggle Fullscreen";
- NSMenuItem* fullScreenItem = [[NSMenuItem alloc] initWithTitle:fullScreenItemTitle
- action:@selector(toggleFullScreen:)
- keyEquivalent:@"f"];
- [fullScreenItem setKeyEquivalentModifierMask:NSEventModifierFlagFunction];
- // Screenshot
- NSString* ScreenShotTitle = @"Take Screenshot";
- unichar c = NSF9FunctionKey;
- NSString* f9 = [NSString stringWithCharacters:&c length:1];
- NSMenuItem* ScreenShotItem = [[NSMenuItem alloc] initWithTitle:ScreenShotTitle
- action:@selector(saveScreenShot)
- keyEquivalent:f9];
- [ScreenShotItem setKeyEquivalentModifierMask:NSEventModifierFlagFunction];
- // Pause game
- NSString* pauseTitle = @"Toggle pause";
- c = NSF10FunctionKey;
- NSString* f10 = [NSString stringWithCharacters:&c length:1];
- NSMenuItem* pauseItem = [[NSMenuItem alloc] initWithTitle:pauseTitle
- action:@selector(togglePause)
- keyEquivalent:f10];
- [pauseItem setKeyEquivalentModifierMask:NSEventModifierFlagFunction];
- // Load last save
- NSString* loadLastTitle = @"Load Last Saved";
- c = NSF11FunctionKey;
- NSString* f11 = [NSString stringWithCharacters:&c length:1];
- NSMenuItem* loadLastItem = [[NSMenuItem alloc] initWithTitle:loadLastTitle
- action:@selector(loadLastSaved)
- keyEquivalent:f11];
- [loadLastItem setKeyEquivalentModifierMask:NSEventModifierFlagFunction];
- // Undo Load State
- NSString* undoLoadTitle = @"Undo Load";
- c = NSF12FunctionKey;
- NSString* f12 = [NSString stringWithCharacters:&c length:1];
- NSMenuItem* undoLoadItem = [[NSMenuItem alloc] initWithTitle:undoLoadTitle
- action:@selector(undoLoadState)
- keyEquivalent:f12];
- [undoLoadItem setKeyEquivalentModifierMask:NSEventModifierFlagShift];
- // Undo Save State
- NSString* undoSaveTitle = @"Undo Save";
- NSMenuItem* undoSaveItem = [[NSMenuItem alloc] initWithTitle:undoSaveTitle
- action:@selector(undoSaveState)
- keyEquivalent:f12];
- [undoSaveItem setKeyEquivalentModifierMask:NSEventModifierFlagFunction];
- // Load and Save States
- for (unichar i = NSF1FunctionKey; i <= NSF8FunctionKey; i++)
- {
- NSInteger stateNum = i - NSF1FunctionKey + 1;
- NSString* lstateTitle = [NSString stringWithFormat:@"Load State %ld", (long)stateNum];
- c = i;
- NSString* t = [NSString stringWithCharacters:&c length:1];
- NSMenuItem* lstateItem = [[NSMenuItem alloc] initWithTitle:lstateTitle
- action:@selector(loadState:)
- keyEquivalent:t];
- [lstateItem setTag:stateNum];
- [lstateItem setKeyEquivalentModifierMask:NSEventModifierFlagFunction];
- [loadStateMenu addItem:lstateItem];
- NSString* sstateTitle = [NSString stringWithFormat:@"Save State %ld", (long)stateNum];
- c = i;
- NSMenuItem* sstateItem = [[NSMenuItem alloc] initWithTitle:sstateTitle
- action:@selector(saveState:)
- keyEquivalent:t];
- [sstateItem setKeyEquivalentModifierMask:NSEventModifierFlagShift];
- [sstateItem setTag:stateNum];
- [saveStateMenu addItem:sstateItem];
- }
- // App Main menu
- [appMenu addItem:quitMenuItem];
- // State Menu
- [loadStateItem setSubmenu:loadStateMenu];
- [saveStateItem setSubmenu:saveStateMenu];
- [stateMenu addItem:loadLastItem];
- [stateMenu addItem:undoLoadItem];
- [stateMenu addItem:undoSaveItem];
- [stateMenu addItem:loadStateItem];
- [stateMenu addItem:saveStateItem];
- // Misc Menu
- [miscMenu addItem:fullScreenItem];
- [miscMenu addItem:ScreenShotItem];
- [miscMenu addItem:pauseItem];
- [appMenuItem setSubmenu:appMenu];
- [stateMenuItem setSubmenu:stateMenu];
- [miscMenuItem setSubmenu:miscMenu];
- [NSApp setMainMenu:menuBar];
- }
- }
- } // namespace
- std::unique_ptr<Platform> Platform::CreateMacOSPlatform()
- {
- return std::make_unique<PlatformMacOS>();
- }
|