From b4293edd70c3b3d075edfbbf2b00a5c831936f87 Mon Sep 17 00:00:00 2001
From: Ray <raysan5@gmail.com>
Date: Thu, 6 Feb 2020 16:30:48 +0100
Subject: [PATCH] Update GLFW to latest master

---
 src/external/glfw/CMake/Info.plist.in      |  38 ++++
 src/external/glfw/CMakeLists.txt           |   4 +-
 src/external/glfw/README.md                |  45 +++-
 src/external/glfw/include/GLFW/glfw3.h     |   7 +-
 src/external/glfw/src/cocoa_init.m         |  35 ++-
 src/external/glfw/src/cocoa_joystick.m     |   2 +-
 src/external/glfw/src/cocoa_monitor.m      |  96 +++++++--
 src/external/glfw/src/cocoa_platform.h     |  15 +-
 src/external/glfw/src/cocoa_window.m       | 117 ++++++----
 src/external/glfw/src/egl_context.c        |  29 +--
 src/external/glfw/src/glfw.rc.in           |  30 +++
 src/external/glfw/src/glfw3Config.cmake.in |   2 +
 src/external/glfw/src/internal.h           |   2 +
 src/external/glfw/src/nsgl_context.h       |   5 -
 src/external/glfw/src/nsgl_context.m       | 100 +++------
 src/external/glfw/src/posix_time.c         |   7 +-
 src/external/glfw/src/vulkan.c             |   4 +
 src/external/glfw/src/win32_monitor.c      |   1 +
 src/external/glfw/src/win32_window.c       | 179 ++++++++--------
 src/external/glfw/src/wl_init.c            |   5 +
 src/external/glfw/src/x11_init.c           |   1 +
 src/external/glfw/src/x11_monitor.c        |  24 ++-
 src/external/glfw/src/x11_platform.h       |   1 +
 src/external/glfw/src/x11_window.c         | 238 ++++++++++++++-------
 24 files changed, 637 insertions(+), 350 deletions(-)
 create mode 100644 src/external/glfw/CMake/Info.plist.in
 create mode 100644 src/external/glfw/src/glfw.rc.in

diff --git a/src/external/glfw/CMake/Info.plist.in b/src/external/glfw/CMake/Info.plist.in
new file mode 100644
index 000000000..684ad7908
--- /dev/null
+++ b/src/external/glfw/CMake/Info.plist.in
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
+	<key>CFBundleGetInfoString</key>
+	<string>${MACOSX_BUNDLE_INFO_STRING}</string>
+	<key>CFBundleIconFile</key>
+	<string>${MACOSX_BUNDLE_ICON_FILE}</string>
+	<key>CFBundleIdentifier</key>
+	<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleLongVersionString</key>
+	<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
+	<key>CFBundleName</key>
+	<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
+	<key>CSResourcesFileMapped</key>
+	<true/>
+	<key>LSRequiresCarbon</key>
+	<true/>
+	<key>NSHumanReadableCopyright</key>
+	<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
+	<key>NSHighResolutionCapable</key>
+	<true/>
+</dict>
+</plist>
diff --git a/src/external/glfw/CMakeLists.txt b/src/external/glfw/CMakeLists.txt
index 6063b46a1..65c897519 100644
--- a/src/external/glfw/CMakeLists.txt
+++ b/src/external/glfw/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.1)
 
 project(GLFW VERSION 3.4.0 LANGUAGES C)
 
@@ -75,7 +75,7 @@ if (MSVC)
         include(CheckIncludeFile)
         check_include_file(dinput.h DINPUT_H_FOUND)
         if (NOT DINPUT_H_FOUND)
-            message(FATAL_ERROR "DirectX 9 SDK not found")
+            message(FATAL_ERROR "DirectX 9 headers not found; install DirectX 9 SDK")
         endif()
         # Workaround for VS 2008 not shipping with stdint.h
         list(APPEND glfw_INCLUDE_DIRS "${GLFW_SOURCE_DIR}/deps/vs2008")
diff --git a/src/external/glfw/README.md b/src/external/glfw/README.md
index 8b849f1a8..c543d25fb 100644
--- a/src/external/glfw/README.md
+++ b/src/external/glfw/README.md
@@ -11,7 +11,7 @@ application development.  It provides a simple, platform-independent API for
 creating windows, contexts and surfaces, reading input, handling events, etc.
 
 GLFW natively supports Windows, macOS and Linux and other Unix-like systems.  On
