From 6372d5127b28e5eea2b0234f86c710987adadb25 Mon Sep 17 00:00:00 2001
From: Gerasim Troeglazov <3dEyes@gmail.com>
Date: Sun, 6 Feb 2022 20:26:48 +1000
Subject: Add Haiku support


diff --git a/CMakeLists.txt b/CMakeLists.txt
index b4bbded..68aac62 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
 cmake_minimum_required(VERSION 3.0...3.20 FATAL_ERROR)
 
-project(GLFW VERSION 3.3.6 LANGUAGES C)
+project(GLFW VERSION 3.3.6 LANGUAGES C CXX)
 
 set(CMAKE_LEGACY_CYGWIN_WIN32 OFF)
 
@@ -157,6 +157,9 @@ elseif (WIN32)
 elseif (APPLE)
     set(_GLFW_COCOA 1)
     message(STATUS "Using Cocoa for window creation")
+elseif (HAIKU)
+    set(_GLFW_HAIKU 1)
+    message(STATUS "Using Haiku for window creation")
 elseif (UNIX)
     set(_GLFW_X11 1)
     message(STATUS "Using X11 for window creation")
@@ -200,6 +203,14 @@ if (_GLFW_WIN32)
     endif()
 endif()
 
+#--------------------------------------------------------------------
+# Use Haiku for window creation
+#--------------------------------------------------------------------
+if (_GLFW_HAIKU)
+    find_package(OSMesa REQUIRED)
+    list(APPEND glfw_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}" "-lbe -lgame -ldevice -lGL")
+endif()
+
 #--------------------------------------------------------------------
 # Use X11 for window creation
 #--------------------------------------------------------------------
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b6dd86c..9ff9b28 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -64,6 +64,12 @@ elseif (_GLFW_WAYLAND)
         PROTOCOL
         "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
         BASENAME idle-inhibit-unstable-v1)
+elseif (_GLFW_HAIKU)
+    set(glfw_HEADERS ${common_HEADERS} haiku_platform.h haiku_joystick.h haiku_platform_window.h
+                     haiku_platform_view.h posix_time.h posix_thread.h haiku_context.h)
+    set(glfw_SOURCES ${common_SOURCES} haiku_init.cpp haiku_monitor.cpp haiku_window.cpp
+                     haiku_platform_window.cpp haiku_platform_view.cpp haiku_joystick.cpp posix_time.c
+                     posix_thread.c haiku_context.cpp)
 elseif (_GLFW_OSMESA)
     set(glfw_HEADERS ${common_HEADERS} null_platform.h null_joystick.h
                      posix_time.h posix_thread.h osmesa_context.h)
diff --git a/src/glfw_config.h.in b/src/glfw_config.h.in
index f418c99..631eb2d 100644
--- a/src/glfw_config.h.in
+++ b/src/glfw_config.h.in
@@ -42,6 +42,8 @@
 #cmakedefine _GLFW_COCOA
 // Define this to 1 if building GLFW for Wayland
 #cmakedefine _GLFW_WAYLAND
+// Define this to 1 if building GLFW for Haiku
+#cmakedefine _GLFW_HAIKU
 // Define this to 1 if building GLFW for OSMesa
 #cmakedefine _GLFW_OSMESA
 
