From 19789b360bf0824d5241036c0b5749750a9c8a91 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Wed, 5 Jul 2023 09:34:27 +0200 Subject: [PATCH] Added a rough MacOS backend --- Examples/Example1/CMakeLists.txt | 2 +- Examples/Example2/CMakeLists.txt | 2 +- Examples/Example2/cube.cpp | 3 + Examples/Example2/linmath.h | 4 +- Examples/Example3/CMakeLists.txt | 2 +- Examples/Teapots/CMakeLists.txt | 4 + WSIWindow/CInstance.cpp | 11 +- WSIWindow/CMakeLists.txt | 32 +- WSIWindow/Validation.h | 3 + WSIWindow/VulkanWrapper/vulkan/vulkan.h | 4 +- WSIWindow/VulkanWrapper/vulkan_wrapper.cpp | 10 + WSIWindow/VulkanWrapper/vulkan_wrapper.h | 8 + WSIWindow/WSIWindow.cpp | 4 + WSIWindow/window_cocoa.h | 84 ++++ WSIWindow/window_cocoa.mm | 555 +++++++++++++++++++++ 15 files changed, 715 insertions(+), 13 deletions(-) create mode 100644 WSIWindow/window_cocoa.h create mode 100644 WSIWindow/window_cocoa.mm diff --git a/Examples/Example1/CMakeLists.txt b/Examples/Example1/CMakeLists.txt index a380fb0..b275333 100644 --- a/Examples/Example1/CMakeLists.txt +++ b/Examples/Example1/CMakeLists.txt @@ -1,7 +1,7 @@ project(Example1) cmake_minimum_required(VERSION 2.8) -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR APPLE) add_definitions(-std=c++11) endif() diff --git a/Examples/Example2/CMakeLists.txt b/Examples/Example2/CMakeLists.txt index e9eb73a..2ba2b58 100644 --- a/Examples/Example2/CMakeLists.txt +++ b/Examples/Example2/CMakeLists.txt @@ -1,7 +1,7 @@ project(Example2) cmake_minimum_required(VERSION 2.8) -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR APPLE) add_definitions(-std=c++11) endif() diff --git a/Examples/Example2/cube.cpp b/Examples/Example2/cube.cpp index 690806c..5cfb82a 100644 --- a/Examples/Example2/cube.cpp +++ b/Examples/Example2/cube.cpp @@ -42,6 +42,9 @@ #include "linmath.h" #include "cube.h" +#ifndef VK_DYNAMIC_STATE_RANGE_SIZE +#define VK_DYNAMIC_STATE_RANGE_SIZE 9 +#endif #define DEMO_TEXTURE_COUNT 1 #define APP_SHORT_NAME "cube" diff --git a/Examples/Example2/linmath.h b/Examples/Example2/linmath.h index 6df757e..65e6f40 100644 --- a/Examples/Example2/linmath.h +++ b/Examples/Example2/linmath.h @@ -5,7 +5,9 @@ #include -# define M_PI 3.14159265358979323846 /* pi */ +#ifndef __APPLE__ +#define M_PI 3.14159265358979323846 /* pi */ +#endif // Converts degrees to radians. #define degreesToRadians(angleDegrees) (angleDegrees * M_PI / 180.0) diff --git a/Examples/Example3/CMakeLists.txt b/Examples/Example3/CMakeLists.txt index 7ca662d..e78f923 100644 --- a/Examples/Example3/CMakeLists.txt +++ b/Examples/Example3/CMakeLists.txt @@ -1,7 +1,7 @@ project(Example3) cmake_minimum_required(VERSION 2.8) -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR APPLE) add_definitions(-std=c++11) endif() diff --git a/Examples/Teapots/CMakeLists.txt b/Examples/Teapots/CMakeLists.txt index 8c24c1c..cfe35c0 100644 --- a/Examples/Teapots/CMakeLists.txt +++ b/Examples/Teapots/CMakeLists.txt @@ -13,6 +13,10 @@ endif() if(ANDROID) add_definitions(-DVK_USE_PLATFORM_ANDROID_KHR) endif() +if(APPLE) + add_definitions(-DVK_USE_PLATFORM_METAL_EXT) + add_definitions(-D-DWIN32_LEAN_AND_MEAN) +endif() #------------------------------------------------------------------------------------------------- #---------------------------------------Find glslangValidator------------------------------------- if(NOT EXISTS ${GLSLANG_VALIDATOR}) diff --git a/WSIWindow/CInstance.cpp b/WSIWindow/CInstance.cpp index 5f1d92a..556d53b 100644 --- a/WSIWindow/CInstance.cpp +++ b/WSIWindow/CInstance.cpp @@ -150,6 +150,10 @@ CInstance::CInstance(const bool enable_validation, const char* app_name, const c extensions.Pick(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); #elif VK_USE_PLATFORM_MIR_KHR extensions.Pick(VK_KHR_MIR_SURFACE_EXTENSION_NAME); +#elif VK_USE_PLATFORM_METAL_EXT + extensions.Pick("VK_KHR_portability_enumeration"); + //extensions.Pick("VK_KHR_portability_subset"); + extensions.Pick("VK_EXT_metal_surface"); #endif } else LOGE("Failed to load VK_KHR_Surface"); @@ -176,11 +180,16 @@ void CInstance::Create(const CLayers& layers, const CExtensions& extensions, con app_info.engineVersion = 1; app_info.apiVersion = VK_API_VERSION_1_0; + VkInstanceCreateFlags inst_flags = 0; +#ifdef VK_USE_PLATFORM_METAL_EXT + inst_flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; +#endif + // initialize the VkInstanceCreateInfo structure VkInstanceCreateInfo inst_info = {}; inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; inst_info.pNext = NULL; - inst_info.flags = 0; + inst_info.flags = inst_flags; inst_info.pApplicationInfo = &app_info; inst_info.enabledExtensionCount = extensions.PickCount(); inst_info.ppEnabledExtensionNames = extensions.PickList(); diff --git a/WSIWindow/CMakeLists.txt b/WSIWindow/CMakeLists.txt index ac05b4b..c2b7608 100644 --- a/WSIWindow/CMakeLists.txt +++ b/WSIWindow/CMakeLists.txt @@ -3,16 +3,14 @@ cmake_minimum_required(VERSION 2.8) set(LIBRARY_NAME WSIWindow) file(GLOB SRC_LIST "*.h" "*.cpp") # List all source files from current directory +file(GLOB OBJC_SRC_LIST "*.mm") +if(APPLE) + list(APPEND SRC_LIST ${OBJC_SRC_LIST}) +endif() add_library (${LIBRARY_NAME} STATIC ${SRC_LIST}) # and add them to the WSIWindow library. target_include_directories (${LIBRARY_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") #for find_package - -#=========================VULKAN_WRAPPER========================== -if (USE_VULKAN_WRAPPER) - add_subdirectory(VulkanWrapper) - target_link_libraries(${LIBRARY_NAME} VULKAN_WRAPPER) -endif() #================================================================= #============================VALIDATION=========================== # Validation may be enabled in both debug and release builds. @@ -94,6 +92,28 @@ endif() #if(ANDROID) # add_definitions(-DVK_USE_PLATFORM_ANDROID_KHR) #endif() +#================================================================= +#==============================MACOS============================== +if(APPLE) + add_definitions(-std=c++11) + add_definitions(-DVK_USE_PLATFORM_METAL_EXT) + + find_library(QUARTZCORE_LIB QuartzCore) + + target_link_libraries(${LIBRARY_NAME} + "-framework Cocoa" + ${QUARTZCORE_LIB} + ) + + set (VULKAN_LOADER_NAME "vulkan") +endif() + +#=========================VULKAN_WRAPPER========================== +if (USE_VULKAN_WRAPPER) + add_subdirectory(VulkanWrapper) + target_link_libraries(${LIBRARY_NAME} VULKAN_WRAPPER) +endif() + #================================================================= #=======================Find Vulkan Loader======================== if(TARGET ${VULKAN_LOADER_NAME}) # Check if "vulkan" loader target exists diff --git a/WSIWindow/Validation.h b/WSIWindow/Validation.h index b3db600..2cf5225 100644 --- a/WSIWindow/Validation.h +++ b/WSIWindow/Validation.h @@ -83,6 +83,9 @@ #include #define cTICK "\u2713" #define PAUSE +#elif __APPLE__ + #define cTICK "\u2713" + #define PAUSE #endif enum eColor { eRESET, eRED, eGREEN, eYELLOW, eBLUE, eMAGENTA, eCYAN, eWHITE, // normal colors diff --git a/WSIWindow/VulkanWrapper/vulkan/vulkan.h b/WSIWindow/VulkanWrapper/vulkan/vulkan.h index 5253f98..82a6026 100644 --- a/WSIWindow/VulkanWrapper/vulkan/vulkan.h +++ b/WSIWindow/VulkanWrapper/vulkan/vulkan.h @@ -6639,7 +6639,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateIOSSurfaceMVK( #endif #endif /* VK_USE_PLATFORM_IOS_MVK */ -#ifdef VK_USE_PLATFORM_MACOS_MVK +#ifdef VK_USE_PLATFORM_METAL_EXT #define VK_MVK_macos_surface 1 #define VK_MVK_MACOS_SURFACE_SPEC_VERSION 2 #define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface" @@ -6663,7 +6663,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateMacOSSurfaceMVK( const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); #endif -#endif /* VK_USE_PLATFORM_MACOS_MVK */ +#endif /* VK_USE_PLATFORM_METAL_EXT */ #define VK_EXT_external_memory_dma_buf 1 #define VK_EXT_EXTERNAL_MEMORY_DMA_BUF_SPEC_VERSION 1 diff --git a/WSIWindow/VulkanWrapper/vulkan_wrapper.cpp b/WSIWindow/VulkanWrapper/vulkan_wrapper.cpp index 230e2e0..d8ce710 100644 --- a/WSIWindow/VulkanWrapper/vulkan_wrapper.cpp +++ b/WSIWindow/VulkanWrapper/vulkan_wrapper.cpp @@ -31,6 +31,8 @@ extern "C" { int InitVulkan(void) { #ifdef _WIN32 HMODULE libvulkan = LoadLibrary("vulkan-1.dll"); +#elif __APPLE__ + void* libvulkan = dlopen("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL); #else void* libvulkan = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL); #endif @@ -259,6 +261,10 @@ int InitVulkan(void) { vkGetPhysicalDeviceWin32PresentationSupportKHR = reinterpret_cast( dlsym(libvulkan, "vkGetPhysicalDeviceWin32PresentationSupportKHR")); #endif + +#ifdef VK_USE_PLATFORM_METAL_EXT + vkCreateMetalSurfaceEXT = reinterpret_cast(dlsym(libvulkan, "vkCreateMetalSurfaceEXT")); +#endif vkCreateDebugReportCallbackEXT = reinterpret_cast(dlsym(libvulkan, "vkCreateDebugReportCallbackEXT")); vkDestroyDebugReportCallbackEXT = reinterpret_cast(dlsym(libvulkan, "vkDestroyDebugReportCallbackEXT")); vkDebugReportMessageEXT = reinterpret_cast(dlsym(libvulkan, "vkDebugReportMessageEXT")); @@ -450,6 +456,10 @@ PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR; PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR; #endif + +#ifdef VK_USE_PLATFORM_METAL_EXT +PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT; +#endif PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT; PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT; PFN_vkDebugReportMessageEXT vkDebugReportMessageEXT; diff --git a/WSIWindow/VulkanWrapper/vulkan_wrapper.h b/WSIWindow/VulkanWrapper/vulkan_wrapper.h index 811b5ac..442afa4 100644 --- a/WSIWindow/VulkanWrapper/vulkan_wrapper.h +++ b/WSIWindow/VulkanWrapper/vulkan_wrapper.h @@ -24,6 +24,10 @@ extern "C" { #define VK_NO_PROTOTYPES 1 #include +#ifdef VK_USE_PLATFORM_METAL_EXT +#include +#endif + /* Initialize the Vulkan function pointer variables declared in this header. * Returns 0 if vulkan is not available, non-zero if it is available. */ @@ -229,6 +233,10 @@ extern PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; extern PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR; #endif +#ifdef VK_USE_PLATFORM_METAL_EXT +extern PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT; +#endif + // VK_EXT_debug_report extern PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT; extern PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT; diff --git a/WSIWindow/WSIWindow.cpp b/WSIWindow/WSIWindow.cpp index 66c050b..d0e3c64 100644 --- a/WSIWindow/WSIWindow.cpp +++ b/WSIWindow/WSIWindow.cpp @@ -24,6 +24,7 @@ #include "window_android.h" #include "window_win32.h" #include "window_xcb.h" +#include "window_cocoa.h" //============================================================== WSIWindow::WSIWindow(const char* title, const uint width, const uint height) { @@ -36,6 +37,9 @@ WSIWindow::WSIWindow(const char* title, const uint width, const uint height) { #elif VK_USE_PLATFORM_ANDROID_KHR LOGI("PLATFORM: ANDROID\n"); pimpl = new Window_android(title, width, height); +#elif VK_USE_PLATFORM_METAL_EXT + LOGI("PLATFORM: COCOA\n"); + pimpl = new Window_cocoa(title, width, height); #endif // TODO: // #ifdef VK_USE_PLATFORM_XLIB_KHR diff --git a/WSIWindow/window_cocoa.h b/WSIWindow/window_cocoa.h new file mode 100644 index 0000000..bb91b52 --- /dev/null +++ b/WSIWindow/window_cocoa.h @@ -0,0 +1,84 @@ +/* +*-------------------------------------------------------------------------- +* Copyright (c) 2016 Valve Corporation +* Copyright (c) 2016 LunarG, Inc. +* Copyright (c) 2016-2017 Rene Lindsay +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* Author: Rene Lindsay +* +*-------------------------------------------------------------------------- +*/ + +//==========================Cocoa=============================== +#ifdef VK_USE_PLATFORM_METAL_EXT + +#ifndef WINDOW_COCOA +#define WINDOW_COCOA +//------------------------------------------------- +#include "WindowImpl.h" + +#ifdef __OBJC__ +#import +#else +typedef void* id; +#endif + +//#pragma warning(disable:4996) +// clang-format off +// Convert native Cocoa keyboard scancode to cross-platform USB HID code. +const unsigned char COCOA_TO_HID[256] = { + //TODO +}; +// clang-format on +//=============================Cocoa============================ +class Window_cocoa : public WindowImpl { + id appDelegate; + id window; + id windowDelegate; + id view; + id layer; + + void SetTitle(const char* title); + void SetWinPos (uint x, uint y); + void SetWinSize(uint w, uint h); + void CreateSurface(VkInstance instance); + + public: + Window_cocoa(const char* title, uint width, uint height); + virtual ~Window_cocoa(); + EventType GetEvent(bool wait_for_event = false); + bool CanPresent(VkPhysicalDevice phy, uint32_t queue_family); // check if this window can present this queue type + + inline id getNativeAppDelegate() { + return appDelegate; + } + + inline id getNativeWindow() { + return window; + } + + inline id getNativeWindowDelegate() { + return windowDelegate; + } + + inline id getNativeView() { + return view; + } +}; +//============================================================== +#endif + +#endif // VK_USE_PLATFORM_COCOA_KHR +//============================================================== diff --git a/WSIWindow/window_cocoa.mm b/WSIWindow/window_cocoa.mm new file mode 100644 index 0000000..ab4dda9 --- /dev/null +++ b/WSIWindow/window_cocoa.mm @@ -0,0 +1,555 @@ +/* +*-------------------------------------------------------------------------- +* Copyright (c) 2016 Valve Corporation +* Copyright (c) 2016 LunarG, Inc. +* Copyright (c) 2016-2017 Rene Lindsay +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* Author: Rene Lindsay +* +*-------------------------------------------------------------------------- +*/ + +#include "window_cocoa.h" + +#import + +//Last registered event +auto lastEvent = EventType::NONE; + +//=====================Cocoa OBJECTS===================== +static const NSRange nsEmptyRange = { NSNotFound, 0 }; + +@interface CocoaAppDelegate : NSObject { + +} + +- (void) menuItemClicked:(id) sender; + +@end + +@implementation CocoaAppDelegate + +- (void) menuItemClicked:(id) sender { + +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { + return NSTerminateCancel; +} + +- (void)applicationDidChangeScreenParameters:(NSNotification *) notification { + +} + +- (void)applicationWillFinishLaunching:(NSNotification *)notification { + +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification { + [NSApp stop:nil]; +} + +- (void)applicationDidHide:(NSNotification *)notification { + +} + +@end + +@interface CocoaHelper : NSObject +@end + +@implementation CocoaHelper + +- (void)selectedKeyboardInputSourceChanged:(NSObject* )object { + +} + +- (void)doNothing:(id)object { +} + +@end + +@interface CocoaWindowDelegate : NSObject { + Window_cocoa* window; + bool initFocusFinished; +} + +- (instancetype)initWithWSIWindow:(Window_cocoa*)initWindow; + +@end + +@implementation CocoaWindowDelegate + +- (instancetype)initWithWSIWindow:(Window_cocoa*)initWindow { + self = [super init]; + if (self != nil) { + window = initWindow; + initFocusFinished = false; + } + + return self; +} + +- (BOOL)windowShouldClose:(id)sender { + lastEvent = EventType::CLOSE; + + return NO; +} + +- (void)windowDidResize:(NSNotification *)notification { + const NSRect contentRect = [window->getNativeView() frame]; + const NSRect fbRect = [window->getNativeView() convertRectToBacking:contentRect]; + + //TODO +} + +- (void)windowDidMove:(NSNotification*)notification { + +} + +- (void)windowDidMiniaturize:(NSNotification*)notification { + +} + +- (void)windowDidDeminiaturize:(NSNotification*)notification { + +} + +- (void)windowDidBecomeKey:(NSNotification*)notification { + +} + +- (void)windowDidResignKey:(NSNotification*)notification { + +} + +- (void)windowDidChangeOcclusionState:(NSNotification*)notification { + +} + +@end + +//View + +@interface CocoaContentView : NSView { + Window_cocoa* window; + NSTrackingArea* trackingArea; + NSMutableAttributedString* markedText; +} + +- (instancetype)initWithWSIWindow:(Window_cocoa*)initWindow; + +@end + +@implementation CocoaContentView + +- (instancetype)initWithWSIWindow:(Window_cocoa*)initWindow { + self = [super init]; + if (self != nil) { + window = initWindow; + trackingArea = nil; + markedText = [[NSMutableAttributedString alloc] init]; + + [self updateTrackingAreas]; + [self registerForDraggedTypes:@[NSPasteboardTypeURL]]; + } + + return self; +} + +- (void)dealloc { + [trackingArea release]; + [markedText release]; + [super dealloc]; + [super release]; +} + +- (BOOL)isOpaque { + return YES; +} + +- (BOOL)canBecomeKeyView { + return YES; +} + +- (BOOL)acceptsFirstResponder { + return YES; +} + +- (BOOL)wantsUpdateLayer { + return YES; +} + +- (void)updateLayer { + +} + +- (void)cursorUpdate:(NSEvent*)event { + +} + +- (BOOL)acceptsFirstMouse:(NSEvent*)event { + return YES; +} + +- (void)mouseDown:(NSEvent*)event { + //TODO +} + +- (void)mouseDragged:(NSEvent*)event { + [self mouseMoved:event]; +} + +- (void)mouseUp:(NSEvent*)event { + //TODO +} + +- (void)mouseMoved:(NSEvent*)event { + //TODO +} + +- (void)rightMouseDown:(NSEvent*)event { + //TODO +} + +- (void)rightMouseDragged:(NSEvent*)event { + [self mouseMoved:event]; +} + +- (void)rightMouseUp:(NSEvent*)event { + //TODO +} + +- (void)otherMouseDown:(NSEvent*)event { + +} + +- (void)otherMouseDragged:(NSEvent*)event { + [self mouseMoved:event]; +} + +- (void)otherMouseUp:(NSEvent*)event { + +} + +- (void)mouseExited:(NSEvent*)event { + //TODO +} + +- (void)mouseEntered:(NSEvent*)event { + //TODO +} + +- (void)viewDidChangeBackingProperties { + const NSRect contentRect = [window->getNativeView() frame]; + const NSRect fbRect = [window->getNativeView() convertRectToBacking:contentRect]; + //TODO +} + +- (void)drawRect:(NSRect)rect { + +} + +- (void)updateTrackingAreas { + if (trackingArea != nil) { + [self removeTrackingArea:trackingArea]; + [trackingArea release]; + } + + const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow | + NSTrackingEnabledDuringMouseDrag | + NSTrackingCursorUpdate | + NSTrackingInVisibleRect | + NSTrackingAssumeInside; + + trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] + options:options + owner:self + userInfo:nil]; + + [self addTrackingArea:trackingArea]; + [super updateTrackingAreas]; +} + +- (void)keyDown:(NSEvent *)event { + //TODO +} + +- (void)flagsChanged:(NSEvent *)event { + //TODO +} + +- (void)keyUp:(NSEvent*)event { + //TODO +} + +- (void)scrollWheel:(NSEvent*)event { + //TODO +} + +- (NSDragOperation)draggingEntered:(id )sender { + return NSDragOperationGeneric; +} + +- (BOOL)performDragOperation:(id )sender { + return YES; +} + +- (BOOL)hasMarkedText { + return [markedText length] > 0; +} + +- (NSRange)markedRange { + if ([markedText length] > 0) + return NSMakeRange(0, [markedText length] - 1); + else + return nsEmptyRange; +} + +- (NSRange)selectedRange { + return nsEmptyRange; +} + +- (void)setMarkedText:(id)string + selectedRange:(NSRange)selectedRange + replacementRange:(NSRange)replacementRange { + [markedText release]; + if ([string isKindOfClass:[NSAttributedString class]]) + markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string]; + else + markedText = [[NSMutableAttributedString alloc] initWithString:string]; +} + +- (void)unmarkText { + [[markedText mutableString] setString:@""]; +} + +- (NSArray*)validAttributesForMarkedText { + return [NSArray array]; +} + +- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range + actualRange:(NSRangePointer)actualRange { + return nil; +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)point { + return 0; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)range + actualRange:(NSRangePointer)actualRange { + const NSRect frame = [window->getNativeView() frame]; + return NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0); +} + +- (void)insertText:(id)string replacementRange:(NSRange)replacementRange { + //TODO +} + +- (void)doCommandBySelector:(SEL)selector { + +} + +@end + +//Window Object + +@interface CocoaWindow : NSWindow {} +@end + +@implementation CocoaWindow + +- (BOOL)canBecomeKeyWindow { + return YES; +} + +- (BOOL)canBecomeMainWindow { + return YES; +} + +@end + +//=====================Cocoa IMPLEMENTATION===================== +Window_cocoa::Window_cocoa(const char* title, uint width, uint height) { + shape.width = width; + shape.height = height; + running = true; + LOGI("Creating Cocoa Window...\n"); + + //=====================Helper===================== + CocoaHelper* helper = [[CocoaHelper alloc] init]; + + [NSThread detachNewThreadSelector:@selector(doNothing:) + toTarget:helper + withObject:nil]; + + [NSApplication sharedApplication]; + + //=====================App delegate===================== + id appDelegate = [[CocoaAppDelegate alloc] init]; + [NSApp setDelegate:appDelegate]; + + NSEvent* (^block)(NSEvent*) = ^ NSEvent* (NSEvent* event) { + if ([event modifierFlags] & NSEventModifierFlagCommand) + [[NSApp keyWindow] sendEvent:event]; + + return event; + }; + + [[NSNotificationCenter defaultCenter] + addObserver:helper + selector:@selector(selectedKeyboardInputSourceChanged:) + name:NSTextInputContextKeyboardSelectionDidChangeNotification + object:nil]; + + CGEventSourceSetLocalEventsSuppressionInterval(CGEventSourceCreate(kCGEventSourceStateHIDSystemState), 0.0); + + //if (![[NSRunningApplication currentApplication] isFinishedLaunching]) + // [NSApp run]; + + //=====================Window delegate===================== + windowDelegate = [[CocoaWindowDelegate alloc] initWithWSIWindow:this]; + //if (windowDelegate == NULL) { + // LVND_ERROR("Failed to create NS window delegate"); + //} + + NSRect contentRect = NSMakeRect(0, 0, width, height); + + NSUInteger styleMask = NSWindowStyleMaskMiniaturizable; + styleMask |= (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable); + styleMask |= NSWindowStyleMaskResizable; + window = [[CocoaWindow alloc] + initWithContentRect:contentRect + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + //if (window == NULL) { + // LVND_ERROR("Failed to create NS window"); + //} + + [(NSWindow*)window center]; + + const NSWindowCollectionBehavior behavior = + NSWindowCollectionBehaviorFullScreenPrimary | + NSWindowCollectionBehaviorManaged; + [window setCollectionBehavior:behavior]; + + view = [[CocoaContentView alloc] initWithWSIWindow:this]; + //if (view == NULL) { + // LVND_ERROR("Failed to create NS view"); + //} + + [window setContentView:view]; + [window makeFirstResponder:view]; + [window setTitle:@(title)]; + [window setDelegate:windowDelegate]; + [window setAcceptsMouseMovedEvents:YES]; + [window setRestorable:NO]; + + [NSApp activateIgnoringOtherApps:YES]; + [window makeKeyAndOrderFront:view]; + + //Getting some properties + /* + const NSRect fbRect = [(id)window->handle->view convertRectToBacking:contentRect]; + window->framebufferWidth = fbRect.size.width; + window->framebufferHeight = fbRect.size.height; + + //Retina + if (window->width == window->framebufferWidth && window->height == window->framebufferHeight) + window->handle->isRetina = false; + else + window->handle->isRetina = true; + + const NSPoint mousePos = [(id)window->handle->window mouseLocationOutsideOfEventStream]; + window->mouseX = mousePos.x; + window->mouseY = mousePos.y; + */ + + //[app setDelegate:handle->delegate]; + + //[(id)window->handle->window orderFrontRegardless]; + [NSApp run]; +} + +Window_cocoa::~Window_cocoa() { + //TODO +} + +void Window_cocoa::SetTitle(const char* title) { + //TODO +} + +void Window_cocoa::SetWinPos(uint x, uint y) { + //TODO +} + +void Window_cocoa::SetWinSize(uint w, uint h) { + //TODO +} + +void Window_cocoa::CreateSurface(VkInstance instance) { + if (surface) return; + this->instance = instance; + + CAMetalLayer* metalLayer = [CAMetalLayer layer]; + //[layer setContentsScale:window->framebufferScaleX]; + layer = metalLayer; + + NSWindow* nswindow = window; + nswindow.contentView.layer = layer; + nswindow.contentView.wantsLayer = YES; + + VkMetalSurfaceCreateInfoEXT surfaceCreateInfo; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; + surfaceCreateInfo.pNext = NULL; + surfaceCreateInfo.pLayer = layer; + surfaceCreateInfo.flags = 0; + + VKERRCHECK(vkCreateMetalSurfaceEXT(instance, &surfaceCreateInfo, NULL, &surface)); + + LOGI("Vulkan Surface created\n"); +} + +EventType Window_cocoa::GetEvent(bool wait_for_event) { + @autoreleasepool { + + //for (;;) { + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event == nil) + return {EventType::UNKNOWN}; + + [NSApp sendEvent:event]; + //} + + } // autoreleasepool + + switch (lastEvent) { + case EventType::CLOSE: + return CloseEvent(); + default: + return {EventType::NONE}; + } +} + +// Return true if this window can present the given queue type +bool Window_cocoa::CanPresent(VkPhysicalDevice gpu, uint32_t queue_family) { + //TODO +}