Implement basic windowing on Mac

This commit is contained in:
Wojtek Figat
2021-12-30 13:03:39 +01:00
parent 5a3138fd7b
commit 25cc8c6ab2
4 changed files with 197 additions and 25 deletions

View File

@@ -4,6 +4,7 @@
#include "MacPlatform.h"
#include "MacWindow.h"
#include "MacUtils.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Types/String.h"
@@ -41,6 +42,7 @@
#include <CoreGraphics/CoreGraphics.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <IOKit/IOKitLib.h>
#include <Cocoa/Cocoa.h>
#include <dlfcn.h>
#if CRASH_LOG_ENABLE
#include <execinfo.h>
@@ -50,8 +52,9 @@ CPUInfo MacCpu;
Guid DeviceId;
String UserLocale, ComputerName;
double SecondsPerCycle;
NSAutoreleasePool* AutoreleasePool = nullptr;
String ToString(CFStringRef str)
String MacUtils::ToString(CFStringRef str)
{
String result;
const int32 length = CFStringGetLength(str);
@@ -64,17 +67,42 @@ String ToString(CFStringRef str)
return result;
}
CFStringRef ToString(const String& str)
CFStringRef MacUtils::ToString(const String& str)
{
return CFStringCreateWithBytes(nullptr, (const UInt8*)str.Get(), str.Length() * sizeof(Char), kCFStringEncodingUTF16LE, false);
}
Vector2 MacUtils::PosToCoca(const Vector2& pos)
{
// MacOS uses y-coordinate starting at the bottom of the screen
Vector2 result = pos;
result.Y *= -1;
result += GetScreensOrigin();
return result;
}
Vector2 MacUtils::GetScreensOrigin()
{
Vector2 result = Vector2::Zero;
NSArray* screenArray = [NSScreen screens];
for (NSUInteger i = 0; i < [screenArray count]; i++)
{
NSRect rect = [[screenArray objectAtIndex:i] frame];
Vector2 pos(rect.origin.x, rect.origin.y + rect.size.height);
if (pos.X < result.X)
result.X = pos.X;
if (pos.Y > result.Y)
result.Y = pos.Y;
}
return result;
}
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
{
if (CommandLine::Options.Headless)
return DialogResult::None;
CFStringRef textRef = ToString(text);
CFStringRef captionRef = ToString(caption);
CFStringRef textRef = MacUtils::ToString(text);
CFStringRef captionRef = MacUtils::ToString(caption);
CFOptionFlags flags = 0;
switch (buttons)
{
@@ -309,7 +337,7 @@ bool MacPlatform::Init()
io_registry_entry_t ioRegistryRoot = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/");
CFStringRef deviceUuid = (CFStringRef)IORegistryEntryCreateCFProperty(ioRegistryRoot, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0);
IOObjectRelease(ioRegistryRoot);
String uuidStr = ToString(deviceUuid);
String uuidStr = MacUtils::ToString(deviceUuid);
Guid::Parse(uuidStr, DeviceId);
CFRelease(deviceUuid);
}
@@ -319,8 +347,8 @@ bool MacPlatform::Init()
CFLocaleRef locale = CFLocaleCopyCurrent();
CFStringRef localeLang = (CFStringRef)CFLocaleGetValue(locale, kCFLocaleLanguageCode);
CFStringRef localeCountry = (CFStringRef)CFLocaleGetValue(locale, kCFLocaleCountryCode);
UserLocale = ToString(localeLang);
String localeCountryStr = ToString(localeCountry);
UserLocale = MacUtils::ToString(localeLang);
String localeCountryStr = MacUtils::ToString(localeCountry);
if (localeCountryStr.HasChars())
UserLocale += TEXT("-") + localeCountryStr;
CFRelease(locale);
@@ -331,7 +359,7 @@ bool MacPlatform::Init()
// Get computer name
{
CFStringRef computerName = SCDynamicStoreCopyComputerName(nullptr, nullptr);
ComputerName = ToString(computerName);
ComputerName = MacUtils::ToString(computerName);
CFRelease(computerName);
}
@@ -342,6 +370,13 @@ bool MacPlatform::Init()
OnPlatformUserAdd(New<User>(username));
}
// Init application
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
NSMenu* mainMenu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
[NSApp setMainMenu:mainMenu];
AutoreleasePool = [[NSAutoreleasePool alloc] init];
Input::Mouse = New<MacMouse>();
Input::Keyboard = New<MacKeyboard>();
@@ -365,10 +400,22 @@ void MacPlatform::LogInfo()
void MacPlatform::BeforeRun()
{
[NSApp finishLaunching];
}
void MacPlatform::Tick()
{
// Process system events
while (true)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
if (event == nil)
break;
[NSApp sendEvent:event];
}
[AutoreleasePool drain];
AutoreleasePool = [[NSAutoreleasePool alloc] init];
}
void MacPlatform::BeforeExit()

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/String.h"
#include <CoreFoundation/CoreFoundation.h>
class FLAXENGINE_API MacUtils
{
public:
static String ToString(CFStringRef str);
static CFStringRef ToString(const String& str);
static Vector2 PosToCoca(const Vector2& pos);
static Vector2 GetScreensOrigin();
};

View File