diff --git a/src/haiku_context.cpp b/src/haiku_context.cpp
new file mode 100644
index 0000000..f1038ed
--- /dev/null
+++ b/src/haiku_context.cpp
@@ -0,0 +1,373 @@
+//========================================================================
+// GLFW 3.3 OSMesa - www.glfw.org
+//------------------------------------------------------------------------
+// Copyright (c) 2016 Google Inc.
+// Copyright (c) 2016-2017 Camilla Löwy <elmindreda@glfw.org>
+// Copyright (c) 2022 Gerasim Troeglazov <3dEyes@gmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//
+//========================================================================
+// Please use C89 style variable declarations in this file because VS 2010
+//========================================================================
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+extern "C" {
+#include "internal.h"
+}
+
+#include "haiku_platform_window.h"
+
+static void makeContextCurrentOSMesa(_GLFWwindow* window)
+{
+    if (window)
+    {
+        int width, height;
+        _glfwPlatformGetFramebufferSize(window, &width, &height);
+        
+        if (window->context.source == GLFW_NATIVE_CONTEXT_API)
+        {
+        	window->context.osmesa.buffer = PLATFORM_VIEW(window)->GetBuffer();
+        	window->context.osmesa.width = PLATFORM_VIEW(window)->Width();
+        	window->context.osmesa.height = PLATFORM_VIEW(window)->Height();
+        } else {
+	        if ((window->context.osmesa.buffer == NULL) ||
+    	        (width != window->context.osmesa.width) ||
+        	    (height != window->context.osmesa.height))
+        	{
+            	if (window->context.osmesa.buffer)
+            		free(window->context.osmesa.buffer);
+
+	            window->context.osmesa.buffer = calloc(4, (size_t) width * height);
+	            window->context.osmesa.width  = width;
+    	        window->context.osmesa.height = height;
+        	}
+        }
+
+        if (!OSMesaMakeCurrent(window->context.osmesa.handle,
+                               window->context.osmesa.buffer,
+                               GL_UNSIGNED_BYTE,
+                               width, height))
+        {
+            _glfwInputError(GLFW_PLATFORM_ERROR,
+                            "OSMesa: Failed to make context current");
+            return;
+        }
+        
+        if (window->context.source == GLFW_NATIVE_CONTEXT_API)
+			OSMesaPixelStore(OSMESA_Y_UP, 0);
+    }
+
+    _glfwPlatformSetTls(&_glfw.contextSlot, window);
+}
+
+static GLFWglproc getProcAddressOSMesa(const char* procname)
+{
+    return (GLFWglproc) OSMesaGetProcAddress(procname);
+}
+
+static void destroyContextOSMesa(_GLFWwindow* window)
+{
+    if (window->context.osmesa.handle)
+    {
+        OSMesaDestroyContext(window->context.osmesa.handle);
+        window->context.osmesa.handle = NULL;
+    }
+
+    if (window->context.osmesa.buffer)
+    {
+		if (window->context.source == GLFW_OSMESA_CONTEXT_API)
+	        free(window->context.osmesa.buffer);
+
+        window->context.osmesa.width = 0;
+        window->context.osmesa.height = 0;
+    }
+}
+
+static void swapBuffersOSMesa(_GLFWwindow* window)
+{
+	if (window)
+		_glfwPlatformSwapBuffers(window);
+}
+
+static void swapIntervalOSMesa(int interval)
+{
+}
+
+static int extensionSupportedOSMesa(const char* extension)
+{
+    return GLFW_FALSE;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//////                       GLFW internal API                      //////
+//////////////////////////////////////////////////////////////////////////
+
+GLFWbool _glfwInitOSMesa(void)
+{
+    if (_glfw.osmesa.handle)
+        return GLFW_TRUE;
+
+    _glfw.osmesa.handle = _glfw_dlopen("libOSMesa.so.8");
+
+    if (!_glfw.osmesa.handle)
+    {
+        _glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: Library not found");
+        return GLFW_FALSE;
+    }
+
+    _glfw.osmesa.CreateContextExt = (PFN_OSMesaCreateContextExt)
+        _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextExt");
+    _glfw.osmesa.CreateContextAttribs = (PFN_OSMesaCreateContextAttribs)
+        _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextAttribs");
+    _glfw.osmesa.DestroyContext = (PFN_OSMesaDestroyContext)
+        _glfw_dlsym(_glfw.osmesa.handle, "OSMesaDestroyContext");
+    _glfw.osmesa.MakeCurrent = (PFN_OSMesaMakeCurrent)
+        _glfw_dlsym(_glfw.osmesa.handle, "OSMesaMakeCurrent");
+    _glfw.osmesa.GetColorBuffer = (PFN_OSMesaGetColorBuffer)
+        _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetColorBuffer");
+    _glfw.osmesa.GetDepthBuffer = (PFN_OSMesaGetDepthBuffer)
+        _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetDepthBuffer");
+    _glfw.osmesa.GetProcAddress = (PFN_OSMesaGetProcAddress)
+        _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress");
+    _glfw.osmesa.PixelStore = (PFN_OSMesaPixelStore)
+        _glfw_dlsym(_glfw.osmesa.handle, "OSMesaPixelStore");
+
+    if (!_glfw.osmesa.CreateContextExt ||
+        !_glfw.osmesa.DestroyContext ||
+        !_glfw.osmesa.MakeCurrent ||
+        !_glfw.osmesa.GetColorBuffer ||
+        !_glfw.osmesa.GetDepthBuffer ||
+        !_glfw.osmesa.GetProcAddress ||
+        !_glfw.osmesa.PixelStore)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "OSMesa: Failed to load required entry points");
+
+        _glfwTerminateOSMesa();
+        return GLFW_FALSE;
+    }
+
+    return GLFW_TRUE;
+}
+
+void _glfwTerminateOSMesa(void)
+{
+    if (_glfw.osmesa.handle)
+    {
+        _glfw_dlclose(_glfw.osmesa.handle);
+        _glfw.osmesa.handle = NULL;
+    }
+}
+
+#define setAttrib(a, v) \
+{ \
+    assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
+    attribs[index++] = a; \
+    attribs[index++] = v; \
+}
+
+GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
+                                  const _GLFWctxconfig* ctxconfig,
+                                  const _GLFWfbconfig* fbconfig)
+{
+    OSMesaContext share = NULL;
+    const int accumBits = fbconfig->accumRedBits +
+                          fbconfig->accumGreenBits +
+                          fbconfig->accumBlueBits +
+                          fbconfig->accumAlphaBits;
+
+    if (ctxconfig->client == GLFW_OPENGL_ES_API)
+    {
+        _glfwInputError(GLFW_API_UNAVAILABLE,
+                        "OSMesa: OpenGL ES is not available on OSMesa");
+        return GLFW_FALSE;
+    }
+
+    if (ctxconfig->share)
+        share = ctxconfig->share->context.osmesa.handle;
+
+    if (OSMesaCreateContextAttribs)
+    {
+        int index = 0, attribs[40];
+
+        setAttrib(OSMESA_FORMAT, ctxconfig->source == GLFW_NATIVE_CONTEXT_API ? OSMESA_BGRA : OSMESA_RGBA);
+        setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits);
+        setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits);
+        setAttrib(OSMESA_ACCUM_BITS, accumBits);
+
+        if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE)
+        {
+            setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE);
+        }
+        else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE)
+        {
+            setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE);
+        }
+
+        if (ctxconfig->major != 1 || ctxconfig->minor != 0)
+        {
+            setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major);
+            setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor);
+        }
+
+        if (ctxconfig->forward)
+        {
+            _glfwInputError(GLFW_VERSION_UNAVAILABLE,
+                            "OSMesa: Forward-compatible contexts not supported");
+            return GLFW_FALSE;
+        }
+
+        setAttrib(0, 0);
+
+        window->context.osmesa.handle =
+            OSMesaCreateContextAttribs(attribs, share);
+    }
+    else
+    {
+        if (ctxconfig->profile)
+        {
+            _glfwInputError(GLFW_VERSION_UNAVAILABLE,
+                            "OSMesa: OpenGL profiles unavailable");
+            return GLFW_FALSE;
+        }
+
+        window->context.osmesa.handle =
+            OSMesaCreateContextExt(ctxconfig->source == GLFW_NATIVE_CONTEXT_API ? OSMESA_BGRA : OSMESA_RGBA,
+                                   fbconfig->depthBits, fbconfig->stencilBits, accumBits, share);
+    }
+
+    if (window->context.osmesa.handle == NULL)
+    {
+        _glfwInputError(GLFW_VERSION_UNAVAILABLE,
+                        "OSMesa: Failed to create context");
+        return GLFW_FALSE;
+    }
+
+    window->context.makeCurrent = makeContextCurrentOSMesa;
+    window->context.swapBuffers = swapBuffersOSMesa;
+    window->context.swapInterval = swapIntervalOSMesa;
+    window->context.extensionSupported = extensionSupportedOSMesa;
+    window->context.getProcAddress = getProcAddressOSMesa;
+    window->context.destroy = destroyContextOSMesa;
+
+    return GLFW_TRUE;
+}
+
+#undef setAttrib
+
+
+//////////////////////////////////////////////////////////////////////////
+//////                        GLFW native API                       //////
+//////////////////////////////////////////////////////////////////////////
+
+GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width,
+                                     int* height, int* format, void** buffer)
+{
+    void* mesaBuffer;
+    GLint mesaWidth, mesaHeight, mesaFormat;
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
+
+    if (window->context.source != GLFW_OSMESA_CONTEXT_API)
+    {
+        _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
+        return GLFW_FALSE;
+    }
+
+    if (!OSMesaGetColorBuffer(window->context.osmesa.handle,
+                              &mesaWidth, &mesaHeight,
+                              &mesaFormat, &mesaBuffer))
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "OSMesa: Failed to retrieve color buffer");
+        return GLFW_FALSE;
+    }
+
+    if (width)
+        *width = mesaWidth;
+    if (height)
+        *height = mesaHeight;
+    if (format)
+        *format = mesaFormat;
+    if (buffer)
+        *buffer = mesaBuffer;
+
+    return GLFW_TRUE;
+}
+
+GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle,
+                                     int* width, int* height,
+                                     int* bytesPerValue,
+                                     void** buffer)
+{
+    void* mesaBuffer;
+    GLint mesaWidth, mesaHeight, mesaBytes;
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    assert(window != NULL);
+
+    _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
+
+    if (window->context.source != GLFW_OSMESA_CONTEXT_API)
+    {
+        _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
+        return GLFW_FALSE;
+    }
+
+    if (!OSMesaGetDepthBuffer(window->context.osmesa.handle,
+                              &mesaWidth, &mesaHeight,
+                              &mesaBytes, &mesaBuffer))
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR,
+                        "OSMesa: Failed to retrieve depth buffer");
+        return GLFW_FALSE;
+    }
+
+    if (width)
+        *width = mesaWidth;
+    if (height)
+        *height = mesaHeight;
+    if (bytesPerValue)
+        *bytesPerValue = mesaBytes;
+    if (buffer)
+        *buffer = mesaBuffer;
+
+    return GLFW_TRUE;
+}
+
+GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle)
+{
+    _GLFWwindow* window = (_GLFWwindow*) handle;
+    _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
+
+    if (window->context.source != GLFW_OSMESA_CONTEXT_API)
+    {
+        _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
+        return NULL;
+    }
+
+    return window->context.osmesa.handle;
+}
diff --git a/src/haiku_context.h b/src/haiku_context.h
new file mode 100644
index 0000000..1d3957e
--- /dev/null
+++ b/src/haiku_context.h
@@ -0,0 +1,92 @@
+//========================================================================
+// GLFW 3.3 OSMesa - www.glfw.org
+//------------------------------------------------------------------------
+// Copyright (c) 2016 Google Inc.
+// Copyright (c) 2016-2017 Camilla Löwy <elmindreda@glfw.org>
+// Copyright (c) 2022 Gerasim Troeglazov <3dEyes@gmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//
+//========================================================================
+
+#define OSMESA_RGBA 0x1908
+#define OSMESA_BGRA	0x1
+#define OSMESA_FORMAT 0x22
+#define OSMESA_DEPTH_BITS 0x30
+#define OSMESA_STENCIL_BITS 0x31
+#define OSMESA_ACCUM_BITS 0x32
+#define OSMESA_PROFILE 0x33
+#define OSMESA_CORE_PROFILE 0x34
+#define OSMESA_COMPAT_PROFILE 0x35
+#define OSMESA_CONTEXT_MAJOR_VERSION 0x36
+#define OSMESA_CONTEXT_MINOR_VERSION 0x37
+#define OSMESA_Y_UP	0x11
+
+typedef void* OSMesaContext;
+
+typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext);
+typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext);
+typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext);
+typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int);
+typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**);
+typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**);
+typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*);
+typedef void (GLAPIENTRY * PFN_OSMesaPixelStore)(int,int);
+#define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt
+#define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs
+#define OSMesaDestroyContext _glfw.osmesa.DestroyContext
+#define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent
+#define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer
+#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer
+#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress
+#define OSMesaPixelStore _glfw.osmesa.PixelStore
+
+#define _GLFW_OSMESA_CONTEXT_STATE              _GLFWcontextOSMesa osmesa
+#define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE      _GLFWlibraryOSMesa osmesa
+
+typedef struct _GLFWcontextOSMesa
+{
+    OSMesaContext       handle;
+    int                 width;
+    int                 height;
+    void*               buffer;
+} _GLFWcontextOSMesa;
+
+typedef struct _GLFWlibraryOSMesa
+{
+    void*           handle;
+
+    PFN_OSMesaCreateContextExt      CreateContextExt;
+    PFN_OSMesaCreateContextAttribs  CreateContextAttribs;
+    PFN_OSMesaDestroyContext        DestroyContext;
+    PFN_OSMesaMakeCurrent           MakeCurrent;
+    PFN_OSMesaGetColorBuffer        GetColorBuffer;
+    PFN_OSMesaGetDepthBuffer        GetDepthBuffer;
+    PFN_OSMesaGetProcAddress        GetProcAddress;
+    PFN_OSMesaPixelStore            PixelStore;
+} _GLFWlibraryOSMesa;
+
+
+GLFWbool _glfwInitOSMesa(void);
+void _glfwTerminateOSMesa(void);
+GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window,
+                                  const _GLFWctxconfig* ctxconfig,
+                                  const _GLFWfbconfig* fbconfig);
+
diff --git a/src/haiku_init.cpp b/src/haiku_init.cpp
new file mode 100644
index 0000000..9d4303b
--- /dev/null
+++ b/src/haiku_init.cpp
@@ -0,0 +1,219 @@
+//========================================================================
+// Copyright (c) 2021-2022 Gerasim Troeglazov <3dEyes@gmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//========================================================================
+
+extern "C" {
+#include "internal.h"
+}
+
+#include <AppKit.h>
+#include <MessageQueue.h>
+#include <storage/Path.h>
+#include <storage/Entry.h>
+#include <storage/File.h>
+#include <storage/AppFileInfo.h>
+
+#include <Rect.h>
+#include <Screen.h>
+
+// Default application signature
+const char *signature = "application/x-vnd.GLFW-executable";
+
+// Create key code translation tables
+//
+static void createKeyTables(void)
+{
+    int scancode;
+
+    memset(_glfw.haiku.keycodes, -1, sizeof(_glfw.haiku.keycodes));
+    memset(_glfw.haiku.scancodes, -1, sizeof(_glfw.haiku.scancodes));
+
+    _glfw.haiku.keycodes[0x01] = GLFW_KEY_ESCAPE;
+    _glfw.haiku.keycodes[0x02] = GLFW_KEY_F1;
+    _glfw.haiku.keycodes[0x03] = GLFW_KEY_F2;
+    _glfw.haiku.keycodes[0x04] = GLFW_KEY_F3;
+    _glfw.haiku.keycodes[0x05] = GLFW_KEY_F4;
+    _glfw.haiku.keycodes[0x06] = GLFW_KEY_F5;
+    _glfw.haiku.keycodes[0x07] = GLFW_KEY_F6;
+    _glfw.haiku.keycodes[0x08] = GLFW_KEY_F7;
+    _glfw.haiku.keycodes[0x09] = GLFW_KEY_F8;
+    _glfw.haiku.keycodes[0x0A] = GLFW_KEY_F9;
+    _glfw.haiku.keycodes[0x0B] = GLFW_KEY_F10;
+    _glfw.haiku.keycodes[0x0C] = GLFW_KEY_F11;
+    _glfw.haiku.keycodes[0x0D] = GLFW_KEY_F12;
+	_glfw.haiku.keycodes[0x0E] = GLFW_KEY_PRINT_SCREEN;
+    _glfw.haiku.keycodes[0x0F] = GLFW_KEY_SCROLL_LOCK;
+    _glfw.haiku.keycodes[0x10] = GLFW_KEY_PAUSE;
+
+    _glfw.haiku.keycodes[0x11] = GLFW_KEY_GRAVE_ACCENT;
+    _glfw.haiku.keycodes[0x12] = GLFW_KEY_1;
+    _glfw.haiku.keycodes[0x13] = GLFW_KEY_2;
+    _glfw.haiku.keycodes[0x14] = GLFW_KEY_3;
+    _glfw.haiku.keycodes[0x15] = GLFW_KEY_4;
+    _glfw.haiku.keycodes[0x16] = GLFW_KEY_5;
+    _glfw.haiku.keycodes[0x17] = GLFW_KEY_6;
+    _glfw.haiku.keycodes[0x18] = GLFW_KEY_7;
+    _glfw.haiku.keycodes[0x19] = GLFW_KEY_8;
+    _glfw.haiku.keycodes[0x1A] = GLFW_KEY_9;
+    _glfw.haiku.keycodes[0x1B] = GLFW_KEY_0;
+    _glfw.haiku.keycodes[0x1C] = GLFW_KEY_MINUS;
+    _glfw.haiku.keycodes[0x1D] = GLFW_KEY_EQUAL;
+    _glfw.haiku.keycodes[0x1E] = GLFW_KEY_BACKSPACE;
+    _glfw.haiku.keycodes[0x1F] = GLFW_KEY_INSERT;
+    _glfw.haiku.keycodes[0x20] = GLFW_KEY_HOME;
+    _glfw.haiku.keycodes[0x21] = GLFW_KEY_PAGE_UP;
+    _glfw.haiku.keycodes[0x22] = GLFW_KEY_NUM_LOCK;
+    _glfw.haiku.keycodes[0x23] = GLFW_KEY_KP_DIVIDE;
+    _glfw.haiku.keycodes[0x24] = GLFW_KEY_KP_MULTIPLY;
+    _glfw.haiku.keycodes[0x25] = GLFW_KEY_KP_SUBTRACT;
+
+    _glfw.haiku.keycodes[0x26] = GLFW_KEY_TAB;
+    _glfw.haiku.keycodes[0x27] = GLFW_KEY_Q;
+    _glfw.haiku.keycodes[0x28] = GLFW_KEY_W;
+    _glfw.haiku.keycodes[0x29] = GLFW_KEY_E;
+    _glfw.haiku.keycodes[0x2A] = GLFW_KEY_R;
+    _glfw.haiku.keycodes[0x2B] = GLFW_KEY_T;
+    _glfw.haiku.keycodes[0x2C] = GLFW_KEY_Y;
+    _glfw.haiku.keycodes[0x2D] = GLFW_KEY_U;
+    _glfw.haiku.keycodes[0x2E] = GLFW_KEY_I;
+    _glfw.haiku.keycodes[0x2F] = GLFW_KEY_O;
+    _glfw.haiku.keycodes[0x30] = GLFW_KEY_P;
+    _glfw.haiku.keycodes[0x31] = GLFW_KEY_LEFT_BRACKET;
+    _glfw.haiku.keycodes[0x32] = GLFW_KEY_RIGHT_BRACKET;
+    _glfw.haiku.keycodes[0x33] = GLFW_KEY_BACKSLASH;
+    _glfw.haiku.keycodes[0x34] = GLFW_KEY_DELETE;
+    _glfw.haiku.keycodes[0x35] = GLFW_KEY_END;
+    _glfw.haiku.keycodes[0x36] = GLFW_KEY_PAGE_DOWN;
+    _glfw.haiku.keycodes[0x37] = GLFW_KEY_KP_7;
+    _glfw.haiku.keycodes[0x38] = GLFW_KEY_KP_8;
+    _glfw.haiku.keycodes[0x39] = GLFW_KEY_KP_9;
+    _glfw.haiku.keycodes[0x3A] = GLFW_KEY_KP_ADD;
+
+    _glfw.haiku.keycodes[0x3B] = GLFW_KEY_CAPS_LOCK;
+    _glfw.haiku.keycodes[0x3C] = GLFW_KEY_A;
+    _glfw.haiku.keycodes[0x3D] = GLFW_KEY_S;
+    _glfw.haiku.keycodes[0x3E] = GLFW_KEY_D;
+    _glfw.haiku.keycodes[0x3F] = GLFW_KEY_F;
+    _glfw.haiku.keycodes[0x40] = GLFW_KEY_G;
+    _glfw.haiku.keycodes[0x41] = GLFW_KEY_H;
+    _glfw.haiku.keycodes[0x42] = GLFW_KEY_J;
+    _glfw.haiku.keycodes[0x43] = GLFW_KEY_K;
+    _glfw.haiku.keycodes[0x44] = GLFW_KEY_L;
+    _glfw.haiku.keycodes[0x45] = GLFW_KEY_SEMICOLON;
+    _glfw.haiku.keycodes[0x46] = GLFW_KEY_APOSTROPHE;
+    _glfw.haiku.keycodes[0x47] = GLFW_KEY_ENTER;
+    _glfw.haiku.keycodes[0x48] = GLFW_KEY_KP_4;
+    _glfw.haiku.keycodes[0x49] = GLFW_KEY_KP_5;
+    _glfw.haiku.keycodes[0x4A] = GLFW_KEY_KP_6;
+
+    _glfw.haiku.keycodes[0x4B] = GLFW_KEY_LEFT_SHIFT;
+    _glfw.haiku.keycodes[0x4C] = GLFW_KEY_Z;
+    _glfw.haiku.keycodes[0x4D] = GLFW_KEY_X;
+    _glfw.haiku.keycodes[0x4E] = GLFW_KEY_C;
+    _glfw.haiku.keycodes[0x4F] = GLFW_KEY_V;
+    _glfw.haiku.keycodes[0x50] = GLFW_KEY_B;
+    _glfw.haiku.keycodes[0x51] = GLFW_KEY_N;
+    _glfw.haiku.keycodes[0x52] = GLFW_KEY_M;
+    _glfw.haiku.keycodes[0x53] = GLFW_KEY_COMMA;
+    _glfw.haiku.keycodes[0x54] = GLFW_KEY_PERIOD;
+    _glfw.haiku.keycodes[0x55] = GLFW_KEY_SLASH;
+    _glfw.haiku.keycodes[0x56] = GLFW_KEY_RIGHT_SHIFT;
+    _glfw.haiku.keycodes[0x57] = GLFW_KEY_UP;
+    _glfw.haiku.keycodes[0x58] = GLFW_KEY_KP_1;
+    _glfw.haiku.keycodes[0x59] = GLFW_KEY_KP_2;
+    _glfw.haiku.keycodes[0x5A] = GLFW_KEY_KP_3;
+    _glfw.haiku.keycodes[0x5B] = GLFW_KEY_KP_ENTER;
+
+    _glfw.haiku.keycodes[0x5C] = GLFW_KEY_LEFT_CONTROL;
+    _glfw.haiku.keycodes[0x5D] = GLFW_KEY_LEFT_ALT;
+    _glfw.haiku.keycodes[0x5E] = GLFW_KEY_SPACE;
+    _glfw.haiku.keycodes[0x5F] = GLFW_KEY_RIGHT_ALT;
+    _glfw.haiku.keycodes[0x60] = GLFW_KEY_RIGHT_CONTROL;
+    _glfw.haiku.keycodes[0x61] = GLFW_KEY_LEFT;
+    _glfw.haiku.keycodes[0x62] = GLFW_KEY_DOWN;
+    _glfw.haiku.keycodes[0x63] = GLFW_KEY_RIGHT;
+    _glfw.haiku.keycodes[0x64] = GLFW_KEY_KP_0;
+    _glfw.haiku.keycodes[0x65] = GLFW_KEY_KP_DECIMAL;
+    _glfw.haiku.keycodes[0x66] = GLFW_KEY_LEFT_SUPER;
+    _glfw.haiku.keycodes[0x67] = GLFW_KEY_RIGHT_SUPER;
+
+    for (scancode = 0;  scancode < 256;  scancode++)
+    {
+        if (_glfw.haiku.keycodes[scancode] >= 0)
+            _glfw.haiku.scancodes[_glfw.haiku.keycodes[scancode]] = scancode;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////                       GLFW platform API                      //////
+//////////////////////////////////////////////////////////////////////////
+
+int _glfwPlatformInit(void)
+{
+	if (!be_app)
+	{
+	    image_info info;
+	    int32 cookie = 0;
+	    if (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
+	        BFile file(info.name, O_RDONLY);
+	        if (file.InitCheck() == B_OK) {
+	            BAppFileInfo app_info(&file);
+	            if (app_info.InitCheck() == B_OK) {
+	                char sig[B_MIME_TYPE_LENGTH];
+	                if (app_info.GetSignature(sig) == B_OK)
+	                    signature = strndup(sig, B_MIME_TYPE_LENGTH);
+	            }
+	        }
+	    }
+		be_app = new BApplication(signature);
+	}
+
+	createKeyTables();
+
+	_glfw.haiku.messageQueue = (void*)new BMessageQueue();
+
+	_glfwInitTimerPOSIX();
+	_glfwInitJoysticksHaiku();
+	
+	BScreen scr(B_MAIN_SCREEN_ID);
+
+	float dpi = 90.0;
+	float widthMM = (scr.Frame().Width() / dpi) * 2.54;
+	float heightMM = (scr.Frame().Height() / dpi) * 2.54;
+
+    _GLFWmonitor *monitor = _glfwAllocMonitor("Default monitor", widthMM, heightMM);
+    _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST);
+    
+    return GLFW_TRUE;
+}
+
+void _glfwPlatformTerminate(void)
+{
+	delete (BMessageQueue*)_glfw.haiku.messageQueue;
+    _glfwTerminateOSMesa();
+    _glfwTerminateJoysticksHaiku();
+}
+
+const char* _glfwPlatformGetVersionString(void)
+{
+    return _GLFW_VERSION_NUMBER " Haiku OSMesa";
+}
diff --git a/src/haiku_joystick.cpp b/src/haiku_joystick.cpp
new file mode 100644
index 0000000..2e8fc5a
--- /dev/null
+++ b/src/haiku_joystick.cpp
@@ -0,0 +1,151 @@
+//========================================================================
+// Copyright (c) 2021 Gerasim Troeglazov <3dEyes@gmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//========================================================================
+
+extern "C" {
+#include "internal.h"
+}
+
+#include <stdio.h>
+#include <string.h>
+
+#include <String.h>
+#include <Joystick.h>
+
+//////////////////////////////////////////////////////////////////////////
+//////                       GLFW internal API                      //////
+//////////////////////////////////////////////////////////////////////////
+
+// Initialize joystick interface
+//
+GLFWbool _glfwInitJoysticksHaiku(void)
+{
+	BJoystick *joystick = new BJoystick();
+
+	if (joystick->CountDevices() <= 0)
+		return GLFW_FALSE;
+
+	char devName[B_OS_NAME_LENGTH];
+	memset(devName, 0, B_OS_NAME_LENGTH);
+	joystick->GetDeviceName(0, devName);
+	if (joystick->Open(devName) == B_ERROR)
+		return GLFW_FALSE;
+
+	char name[B_OS_NAME_LENGTH];
+	memset(name, 0, B_OS_NAME_LENGTH);
+	strncpy(name, "Generic USB Joystick", sizeof(name));
+
+	char guid[33] = "";
+    sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00",
+		name[0], name[1], name[2], name[3],
+		name[4], name[5], name[6], name[7],
+		name[8], name[9], name[10]);
+
+	_GLFWjoystick* js = _glfwAllocJoystick(name, guid,
+		joystick->CountAxes(), joystick->CountButtons(), joystick->CountHats());
+
+    js->haiku.device  = (void*)joystick;
+    js->haiku.lastButtonValues = 0;
+
+    _glfwInputJoystick(js, GLFW_CONNECTED);
+
+	return GLFW_TRUE;
+}
+
+// Close all opened joystick handles
+//
+void _glfwTerminateJoysticksHaiku(void)
+{
+	for (int jid = 0;  jid <= GLFW_JOYSTICK_LAST;  jid++)
+	{
+		if (_glfw.joysticks[jid].present)
+			delete (BJoystick*)(_glfw.joysticks[jid].haiku.device);
+	}
+}
+//////////////////////////////////////////////////////////////////////////
+//////                       GLFW platform API                      //////
+//////////////////////////////////////////////////////////////////////////
+
+int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
+{
+	BJoystick *joystick = (BJoystick*)js->haiku.device;
+	joystick->Update();
+	if (mode & _GLFW_POLL_AXES)
+	{
+		int16 axes[joystick->CountAxes()];
+		joystick->GetAxisValues(axes);
+		for (int axesIdx = 0; axesIdx < joystick->CountAxes(); axesIdx++)
+		{
+			double value = 0;
+			if (axes[axesIdx] >= 0)
+                value = axes[axesIdx] / 32767.0;
+            else
+                value = axes[axesIdx] / 32768.0;
+
+			_glfwInputJoystickAxis(js, (int) axesIdx, value);
+		}
+	}
+
+	if (mode & _GLFW_POLL_BUTTONS)
+	{
+		uint32 buttonValues = joystick->ButtonValues();
+		for (int buttonIdx = 0; buttonIdx < joystick->CountButtons(); buttonIdx++)
+		{
+			bool buttonValue = buttonValues & (1 << buttonIdx);
+			bool lastButtonValue = js->haiku.lastButtonValues & (1 << buttonIdx);
+			if (buttonValue != lastButtonValue)
+				_glfwInputJoystickButton(js, (int)buttonIdx, buttonValue ? GLFW_PRESS : GLFW_RELEASE);
+		}
+		js->haiku.lastButtonValues = buttonValues;
+
+		int hatsCount = joystick->CountHats();
+		uint8 hatsValues[hatsCount];
+		joystick->GetHatValues(hatsValues, 0);
+
+		for (int i = 0;  i < hatsCount;  i++)
+		{
+			const int states[9] = {
+				GLFW_HAT_CENTERED,
+				GLFW_HAT_UP,
+				GLFW_HAT_RIGHT_UP,
+				GLFW_HAT_RIGHT,
+				GLFW_HAT_RIGHT_DOWN,
+				GLFW_HAT_DOWN,
+				GLFW_HAT_LEFT_DOWN,
+				GLFW_HAT_LEFT,
+				GLFW_HAT_LEFT_UP
+			};
+
+            long state = hatsValues[i];
+			if (state < 0 || state > 8)
+				state = 0;
+
+			_glfwInputJoystickHat(js, (int)i, states[state]);
+        }
+	}
+	return js->present;
+}
+
+void _glfwPlatformUpdateGamepadGUID(char* guid)
+{
+}
+
diff --git a/src/haiku_joystick.h b/src/haiku_joystick.h
new file mode 100644
index 0000000..679bc80
--- /dev/null
+++ b/src/haiku_joystick.h
@@ -0,0 +1,40 @@
+//========================================================================
+// Copyright (c) 2021-2022 Gerasim Troeglazov <3dEyes@gmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//========================================================================
+
+#define _GLFW_PLATFORM_JOYSTICK_STATE         _GLFWjoystickHaiku haiku
+#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyLibraryJoystick; }
+
+#define _GLFW_PLATFORM_MAPPING_NAME "Haiku"
+#define GLFW_BUILD_HAIKU_MAPPINGS
+
+// Haiku-specific per-joystick data
+//
+typedef struct _GLFWjoystickHaiku
+{
+	void*		device;
+	uint32_t	lastButtonValues;
+} _GLFWjoystickHaiku;
+
+
+GLFWbool _glfwInitJoysticksHaiku(void);
+void _glfwTerminateJoysticksHaiku(void);
diff --git a/src/haiku_monitor.cpp b/src/haiku_monitor.cpp
new file mode 100644
index 0000000..a33a2a7
--- /dev/null
+++ b/src/haiku_monitor.cpp
@@ -0,0 +1,143 @@
+//========================================================================
+// Copyright (c) 2021 Gerasim Troeglazov <3dEyes@gmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//========================================================================
+
+extern "C" {
+#include "internal.h"
+}
+
+#include <Rect.h>
+#include <Screen.h>
+#include <stdio.h>
+
+static void addVideoMode(_GLFWmonitor *monitor, int32_t width, int32_t height, float refresh)
+{
+    GLFWvidmode mode;
+
+    mode.width = width;
+    mode.height = height;
+    mode.redBits = 8;
+    mode.greenBits = 8;
+    mode.blueBits = 8;
+    mode.refreshRate = (int)refresh;
+
+	if (monitor->modeCount > 0 && monitor->modes)
+	{
+		for (int i = 0; i < monitor->modeCount; i++)
+		{
+			if (monitor->modes[i].width == width && 
+				monitor->modes[i].height == height &&
+				monitor->modes[i].refreshRate == refresh)
+					return;
+		}
+	}
+
+    monitor->modeCount++;
+    if (monitor->modes)
+    	monitor->modes = (GLFWvidmode*)realloc(monitor->modes, monitor->modeCount * sizeof(GLFWvidmode));
+    else
+    	monitor->modes = (GLFWvidmode*)malloc(monitor->modeCount * sizeof(GLFWvidmode));
+
+    monitor->modes[monitor->modeCount - 1] = mode;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////                       GLFW platform API                      //////
+//////////////////////////////////////////////////////////////////////////
+
+void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
+{
+}
+
+void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos)
+{
+	if (xpos)
+	   *xpos = 0;
+	if (ypos)
+	   *ypos = 0;
+}
+
+void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
+                                         float* xscale, float* yscale)
+{
+    if (xscale)
+        *xscale = 1.f;
+    if (yscale)
+        *yscale = 1.f;
+}
+
+void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,
+                                     int* xpos, int* ypos,
+                                     int* width, int* height)
+{
+	if (xpos)
+        *xpos = 0;
+    if (ypos)
+        *ypos = 0;
+    if (width)
+        *width = monitor->modes[0].width;
+    if (height)
+        *height = monitor->modes[0].height;
+}
+
+GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found)
+{
+	BScreen scr(B_MAIN_SCREEN_ID);
+
+	if (!monitor->modes) {
+		display_mode hmode;
+	    scr.GetMode(&hmode);
+	    float refresh = float(hmode.timing.pixel_clock * 1000) / float(hmode.timing.h_total * hmode.timing.v_total);
+#if 0
+		addVideoMode(monitor, 640, 480, refresh);
+		addVideoMode(monitor, 800, 600, refresh);
+		addVideoMode(monitor, 1024, 768, refresh);
+#endif
+		addVideoMode(monitor, hmode.virtual_width, hmode.virtual_height, refresh);
+	}
+
+	*found = monitor->modeCount;
+	return monitor->modes;
+}
+
+void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* _mode)
+{
+	BScreen scr(B_MAIN_SCREEN_ID);
+	GLFWvidmode mode;
+	mode.width = scr.Frame().Width() + 1;
+    mode.height = scr.Frame().Height() + 1;
+    mode.redBits = 8;
+    mode.greenBits = 8;
+    mode.blueBits = 8;
+    mode.refreshRate = 60;
+
+ 	*_mode = mode;
+}
+
+GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
+{
+    return GLFW_FALSE;
+}
+
+void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
+{
+}
diff --git a/src/haiku_platform.h b/src/haiku_platform.h
new file mode 100644
index 0000000..bfa0553
--- /dev/null
+++ b/src/haiku_platform.h
@@ -0,0 +1,71 @@
+//========================================================================
+// Copyright (c) 2021 Gerasim Troeglazov <3dEyes@gmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//========================================================================
+
+#include <dlfcn.h>
+
+#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowHaiku haiku
+
+#define _GLFW_PLATFORM_CONTEXT_STATE         struct { int dummyContext; }
+#define _GLFW_PLATFORM_MONITOR_STATE         struct { int dummyMonitor; }
+#define _GLFW_PLATFORM_CURSOR_STATE          struct { int dummyCursor; }
+#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; }
+#define _GLFW_EGL_CONTEXT_STATE              struct { int dummyEGLContext; }
+#define _GLFW_EGL_LIBRARY_CONTEXT_STATE      struct { int dummyEGLLibraryContext; }
+
+#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE  _GLFWlibraryHaiku haiku
+
+#include "haiku_context.h"
+#include "posix_time.h"
+#include "posix_thread.h"
+#include "haiku_joystick.h"
+
+#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
+#define _glfw_dlclose(handle) dlclose(handle)
+#define _glfw_dlsym(handle, name) dlsym(handle, name)
+
+// Haiku-specific per-window data
+//
+typedef struct _GLFWwindowHaiku
+{
+    int 		width;
+    int 		height;
+    void*		object;
+    uint32_t	lastMouseButtons;
+    int			mouseGrab;
+} _GLFWwindowHaiku;
+
+
+// Haiku-specific global data
+//
+typedef struct _GLFWlibraryHaiku
+{
+	_GLFWwindow*	disabledCursorWindow;
+
+	double			restoreCursorPosX, restoreCursorPosY;
+
+	void*			messageQueue;
+
+	char			keynames[GLFW_KEY_LAST + 1][17];
+	short int		keycodes[256];
+	short int		scancodes[GLFW_KEY_LAST + 1];
+} _GLFWlibraryHaiku;
diff --git a/src/haiku_platform_view.cpp b/src/haiku_platform_view.cpp
new file mode 100644
index 0000000..d490e75
--- /dev/null
+++ b/src/haiku_platform_view.cpp
@@ -0,0 +1,135 @@
+//========================================================================
+// Copyright (c) 2021-2022 Gerasim Troeglazov <3dEyes@gmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//========================================================================
+
+#include <stdio.h>
+
+#include "haiku_platform_view.h"
+
+HaikuPlatformView::HaikuPlatformView(BRect rect, int width, int height, _GLFWwindow* win) :
+	BView(rect, "HaikuPlatformView", B_FOLLOW_ALL, B_WILL_DRAW),
+	window(win),
+	bufferBitmap(NULL),
+	mouseMode(MOUSE_MODE_NORMAL)
+{
+}
+
+HaikuPlatformView::~HaikuPlatformView()
+{
+	if (bufferBitmap)
+		delete bufferBitmap;
+}
+
+void 
+HaikuPlatformView::MessageReceived(BMessage *message)
+{
+	BMessageQueue *queue = (BMessageQueue*)(_glfw.haiku.messageQueue);
+
+	switch (message->what)
+	{
+		case B_MOUSE_DOWN:
+		{
+			SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
+			BMessage *msg = new BMessage(*message);
+			msg->AddPointer("window", (void*)window);
+			queue->AddMessage(msg);
+			return;
+		}
+		case B_MOUSE_UP:
+		{
+			BPoint where;
+			uint32 buttons;
+			GetMouse(&where, &buttons);
+			BMessage *msg = new BMessage(*message);
+			msg->AddPointer("window", (void*)window);
+			msg->AddInt32("buttons", buttons);
+			queue->AddMessage(msg);
+			return;
+		}
+		case B_MOUSE_MOVED:
+		{
+			BMessage *msg = new BMessage(*message);
+			msg->AddPointer("window", (void*)window);
+			queue->AddMessage(msg);
+			return;
+		}
+		default:
+			break;
+	}
+	BView::MessageReceived(message);
+}
+
+void
+HaikuPlatformView::AttachedToWindow()
+{
+	ResizeBitmap(Bounds().IntegerWidth() + 1, Bounds().IntegerHeight() + 1);
+	BView::AttachedToWindow();
+}
+
+void
+HaikuPlatformView::Draw(BRect rect)
+{
+	SetDrawingMode(B_OP_COPY);
+	DrawBitmap(bufferBitmap, rect, rect);
+}
+
+void
+HaikuPlatformView::SetMouseMode(int32 mode)
+{
+	mouseMode = mode;
+
+	if (mouseMode == MOUSE_MODE_RELATIVE)
+		SetEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS, B_NO_POINTER_HISTORY);
+	else
+		SetEventMask(0, 0);
+}
+
+void 
+HaikuPlatformView::Repaint()
+{
+	if (LockLooperWithTimeout(10000) == B_OK) {
+		SetDrawingMode(B_OP_COPY);
+		DrawBitmap(bufferBitmap);
+		UnlockLooper();
+	}
+}
+
+void
+HaikuPlatformView::ResizeBitmap(int width, int height)
+{	
+	if (LockLooperWithTimeout(10000) == B_OK)
+	{
+		if (bufferBitmap)
+		{
+			if (width == Width() && height == Height())
+			{
+				UnlockLooper();
+				return;
+			}
+			delete bufferBitmap;
+		}
+
+		bufferBitmap = new BBitmap(BRect(0, 0, width - 1, height - 1), B_RGB32, true);
+
+	 	UnlockLooper();
+	}
+}
diff --git a/src/haiku_platform_view.h b/src/haiku_platform_view.h
new file mode 100644
index 0000000..fe4ff46
--- /dev/null
+++ b/src/haiku_platform_view.h
@@ -0,0 +1,74 @@
+//========================================================================
+// Copyright (c) 2021-2022 Gerasim Troeglazov <3dEyes@gmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//========================================================================
+
+#ifndef _HAIKU_PLATFORM_VIEW_H
+#define _HAIKU_PLATFORM_VIEW_H
+
+extern "C" {
+#include "internal.h"
+}
+
+#include <SupportDefs.h>
+#include <Window.h>
+#include <Bitmap.h>
+#include <View.h>
+#include <Cursor.h>
+#include <Rect.h>
+#include <kernel/OS.h>
+#include <MessageQueue.h>
+#include <game/WindowScreen.h>
+
+#define MOUSE_MODE_NORMAL	0
+#define MOUSE_MODE_RELATIVE	1
+
+class HaikuPlatformView : public BView 
+{
+ public:
+		HaikuPlatformView(BRect rect, int width, int height, _GLFWwindow* win);
+		~HaikuPlatformView();
+
+		virtual void		MessageReceived(BMessage *message);
+		virtual void		Draw(BRect r);
+		virtual void		AttachedToWindow();
+
+		void 				Repaint();
+
+		void				SetMouseMode(int32 mode);
+
+		char*				GetBuffer() { return (char*)bufferBitmap->Bits(); }
+		uint32				GetBufferSize() { return bufferBitmap->BitsLength(); }
+
+		void				ResizeBitmap(int width, int height);
+
+		int					Width() { return bufferBitmap->Bounds().IntegerWidth() + 1; }
+		int					Height() { return bufferBitmap->Bounds().IntegerHeight() + 1; }
+
+ private:
+		_GLFWwindow*		window;
+
+		BBitmap*			bufferBitmap;
+
+		int32				mouseMode;
+};
+
+#endif //_HAIKU_PLATFORM_VIEW_H
diff --git a/src/haiku_platform_window.cpp b/src/haiku_platform_window.cpp
new file mode 100644
index 0000000..af75685
--- /dev/null
+++ b/src/haiku_platform_window.cpp
@@ -0,0 +1,119 @@
+//========================================================================
+// Copyright (c) 2021-2022 Gerasim Troeglazov <3dEyes@gmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//========================================================================
+
+extern "C" {
+#include "internal.h"
+}
+
+#include "haiku_platform_window.h"
+
+#include <stdlib.h>
+
+HaikuPlatformWindow::HaikuPlatformWindow(BRect frame, const char* title, window_type type,
+	uint32 flags, _GLFWwindow* glwin)
+	: BWindow(frame, title, type, flags)
+{
+	window = glwin;
+
+	cursorEmpty = new BCursor(B_CURSOR_ID_NO_CURSOR);
+	cursorStd = new BCursor(B_CURSOR_ID_SYSTEM_DEFAULT);
+
+	fView = new HaikuPlatformView(Bounds(), window->haiku.width, window->haiku.height, window);
+	AddChild(fView);
+}
+
+
+HaikuPlatformWindow::~HaikuPlatformWindow()
+{
+	delete cursorEmpty;
+	delete cursorStd;
+}
+
+void 
+HaikuPlatformWindow::MessageReceived(BMessage *message)
+{
+	switch (message->what)
+	{
+		case kShowCursor:
+			fView->SetViewCursor(cursorStd);
+			break;
+		case kHideCursor:
+			fView->SetViewCursor(cursorEmpty);
+			break;
+		case kMouseModeNormal:
+			fView->SetMouseMode(MOUSE_MODE_NORMAL);
+			break;
+		case kMouseModeRelative:
+			fView->SetMouseMode(MOUSE_MODE_RELATIVE);
+			break;
+		case B_UNMAPPED_KEY_UP:
+		case B_KEY_UP:
+		case B_UNMAPPED_KEY_DOWN:
+		case B_KEY_DOWN:
+		case B_MOUSE_WHEEL_CHANGED:
+		{
+			BMessage *msg = new BMessage(*message);
+			msg->AddPointer("window", (void*)window);
+			PLATFORM_QUEUE->AddMessage(msg);
+			break;
+		}
+		default:
+			break;
+	}
+	BWindow::MessageReceived(message);
+}
+
+void
+HaikuPlatformWindow::FrameResized(float width, float height)
+{
+	BMessage *msg = new BMessage(B_WINDOW_RESIZED);
+	msg->AddPointer("window", (void*)window);
+	msg->AddFloat("width", width + 1);
+	msg->AddFloat("height", height + 1);
+	PLATFORM_QUEUE->AddMessage(msg);
+}
+
+void
+HaikuPlatformWindow::FrameMoved(BPoint point)
+{
+	BMessage *msg = new BMessage(B_WINDOW_MOVED);
+	msg->AddPointer("window", (void*)window);
+	msg->AddPoint("where", point);
+	PLATFORM_QUEUE->AddMessage(msg);
+}
+
+void
+HaikuPlatformWindow::WindowActivated(bool active)
+{
+	BMessage *msg = new BMessage(B_WINDOW_ACTIVATED);
+	msg->AddPointer("window", (void*)window);
+	msg->AddBool("active", active);
+	PLATFORM_QUEUE->AddMessage(msg);
+}
+
+bool
+HaikuPlatformWindow::QuitRequested()
+{
+	_glfwInputWindowCloseRequest(window);
+	return false;
+}
diff --git a/src/haiku_platform_window.h b/src/haiku_platform_window.h
new file mode 100644
index 0000000..b2e5461
--- /dev/null
+++ b/src/haiku_platform_window.h
@@ -0,0 +1,68 @@
+//========================================================================
+// Copyright (c) 2021 Gerasim Troeglazov <3dEyes@gmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//========================================================================
+
+#ifndef _HAIKU_PLATFORM_WINDOW_H
+#define _HAIKU_PLATFORM_WINDOW_H
+
+#include <stdio.h>
+
+#include <OS.h>
+#include <View.h>
+#include <Window.h>
+#include <SupportDefs.h>
+#include <MessageQueue.h>
+
+const uint32 kShowCursor 		= 'mSHO';
+const uint32 kHideCursor 		= 'mHID';
+const uint32 kMouseModeNormal	= 'mNOR';
+const uint32 kMouseModeRelative	= 'mREL';
+
+#include "haiku_platform_view.h"
+
+class HaikuPlatformWindow : public BWindow {
+	public:
+						HaikuPlatformWindow(BRect frame, const char* title, window_type type, uint32 flags, _GLFWwindow* glwin);
+		virtual			~HaikuPlatformWindow();
+
+		virtual void 	MessageReceived(BMessage *message);
+		virtual	bool	QuitRequested();
+		virtual void	FrameResized(float width, float height);
+		virtual void 	FrameMoved(BPoint point);
+		virtual void	WindowActivated(bool active);
+
+		HaikuPlatformView*	View(void) { return fView; }
+
+	private:
+		_GLFWwindow* window;
+		HaikuPlatformView *fView;
+		BCursor *cursorEmpty;
+		BCursor *cursorStd;
+};
+
+#define PLATFORM_WINDOW(window) ((HaikuPlatformWindow*)(window->haiku.object))
+#define PLATFORM_VIEW(window) (PLATFORM_WINDOW(window)->View())
+#define PLATFORM_QUEUE ((BMessageQueue*)(_glfw.haiku.messageQueue))
+
+#endif //_HAIKU_PLATFORM_WINDOW_H
+
+
diff --git a/src/haiku_window.cpp b/src/haiku_window.cpp
new file mode 100644
index 0000000..966b1f7
--- /dev/null
+++ b/src/haiku_window.cpp
@@ -0,0 +1,715 @@
+//========================================================================
+// Copyright (c) 2021-2022 Gerasim Troeglazov <3dEyes@gmail.com>
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would
+//    be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+//    be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+//    distribution.
+//========================================================================
+
+extern "C" {
+#include "internal.h"
+}
+
+#include <stdio.h>
+
+#include <GL/gl.h>
+
+#include <Application.h>
+#include <Window.h>
+#include <Rect.h>
+#include <View.h>
+#include <Bitmap.h>
+#include <Screen.h>
+#include <Cursor.h>
+#include <MessageQueue.h>
+#include <InterfaceDefs.h>
+#include <game/WindowScreen.h>
+
+#include "haiku_platform_window.h"
+#include "haiku_platform_view.h"
+
+static int createNativeWindow(_GLFWwindow* window,
+                              const _GLFWwndconfig* wndconfig)
+{
+	window->haiku.width = wndconfig->width;
+	window->haiku.height = wndconfig->height;
+
+	BRect winPos = BRect(100, 100, 100 + (wndconfig->width - 1), 100 + (wndconfig->height - 1));
+
+	if (window->monitor)
+	{
+		BScreen scr(B_MAIN_SCREEN_ID);
+    	window->haiku.width = scr.Frame().Width() + 1;
+    	window->haiku.height = scr.Frame().Height() + 1;
+    	winPos = BRect(0, 0, window->haiku.width, window->haiku.height);
+	}
+
+	uint32 flags = B_NOT_RESIZABLE | B_NOT_ZOOMABLE;
+
+	if (wndconfig->resizable && !window->monitor)
+	{
+		flags &= ~B_NOT_RESIZABLE;
+		flags &= ~B_NOT_ZOOMABLE;
+	}
+
+	HaikuPlatformWindow *haiku_window = new HaikuPlatformWindow(winPos,
+		wndconfig->title, B_TITLED_WINDOW, flags, window);
+
+	window->haiku.object = (void*)(haiku_window);
+	window->haiku.mouseGrab = GLFW_FALSE;
+
+	if (window->monitor)
+		haiku_window->Show();
+
+    return GLFW_TRUE;
+}
+
+// Translates Haiku key modifiers into GLFW ones
+//
+static int translateFlags(int32 modificators)
+{
+    int mods = 0;
+
+    if (modificators & B_SHIFT_KEY)
+        mods |= GLFW_MOD_SHIFT;
+    if (modificators & B_CONTROL_KEY)
+        mods |= GLFW_MOD_CONTROL;
+    if (modificators & B_COMMAND_KEY)
+        mods |= GLFW_MOD_ALT;
+    if (modificators & B_OPTION_KEY)
+        mods |= GLFW_MOD_SUPER;
+    if (modificators & B_CAPS_LOCK)
+        mods |= GLFW_MOD_CAPS_LOCK;
+
+    return mods;
+}
+
+// Translates a Haiku keycode to a GLFW keycode
+//
+static int translateKey(unsigned int key)
+{
+    if (key >= sizeof(_glfw.haiku.keycodes) / sizeof(_glfw.haiku.keycodes[0]))
+        return GLFW_KEY_UNKNOWN;
+
+    return _glfw.haiku.keycodes[key];
+}
+
+// Decode a Unicode code point from a UTF-8 stream
+// Based on cutef8 by Jeff Bezanson (Public Domain)
+//
+static unsigned int decodeUTF8(const char** s)
+{
+    unsigned int ch = 0, count = 0;
+    static const unsigned int offsets[] =
+    {
+        0x00000000u, 0x00003080u, 0x000e2080u,
+        0x03c82080u, 0xfa082080u, 0x82082080u
+    };
+
+    do
+    {
+        ch = (ch << 6) + (unsigned char) **s;
+        (*s)++;
+        count++;
+    } while ((**s & 0xc0) == 0x80);
+
+    return ch - offsets[count - 1];
+}
+
+// Updates the cursor image according to its cursor mode
+//
+static void updateCursorImage(_GLFWwindow* window)
+{
+    if (window->cursorMode == GLFW_CURSOR_NORMAL ||
+	    	!PLATFORM_WINDOW(window)->IsActive()) {
+    	PLATFORM_WINDOW(window)->PostMessage(kShowCursor);
+    } else {
+    	PLATFORM_WINDOW(window)->PostMessage(kHideCursor);
+    }
+}
+
+// Apply disabled cursor mode to a focused window
+//
+static void disableCursor(_GLFWwindow* window)
+{
+    _glfw.haiku.disabledCursorWindow = window;
+    _glfwPlatformGetCursorPos(window, &_glfw.haiku.restoreCursorPosX, &_glfw.haiku.restoreCursorPosY);
+    updateCursorImage(window);
+    _glfwCenterCursorInContentArea(window);
+   	PLATFORM_WINDOW(window)->PostMessage(kMouseModeRelative);
+}
+
+// Exit disabled cursor mode for the specified window
+//
+static void enableCursor(_GLFWwindow* window)
+{
+    _glfw.haiku.disabledCursorWindow = NULL;
+    _glfwPlatformSetCursorPos(window, _glfw.haiku.restoreCursorPosX, _glfw.haiku.restoreCursorPosY);
+    window->haiku.mouseGrab = GLFW_FALSE;
+    updateCursorImage(window);
+	PLATFORM_WINDOW(window)->PostMessage(kMouseModeNormal);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//////                       GLFW platform API                      //////
+//////////////////////////////////////////////////////////////////////////
+
+int _glfwPlatformCreateWindow(_GLFWwindow* window,
+                              const _GLFWwndconfig* wndconfig,
+                              const _GLFWctxconfig* ctxconfig,
+                              const _GLFWfbconfig* fbconfig)
+{
+    if (!createNativeWindow(window, wndconfig))
+        return GLFW_FALSE;
+
+    if (ctxconfig->client != GLFW_NO_API)
+    {
+        if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API || ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
+        {
+            if (!_glfwInitOSMesa())
+                return GLFW_FALSE;
+            if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
+                return GLFW_FALSE;
+        }
+        else
+        {
+            _glfwInputError(GLFW_API_UNAVAILABLE, "Haiku: EGL not available");
+            return GLFW_FALSE;
+        }
+    }
+
+    return GLFW_TRUE;
+}
+
+void _glfwPlatformDestroyWindow(_GLFWwindow* window)
+{
+	PLATFORM_WINDOW(window)->Lock();
+	PLATFORM_WINDOW(window)->Quit();
+
+    if (window->context.destroy)
+        window->context.destroy(window);
+}
+
+void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
+{
+	PLATFORM_WINDOW(window)->SetTitle(title);
+}
+
+void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count,
+                                const GLFWimage* images)
+{
+}
+
+void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
+                                   _GLFWmonitor* monitor,
+                                   int xpos, int ypos,
+                                   int width, int height,
+                                   int refreshRate)
+{
+	_glfwInputWindowMonitor(window, monitor);
+}
+
+void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
+{
+	BRect rect = PLATFORM_WINDOW(window)->Frame();
+	if (xpos)
+		*xpos = rect.left;
+	if (ypos)
+		*ypos = rect.top;
+}
+
+void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
+{
+	PLATFORM_WINDOW(window)->MoveTo(xpos, ypos);
+}
+
+void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
+{
+    if (width)
+        *width = window->haiku.width;
+    if (height)
+        *height = window->haiku.height;
+}
+
+void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
+{
+    window->haiku.width = width;
+    window->haiku.height = height;
+    PLATFORM_WINDOW(window)->ResizeTo(width, height);
+}
+
+void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
+                                      int minwidth, int minheight,
+                                      int maxwidth, int maxheight)
+{
+}
+
+void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n, int d)
+{
+}
+
+void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
+{
+    if (width)
+        *width = PLATFORM_VIEW(window)->Width();
+    if (height)
+        *height = PLATFORM_VIEW(window)->Height();	
+}
+
+void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
+                                     int* left, int* top,
+                                     int* right, int* bottom)
+{
+	BRect rect = PLATFORM_WINDOW(window)->Frame();
+	if (left)
+		*left = rect.left;
+	if (top)
+		*top = rect.top;
+	if (right)
+		*right = rect.right;
+	if (bottom)
+		*bottom = rect.bottom;
+}
+
+void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
+                                        float* xscale, float* yscale)
+{
+    if (xscale)
+        *xscale = 1.f;
+    if (yscale)
+        *yscale = 1.f;
+}
+
+void _glfwPlatformIconifyWindow(_GLFWwindow* window)
+{
+	if (!PLATFORM_WINDOW(window)->IsMinimized())
+		PLATFORM_WINDOW(window)->Minimize(true);
+}
+
+void _glfwPlatformRestoreWindow(_GLFWwindow* window)
+{
+	if (PLATFORM_WINDOW(window)->IsMinimized())
+		PLATFORM_WINDOW(window)->Minimize(false);
+}
+
+void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
+{
+}
+
+int _glfwPlatformWindowMaximized(_GLFWwindow* window)
+{
+    return GLFW_FALSE;
+}
+
+int _glfwPlatformWindowHovered(_GLFWwindow* window)
+{
+    return GLFW_FALSE;
+}
+
+int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
+{
+    return GLFW_FALSE;
+}
+
+void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
+{
+	uint32 flags = PLATFORM_WINDOW(window)->Flags();
+	if (enabled)
+	{
+		flags &= ~B_NOT_RESIZABLE;
+		flags &= ~B_NOT_ZOOMABLE;
+	}
+	else
+	{
+		flags |= B_NOT_RESIZABLE | B_NOT_ZOOMABLE;
+	}
+	PLATFORM_WINDOW(window)->SetFlags(flags);
+}
+
+void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
+{
+}
+
+void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
+{
+}
+
+float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
+{
+    return 1.f;
+}
+
+void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
+{
+}
+
+void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
+{
+}
+
+GLFWbool _glfwPlatformRawMouseMotionSupported(void)
+{
+    return GLFW_FALSE;
+}
+
+void _glfwPlatformShowWindow(_GLFWwindow* window)
+{
+	if (PLATFORM_WINDOW(window)->IsHidden())
+		PLATFORM_WINDOW(window)->Show();
+}
+
+
+void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
+{
+}
+
+void _glfwPlatformUnhideWindow(_GLFWwindow* window)
+{
+	if (PLATFORM_WINDOW(window)->IsHidden())
+		PLATFORM_WINDOW(window)->Show();
+}
+
+void _glfwPlatformHideWindow(_GLFWwindow* window)
+{
+	if (!PLATFORM_WINDOW(window)->IsHidden())
+		PLATFORM_WINDOW(window)->Hide();
+}
+
+void _glfwPlatformFocusWindow(_GLFWwindow* window)
+{
+	if (!PLATFORM_WINDOW(window)->IsActive())
+		PLATFORM_WINDOW(window)->Activate(true);
+}
+
+int _glfwPlatformWindowFocused(_GLFWwindow* window)
+{
+    return PLATFORM_WINDOW(window)->IsActive() ? GLFW_TRUE : GLFW_FALSE;
+}
+
+int _glfwPlatformWindowIconified(_GLFWwindow* window)
+{
+    return GLFW_FALSE;
+}
+
+int _glfwPlatformWindowVisible(_GLFWwindow* window)
+{
+    return PLATFORM_WINDOW(window)->IsHidden() ? GLFW_FALSE : GLFW_TRUE;
+}
+
+void _glfwPlatformPollEvents(void)
+{
+	PLATFORM_QUEUE->Lock();
+	while(!PLATFORM_QUEUE->IsEmpty())
+	{
+		BMessage *message = PLATFORM_QUEUE->NextMessage();
+		_GLFWwindow* window = (_GLFWwindow*)(message->GetPointer("window"));
+		switch(message->what)
+		{
+			case B_MOUSE_DOWN:
+			{
+				int32 mod = modifiers();
+				uint32 buttons = message->FindInt32("buttons");
+
+				if (buttons & B_PRIMARY_MOUSE_BUTTON)
+				{
+					if (window->cursorMode == GLFW_CURSOR_DISABLED && window->haiku.mouseGrab == GLFW_FALSE)
+					{
+						window->haiku.mouseGrab = GLFW_TRUE;
+						disableCursor(window);
+					}
+					_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, translateFlags(mod));
+				}
+			
+				if (buttons & B_SECONDARY_MOUSE_BUTTON)
+					_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, translateFlags(mod));
+			
+				if (buttons & B_TERTIARY_MOUSE_BUTTON)
+					_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, translateFlags(mod));
+
+				window->haiku.lastMouseButtons = buttons;
+
+				break;
+			}
+			case B_MOUSE_UP:
+			{
+				int32 mod = modifiers();
+				uint32 buttons = message->FindInt32("buttons");
+
+				if ((window->haiku.lastMouseButtons & B_PRIMARY_MOUSE_BUTTON) && !(buttons & B_PRIMARY_MOUSE_BUTTON))
+					_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, translateFlags(mod));
+			
+				if ((window->haiku.lastMouseButtons & B_SECONDARY_MOUSE_BUTTON) && !(buttons & B_SECONDARY_MOUSE_BUTTON))
+					_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_RELEASE, translateFlags(mod));
+			
+				if ((window->haiku.lastMouseButtons & B_TERTIARY_MOUSE_BUTTON) && !(buttons & B_TERTIARY_MOUSE_BUTTON))
+					_glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_RELEASE, translateFlags(mod));
+
+				break;
+			}
+			case B_MOUSE_MOVED:
+			{
+				uint32 transit;
+				message->FindInt32("be:transit", (int32*)&transit);
+				BPoint where;
+				message->FindPoint("be:view_where", &where);
+
+				if (transit == B_ENTERED_VIEW)
+				{
+					_glfwInputCursorEnter(window, GLFW_TRUE);
+				}
+				else if (transit == B_EXITED_VIEW)
+				{
+					_glfwInputCursorEnter(window, GLFW_FALSE);
+				}
+				else if (transit == B_INSIDE_VIEW || transit == B_OUTSIDE_VIEW)
+				{
+					if (window->cursorMode == GLFW_CURSOR_DISABLED)
+					{
+						if (PLATFORM_WINDOW(window)->IsActive() && window->haiku.mouseGrab == GLFW_TRUE)
+						{
+							int cx = PLATFORM_WINDOW(window)->Bounds().Width() / 2;
+							int cy = PLATFORM_WINDOW(window)->Bounds().Height() / 2;
+							float dx = where.x - cx;
+							float dy = where.y - cy;
+							BPoint center((PLATFORM_WINDOW(window)->Frame().right + PLATFORM_WINDOW(window)->Frame().left) / 2,
+								(PLATFORM_WINDOW(window)->Frame().bottom + PLATFORM_WINDOW(window)->Frame().top) / 2);
+							set_mouse_position(center.x, center.y);
+							_glfwInputCursorPos(window, window->virtualCursorPosX + dx, window->virtualCursorPosY + dy);
+						}
+					}
+					else
+					{
+						_glfwInputCursorPos(window, where.x, where.y);
+					}
+				}			
+				break;
+			}
+			case B_MOUSE_WHEEL_CHANGED:
+			{
+				float shift_x=0;
+				float shift_y=0;
+				if (message->FindFloat("be:wheel_delta_x", &shift_x) != B_OK)
+					shift_x = 0;
+				if (message->FindFloat("be:wheel_delta_y", &shift_y) != B_OK)
+					shift_y = 0;
+				if (window)
+					_glfwInputScroll(window, shift_x, -shift_y);
+				break;
+			}
+			case B_WINDOW_ACTIVATED:
+			{
+				bool active = message->FindBool("active");
+				if (active)
+				{
+					_glfwInputWindowFocus(window, GLFW_TRUE);
+					if (window->cursorMode == GLFW_CURSOR_DISABLED && window->haiku.mouseGrab == GLFW_TRUE)
+						disableCursor(window);
+				}
+				else
+				{
+					if (window->cursorMode == GLFW_CURSOR_DISABLED)
+						enableCursor(window);
+
+					if (window->monitor && window->autoIconify)
+						_glfwPlatformIconifyWindow(window);
+
+					_glfwInputWindowFocus(window, GLFW_FALSE);
+				}
+				break;
+			}
+			case B_WINDOW_MOVED:
+			{
+				BPoint point = message->FindPoint("where");
+				_glfwInputWindowPos(window, point.x, point.y);
+				break;
+			}
+			case B_WINDOW_RESIZED:
+			{
+				int width = int(message->FindFloat("width"));
+				int height = int(message->FindFloat("height"));
+
+				if (width != window->haiku.width || height != window->haiku.height)
+				{
+					window->haiku.width = width;
+					window->haiku.height = height;
+
+					_glfwInputFramebufferSize(window, width, height);
+					_glfwInputWindowSize(window, width, height);
+
+					PLATFORM_VIEW(window)->ResizeBitmap(width, height);
+
+					window->context.makeCurrent(window);
+				}
+				break;
+			}
+			case B_UNMAPPED_KEY_UP:
+			case B_KEY_UP:
+			case B_UNMAPPED_KEY_DOWN:
+			case B_KEY_DOWN:
+			{
+				const int action = (message->what == B_UNMAPPED_KEY_UP || message->what == B_KEY_UP) ? GLFW_RELEASE : GLFW_PRESS;
+				const int mods = translateFlags(modifiers());
+				
+				int32 scancode;
+				message->FindInt32("key", &scancode);	
+				int32 key = translateKey(scancode);
+
+				_glfwInputKey(window, key, key, action, mods);
+
+				if (action == GLFW_PRESS)
+				{
+					const char* bytes;
+					if(message->FindString("bytes", &bytes) == B_OK)
+					{
+						const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
+						_glfwInputChar(window, decodeUTF8(&bytes), mods, plain);
+					}
+				}
+				break;
+			}
+		}
+	}
+	PLATFORM_QUEUE->Unlock();
+}
+
+void _glfwPlatformWaitEvents(void)
+{
+	while(PLATFORM_QUEUE->IsEmpty())
+		snooze(10);
+
+	_glfwPlatformPollEvents();
+}
+
+void _glfwPlatformWaitEventsTimeout(double timeout)
+{
+	bigtime_t enterTime = system_time();
+
+	while(PLATFORM_QUEUE->IsEmpty())
+	{
+		bigtime_t elapsed = system_time() - enterTime;
+		if (elapsed > timeout * 1000000)
+			break;
+		snooze(10);
+	}
+
+	_glfwPlatformPollEvents();
+}
+
+void _glfwPlatformPostEmptyEvent(void)
+{
+	BMessage *msg = new BMessage(B_PULSE);
+	PLATFORM_QUEUE->AddMessage(msg);
+}
+
+void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
+{
+	uint32 buttons;
+	BPoint screenWhere;
+	get_mouse(&screenWhere, &buttons);
+	BPoint mousePos = screenWhere - PLATFORM_WINDOW(window)->Frame().LeftTop();
+
+	if (xpos)
+		*xpos = mousePos.x;
+	if (ypos)
+		*ypos = mousePos.y;
+}
+
+void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
+{
+	set_mouse_position(PLATFORM_WINDOW(window)->Frame().left + x, PLATFORM_WINDOW(window)->Frame().top + y);
+}
+
+void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
+{
+	if (mode == GLFW_CURSOR_DISABLED) {
+		if (_glfwPlatformWindowFocused(window)) {
+			disableCursor(window);
+			window->haiku.mouseGrab = GLFW_TRUE;
+		}
+	} else if (_glfw.haiku.disabledCursorWindow == window)
+		enableCursor(window);
+
+	updateCursorImage(window);
+}
+
+int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
+                              const GLFWimage* image,
+                              int xhot, int yhot)
+{
+    return GLFW_TRUE;
+}
+
+int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
+{
+    return GLFW_TRUE;
+}
+
+void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
+{
+}
+
+void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
+{
+}
+
+void _glfwPlatformSetClipboardString(const char* string)
+{
+}
+
+const char* _glfwPlatformGetClipboardString(void)
+{
+    return NULL;
+}
+
+const char* _glfwPlatformGetScancodeName(int scancode)
+{
+    return "";
+}
+
+int _glfwPlatformGetKeyScancode(int key)
+{
+	return _glfw.haiku.scancodes[key];
+}
+
+void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
+{
+}
+
+void _glfwPlatformSwapBuffers(_GLFWwindow* window)
+{
+   	HaikuPlatformView *view = PLATFORM_VIEW(window);
+   	glFinish();
+   	view->Repaint();
+}
+
+int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
+                                                      VkPhysicalDevice device,
+                                                      uint32_t queuefamily)
+{
+    return GLFW_FALSE;
+}
+
+VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
+                                          _GLFWwindow* window,
+                                          const VkAllocationCallbacks* allocator,
+                                          VkSurfaceKHR* surface)
+{
+    return VK_ERROR_INITIALIZATION_FAILED;
+}
+
ddiff --git a/src/internal.h b/src/internal.h
index ad619b4..6a6c704 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -188,6 +188,8 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
  #include "x11_platform.h"
 #elif defined(_GLFW_WAYLAND)
  #include "wl_platform.h"