-Linux both X11 and Wayland is supported.
+Linux both X11 and Wayland are supported.
 
 GLFW is licensed under the [zlib/libpng
 license](http://www.glfw.org/license.html).
@@ -85,10 +85,11 @@ in the documentation for more information.
 
 ## Dependencies
 
-GLFW itself depends only on the headers and libraries for your window system.
+GLFW itself needs only CMake 3.1 or later and the headers and libraries for your
+OS and window system.
 
 The (experimental) Wayland backend also depends on the `extra-cmake-modules`
-package, which is used to generated Wayland protocol headers.
+package, which is used to generate Wayland protocol headers.
 
 The examples and test programs depend on a number of tiny libraries.  These are
 located in the `deps/` directory.
@@ -123,33 +124,57 @@ information on what to include when reporting a bug.
  - Added `GLFW_RESIZE_EW_CURSOR` alias for `GLFW_HRESIZE_CURSOR` (#427)
  - Added `GLFW_RESIZE_NS_CURSOR` alias for `GLFW_VRESIZE_CURSOR` (#427)
  - Added `GLFW_POINTING_HAND_CURSOR` alias for `GLFW_HAND_CURSOR` (#427)
+ - Updated the minimum required CMake version to 3.1
  - Disabled tests and examples by default when built as a CMake subdirectory
  - Bugfix: The CMake config-file package used an absolute path and was not
    relocatable (#1470)
  - Bugfix: Video modes with a duplicate screen area were discarded (#1555,#1556)
  - Bugfix: Compiling with -Wextra-semi caused warnings (#1440)
+ - Bugfix: Built-in mappings failed because some OEMs re-used VID/PID (#1583)
  - [Win32] Added the `GLFW_WIN32_KEYBOARD_MENU` window hint for enabling access
            to the window menu
+ - [Win32] Added a version info resource to the GLFW DLL
  - [Win32] Bugfix: `GLFW_INCLUDE_VULKAN` plus `VK_USE_PLATFORM_WIN32_KHR` caused
    symbol redefinition (#1524)
  - [Win32] Bugfix: The cursor position event was emitted before its cursor enter
    event (#1490)
  - [Win32] Bugfix: The window hint `GLFW_MAXIMIZED` did not move or resize the
    window (#1499)
+ - [Win32] Bugfix: Disabled cursor mode interfered with some non-client actions
+ - [Win32] Bugfix: Super key was not released after Win+V hotkey (#1622)
+ - [Win32] Bugfix: `glfwGetKeyName` could access out of bounds and return an
+   invalid pointer
+ - [Win32] Bugfix: Some synthetic key events were reported as `GLFW_KEY_UNKNOWN`
+   (#1623)
+ - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619)
+ - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle
+ - [Cocoa] Removed dependency on the CoreVideo framework
  - [Cocoa] Bugfix: `glfwSetWindowSize` used a bottom-left anchor point (#1553)
  - [Cocoa] Bugfix: Window remained on screen after destruction until event poll
    (#1412)
+ - [Cocoa] Bugfix: Event processing before window creation would assert (#1543)
+ - [Cocoa] Bugfix: Undecorated windows could not be iconified on recent macOS
  - [X11] Bugfix: The CMake files did not check for the XInput headers (#1480)
  - [X11] Bugfix: Key names were not updated when the keyboard layout changed
    (#1462,#1528)
  - [X11] Bugfix: Decorations could not be enabled after window creation (#1566)
  - [X11] Bugfix: Content scale fallback value could be inconsistent (#1578)
+ - [X11] Bugfix: `glfwMaximizeWindow` had no effect on hidden windows
+ - [X11] Bugfix: Clearing `GLFW_FLOATING` on a hidden window caused invalid read
+ - [X11] Bugfix: Changing `GLFW_FLOATING` on a hidden window could silently fail
+ - [X11] Bugfix: Disabled cursor mode was interrupted by indicator windows
+ - [X11] Bugfix: Monitor physical dimensions could be reported as zero mm
+ - [X11] Bugfix: Window position events were not emitted during resizing (#1613)
+ - [X11] Bugfix: `glfwFocusWindow` could terminate on older WMs or without a WM
+ - [X11] Bugfix: Querying a disconnected monitor could segfault (#1602)
+ - [Wayland] Removed support for `wl_shell` (#1443)
  - [Wayland] Bugfix: The `GLFW_HAND_CURSOR` shape used the wrong image (#1432)
+ - [Wayland] Bugfix: `CLOCK_MONOTONIC` was not correctly enabled
+ - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled
  - [NSGL] Removed enforcement of forward-compatible flag for core contexts
- - Export CMake `GLFW_PKG_DEPS` and `GLFW_PKG_LIBS` to parent scope for use
-  in client pkg-configs (#1307)
-- Added a `glfw_objlib` CMake OBJECT library target for embedding into static
-  libraries (#1307)
+ - [NSGL] Bugfix: `GLFW_COCOA_RETINA_FRAMEBUFFER` had no effect on newer
+   macOS versions (#1442)
+ - [NSGL] Bugfix: Workaround for swap interval on 10.14 broke on 10.12 (#1483)
 
 
 ## Contact
@@ -222,6 +247,7 @@ skills.
  - GeO4d
  - Marcus Geelnard
  - Charles Giessen
+ - Ryan C. Gordon
  - Stephen Gowen
  - Kovid Goyal
  - Eloi Marín Gratacós
@@ -242,6 +268,7 @@ skills.
  - Cem Karan
  - Osman Keskin
  - Josh Kilmer
+ - Byunghoon Kim
  - Cameron King
  - Peter Knut
  - Christoph Kubisch
@@ -249,6 +276,7 @@ skills.
  - Rokas Kupstys
  - Konstantin Käfer
  - Eric Larson
+ - Francis Lecavalier
  - Robin Leffmann
  - Glenn Lewis
  - Shane Liesegang
@@ -257,6 +285,7 @@ skills.
  - Eyal Lotem
  - Aaron Loucks
  - Luflosi
+ - lukect
  - Tristam MacDonald
  - Hans Mackowiak
  - Дмитри Малышев
@@ -302,6 +331,7 @@ skills.
  - Alexandre Pretyman
  - Pablo Prietz
  - przemekmirek
+ - pthom
  - Guillaume Racicot
  - Philip Rideout
  - Eddie Ringle
@@ -332,6 +362,7 @@ skills.
  - Paul Sultana
  - Nathan Sweet
  - TTK-Bandit
+ - Jared Tiala
  - Sergey Tikhomirov
  - Arthur Tombs
  - Ioannis Tsakpinis
diff --git a/src/external/glfw/include/GLFW/glfw3.h b/src/external/glfw/include/GLFW/glfw3.h
index 8b5b87dd1..4f5f3607f 100644
--- a/src/external/glfw/include/GLFW/glfw3.h
+++ b/src/external/glfw/include/GLFW/glfw3.h
@@ -2685,7 +2685,7 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value);
  *  [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html)
  *  in the Mac Developer Library.  The GLFW test and example programs use
  *  a custom `Info.plist` template for this, which can be found as
- *  `CMake/MacOSXBundleInfo.plist.in` in the source tree.
+ *  `CMake/Info.plist.in` in the source tree.
  *
  *  @remark @macos When activating frame autosaving with
  *  [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified
@@ -5776,8 +5776,9 @@ GLFWAPI int glfwVulkanSupported(void);
  *  returned array, as it is an error to specify an extension more than once in
  *  the `VkInstanceCreateInfo` struct.
  *
- *  @remark @macos This function currently only supports the
- *  `VK_MVK_macos_surface` extension from MoltenVK.
+ *  @remark @macos This function currently supports either the
+ *  `VK_MVK_macos_surface` extension from MoltenVK or `VK_EXT_metal_surface`
+ *  extension.
  *
  *  @pointer_lifetime The returned array is allocated and freed by GLFW.  You
  *  should not free it yourself.  It is guaranteed to be valid only until the
diff --git a/src/external/glfw/src/cocoa_init.m b/src/external/glfw/src/cocoa_init.m
index 01c1b319e..cbc9462ff 100644
--- a/src/external/glfw/src/cocoa_init.m
+++ b/src/external/glfw/src/cocoa_init.m
@@ -431,9 +431,8 @@ static GLFWbool initializeTIS(void)
         // In case we are unbundled, make us a proper UI application
         [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
 
-        // Menu bar setup must go between sharedApplication above and
-        // finishLaunching below, in order to properly emulate the behavior
-        // of NSApplicationMain
+        // Menu bar setup must go between sharedApplication and finishLaunching
+        // in order to properly emulate the behavior of NSApplicationMain
 
         if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"])
         {
@@ -448,9 +447,9 @@ static GLFWbool initializeTIS(void)
 
 - (void)applicationDidFinishLaunching:(NSNotification *)notification
 {
-    [NSApp stop:nil];
-
+    _glfw.ns.finishedLaunching = GLFW_TRUE;
     _glfwPlatformPostEmptyEvent();
+    [NSApp stop:nil];
 }
 
 - (void)applicationDidHide:(NSNotification *)notification
@@ -464,6 +463,32 @@ static GLFWbool initializeTIS(void)
 @end // GLFWApplicationDelegate
 
 
+//////////////////////////////////////////////////////////////////////////
+//////                       GLFW internal API                      //////
+//////////////////////////////////////////////////////////////////////////
+
+void* _glfwLoadLocalVulkanLoaderNS(void)
+{
+    CFBundleRef bundle = CFBundleGetMainBundle();
+    if (!bundle)
+        return NULL;
+
+    CFURLRef url =
+        CFBundleCopyAuxiliaryExecutableURL(bundle, CFSTR("libvulkan.1.dylib"));
+    if (!url)
+        return NULL;
+
+    char path[PATH_MAX];
+    void* handle = NULL;
+
+    if (CFURLGetFileSystemRepresentation(url, true, (UInt8*) path, sizeof(path) - 1))
+        handle = _glfw_dlopen(path);
+
+    CFRelease(url);
+    return handle;
+}
+
+
 //////////////////////////////////////////////////////////////////////////
 //////                       GLFW platform API                      //////
 //////////////////////////////////////////////////////////////////////////
diff --git a/src/external/glfw/src/cocoa_joystick.m b/src/external/glfw/src/cocoa_joystick.m
index b98f9f67d..88636a87b 100644
--- a/src/external/glfw/src/cocoa_joystick.m
+++ b/src/external/glfw/src/cocoa_joystick.m
@@ -331,7 +331,7 @@ void _glfwInitJoysticksNS(void)
         return;
     }
 
-    for (int i = 0;  i < sizeof(usages) / sizeof(long);  i++)
+    for (size_t i = 0;  i < sizeof(usages) / sizeof(long);  i++)
     {
         const long page = kHIDPage_GenericDesktop;
 
diff --git a/src/external/glfw/src/cocoa_monitor.m b/src/external/glfw/src/cocoa_monitor.m
index 9ef2cdc97..42f2dce28 100644
--- a/src/external/glfw/src/cocoa_monitor.m
+++ b/src/external/glfw/src/cocoa_monitor.m
@@ -144,7 +144,7 @@ static GLFWbool modeIsGood(CGDisplayModeRef mode)
 // Convert Core Graphics display mode to GLFW video mode
 //
 static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode,
-                                            CVDisplayLinkRef link)
+                                            double fallbackRefreshRate)
 {
     GLFWvidmode result;
     result.width = (int) CGDisplayModeGetWidth(mode);
@@ -152,11 +152,7 @@ static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode,
     result.refreshRate = (int) round(CGDisplayModeGetRefreshRate(mode));
 
     if (result.refreshRate == 0)
-    {
-        const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
-        if (!(time.flags & kCVTimeIsIndefinite))
-            result.refreshRate = (int) (time.timeScale / (double) time.timeValue);
-    }
+        result.refreshRate = (int) round(fallbackRefreshRate);
 
 #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
     CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);
@@ -238,6 +234,68 @@ static GLFWbool refreshMonitorScreen(_GLFWmonitor* monitor)
     return GLFW_FALSE;
 }
 
+// Returns the display refresh rate queried from the I/O registry
+//
+static double getFallbackRefreshRate(CGDirectDisplayID displayID)
+{
+    double refreshRate = 60.0;
+
+    io_iterator_t it;
+    io_service_t service;
+
+    if (IOServiceGetMatchingServices(kIOMasterPortDefault,
+                                     IOServiceMatching("IOFramebuffer"),
+                                     &it) != 0)
+    {
+        return refreshRate;
+    }
+
+    while ((service = IOIteratorNext(it)) != 0)
+    {
+        const CFNumberRef indexRef =
+            IORegistryEntryCreateCFProperty(service,
+                                            CFSTR("IOFramebufferOpenGLIndex"),
+                                            kCFAllocatorDefault,
+                                            kNilOptions);
+        if (!indexRef)
+            continue;
+
+        uint32_t index = 0;
+        CFNumberGetValue(indexRef, kCFNumberIntType, &index);
+        CFRelease(indexRef);
+
+        if (CGOpenGLDisplayMaskToDisplayID(1 << index) != displayID)
+            continue;
+
+        const CFNumberRef clockRef =
+            IORegistryEntryCreateCFProperty(service,
+                                            CFSTR("IOFBCurrentPixelClock"),
+                                            kCFAllocatorDefault,
+                                            kNilOptions);
+        const CFNumberRef countRef =
+            IORegistryEntryCreateCFProperty(service,
+                                            CFSTR("IOFBCurrentPixelCount"),
+                                            kCFAllocatorDefault,
+                                            kNilOptions);
+        if (!clockRef || !countRef)
+            break;
+
+        uint32_t clock = 0, count = 0;
+        CFNumberGetValue(clockRef, kCFNumberIntType, &clock);
+        CFNumberGetValue(countRef, kCFNumberIntType, &count);
+        CFRelease(clockRef);
+        CFRelease(countRef);
+
+        if (clock > 0 && count > 0)
+            refreshRate = clock / (double) count;
+
+        break;
+    }
+
+    IOObjectRelease(it);
+    return refreshRate;
+}
+
 
 //////////////////////////////////////////////////////////////////////////
 //////                       GLFW internal API                      //////
@@ -294,6 +352,11 @@ void _glfwPollMonitorsNS(void)
 
         free(name);
 
+        CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);
+        if (CGDisplayModeGetRefreshRate(mode) == 0.0)
+            monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(displays[i]);
+        CGDisplayModeRelease(mode);
+
         _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST);
     }
 
@@ -318,9 +381,6 @@ void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
     if (_glfwCompareVideoModes(&current, best) == 0)
         return;
 
-    CVDisplayLinkRef link;
-    CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
-
     CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
     const CFIndex count = CFArrayGetCount(modes);
     CGDisplayModeRef native = NULL;
@@ -331,7 +391,8 @@ void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
         if (!modeIsGood(dm))
             continue;
 
-        const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, link);
+        const GLFWvidmode mode =
+            vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
         if (_glfwCompareVideoModes(best, &mode) == 0)
         {
             native = dm;
@@ -350,7 +411,6 @@ void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
     }
 
     CFRelease(modes);
-    CVDisplayLinkRelease(link);
 }
 
 // Restore the previously saved (original) video mode
@@ -440,9 +500,6 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
 
     *count = 0;
 
-    CVDisplayLinkRef link;
-    CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
-
     CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
     const CFIndex found = CFArrayGetCount(modes);
     GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode));
@@ -453,7 +510,8 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
         if (!modeIsGood(dm))
             continue;
 
-        const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, link);
+        const GLFWvidmode mode =
+            vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
         CFIndex j;
 
         for (j = 0;  j < *count;  j++)
@@ -471,7 +529,6 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
     }
 
     CFRelease(modes);
-    CVDisplayLinkRelease(link);
     return result;
 
     } // autoreleasepool
@@ -481,15 +538,10 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode)
 {
     @autoreleasepool {
 
-    CVDisplayLinkRef link;
-    CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
-
     CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID);
-    *mode = vidmodeFromCGDisplayMode(native, link);
+    *mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate);
     CGDisplayModeRelease(native);
 
-    CVDisplayLinkRelease(link);
-
     } // autoreleasepool
 }
 
diff --git a/src/external/glfw/src/cocoa_platform.h b/src/external/glfw/src/cocoa_platform.h
index 21d83bcc7..9a979af21 100644
--- a/src/external/glfw/src/cocoa_platform.h
+++ b/src/external/glfw/src/cocoa_platform.h
@@ -28,8 +28,6 @@
 #include <dlfcn.h>
 
 #include <Carbon/Carbon.h>
-#include <CoreVideo/CVBase.h>
-#include <CoreVideo/CVDisplayLink.h>
 
 // NOTE: All of NSGL was deprecated in the 10.14 SDK
 //       This disables the pointless warnings for every symbol we use
@@ -63,6 +61,7 @@ typedef void* id;
 #endif
 
 typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
+typedef VkFlags VkMetalSurfaceCreateFlagsEXT;
 
 typedef struct VkMacOSSurfaceCreateInfoMVK
 {
@@ -72,7 +71,16 @@ typedef struct VkMacOSSurfaceCreateInfoMVK
     const void*                     pView;
 } VkMacOSSurfaceCreateInfoMVK;
 
+typedef struct VkMetalSurfaceCreateInfoEXT
+{
+    VkStructureType                 sType;
+    const void*                     pNext;
+    VkMetalSurfaceCreateFlagsEXT    flags;
+    const void*                     pLayer;
+} VkMetalSurfaceCreateInfoEXT;
+
 typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*);
+typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMetalSurfaceCreateInfoEXT*,const VkAllocationCallbacks*,VkSurfaceKHR*);
 
 #include "posix_thread.h"
 #include "cocoa_joystick.h"
@@ -170,6 +178,7 @@ typedef struct _GLFWmonitorNS
     CGDisplayModeRef    previousMode;
     uint32_t            unitNumber;
     id                  screen;
+    double              fallbackRefreshRate;
 
 } _GLFWmonitorNS;
 
@@ -198,3 +207,5 @@ void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
 
 float _glfwTransformYNS(float y);
 
+void* _glfwLoadLocalVulkanLoaderNS(void);
+
diff --git a/src/external/glfw/src/cocoa_window.m b/src/external/glfw/src/cocoa_window.m
index bfec8b7b8..e12b5cda7 100644
--- a/src/external/glfw/src/cocoa_window.m
+++ b/src/external/glfw/src/cocoa_window.m
@@ -322,12 +322,6 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
     _glfwInputWindowFocus(window, GLFW_FALSE);
 }
 
-- (void)windowDidChangeScreen:(NSNotification *)notification
-{
-    if (window->context.source == GLFW_NATIVE_CONTEXT_API)
-        _glfwUpdateDisplayLinkDisplayNSGL(window);
-}
-
 @end
 
 
@@ -891,10 +885,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
     @autoreleasepool {
 
     if (!_glfw.ns.finishedLaunching)
-    {
         [NSApp run];
-        _glfw.ns.finishedLaunching = GLFW_TRUE;
-    }
 
     if (!createNativeWindow(window, wndconfig, fbconfig))
         return GLFW_FALSE;
@@ -967,13 +958,14 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
     } // autoreleasepool
 }
 
-void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title)
+void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
 {
     @autoreleasepool {
-    [window->ns.object setTitle:@(title)];
+    NSString* string = @(title);
+    [window->ns.object setTitle:string];
     // HACK: Set the miniwindow title explicitly as setTitle: doesn't update it
     //       if the window lacks NSWindowStyleMaskTitled
-    [window->ns.object setMiniwindowTitle:@(title)];
+    [window->ns.object setMiniwindowTitle:string];
     } // autoreleasepool
 }
 
@@ -1385,6 +1377,9 @@ void _glfwPlatformPollEvents(void)
 {
     @autoreleasepool {
 
+    if (!_glfw.ns.finishedLaunching)
+        [NSApp run];
+
     for (;;)
     {
         NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
@@ -1404,6 +1399,9 @@ void _glfwPlatformWaitEvents(void)
 {
     @autoreleasepool {
 
+    if (!_glfw.ns.finishedLaunching)
+        [NSApp run];
+
     // I wanted to pass NO to dequeue:, and rely on PollEvents to
     // dequeue and send.  For reasons not at all clear to me, passing
     // NO to dequeue: causes this method never to return.
@@ -1422,6 +1420,9 @@ void _glfwPlatformWaitEventsTimeout(double timeout)
 {
     @autoreleasepool {
 
+    if (!_glfw.ns.finishedLaunching)
+        [NSApp run];
+
     NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout];
     NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny
                                         untilDate:date
@@ -1439,6 +1440,9 @@ void _glfwPlatformPostEmptyEvent(void)
 {
     @autoreleasepool {
 
+    if (!_glfw.ns.finishedLaunching)
+        [NSApp run];
+
     NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
                                         location:NSMakePoint(0, 0)
                                    modifierFlags:0
@@ -1512,6 +1516,13 @@ const char* _glfwPlatformGetScancodeName(int scancode)
 {
     @autoreleasepool {
 
+    if (scancode < 0 || scancode > 0xff ||
+        _glfw.ns.keycodes[scancode] == GLFW_KEY_UNKNOWN)
+    {
+        _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode");
+        return NULL;
+    }
+
     const int key = _glfw.ns.keycodes[scancode];
 
     UInt32 deadKeyState = 0;
@@ -1607,13 +1618,13 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
 
     // HACK: Try to use a private message
     if (shape == GLFW_RESIZE_EW_CURSOR)
-        cursorSelector = @selector(_windowResizeEastWestCursor);
+        cursorSelector = NSSelectorFromString(@"_windowResizeEastWestCursor");
     else if (shape == GLFW_RESIZE_NS_CURSOR)
-        cursorSelector = @selector(_windowResizeNorthSouthCursor);
+        cursorSelector = NSSelectorFromString(@"_windowResizeNorthSouthCursor");
     else if (shape == GLFW_RESIZE_NWSE_CURSOR)
-        cursorSelector = @selector(_windowResizeNorthWestSouthEastCursor);
+        cursorSelector = NSSelectorFromString(@"_windowResizeNorthWestSouthEastCursor");
     else if (shape == GLFW_RESIZE_NESW_CURSOR)
-        cursorSelector = @selector(_windowResizeNorthEastSouthWestCursor);
+        cursorSelector = NSSelectorFromString(@"_windowResizeNorthEastSouthWestCursor");
 
     if (cursorSelector && [NSCursor respondsToSelector:cursorSelector])
     {
@@ -1711,11 +1722,16 @@ const char* _glfwPlatformGetClipboardString(void)
 
 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
 {
-    if (!_glfw.vk.KHR_surface || !_glfw.vk.MVK_macos_surface)
-        return;
-
-    extensions[0] = "VK_KHR_surface";
-    extensions[1] = "VK_MVK_macos_surface";
+    if (_glfw.vk.KHR_surface && _glfw.vk.EXT_metal_surface)
+    {
+        extensions[0] = "VK_KHR_surface";
+        extensions[1] = "VK_EXT_metal_surface";
+    }
+    else if (_glfw.vk.KHR_surface && _glfw.vk.MVK_macos_surface)
+    {
+        extensions[0] = "VK_KHR_surface";
+        extensions[1] = "VK_MVK_macos_surface";
+    }
 }
 
 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
@@ -1733,19 +1749,6 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
     @autoreleasepool {
 
 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100
-    VkResult err;
-    VkMacOSSurfaceCreateInfoMVK sci;
-    PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK;
-
-    vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK)
-        vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK");
-    if (!vkCreateMacOSSurfaceMVK)
-    {
-        _glfwInputError(GLFW_API_UNAVAILABLE,
-                        "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension");
-        return VK_ERROR_EXTENSION_NOT_PRESENT;
-    }
-
     // HACK: Dynamically load Core Animation to avoid adding an extra
     //       dependency for the majority who don't use MoltenVK
     NSBundle* bundle = [NSBundle bundleWithPath:@"/System/Library/Frameworks/QuartzCore.framework"];
@@ -1771,11 +1774,49 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
     [window->ns.view setLayer:window->ns.layer];
     [window->ns.view setWantsLayer:YES];
 
-    memset(&sci, 0, sizeof(sci));
-    sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
-    sci.pView = window->ns.view;
+    VkResult err;
+
+    if (_glfw.vk.EXT_metal_surface)
+    {
+        VkMetalSurfaceCreateInfoEXT sci;
+
+        PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT;
+        vkCreateMetalSurfaceEXT = (PFN_vkCreateMetalSurfaceEXT)
+            vkGetInstanceProcAddr(instance, "vkCreateMetalSurfaceEXT");
+        if (!vkCreateMetalSurfaceEXT)
+        {
+            _glfwInputError(GLFW_API_UNAVAILABLE,
+                            "Cocoa: Vulkan instance missing VK_EXT_metal_surface extension");
+            return VK_ERROR_EXTENSION_NOT_PRESENT;
+        }
+
+        memset(&sci, 0, sizeof(sci));
+        sci.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
+        sci.pLayer = window->ns.layer;
+
+        err = vkCreateMetalSurfaceEXT(instance, &sci, allocator, surface);
+    }
+    else
+    {
+        VkMacOSSurfaceCreateInfoMVK sci;
+
+        PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK;
+        vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK)
+            vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK");
+        if (!vkCreateMacOSSurfaceMVK)
+        {
+            _glfwInputError(GLFW_API_UNAVAILABLE,
+                            "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension");
+            return VK_ERROR_EXTENSION_NOT_PRESENT;
+        }
+
+        memset(&sci, 0, sizeof(sci));
+        sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
+        sci.pView = window->ns.view;
+
+        err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface);
+    }
 
-    err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface);
     if (err)
     {
         _glfwInputError(GLFW_PLATFORM_ERROR,
diff --git a/src/external/glfw/src/egl_context.c b/src/external/glfw/src/egl_context.c
index 6a33396f8..706a79224 100644
--- a/src/external/glfw/src/egl_context.c
+++ b/src/external/glfw/src/egl_context.c
@@ -123,23 +123,24 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
             continue;
 
 #if defined(_GLFW_X11)
-        XVisualInfo vi = {0};
+        {
+            XVisualInfo vi = {0};
 
-        // Only consider EGLConfigs with associated Visuals
-        vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID);
-        if (!vi.visualid)
-            continue;
+            // Only consider EGLConfigs with associated Visuals
+            vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID);
+            if (!vi.visualid)
+                continue;
 
-        if (desired->transparent)
-        {
-            int count;
-            XVisualInfo* vis = XGetVisualInfo(_glfw.x11.display,
-                                              VisualIDMask, &vi,
-                                              &count);
-            if (vis)
+            if (desired->transparent)
             {
-                u->transparent = _glfwIsVisualTransparentX11(vis[0].visual);
-                XFree(vis);
+                int count;
+                XVisualInfo* vis =
+                    XGetVisualInfo(_glfw.x11.display, VisualIDMask, &vi, &count);
+                if (vis)
+                {
+                    u->transparent = _glfwIsVisualTransparentX11(vis[0].visual);
+                    XFree(vis);
+                }
             }
         }
 #endif // _GLFW_X11
diff --git a/src/external/glfw/src/glfw.rc.in b/src/external/glfw/src/glfw.rc.in
new file mode 100644
index 000000000..ac3460a7c
--- /dev/null
+++ b/src/external/glfw/src/glfw.rc.in
@@ -0,0 +1,30 @@
+
+#include <winver.h>
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION @GLFW_VERSION_MAJOR@,@GLFW_VERSION_MINOR@,@GLFW_VERSION_PATCH@,0
+PRODUCTVERSION @GLFW_VERSION_MAJOR@,@GLFW_VERSION_MINOR@,@GLFW_VERSION_PATCH@,0
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+FILEFLAGS 0
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_DLL
+FILESUBTYPE 0
+{
+    BLOCK "StringFileInfo"
+    {
+        BLOCK "040904B0"
+        {
+            VALUE "CompanyName", "GLFW"
+            VALUE "FileDescription", "GLFW @GLFW_VERSION@ DLL"
+            VALUE "FileVersion", "@GLFW_VERSION@"
+            VALUE "OriginalFilename", "glfw3.dll"
+            VALUE "ProductName", "GLFW"
+            VALUE "ProductVersion", "@GLFW_VERSION@"
+        }
+    }
+    BLOCK "VarFileInfo"
+    {
+        VALUE "Translation", 0x409, 1200
+    }
+}
+
diff --git a/src/external/glfw/src/glfw3Config.cmake.in b/src/external/glfw/src/glfw3Config.cmake.in
index 1fa200e27..4a13a88b9 100644
--- a/src/external/glfw/src/glfw3Config.cmake.in
+++ b/src/external/glfw/src/glfw3Config.cmake.in
@@ -1 +1,3 @@
+include(CMakeFindDependencyMacro)
+find_dependency(Threads)
 include("${CMAKE_CURRENT_LIST_DIR}/glfw3Targets.cmake")
diff --git a/src/external/glfw/src/internal.h b/src/external/glfw/src/internal.h
index 4c75c9b1c..6d7587c8c 100644
--- a/src/external/glfw/src/internal.h
+++ b/src/external/glfw/src/internal.h
@@ -128,6 +128,7 @@ typedef enum VkStructureType
     VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000,
     VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
     VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000,
+    VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT = 1000217000,
     VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
 } VkStructureType;
 
@@ -559,6 +560,7 @@ struct _GLFWlibrary
         GLFWbool        KHR_win32_surface;
 #elif defined(_GLFW_COCOA)
         GLFWbool        MVK_macos_surface;
+        GLFWbool        EXT_metal_surface;
 #elif defined(_GLFW_X11)
         GLFWbool        KHR_xlib_surface;
         GLFWbool        KHR_xcb_surface;
diff --git a/src/external/glfw/src/nsgl_context.h b/src/external/glfw/src/nsgl_context.h
index edd958e16..9c31436cf 100644
--- a/src/external/glfw/src/nsgl_context.h
+++ b/src/external/glfw/src/nsgl_context.h
@@ -44,10 +44,6 @@ typedef struct _GLFWcontextNSGL
 {
     id                pixelFormat;
     id                object;
-    CVDisplayLinkRef  displayLink;
-    atomic_int        swapInterval;
-    int               swapIntervalsPassed;
-    id                swapIntervalCond;
 
 } _GLFWcontextNSGL;
 
@@ -67,5 +63,4 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
                                 const _GLFWctxconfig* ctxconfig,
                                 const _GLFWfbconfig* fbconfig);
 void _glfwDestroyContextNSGL(_GLFWwindow* window);
-void _glfwUpdateDisplayLinkDisplayNSGL(_GLFWwindow* window);
 
diff --git a/src/external/glfw/src/nsgl_context.m b/src/external/glfw/src/nsgl_context.m
index e455d412a..e011fa5e0 100644
--- a/src/external/glfw/src/nsgl_context.m
+++ b/src/external/glfw/src/nsgl_context.m
@@ -28,29 +28,8 @@
 
 #include "internal.h"
 
-// Display link callback for manual swap interval implementation
-// This is based on a similar workaround added to SDL2
-//
-static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink,
-                                    const CVTimeStamp* now,
-                                    const CVTimeStamp* outputTime,
-                                    CVOptionFlags flagsIn,
-                                    CVOptionFlags* flagsOut,
-                                    void* userInfo)
-{
-    _GLFWwindow* window = (_GLFWwindow *) userInfo;
-
-    const int interval = atomic_load(&window->context.nsgl.swapInterval);
-    if (interval > 0)
-    {
-        [window->context.nsgl.swapIntervalCond lock];
-        window->context.nsgl.swapIntervalsPassed++;
-        [window->context.nsgl.swapIntervalCond signal];
-        [window->context.nsgl.swapIntervalCond unlock];
-    }
-
-    return kCVReturnSuccess;
-}
+#include <unistd.h>
+#include <math.h>
 
 static void makeContextCurrentNSGL(_GLFWwindow* window)
 {
@@ -70,19 +49,28 @@ static void swapBuffersNSGL(_GLFWwindow* window)
 {
     @autoreleasepool {
 
-    const int interval = atomic_load(&window->context.nsgl.swapInterval);
-    if (interval > 0)
+    // HACK: Simulate vsync with usleep as NSGL swap interval does not apply to
+    //       windows with a non-visible occlusion state
+    if (!([window->ns.object occlusionState] & NSWindowOcclusionStateVisible))
     {
-        [window->context.nsgl.swapIntervalCond lock];
-        do
+        int interval = 0;
+        [window->context.nsgl.object getValues:&interval
+                                  forParameter:NSOpenGLContextParameterSwapInterval];
+
+        if (interval > 0)
         {
-            [window->context.nsgl.swapIntervalCond wait];
-        } while (window->context.nsgl.swapIntervalsPassed % interval != 0);
-        window->context.nsgl.swapIntervalsPassed = 0;
-        [window->context.nsgl.swapIntervalCond unlock];
+            const double framerate = 60.0;
+            const uint64_t frequency = _glfwPlatformGetTimerFrequency();
+            const uint64_t value = _glfwPlatformGetTimerValue();
+
+            const double elapsed = value / (double) frequency;
+            const double period = 1.0 / framerate;
+            const double delay = period - fmod(elapsed, period);
+
+            usleep(floorl(delay * 1e6));
+        }
     }
 
-    // ARP appears to be unnecessary, but this is future-proof
     [window->context.nsgl.object flushBuffer];
 
     } // autoreleasepool
@@ -91,11 +79,14 @@ static void swapBuffersNSGL(_GLFWwindow* window)
 static void swapIntervalNSGL(int interval)
 {
     @autoreleasepool {
+
     _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
-    atomic_store(&window->context.nsgl.swapInterval, interval);
-    [window->context.nsgl.swapIntervalCond lock];
-    window->context.nsgl.swapIntervalsPassed = 0;
-    [window->context.nsgl.swapIntervalCond unlock];
+    if (window)
+    {
+        [window->context.nsgl.object setValues:&interval
+                                  forParameter:NSOpenGLContextParameterSwapInterval];
+    }
+
     } // autoreleasepool
 }
 
@@ -123,17 +114,6 @@ static void destroyContextNSGL(_GLFWwindow* window)
 {
     @autoreleasepool {
 
-    if (window->context.nsgl.displayLink)
-    {
-        if (CVDisplayLinkIsRunning(window->context.nsgl.displayLink))
-            CVDisplayLinkStop(window->context.nsgl.displayLink);
-
-        CVDisplayLinkRelease(window->context.nsgl.displayLink);
-    }
-
-    [window->context.nsgl.swapIntervalCond release];
-    window->context.nsgl.swapIntervalCond = nil;
-
     [window->context.nsgl.pixelFormat release];
     window->context.nsgl.pixelFormat = nil;
 
@@ -354,17 +334,10 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
                                   forParameter:NSOpenGLContextParameterSurfaceOpacity];
     }
 
-    if (window->ns.retina)
-        [window->ns.view setWantsBestResolutionOpenGLSurface:YES];
-
-    GLint interval = 0;
-    [window->context.nsgl.object setValues:&interval
-                              forParameter:NSOpenGLContextParameterSwapInterval];
+    [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina];
 
     [window->context.nsgl.object setView:window->ns.view];
 
-    window->context.nsgl.swapIntervalCond = [NSCondition new];
-
     window->context.makeCurrent = makeContextCurrentNSGL;
     window->context.swapBuffers = swapBuffersNSGL;
     window->context.swapInterval = swapIntervalNSGL;
@@ -372,26 +345,9 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window,
     window->context.getProcAddress = getProcAddressNSGL;
     window->context.destroy = destroyContextNSGL;
 
-    CVDisplayLinkCreateWithActiveCGDisplays(&window->context.nsgl.displayLink);
-    CVDisplayLinkSetOutputCallback(window->context.nsgl.displayLink,
-                                   &displayLinkCallback,
-                                   window);
-    CVDisplayLinkStart(window->context.nsgl.displayLink);
-
-    _glfwUpdateDisplayLinkDisplayNSGL(window);
     return GLFW_TRUE;
 }
 
-void _glfwUpdateDisplayLinkDisplayNSGL(_GLFWwindow* window)
-{
-    CGDirectDisplayID displayID =
-        [[[window->ns.object screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue];
-    if (!displayID)
-        return;
-
-    CVDisplayLinkSetCurrentCGDisplay(window->context.nsgl.displayLink, displayID);
-}
-
 
 //////////////////////////////////////////////////////////////////////////
 //////                        GLFW native API                       //////
diff --git a/src/external/glfw/src/posix_time.c b/src/external/glfw/src/posix_time.c
index 301cb958c..ae3d5c789 100644
--- a/src/external/glfw/src/posix_time.c
+++ b/src/external/glfw/src/posix_time.c
@@ -27,8 +27,11 @@
 // It is fine to use C99 in this file because it will not be built with VS
 //========================================================================
 
+#define _POSIX_C_SOURCE 199309L
+
 #include "internal.h"
 
+#include <unistd.h>
 #include <sys/time.h>
 #include <time.h>
 
@@ -41,7 +44,7 @@
 //
 void _glfwInitTimerPOSIX(void)
 {
-#if defined(CLOCK_MONOTONIC)
+#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK)
     struct timespec ts;
 
     if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
@@ -64,7 +67,7 @@ void _glfwInitTimerPOSIX(void)
 
 uint64_t _glfwPlatformGetTimerValue(void)
 {
-#if defined(CLOCK_MONOTONIC)
+#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK)
     if (_glfw.timer.posix.monotonic)
     {
         struct timespec ts;
diff --git a/src/external/glfw/src/vulkan.c b/src/external/glfw/src/vulkan.c
index 6fd0af2c3..b5340520b 100644
--- a/src/external/glfw/src/vulkan.c
+++ b/src/external/glfw/src/vulkan.c
@@ -57,6 +57,8 @@ GLFWbool _glfwInitVulkan(int mode)
     _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll");
 #elif defined(_GLFW_COCOA)
     _glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib");
+    if (!_glfw.vk.handle)
+        _glfw.vk.handle = _glfwLoadLocalVulkanLoaderNS();
 #else
     _glfw.vk.handle = _glfw_dlopen("libvulkan.so.1");
 #endif
@@ -130,6 +132,8 @@ GLFWbool _glfwInitVulkan(int mode)
 #elif defined(_GLFW_COCOA)
         else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0)
             _glfw.vk.MVK_macos_surface = GLFW_TRUE;
+        else if (strcmp(ep[i].extensionName, "VK_EXT_metal_surface") == 0)
+            _glfw.vk.EXT_metal_surface = GLFW_TRUE;
 #elif defined(_GLFW_X11)
         else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0)
             _glfw.vk.KHR_xlib_surface = GLFW_TRUE;
diff --git a/src/external/glfw/src/win32_monitor.c b/src/external/glfw/src/win32_monitor.c
index 3067b65ec..5f91c579b 100644
--- a/src/external/glfw/src/win32_monitor.c
+++ b/src/external/glfw/src/win32_monitor.c
@@ -33,6 +33,7 @@
 #include <string.h>
 #include <limits.h>
 #include <malloc.h>
+#include <wchar.h>
 
 
 // Callback for EnumDisplayMonitors in createMonitor
diff --git a/src/external/glfw/src/win32_window.c b/src/external/glfw/src/win32_window.c
index 3a50c542b..0ae0998a3 100644
--- a/src/external/glfw/src/win32_window.c
+++ b/src/external/glfw/src/win32_window.c
@@ -36,8 +36,6 @@
 #include <windowsx.h>
 #include <shellapi.h>
 
-#define _GLFW_KEY_INVALID -2
-
 // Returns the window style for the specified window
 //
 static DWORD getWindowStyle(const _GLFWwindow* window)
@@ -448,77 +446,6 @@ static int getKeyMods(void)
     return mods;
 }
 
-// Retrieves and translates modifier keys
-//
-static int getAsyncKeyMods(void)
-{
-    int mods = 0;
-
-    if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
-        mods |= GLFW_MOD_SHIFT;
-    if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
-        mods |= GLFW_MOD_CONTROL;
-    if (GetAsyncKeyState(VK_MENU) & 0x8000)
-        mods |= GLFW_MOD_ALT;
-    if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & 0x8000)
-        mods |= GLFW_MOD_SUPER;
-    if (GetAsyncKeyState(VK_CAPITAL) & 1)
-        mods |= GLFW_MOD_CAPS_LOCK;
-    if (GetAsyncKeyState(VK_NUMLOCK) & 1)
-        mods |= GLFW_MOD_NUM_LOCK;
-
-    return mods;
-}
-
-// Translates a Windows key to the corresponding GLFW key
-//
-static int translateKey(WPARAM wParam, LPARAM lParam)
-{
-    // The Ctrl keys require special handling
-    if (wParam == VK_CONTROL)
-    {
-        MSG next;
-        DWORD time;
-
-        // Right side keys have the extended key bit set
-        if (HIWORD(lParam) & KF_EXTENDED)
-            return GLFW_KEY_RIGHT_CONTROL;
-
-        // HACK: Alt Gr sends Left Ctrl and then Right Alt in close sequence
-        //       We only want the Right Alt message, so if the next message is
-        //       Right Alt we ignore this (synthetic) Left Ctrl message
-        time = GetMessageTime();
-
-        if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE))
-        {
-            if (next.message == WM_KEYDOWN ||
-                next.message == WM_SYSKEYDOWN ||
-                next.message == WM_KEYUP ||
-                next.message == WM_SYSKEYUP)
-            {
-                if (next.wParam == VK_MENU &&
-                    (HIWORD(next.lParam) & KF_EXTENDED) &&
-                    next.time == time)
-                {
-                    // Next message is Right Alt down so discard this
-                    return _GLFW_KEY_INVALID;
-                }
-            }
-        }
-
-        return GLFW_KEY_LEFT_CONTROL;
-    }
-
-    if (wParam == VK_PROCESSKEY)
-    {
-        // IME notifies that keys have been filtered by setting the virtual
-        // key-code to VK_PROCESSKEY
-        return _GLFW_KEY_INVALID;
-    }
-
-    return _glfw.win32.keycodes[HIWORD(lParam) & 0x1FF];
-}
-
 static void fitToMonitor(_GLFWwindow* window)
 {
     MONITORINFO mi = { sizeof(mi) };
@@ -748,13 +675,64 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
         case WM_KEYUP:
         case WM_SYSKEYUP:
         {
-            const int key = translateKey(wParam, lParam);
-            const int scancode = (HIWORD(lParam) & 0x1ff);
+            int key, scancode;
             const int action = (HIWORD(lParam) & KF_UP) ? GLFW_RELEASE : GLFW_PRESS;
             const int mods = getKeyMods();
 
-            if (key == _GLFW_KEY_INVALID)
+            scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff));
+            if (!scancode)
+            {
+                // NOTE: Some synthetic key messages have a scancode of zero
+                // HACK: Map the virtual key back to a usable scancode
+                scancode = MapVirtualKeyW((UINT) wParam, MAPVK_VK_TO_VSC);
+            }
+
+            key = _glfw.win32.keycodes[scancode];
+
+            // The Ctrl keys require special handling
+            if (wParam == VK_CONTROL)
+            {
+                if (HIWORD(lParam) & KF_EXTENDED)
+                {
+                    // Right side keys have the extended key bit set
+                    key = GLFW_KEY_RIGHT_CONTROL;
+                }
+                else
+                {
+                    // NOTE: Alt Gr sends Left Ctrl followed by Right Alt
+                    // HACK: We only want one event for Alt Gr, so if we detect
+                    //       this sequence we discard this Left Ctrl message now
+                    //       and later report Right Alt normally
+                    MSG next;
+                    const DWORD time = GetMessageTime();
+
+                    if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE))
+                    {
+                        if (next.message == WM_KEYDOWN ||
+                            next.message == WM_SYSKEYDOWN ||
+                            next.message == WM_KEYUP ||
+                            next.message == WM_SYSKEYUP)
+                        {
+                            if (next.wParam == VK_MENU &&
+                                (HIWORD(next.lParam) & KF_EXTENDED) &&
+                                next.time == time)
+                            {
+                                // Next message is Right Alt down so discard this
+                                break;
+                            }
+                        }
+                    }
+
+                    // This is a regular Left Ctrl message
+                    key = GLFW_KEY_LEFT_CONTROL;
+                }
+            }
+            else if (wParam == VK_PROCESSKEY)
+            {
+                // IME notifies that keys have been filtered by setting the
+                // virtual key-code to VK_PROCESSKEY
                 break;
+            }
 
             if (action == GLFW_RELEASE && wParam == VK_SHIFT)
             {
@@ -1934,30 +1912,40 @@ void _glfwPlatformPollEvents(void)
         }
     }
 
+    // HACK: Release modifier keys that the system did not emit KEYUP for
+    // NOTE: Shift keys on Windows tend to "stick" when both are pressed as
+    //       no key up message is generated by the first key release
+    // NOTE: Windows key is not reported as released by the Win+V hotkey
+    //       Other Win hotkeys are handled implicitly by _glfwInputWindowFocus
+    //       because they change the input focus
+    // NOTE: The other half of this is in the WM_*KEY* handler in windowProc
     handle = GetActiveWindow();
     if (handle)
     {
-        // NOTE: Shift keys on Windows tend to "stick" when both are pressed as
-        //       no key up message is generated by the first key release
-        //       The other half of this is in the handling of WM_KEYUP
-        // HACK: Query actual key state and synthesize release events as needed
         window = GetPropW(handle, L"GLFW");
         if (window)
         {
-            const GLFWbool lshift = (GetAsyncKeyState(VK_LSHIFT) & 0x8000) != 0;
-            const GLFWbool rshift = (GetAsyncKeyState(VK_RSHIFT) & 0x8000) != 0;
-
-            if (!lshift && window->keys[GLFW_KEY_LEFT_SHIFT] == GLFW_PRESS)
+            int i;
+            const int keys[4][2] =
             {
-                const int mods = getAsyncKeyMods();
-                const int scancode = _glfw.win32.scancodes[GLFW_KEY_LEFT_SHIFT];
-                _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, GLFW_RELEASE, mods);
-            }
-            else if (!rshift && window->keys[GLFW_KEY_RIGHT_SHIFT] == GLFW_PRESS)
+                { VK_LSHIFT, GLFW_KEY_LEFT_SHIFT },
+                { VK_RSHIFT, GLFW_KEY_RIGHT_SHIFT },
+                { VK_LWIN, GLFW_KEY_LEFT_SUPER },
+                { VK_RWIN, GLFW_KEY_RIGHT_SUPER }
+            };
+
+            for (i = 0;  i < 4;  i++)
             {
-                const int mods = getAsyncKeyMods();
-                const int scancode = _glfw.win32.scancodes[GLFW_KEY_RIGHT_SHIFT];
-                _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, GLFW_RELEASE, mods);
+                const int vk = keys[i][0];
+                const int key = keys[i][1];
+                const int scancode = _glfw.win32.scancodes[key];
+
+                if ((GetKeyState(vk) & 0x8000))
+                    continue;
+                if (window->keys[key] != GLFW_PRESS)
+                    continue;
+
+                _glfwInputKey(window, key, scancode, GLFW_RELEASE, getKeyMods());
             }
         }
     }
@@ -2039,6 +2027,13 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
 
 const char* _glfwPlatformGetScancodeName(int scancode)
 {
+    if (scancode < 0 || scancode > (KF_EXTENDED | 0xff) ||
+        _glfw.win32.keycodes[scancode] == GLFW_KEY_UNKNOWN)
+    {
+        _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode");
+        return NULL;
+    }
+
     return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]];
 }
 
diff --git a/src/external/glfw/src/wl_init.c b/src/external/glfw/src/wl_init.c
index 9e692f0e6..558ff8a80 100644
--- a/src/external/glfw/src/wl_init.c
+++ b/src/external/glfw/src/wl_init.c
@@ -26,6 +26,8 @@
 // It is fine to use C99 in this file because it will not be built with VS
 //========================================================================
 
+#define _POSIX_C_SOURCE 199309L
+
 #include "internal.h"
 
 #include <assert.h>
@@ -38,6 +40,7 @@
 #include <sys/mman.h>
 #include <sys/timerfd.h>
 #include <unistd.h>
+#include <time.h>
 #include <wayland-client.h>
 
 
@@ -125,6 +128,7 @@ static void pointerHandleLeave(void* data,
     _glfw.wl.serial = serial;
     _glfw.wl.pointerFocus = NULL;
     _glfwInputCursorEnter(window, GLFW_FALSE);
+    _glfw.wl.cursorPreviousName = NULL;
 }
 
 static void setCursor(_GLFWwindow* window, const char* name)
@@ -196,6 +200,7 @@ static void pointerHandleMotion(void* data,
             window->wl.cursorPosX = x;
             window->wl.cursorPosY = y;
             _glfwInputCursorPos(window, x, y);
+            _glfw.wl.cursorPreviousName = NULL;
             return;
         case topDecoration:
             if (y < _GLFW_DECORATION_WIDTH)
diff --git a/src/external/glfw/src/x11_init.c b/src/external/glfw/src/x11_init.c
index d44d4e2ae..2b7bc7f10 100644
--- a/src/external/glfw/src/x11_init.c
+++ b/src/external/glfw/src/x11_init.c
@@ -36,6 +36,7 @@
 #include <limits.h>
 #include <stdio.h>
 #include <locale.h>
+#include <unistd.h>
 
 
 // Translate an X11 key code to a GLFW key code.
diff --git a/src/external/glfw/src/x11_monitor.c b/src/external/glfw/src/x11_monitor.c
index 4d3e1a942..809b93e48 100644
--- a/src/external/glfw/src/x11_monitor.c
+++ b/src/external/glfw/src/x11_monitor.c
@@ -322,12 +322,16 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
             XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
         XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
 
-        if (xpos)
-            *xpos = ci->x;
-        if (ypos)
-            *ypos = ci->y;
+        if (ci)
+        {
+            if (xpos)
+                *xpos = ci->x;
+            if (ypos)
+                *ypos = ci->y;
+
+            XRRFreeCrtcInfo(ci);
+        }
 
-        XRRFreeCrtcInfo(ci);
         XRRFreeScreenResources(sr);
     }
 }
@@ -493,9 +497,15 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
             XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root);
         XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc);
 
-        *mode = vidmodeFromModeInfo(getModeInfo(sr, ci->mode), ci);
+        if (ci)
+        {
+            const XRRModeInfo* mi = getModeInfo(sr, ci->mode);
+            if (mi)  // mi can be NULL if the monitor has been disconnected
+                *mode = vidmodeFromModeInfo(mi, ci);
+
+            XRRFreeCrtcInfo(ci);
+        }
 
-        XRRFreeCrtcInfo(ci);
         XRRFreeScreenResources(sr);
     }
     else