@@ -3,31 +3,94 @@
#if PLATFORM_MAC
#include "../Window.h"
#include "MacUtils.h"
#include "Engine/Graphics/RenderTask.h"
#include <Cocoa/Cocoa.h>
@interface MacWindowImpl : NSWindow <NSWindowDelegate>
{
MacWindow* Window;
}
- (void)setWindow:(MacWindow*)window;
@end
@implementation MacWindowImpl
- (void)windowWillClose:(NSNotification*)notification
{
[self setDelegate: nil];
Window->Close(ClosingReason::User);
}
- (void)setWindow:(MacWindow*)window
{
Window = window;
}
@end
MacWindow::MacWindow(const CreateWindowSettings& settings)
: WindowBase(settings)
{
int32 x = Math::TruncToInt(settings.Position.X);
int32 y = Math::TruncToInt(settings.Position.Y);
int32 clientWidth = Math::TruncToInt(settings.Size.X);
int32 clientHeight = Math::TruncToInt(settings.Size.Y);
int32 windowWidth = clientWidth;
int32 windowHeight = clientHeight;
_clientSize = Vector2((float)clientWidth, (float)clientHeight);
_clientSize = Vector2(settings.Size.X, settings.Size.Y);
Vector2 pos = MacUtils::PosToCoca(settings.Position);
NSRect frame = NSMakeRect(pos.X, pos.Y - settings.Size.Y, settings.Size.X, settings.Size.Y);
NSUInteger styleMask = NSWindowStyleMaskClosable;
if (settings.IsRegularWindow)
{
styleMask |= NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView;
if (settings.AllowMinimize)
styleMask |= NSWindowStyleMaskMiniaturizable;
if (settings.HasSizingFrame || settings.AllowMaximize)
styleMask |= NSWindowStyleMaskResizable;
}
else
{
styleMask |= NSWindowStyleMaskBorderless;
}
if (settings.HasBorder)
{
styleMask |= NSWindowStyleMaskTitled;
styleMask &= ~NSWindowStyleMaskFullSizeContentView;
}
// TODO: setup window
MacWindowImpl* window = [[MacWindowImpl alloc] initWithContentRect:frame
styleMask:(styleMask)
backing:NSBackingStoreBuffered
defer:NO];
window.title = (__bridge NSString*)MacUtils::ToString(settings.Title);
[window setWindow:this];
[window setReleasedWhenClosed:NO];
[window setBackgroundColor:[NSColor blueColor]];
[window setMinSize:NSMakeSize(settings.MinimumSize.X, settings.MinimumSize.Y)];
[window setMaxSize:NSMakeSize(settings.MaximumSize.X, settings.MaximumSize.Y)];
[window setOpaque:!settings.SupportsTransparency];
[window setDelegate:window];
_window = window;
// TODO: impl Parent for MacWindow
// TODO: impl StartPosition for MacWindow
// TODO: impl Fullscreen for MacWindow
// TODO: impl ShowInTaskbar for MacWindow
// TODO: impl ActivateWhenFirstShown for MacWindow
// TODO: impl AllowInput for MacWindow
// TODO: impl AllowDragAndDrop for MacWindow
// TODO: impl IsTopmost for MacWindow
}
MacWindow::~MacWindow()
{
// TODO: close window
NSWindow* window = (NSWindow*)_window;
[window close];
[window release];
_window = nullptr;
}
void* MacWindow::GetNativePtr() const
{
// TODO: return window handle
return nullptr;
return _window;
}
void MacWindow::Show()
@@ -43,7 +106,10 @@ void MacWindow::Show()
}
// Show
// TODO: show window
NSWindow* window = (NSWindow*)_window;
[window makeKeyAndOrderFront:window];
if (_settings.ActivateWhenFirstShown)
[NSApp activateIgnoringOtherApps:YES];
_focused = true;
// Base
@@ -56,7 +122,8 @@ void MacWindow::Hide()
if (_visible)
{
// Hide
// TODO: hide window
NSWindow* window = (NSWindow*)_window;
[window orderOut:nil];
// Base
WindowBase::Hide();
@@ -65,22 +132,34 @@ void MacWindow::Hide()
void MacWindow::Minimize()
{
// TODO: Minimize
if (!_settings.AllowMinimize)
return;
NSWindow* window = (NSWindow*)_window;
if (!window.miniaturized)
[window miniaturize:nil];
}
void MacWindow::Maximize()
{
// TODO: Maximize
if (!_settings.AllowMaximize)
return;
NSWindow* window = (NSWindow*)_window;
if (!window.zoomed)
[window zoom:nil];
}
void MacWindow::Restore()
{
// TODO: Restore
NSWindow* window = (NSWindow*)_window;
if (window.miniaturized)
[window deminiaturize:nil];
else if (window.zoomed)
[window zoom:nil];
}
bool MacWindow::IsClosed() const
{
return false;
return _window != nullptr;
}
bool MacWindow::IsForegroundWindow() const
@@ -88,8 +167,34 @@ bool MacWindow::IsForegroundWindow() const
return Platform::GetHasFocus();
}
void MacWindow::BringToFront(bool force)
{
NSWindow* window = (NSWindow*)_window;
[window activateIgnoringOtherApps:force];
}
void MacWindow::SetIsFullscreen(bool isFullscreen)
{
// TODO: fullscreen mode on Mac
}
void MacWindow::SetOpacity(float opacity)
{
NSWindow* window = (NSWindow*)_window;
[window setAlphaValue:opacity];
}
void MacWindow::Focus()
{
NSWindow* window = (NSWindow*)_window;
[window makeKeyAndOrderFront:window];
}
void MacWindow::SetTitle(const StringView& title)
{
_title = title;
NSWindow* window = (NSWindow*)_window;
[window setTitle:(__bridge NSString*)MacUtils::ToString(_title)];
}
#endif

View File

@@ -15,6 +15,7 @@ class MacWindow : public WindowBase
private:
Vector2 _clientSize;
void* _window;
public:
@@ -32,7 +33,11 @@ public:
void Restore() override;
bool IsClosed() const override;
bool IsForegroundWindow() const override;
void BringToFront(bool force) override;
void SetIsFullscreen(bool isFullscreen) override;
void SetOpacity(float opacity) override;
void Focus() override;
void SetTitle(const StringView& title) override;
};
#endif