+#elif defined(_GLFW_HAIKU)
+ #include "haiku_platform.h"
 #elif defined(_GLFW_OSMESA)
  #include "null_platform.h"
 #else
@@ -679,6 +681,10 @@ void _glfwPlatformWaitEvents(void);
 void _glfwPlatformWaitEventsTimeout(double timeout);
 void _glfwPlatformPostEmptyEvent(void);
 
+#ifdef __HAIKU__
+void _glfwPlatformSwapBuffers(_GLFWwindow* window);
+#endif
+
 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions);
 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
                                                       VkPhysicalDevice device,
diff --git a/src/mappings.h b/src/mappings.h
index 11853a0..b7e6356 100644
--- a/src/mappings.h
+++ b/src/mappings.h
@@ -997,5 +997,9 @@ const char* _glfwDefaultMappings[] =
 "03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
 "03000000120c0000101e000011010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
 #endif // GLFW_BUILD_LINUX_MAPPINGS
+
+#if defined(GLFW_BUILD_HAIKU_MAPPINGS)
+"0500000047656e657269632055534200,Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Haiku,",
+#endif // GLFW_BUILD_HAIKU_MAPPINGS
 };
 
diff --git a/src/mappings.h.in b/src/mappings.h.in
index 26b544b..ca2c227 100644
--- a/src/mappings.h.in
+++ b/src/mappings.h.in
@@ -78,5 +78,9 @@ const char* _glfwDefaultMappings[] =
 #if defined(GLFW_BUILD_LINUX_MAPPINGS)
 @GLFW_LINUX_MAPPINGS@
 #endif // GLFW_BUILD_LINUX_MAPPINGS