diff --git a/src/external/glfw/src/x11_platform.h b/src/external/glfw/src/x11_platform.h
index cb3b1068a..04c46647c 100644
--- a/src/external/glfw/src/x11_platform.h
+++ b/src/external/glfw/src/x11_platform.h
@@ -186,6 +186,7 @@ typedef struct _GLFWwindowX11
 {
     Colormap        colormap;
     Window          handle;
+    Window          parent;
     XIC             ic;
 
     GLFWbool        overrideRedirect;
diff --git a/src/external/glfw/src/x11_window.c b/src/external/glfw/src/x11_window.c
index 9f0312cec..23dc89adf 100644
--- a/src/external/glfw/src/x11_window.c
+++ b/src/external/glfw/src/x11_window.c
@@ -613,46 +613,41 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
 
     window->x11.transparent = _glfwIsVisualTransparentX11(visual);
 
-    // Create the actual window
-    {
-        XSetWindowAttributes wa;
-        const unsigned long wamask = CWBorderPixel | CWColormap | CWEventMask;
-
-        wa.colormap = window->x11.colormap;
-        wa.border_pixel = 0;
-        wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
-                        PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
-                        ExposureMask | FocusChangeMask | VisibilityChangeMask |
-                        EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
-
-        _glfwGrabErrorHandlerX11();
-
-        window->x11.handle = XCreateWindow(_glfw.x11.display,
-                                           _glfw.x11.root,
-                                           0, 0,
-                                           width, height,
-                                           0,      // Border width
-                                           depth,  // Color depth
-                                           InputOutput,
-                                           visual,
-                                           wamask,
-                                           &wa);
-
-        _glfwReleaseErrorHandlerX11();
-
-        if (!window->x11.handle)
-        {
-            _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
-                               "X11: Failed to create window");
-            return GLFW_FALSE;
-        }
-
-        XSaveContext(_glfw.x11.display,
-                     window->x11.handle,
-                     _glfw.x11.context,
-                     (XPointer) window);
+    XSetWindowAttributes wa = { 0 };
+    wa.colormap = window->x11.colormap;
+    wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
+                    PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
+                    ExposureMask | FocusChangeMask | VisibilityChangeMask |
+                    EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
+
+    _glfwGrabErrorHandlerX11();
+
+    window->x11.parent = _glfw.x11.root;
+    window->x11.handle = XCreateWindow(_glfw.x11.display,
+                                       _glfw.x11.root,
+                                       0, 0,   // Position
+                                       width, height,
+                                       0,      // Border width
+                                       depth,  // Color depth
+                                       InputOutput,
+                                       visual,
+                                       CWBorderPixel | CWColormap | CWEventMask,
+                                       &wa);
+
+    _glfwReleaseErrorHandlerX11();
+
+    if (!window->x11.handle)
+    {
+        _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
+                           "X11: Failed to create window");
+        return GLFW_FALSE;
     }
 