+
+#if defined(GLFW_BUILD_HAIKU_MAPPINGS)
+@GLFW_HAIKU_MAPPINGS@
+#endif // GLFW_BUILD_HAIKU_MAPPINGS
 };
 
-- 
2.30.2


From e820d0945c82eca4de05b30d3edf679a42cf7388 Mon Sep 17 00:00:00 2001
From: X512 <danger_mail@list.ru>
Date: Sat, 5 Feb 2022 13:57:28 +0900
Subject: haiku: Vulkan window output


diff --git a/src/haiku_platform_view.cpp b/src/haiku_platform_view.cpp
index d490e75..6670abb 100644
--- a/src/haiku_platform_view.cpp
+++ b/src/haiku_platform_view.cpp
@@ -24,19 +24,47 @@
 #include <stdio.h>
 
 #include "haiku_platform_view.h"
+#include <Region.h>
+
+HaikuPlatformView::ViewBitmapHook::ViewBitmapHook(HaikuPlatformView *view):
+	fView(view)
+{
+}
+
+void HaikuPlatformView::ViewBitmapHook::GetSize(uint32_t &width, uint32_t &height)
+{
+	fView->LockLooper();
+	width = (uint32_t)fView->Frame().Width() + 1;
+	height = (uint32_t)fView->Frame().Height() + 1;
+	fView->UnlockLooper();
+}
+
+BBitmap *HaikuPlatformView::ViewBitmapHook::SetBitmap(BBitmap *bmp)
+{
+	fView->LockLooper();
+
+	BBitmap* oldBmp = fView->displayBitmap.Detach();
+	fView->displayBitmap.SetTo(bmp);
+	fView->Invalidate();
+
+	fView->UnlockLooper();
+	return oldBmp;
+}
+
 
 HaikuPlatformView::HaikuPlatformView(BRect rect, int width, int height, _GLFWwindow* win) :
 	BView(rect, "HaikuPlatformView", B_FOLLOW_ALL, B_WILL_DRAW),
 	window(win),
 	bufferBitmap(NULL),
+	bitmapHook(this),
 	mouseMode(MOUSE_MODE_NORMAL)
 {
+	SetViewColor(B_TRANSPARENT_COLOR);
+	SetLowColor(0, 0, 0);
 }
 
 HaikuPlatformView::~HaikuPlatformView()
 {
-	if (bufferBitmap)
-		delete bufferBitmap;
 }
 
 void 
@@ -89,7 +117,12 @@ void
 HaikuPlatformView::Draw(BRect rect)
 {
 	SetDrawingMode(B_OP_COPY);
-	DrawBitmap(bufferBitmap, rect, rect);
+	BRegion region(rect);
+	if (displayBitmap.IsSet()) {
+		DrawBitmap(displayBitmap.Get(), B_ORIGIN);
+		region.Exclude(displayBitmap->Bounds());
+	}
+	FillRegion(&region, B_SOLID_LOW);
 }
 
 void
@@ -104,31 +137,34 @@ HaikuPlatformView::SetMouseMode(int32 mode)
 }
 
 void 
-HaikuPlatformView::Repaint()
+HaikuPlatformView::Present()
 {
 	if (LockLooperWithTimeout(10000) == B_OK) {
-		SetDrawingMode(B_OP_COPY);
-		DrawBitmap(bufferBitmap);
-		UnlockLooper();
+		if (bufferBitmap.IsSet()) {
+			delete bitmapHook.SetBitmap(bufferBitmap.Detach());
+		} else {
+			bitmapHook.SetBitmap(renderBitmap);
+		}
+	 	UnlockLooper();
 	}
 }
 
 void
 HaikuPlatformView::ResizeBitmap(int width, int height)