+    XSaveContext(_glfw.x11.display,
+                 window->x11.handle,
+                 _glfw.x11.context,
+                 (XPointer) window);
+
     if (!wndconfig->decorated)
         _glfwPlatformSetWindowDecorated(window, GLFW_FALSE);
 
@@ -682,7 +677,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
         {
             XChangeProperty(_glfw.x11.display, window->x11.handle,
                             _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
-                            PropModeReplace, (unsigned char*) &states, count);
+                            PropModeReplace, (unsigned char*) states, count);
         }
     }
 
@@ -787,6 +782,13 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
                                    NULL);
     }
 
+    if (window->x11.ic)
+    {
+        unsigned long filter = 0;
+        if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL)
+            XSelectInput(_glfw.x11.display, window->x11.handle, wa.event_mask | filter);
+    }
+
     _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
     _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
 
@@ -1257,6 +1259,12 @@ static void processEvent(XEvent *event)
 
     switch (event->type)
     {
+        case ReparentNotify:
+        {
+            window->x11.parent = event->xreparent.parent;
+            return;
+        }
+
         case KeyPress:
         {
             const int key = translateKey(keycode);
@@ -1541,18 +1549,28 @@ static void processEvent(XEvent *event)
                 window->x11.height = event->xconfigure.height;
             }
 
-            if (event->xconfigure.x != window->x11.xpos ||
-                event->xconfigure.y != window->x11.ypos)
+            int xpos = event->xconfigure.x;
+            int ypos = event->xconfigure.y;
+
+            // NOTE: ConfigureNotify events from the server are in local
+            //       coordinates, so if we are reparented we need to translate
+            //       the position into root (screen) coordinates
+            if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
             {
-                if (window->x11.overrideRedirect || event->xany.send_event)
-                {
-                    _glfwInputWindowPos(window,
-                                        event->xconfigure.x,
-                                        event->xconfigure.y);
+                Window dummy;
+                XTranslateCoordinates(_glfw.x11.display,
+                                      window->x11.parent,
+                                      _glfw.x11.root,
+                                      xpos, ypos,
+                                      &xpos, &ypos,
+                                      &dummy);
+            }
 
-                    window->x11.xpos = event->xconfigure.x;
-                    window->x11.ypos = event->xconfigure.y;
-                }
+            if (xpos != window->x11.xpos || ypos != window->x11.ypos)
+            {
+                _glfwInputWindowPos(window, xpos, ypos);
+                window->x11.xpos = xpos;
+                window->x11.ypos = ypos;
             }
 
             return;
@@ -2340,18 +2358,67 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window)
 
 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
 {
-    if (_glfw.x11.NET_WM_STATE &&
-        _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
-        _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
+    if (!_glfw.x11.NET_WM_STATE ||
+        !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
+        !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
+    {
+        return;
+    }
+
+    if (_glfwPlatformWindowVisible(window))
     {
         sendEventToWM(window,
-                      _glfw.x11.NET_WM_STATE,
-                      _NET_WM_STATE_ADD,
-                      _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
-                      _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
-                      1, 0);
-        XFlush(_glfw.x11.display);
+                    _glfw.x11.NET_WM_STATE,
+                    _NET_WM_STATE_ADD,
+                    _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
+                    _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
+                    1, 0);
     }
+    else
+    {
+        Atom* states = NULL;
+        unsigned long count =
+            _glfwGetWindowPropertyX11(window->x11.handle,
+                                      _glfw.x11.NET_WM_STATE,
+                                      XA_ATOM,
+                                      (unsigned char**) &states);
+
+        // NOTE: We don't check for failure as this property may not exist yet
+        //       and that's fine (and we'll create it implicitly with append)
+
+        Atom missing[2] =
+        {
+            _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
+            _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ
+        };
+        unsigned long missingCount = 2;
+
+        for (unsigned long i = 0;  i < count;  i++)
+        {
+            for (unsigned long j = 0;  j < missingCount;  j++)
+            {
+                if (states[i] == missing[j])
+                {
+                    missing[j] = missing[missingCount - 1];
+                    missingCount--;
+                }
+            }
+        }
+
+        if (states)
+            XFree(states);
+
+        if (!missingCount)
+            return;
+
+        XChangeProperty(_glfw.x11.display, window->x11.handle,
+                        _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
+                        PropModeAppend,
+                        (unsigned char*) missing,
+                        missingCount);
+    }
+
+    XFlush(_glfw.x11.display);
 }
 
 void _glfwPlatformShowWindow(_GLFWwindow* window)
@@ -2371,6 +2438,9 @@ void _glfwPlatformHideWindow(_GLFWwindow* window)
 
 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
 {
+    if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION)
+        return;
+
     sendEventToWM(window,
                   _glfw.x11.NET_WM_STATE,
                   _NET_WM_STATE_ADD,
@@ -2382,7 +2452,7 @@ void _glfwPlatformFocusWindow(_GLFWwindow* window)
 {
     if (_glfw.x11.NET_ACTIVE_WINDOW)
         sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
-    else
+    else if (_glfwPlatformWindowVisible(window))
     {
         XRaiseWindow(_glfw.x11.display, window->x11.handle);
         XSetInputFocus(_glfw.x11.display, window->x11.handle,
@@ -2567,7 +2637,7 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
 
     if (_glfwPlatformWindowVisible(window))
     {
-        const Atom action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
+        const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
         sendEventToWM(window,
                       _glfw.x11.NET_WM_STATE,
                       action,
@@ -2576,15 +2646,16 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
     }
     else
     {
-        Atom* states;
+        Atom* states = NULL;
         unsigned long i, count;
 
         count = _glfwGetWindowPropertyX11(window->x11.handle,
                                           _glfw.x11.NET_WM_STATE,
                                           XA_ATOM,
                                           (unsigned char**) &states);
-        if (!states)
-            return;
+
+        // NOTE: We don't check for failure as this property may not exist yet
+        //       and that's fine (and we'll create it implicitly with append)
 
         if (enabled)
         {
@@ -2594,32 +2665,36 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
                     break;
             }
 
-            if (i == count)
-            {
-                XChangeProperty(_glfw.x11.display, window->x11.handle,
-                                _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
-                                PropModeAppend,
-                                (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
-                                1);
-            }
+            if (i < count)
+                return;
+
+            XChangeProperty(_glfw.x11.display, window->x11.handle,
+                            _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
+                            PropModeAppend,
+                            (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
+                            1);
         }
-        else
+        else if (states)
         {
             for (i = 0;  i < count;  i++)
             {
                 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
-                {
-                    states[i] = states[count - 1];
-                    count--;
-                }
+                    break;
             }
 
+            if (i == count)
+                return;
+
+            states[i] = states[count - 1];
+            count--;
+
             XChangeProperty(_glfw.x11.display, window->x11.handle,
                             _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
-                            PropModeReplace, (unsigned char*) &states, count);
+                            PropModeReplace, (unsigned char*) states, count);
         }
 
-        XFree(states);
+        if (states)
+            XFree(states);
     }
 
     XFlush(_glfw.x11.display);
@@ -2787,6 +2862,13 @@ const char* _glfwPlatformGetScancodeName(int scancode)
     if (!_glfw.x11.xkb.available)
         return NULL;
 
+    if (scancode < 0 || scancode > 0xff ||
+        _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN)
+    {
+        _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode");
+        return NULL;
+    }
+
     const int key = _glfw.x11.keycodes[scancode];
     const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display,
                                              scancode, _glfw.x11.xkb.group, 0);