-{	
+{
 	if (LockLooperWithTimeout(10000) == B_OK)
 	{
-		if (bufferBitmap)
+		if (renderBitmap)
 		{
 			if (width == Width() && height == Height())
 			{
 				UnlockLooper();
 				return;
 			}
-			delete bufferBitmap;
 		}
 
-		bufferBitmap = new BBitmap(BRect(0, 0, width - 1, height - 1), B_RGB32, true);
+		bufferBitmap.SetTo(new BBitmap(BRect(0, 0, width - 1, height - 1), B_RGB32));
+		renderBitmap = bufferBitmap.Get();
 
 	 	UnlockLooper();
 	}
diff --git a/src/haiku_platform_view.h b/src/haiku_platform_view.h
index fe4ff46..f95f5b7 100644
--- a/src/haiku_platform_view.h
+++ b/src/haiku_platform_view.h
@@ -37,12 +37,37 @@ extern "C" {
 #include <kernel/OS.h>
 #include <MessageQueue.h>
 #include <game/WindowScreen.h>
+#include <private/shared/AutoDeleter.h>
 
 #define MOUSE_MODE_NORMAL	0
 #define MOUSE_MODE_RELATIVE	1
 
+class BitmapHook {
+public:
+	virtual ~BitmapHook() {};
+	virtual void GetSize(uint32_t &width, uint32_t &height) = 0;
+	virtual BBitmap *SetBitmap(BBitmap *bmp) = 0;
+};
+
+class VKLayerSurfaceBase {
+public:
+	virtual ~VKLayerSurfaceBase() {};
+	virtual void SetBitmapHook(BitmapHook *hook) = 0;
+};
+
 class HaikuPlatformView : public BView 
 {
+ private:
+		class ViewBitmapHook: public BitmapHook {
+		private:
+			HaikuPlatformView *fView;
+		public:
+			ViewBitmapHook(HaikuPlatformView *view);
+			virtual ~ViewBitmapHook() {};
+			void GetSize(uint32_t &width, uint32_t &height) override;
+			BBitmap *SetBitmap(BBitmap *bmp) override;
+		};
+
  public:
 		HaikuPlatformView(BRect rect, int width, int height, _GLFWwindow* win);
 		~HaikuPlatformView();
@@ -51,22 +76,29 @@ class HaikuPlatformView : public BView
 		virtual void		Draw(BRect r);
 		virtual void		AttachedToWindow();
 
-		void 				Repaint();
+		void				Present();
 
 		void				SetMouseMode(int32 mode);
 
-		char*				GetBuffer() { return (char*)bufferBitmap->Bits(); }
-		uint32				GetBufferSize() { return bufferBitmap->BitsLength(); }
+		char*				GetBuffer() { return (char*)renderBitmap->Bits(); }
+		uint32				GetBufferSize() { return renderBitmap->BitsLength(); }
 
 		void				ResizeBitmap(int width, int height);
 
-		int					Width() { return bufferBitmap->Bounds().IntegerWidth() + 1; }
-		int					Height() { return bufferBitmap->Bounds().IntegerHeight() + 1; }
+		int					Width() { return renderBitmap->Bounds().IntegerWidth() + 1; }
+		int					Height() { return renderBitmap->Bounds().IntegerHeight() + 1; }
+
+		ViewBitmapHook*		GetBitmapHook() { return &bitmapHook; }
 
  private:
 		_GLFWwindow*		window;
 
-		BBitmap*			bufferBitmap;
+		ViewBitmapHook		bitmapHook;
+		ObjectDeleter<BBitmap>
+							bufferBitmap;
+		BBitmap*			renderBitmap;
+		ObjectDeleter<BBitmap>
+							displayBitmap;
 
 		int32				mouseMode;
 };
diff --git a/src/haiku_window.cpp b/src/haiku_window.cpp
index 966b1f7..6cf5e74 100644
--- a/src/haiku_window.cpp
+++ b/src/haiku_window.cpp
@@ -549,12 +549,7 @@ void _glfwPlatformPollEvents(void)
 					window->haiku.width = width;
 					window->haiku.height = height;
 
-					_glfwInputFramebufferSize(window, width, height);
 					_glfwInputWindowSize(window, width, height);
-
-					PLATFORM_VIEW(window)->ResizeBitmap(width, height);
-
-					window->context.makeCurrent(window);
 				}
 				break;
 			}
@@ -689,13 +684,24 @@ int _glfwPlatformGetKeyScancode(int key)
 
 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
 {
+    if (!_glfw.vk.KHR_surface || !_glfw.vk.EXT_headless_surface)
+        return;
+
+    extensions[0] = "VK_KHR_surface";
+    extensions[1] = "VK_EXT_headless_surface";
 }
 
 void _glfwPlatformSwapBuffers(_GLFWwindow* window)
 {
    	HaikuPlatformView *view = PLATFORM_VIEW(window);
    	glFinish();
-   	view->Repaint();
+   	view->Present();
+
+    if (window->haiku.width != PLATFORM_VIEW(window)->Width() || window->haiku.height != PLATFORM_VIEW(window)->Height()) {
+	    _glfwInputFramebufferSize(window, window->haiku.width, window->haiku.height);	
+	    PLATFORM_VIEW(window)->ResizeBitmap(window->haiku.width, window->haiku.height);
+	    window->context.makeCurrent(window);
+    }
 }
 
 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
@@ -705,11 +711,34 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
     return GLFW_FALSE;
 }
 
+#define VK_EXT_headless_surface 1
+#define VK_EXT_HEADLESS_SURFACE_SPEC_VERSION 1
+#define VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME "VK_EXT_headless_surface"
+typedef VkFlags VkHeadlessSurfaceCreateFlagsEXT;
+typedef struct VkHeadlessSurfaceCreateInfoEXT {
+    VkStructureType                    sType;
+    const void*                        pNext;
+    VkHeadlessSurfaceCreateFlagsEXT    flags;
+} VkHeadlessSurfaceCreateInfoEXT;
+typedef VkResult (APIENTRY *PFN_vkCreateHeadlessSurfaceEXT)(VkInstance instance, const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+
 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
                                           _GLFWwindow* window,
                                           const VkAllocationCallbacks* allocator,
                                           VkSurfaceKHR* surface)
 {
-    return VK_ERROR_INITIALIZATION_FAILED;
+    VkHeadlessSurfaceCreateInfoEXT surfaceCreateInfo = {};
+    surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT;
+    PFN_vkCreateHeadlessSurfaceEXT fpCreateHeadlessSurfaceEXT = (PFN_vkCreateHeadlessSurfaceEXT)vkGetInstanceProcAddr(instance, "vkCreateHeadlessSurfaceEXT");
+    if (!fpCreateHeadlessSurfaceEXT){
+        printf("Could not fetch function pointer for the headless extension!");
+        abort();
+    }
+    VkResult res = fpCreateHeadlessSurfaceEXT(instance, &surfaceCreateInfo, nullptr, surface);
+    if (res != VK_SUCCESS) return res;
+    auto surfaceBase = (VKLayerSurfaceBase*)(*surface);
+    HaikuPlatformView *view = PLATFORM_VIEW(window);
+    surfaceBase->SetBitmapHook(view->GetBitmapHook());
+    return res;
 }
 
diff --git a/src/internal.h b/src/internal.h
index 6a6c704..98815a8 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -129,6 +129,7 @@ typedef enum VkStructureType
     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_HEADLESS_SURFACE_CREATE_INFO_EXT = 1000256000,
     VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
 } VkStructureType;
 
@@ -556,6 +557,7 @@ struct _GLFWlibrary
         PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
 #endif
         GLFWbool        KHR_surface;
+        GLFWbool        EXT_headless_surface;
 #if defined(_GLFW_WIN32)
         GLFWbool        KHR_win32_surface;
 #elif defined(_GLFW_COCOA)
diff --git a/src/vulkan.c b/src/vulkan.c
index 22c54e4..e267e25 100644
--- a/src/vulkan.c
+++ b/src/vulkan.c
@@ -126,6 +126,8 @@ GLFWbool _glfwInitVulkan(int mode)
     {
         if (strcmp(ep[i].extensionName, "VK_KHR_surface") == 0)
             _glfw.vk.KHR_surface = GLFW_TRUE;
+        else if (strcmp(ep[i].extensionName, "VK_EXT_headless_surface") == 0)
+            _glfw.vk.EXT_headless_surface = GLFW_TRUE;
 #if defined(_GLFW_WIN32)
         else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0)
             _glfw.vk.KHR_win32_surface = GLFW_TRUE;
-- 
2.30.2


From f64b80c363499fce85e4d590a72cd0e4e0b00518 Mon Sep 17 00:00:00 2001
From: Gerasim Troeglazov <3dEyes@gmail.com>
Date: Mon, 7 Feb 2022 19:53:09 +1000
Subject: Update framebuffer size on window resize


diff --git a/src/haiku_window.cpp b/src/haiku_window.cpp
index 6cf5e74..f7d2752 100644
--- a/src/haiku_window.cpp
+++ b/src/haiku_window.cpp
@@ -548,7 +548,8 @@ void _glfwPlatformPollEvents(void)
 				{
 					window->haiku.width = width;
 					window->haiku.height = height;
-
+					if (window->context.makeCurrent != NULL)
+						_glfwInputFramebufferSize(window, width, height);
 					_glfwInputWindowSize(window, width, height);
 				}
 				break;
-- 
2.30.2


From 284b95d1d6ac5e0f8b47c08f76132c4c14f27ebe Mon Sep 17 00:00:00 2001
From: Gerasim Troeglazov <3dEyes@gmail.com>
Date: Sat, 12 Feb 2022 22:44:09 +1000
Subject: Missing initial NULL value for renderBitmap


diff --git a/src/haiku_platform_view.cpp b/src/haiku_platform_view.cpp
index 6670abb..2c7e127 100644
--- a/src/haiku_platform_view.cpp
+++ b/src/haiku_platform_view.cpp
@@ -56,6 +56,7 @@ HaikuPlatformView::HaikuPlatformView(BRect rect, int width, int height, _GLFWwin
 	BView(rect, "HaikuPlatformView", B_FOLLOW_ALL, B_WILL_DRAW),
 	window(win),
 	bufferBitmap(NULL),
+	renderBitmap(NULL),
 	bitmapHook(this),
 	mouseMode(MOUSE_MODE_NORMAL)
 {
-- 
2.30.2


From b3ee435c9f7132dff1bcfc10d4313a37fa9a5728 Mon Sep 17 00:00:00 2001
From: Gerasim Troeglazov <3dEyes@gmail.com>
Date: Sat, 12 Feb 2022 22:51:37 +1000
Subject: Add cursors, clipboard and files drop support


diff --git a/src/haiku_platform.h b/src/haiku_platform.h
index bfa0553..4d62ce6 100644
--- a/src/haiku_platform.h
+++ b/src/haiku_platform.h
@@ -23,17 +23,16 @@
 
 #include <dlfcn.h>
 
-#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowHaiku haiku
+#define _GLFW_PLATFORM_WINDOW_STATE          _GLFWwindowHaiku  haiku
+#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE  _GLFWlibraryHaiku haiku
+#define _GLFW_PLATFORM_CURSOR_STATE          _GLFWcursorHaiku  haiku
 
 #define _GLFW_PLATFORM_CONTEXT_STATE         struct { int dummyContext; }
 #define _GLFW_PLATFORM_MONITOR_STATE         struct { int dummyMonitor; }
-#define _GLFW_PLATFORM_CURSOR_STATE          struct { int dummyCursor; }
 #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; }
 #define _GLFW_EGL_CONTEXT_STATE              struct { int dummyEGLContext; }
 #define _GLFW_EGL_LIBRARY_CONTEXT_STATE      struct { int dummyEGLLibraryContext; }
 
-#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE  _GLFWlibraryHaiku haiku
-
 #include "haiku_context.h"
 #include "posix_time.h"
 #include "posix_thread.h"
@@ -54,6 +53,12 @@ typedef struct _GLFWwindowHaiku
     int			mouseGrab;
 } _GLFWwindowHaiku;
 
+// Haiku-specific per-cursor data
+//
+typedef struct _GLFWcursorHaiku
+{
+    void*		handle;
+} _GLFWcursorHaiku;
 
 // Haiku-specific global data
 //
diff --git a/src/haiku_platform_window.cpp b/src/haiku_platform_window.cpp
index af75685..a4f233e 100644
--- a/src/haiku_platform_window.cpp
+++ b/src/haiku_platform_window.cpp
@@ -52,20 +52,44 @@ HaikuPlatformWindow::~HaikuPlatformWindow()
 void 
 HaikuPlatformWindow::MessageReceived(BMessage *message)
 {
+	if (message->WasDropped())
+	{
+		BMessage *msg = new BMessage(*message);
+		msg->what = kDropFile;
+		msg->AddPointer("window", (void*)window);
+		msg->AddPoint("drop_point", ConvertFromScreen(message->DropPoint()));
+		PLATFORM_QUEUE->AddMessage(msg);
+		return;
+	}
 	switch (message->what)
 	{
 		case kShowCursor:
+		{
 			fView->SetViewCursor(cursorStd);
 			break;
+		}
 		case kHideCursor:
+		{
 			fView->SetViewCursor(cursorEmpty);
 			break;
+		}
+		case kSetCursor:
+		{
+			BCursor *cursor = (BCursor*)(message->GetPointer("cursor"));
+			if (cursor)
+				fView->SetViewCursor(cursor);
+			break;
+		}
 		case kMouseModeNormal:
+		{
 			fView->SetMouseMode(MOUSE_MODE_NORMAL);
 			break;
+		}
 		case kMouseModeRelative:
+		{
 			fView->SetMouseMode(MOUSE_MODE_RELATIVE);
 			break;
+		}
 		case B_UNMAPPED_KEY_UP:
 		case B_KEY_UP:
 		case B_UNMAPPED_KEY_DOWN:
diff --git a/src/haiku_platform_window.h b/src/haiku_platform_window.h
index b2e5461..d9732e8 100644
--- a/src/haiku_platform_window.h
+++ b/src/haiku_platform_window.h
@@ -29,13 +29,16 @@
 #include <OS.h>
 #include <View.h>
 #include <Window.h>
+#include <Cursor.h>
 #include <SupportDefs.h>
 #include <MessageQueue.h>
 
 const uint32 kShowCursor 		= 'mSHO';
 const uint32 kHideCursor 		= 'mHID';
+const uint32 kSetCursor 		= 'mCUR';
 const uint32 kMouseModeNormal	= 'mNOR';
 const uint32 kMouseModeRelative	= 'mREL';
+const uint32 kDropFile			= 'mDRP';
 
 #include "haiku_platform_view.h"
 
diff --git a/src/haiku_window.cpp b/src/haiku_window.cpp
index f7d2752..2e03cef 100644
--- a/src/haiku_window.cpp
+++ b/src/haiku_window.cpp
@@ -36,6 +36,8 @@ extern "C" {
 #include <Bitmap.h>
 #include <Screen.h>
 #include <Cursor.h>
+#include <Path.h>
+#include <Clipboard.h>
 #include <MessageQueue.h>
 #include <InterfaceDefs.h>
 #include <game/WindowScreen.h>
@@ -421,6 +423,37 @@ void _glfwPlatformPollEvents(void)
 		_GLFWwindow* window = (_GLFWwindow*)(message->GetPointer("window"));
 		switch(message->what)
 		{
+			case kDropFile:
+			{
+				BPoint dropPoint = message->FindPoint("drop_point");
+				_glfwInputCursorPos(window, dropPoint.x, dropPoint.y);
+
+				int32 count;
+				uint32 type;
+				message->GetInfo("refs", &type, &count);
+
+				if (count > 0)
+				{
+					char** paths = (char**)calloc(count, sizeof(char*));
+					entry_ref aRef;
+					for (int i = 0;  i < count;  i++)
+					{
+						message->FindRef("refs", i, &aRef);
+						BEntry entry(&aRef);
+						BPath path;
+						entry.GetPath(&path);
+						paths[i] = strdup(path.Path());
+					}
+
+					_glfwInputDrop(window, count, (const char**) paths);
+
+					for (int i = 0;  i < count;  i++)
+						free(paths[i]);
+					free(paths);
+				}
+
+				break;
+			}
 			case B_MOUSE_DOWN:
 			{
 				int32 mod = modifiers();
@@ -648,29 +681,124 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
                               const GLFWimage* image,
                               int xhot, int yhot)
 {
+	BBitmap *bitmap = new BBitmap(BRect(0, 0, image->width -1, image->height - 1), B_RGBA32);
+	if (bitmap->InitCheck() != B_OK)
+		return GLFW_FALSE;
+
+    unsigned char* source = (unsigned char*) image->pixels;
+    uint32* target = (uint32*)bitmap->Bits();
+
+    for (int i = 0;  i < image->width * image->height;  i++, target++, source += 4)
+    {
+        unsigned int alpha = source[3];
+
+        *target = (alpha << 24) |
+                  ((unsigned char) ((source[0] * alpha) / 255) << 16) |
+                  ((unsigned char) ((source[1] * alpha) / 255) <<  8) |
+                  ((unsigned char) ((source[2] * alpha) / 255) <<  0);
+    }
+
+	cursor->haiku.handle = new BCursor(bitmap, BPoint(xhot, yhot));
+
+    if (!cursor->haiku.handle)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR, "Haiku: Failed to create cursor");
+        return GLFW_FALSE;
+    }
+
     return GLFW_TRUE;
 }
 
 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
 {
+	BCursorID id = B_CURSOR_ID_SYSTEM_DEFAULT;
+
+	switch (shape)
+	{
+		case GLFW_IBEAM_CURSOR:
+			id = B_CURSOR_ID_I_BEAM;
+			break;
+		case GLFW_CROSSHAIR_CURSOR:
+			id = B_CURSOR_ID_CROSS_HAIR;
+			break;
+		case GLFW_HAND_CURSOR:
+			id = B_CURSOR_ID_FOLLOW_LINK;
+			break;
+		case GLFW_HRESIZE_CURSOR:
+			id = B_CURSOR_ID_RESIZE_EAST_WEST;
+			break;
+		case GLFW_VRESIZE_CURSOR:
+			id = B_CURSOR_ID_RESIZE_NORTH_SOUTH;
+			break;
+		default:
+			id = B_CURSOR_ID_SYSTEM_DEFAULT;
+			break;
+	}
+
+	cursor->haiku.handle = new BCursor(id);
+
+    if (!cursor->haiku.handle)
+    {
+        _glfwInputError(GLFW_PLATFORM_ERROR, "Haiku: Failed to create standard cursor");
+        return GLFW_FALSE;
+    }
+
     return GLFW_TRUE;
 }
 
 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
 {
+	if (cursor->haiku.handle)
+		delete static_cast<BCursor*>(cursor->haiku.handle);
 }
 
 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
 {
+	if (!cursor)
+	{
+		BMessage message(kShowCursor);
+		PLATFORM_WINDOW(window)->PostMessage(&message);
+		return;
+	}
+
+	if (window->cursorMode == GLFW_CURSOR_NORMAL)
+	{
+		if (cursor->haiku.handle)
+		{
+			BMessage message(kSetCursor);
+			message.AddPointer("cursor", cursor->haiku.handle);
+			PLATFORM_WINDOW(window)->PostMessage(&message);
+		}
+	}
 }
 
 void _glfwPlatformSetClipboardString(const char* string)
 {
+    BMessage *clip = NULL;
+    if(be_clipboard->Lock())
+    {
+        be_clipboard->Clear();
+        if((clip = be_clipboard->Data()))
+        {
+            clip->AddData("text/plain", B_MIME_TYPE, string, strlen(string));
+            be_clipboard->Commit();
+        }
+        be_clipboard->Unlock();
+    }
 }
 
 const char* _glfwPlatformGetClipboardString(void)
 {
-    return NULL;
+	BMessage *clip = NULL;
+    const char *text = NULL;
+    ssize_t length;
+    if(be_clipboard->Lock())
+    {
+        if((clip = be_clipboard->Data()))
+            clip->FindData("text/plain", B_MIME_TYPE, (const void**)&text, &length);
+        be_clipboard->Unlock();
+    }
+	return text;
 }
 
 const char* _glfwPlatformGetScancodeName(int scancode)
-- 
2.30.2

