org.lwjgl.system.jglfw.PlatformWin.java Source code

Java tutorial

Introduction

Here is the source code for org.lwjgl.system.jglfw.PlatformWin.java

Source

/*
 * Copyright LWJGL. All rights reserved.
 * License terms: http://lwjgl.org/license.php
 * The source in this file is ported from GLFW. License terms: http://www.glfw.org/license.html
 */
package org.lwjgl.system.jglfw;

import org.lwjgl.BufferUtils;
import org.lwjgl.Pointer;
import org.lwjgl.opengl.ContextCapabilities;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.system.APIBuffer;
import org.lwjgl.system.FunctionProvider;
import org.lwjgl.system.windows.*;
import org.lwjgl.system.windows.opengl.WindowsGLContext;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;

import static org.lwjgl.BufferUtils.*;
import static org.lwjgl.opengl.WGLARBCreateContext.*;
import static org.lwjgl.opengl.WGLARBCreateContextProfile.*;
import static org.lwjgl.opengl.WGLARBCreateContextRobustness.*;
import static org.lwjgl.opengl.WGLARBFramebufferSRGB.*;
import static org.lwjgl.opengl.WGLARBMultisample.*;
import static org.lwjgl.opengl.WGLARBPixelFormat.*;
import static org.lwjgl.opengl.WGLEXTSwapControl.*;
import static org.lwjgl.system.APIUtil.*;
import static org.lwjgl.system.MathUtil.*;
import static org.lwjgl.system.MemoryUtil.*;
import static org.lwjgl.system.jglfw.InputUtil.*;
import static org.lwjgl.system.jglfw.JGLFW.*;
import static org.lwjgl.system.jglfw.JGLFWUtil.*;
import static org.lwjgl.system.jglfw.JoystickWin.*;
import static org.lwjgl.system.jglfw.WindowUtil.*;
import static org.lwjgl.system.windows.Dwmapi.*;
import static org.lwjgl.system.windows.Mmsystem.*;
import static org.lwjgl.system.windows.User32.*;
import static org.lwjgl.system.windows.WGL.*;
import static org.lwjgl.system.windows.WinBase.*;
import static org.lwjgl.system.windows.WinGDI.*;
import static org.lwjgl.system.windows.WinUser.*;

class PlatformWin implements Platform<GLFWwindowWin> {

    private static final int _GLFW_RECREATION_NOT_NEEDED = 0, _GLFW_RECREATION_REQUIRED = 1,
            _GLFW_RECREATION_IMPOSSIBLE = 2;

    private static final ByteBuffer DISPLAY = memEncodeUTF16("DISPLAY");

    private static final String GLFW_WNDCLASSNAME = "GLFW30";

    private long classAtom;

    private int foregroundLockTimeout;

    private FunctionProvider user32;
    private FunctionProvider dwmapi;

    private User32.Functions user32Funcs;
    private Dwmapi.Functions dwmapiFuncs;

    private final TimerWin timer = new TimerWin();

    private final ThreadLocal<GLFWwindowWin> currentWindow = new ThreadLocal<GLFWwindowWin>();

    @Override
    public boolean init() {
        APIBuffer __buffer = apiBuffer();
        SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, __buffer.address(), 0);
        foregroundLockTimeout = __buffer.intValue(0);
        SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, NULL, SPIF_SENDCHANGE);

        if (!initLibraries())
            return false;

        if (user32Funcs.SetProcessDPIAware != NULL)
            SetProcessDPIAware(user32Funcs.SetProcessDPIAware);

        timer.init();

        return true;
    }

    @Override
    public void terminate() {
        freeLibraries();

        // Restore previous FOREGROUNDLOCKTIMEOUT system setting
        SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, mathUIntToPtr(foregroundLockTimeout),
                SPIF_SENDCHANGE);
    }

    private void freeLibraries() {
        if (dwmapi != null) {
            dwmapi.destroy();
            dwmapi = null;
            dwmapiFuncs = null;
        }

        if (user32 != null) {
            user32.destroy();
            user32 = null;
            user32Funcs = null;
        }
    }

    @Override
    public List<GLFWmonitor> getMonitors() {
        List<GLFWmonitor> monitors = new ArrayList<GLFWmonitor>(4);

        ByteBuffer adapter = DISPLAY_DEVICE.malloc();
        ByteBuffer device = DISPLAY_DEVICE.malloc();

        int devNum = 0;
        while (true) {
            adapter.clear();
            zeroBuffer(adapter);
            DISPLAY_DEVICE.cbSet(adapter, DISPLAY_DEVICE.SIZEOF);
            if (EnumDisplayDevices((ByteBuffer) null, devNum, adapter, 0) == FALSE)
                break;

            devNum++;

            int adapterStateFlags = DISPLAY_DEVICE.StateFlagsGet(adapter);
            if ((adapterStateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) != 0
                    || (adapterStateFlags & DISPLAY_DEVICE_ACTIVE) == 0)
                continue;

            // Move struct pointer to the device name
            adapter.position(DISPLAY_DEVICE.DEVICENAME);

            // Get monitor name
            device.clear();
            zeroBuffer(device);
            DISPLAY_DEVICE.cbSet(device, DISPLAY_DEVICE.SIZEOF);
            EnumDisplayDevices(adapter, 0, device, 0);
            // Move struct pointer to the device string
            device.position(DISPLAY_DEVICE.DEVICESTRING);

            long dc = CreateDC(DISPLAY, adapter, null, null);

            GLFWmonitor monitor = new GLFWmonitorWin(memDecodeUTF16(device, memStrLen2(device)), // Example  : "Dell U2410(DP)"
                    memDecodeUTF16(adapter, memStrLen2(adapter)), // Example: "\\.\DISPLAY1"
                    GetDeviceCaps(dc, HORZSIZE), GetDeviceCaps(dc, VERTSIZE));

            DeleteDC(dc);

            monitors.add((adapterStateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) != 0 ? 0 : monitors.size(), monitor);
        }

        return monitors;
    }

    @Override
    public String getVersionString() {
        return "3.0.0 Win32";
    }

    @Override
    public void getMonitorPos(GLFWmonitor monitor, IntBuffer xpos, IntBuffer ypos) {
        ByteBuffer settings = DEVMODE.malloc();
        DEVMODE.sizeSet(settings, DEVMODE.SIZEOF);

        EnumDisplaySettingsEx(memEncodeUTF16(monitor.getName()), ENUM_CURRENT_SETTINGS, settings, EDS_ROTATEDMODE);

        if (xpos != null)
            xpos.put(xpos.position(), DEVMODE.positionXGet(settings));

        if (ypos != null)
            ypos.put(ypos.position(), DEVMODE.positionYGet(settings));
    }

    @Override
    public List<GLFWvidmode> getVideoModes(GLFWmonitor monitor) {
        List<GLFWvidmode> vidmodes = new ArrayList<GLFWvidmode>(64);

        ByteBuffer monitorName = memEncodeUTF16(((GLFWmonitorWin) monitor).getAdapterName());

        int modeIndex = 0;
        while (true) {
            ByteBuffer dm = DEVMODE.malloc();
            DEVMODE.sizeSet(dm, DEVMODE.SIZEOF);

            if (EnumDisplaySettings(monitorName, modeIndex, dm) == FALSE)
                break;

            modeIndex++;

            int bpp = DEVMODE.bitsPerPelGet(dm);
            if (bpp < 15) // Skip modes with less than 15 BPP
                continue;

            GLFWvidmode mode = new GLFWvidmode(DEVMODE.pelsWidthGet(dm), DEVMODE.pelsHeightGet(dm), bpp);

            if (vidmodes.contains(mode)) // This is a duplicate, so skip it
                continue;

            vidmodes.add(mode);
        }

        return vidmodes;
    }

    @Override
    public GLFWvidmode getVideoMode(GLFWmonitor monitor) {
        APIBuffer __buffer = apiBuffer();
        ByteBuffer dm = __buffer.buffer();

        zeroMemory(dm, DEVMODE.SIZEOF);
        DEVMODE.sizeSet(dm, DEVMODE.SIZEOF);

        EnumDisplaySettings(monitor.getName(), ENUM_CURRENT_SETTINGS, dm);

        return new GLFWvidmode(DEVMODE.pelsWidthGet(dm), DEVMODE.pelsHeightGet(dm), DEVMODE.bitsPerPelGet(dm));
    }

    @Override
    public void getGammaRamp(GLFWmonitor monitor, ByteBuffer ramp) {
        long dc = CreateDC(DISPLAY, memEncodeUTF16(monitor.getName()), null, null);

        if (GetDeviceGammaRamp(dc, memAddress(ramp)) == FALSE)
            inputError(GLFW_PLATFORM_ERROR, "Win32: failed to retrieve device gamma ramp.");

        DeleteDC(dc);
    }

    @Override
    public void setGammaRamp(GLFWmonitor monitor, ByteBuffer ramp) {
        long dc = CreateDC(DISPLAY, memEncodeUTF16(monitor.getName()), null, null);

        if (SetDeviceGammaRamp(dc, memAddress(ramp)) == FALSE)
            inputError(GLFW_PLATFORM_ERROR, "Win32: failed to set device gamma ramp.");

        DeleteDC(dc);
    }

    @Override
    public GLFWwindowWin createWindowInstance() {
        return new GLFWwindowWin(this);
    }

    @Override
    public boolean createWindow(GLFWwindowWin window, GLFWwndconfig wndconfig, GLFWfbconfig fbconfig) {
        if (classAtom == NULL) {
            classAtom = registerWindowClass();
            if (classAtom == NULL)
                return false;
        }

        if (window.monitor != null) {
            if (!setVideoMode(window.monitor, window.videoMode))
                return false;
        }

        if (!createWindowImpl(window, wndconfig, fbconfig))
            return false;

        int status = analyzeContext(window, wndconfig, fbconfig);

        if (status == _GLFW_RECREATION_IMPOSSIBLE)
            return false;

        if (status == _GLFW_RECREATION_REQUIRED) {
            // Some window hints require us to re-create the context using WGL
            // extensions retrieved through the current context, as we cannot check
            // for WGL extensions or retrieve WGL entry points before we have a
            // current context (actually until we have implicitly loaded the ICD)

            // Yes, this is strange, and yes, this is the proper way on Win32

            // As Windows only allows you to set the pixel format once for a
            // window, we need to destroy the current window and create a new one
            // to be able to use the new pixel format

            // Technically, it may be possible to keep the old window around if
            // we're just creating an OpenGL 3.0+ context with the same pixel
            // format, but it's not worth the added code complexity

            // First we clear the current context (the one we just created)
            // This is usually done by glfwDestroyWindow, but as we're not doing
            // full window destruction, it's duplicated here
            makeContextCurrent(null);

            // Next destroy the Win32 window and WGL context (without resetting or
            // destroying the GLFW window object)
            destroyWindowImpl(window);

            // ...and then create them again, this time with better APIs
            if (!createWindowImpl(window, wndconfig, fbconfig))
                return false;
        }

        return true;
    }

    @Override
    public void swapBuffers(GLFWwindowWin window) {
        SwapBuffers(window.dc);
    }

    @Override
    public void swapInterval(int interval) {
        GLFWwindowWin window = currentWindow.get();

        if (isCompositionEnabled()) {
            // Don't enable vsync when desktop compositing is enabled, as it leads to frame jitter
            return;
        }

        if (window.EXT_swap_control)
            wglSwapIntervalEXT(interval);
    }

    @Override
    public void showWindow(GLFWwindowWin window) {
        ShowWindow(window.handle, SW_SHOWNORMAL);
        BringWindowToTop(window.handle);
        SetForegroundWindow(window.handle);
        SetFocus(window.handle);
    }

    @Override
    public void hideWindow(GLFWwindowWin window) {
    }

    @Override
    public GLFWwindowWin getCurrentContext() {
        return currentWindow.get();
    }

    @Override
    public void makeContextCurrent(GLFWwindowWin window) {
        if (window != null)
            window.context.makeCurrent(window.dc);
        else
            wglMakeCurrent(NULL, NULL);

        currentWindow.set(window);
    }

    @Override
    public void destroyWindow(GLFWwindowWin window) {
        destroyWindowImpl(window);

        if (window.monitor != null)
            restoreVideoMode(window.monitor);
    }

    @Override
    public void setWindowTitle(GLFWwindowWin window, CharSequence title) {
        SetWindowText(window.handle, title);
    }

    @Override
    public void getWindowPos(GLFWwindowWin window, IntBuffer xpos, IntBuffer ypos) {
        APIBuffer __buffer = apiBuffer();
        nClientToScreen(window.handle, __buffer.address());

        if (xpos != null)
            xpos.put(xpos.position(), POINT.xGet(__buffer.buffer()));

        if (ypos != null)
            ypos.put(ypos.position(), POINT.yGet(__buffer.buffer()));
    }

    @Override
    public void setWindowPos(GLFWwindowWin window, int xpos, int ypos) {
        APIBuffer __buffer = apiBuffer();
        ByteBuffer rect = __buffer.buffer();

        RECT.leftSet(rect, xpos);
        RECT.topSet(rect, ypos);
        RECT.rightSet(rect, xpos);
        RECT.bottomSet(rect, ypos);

        AdjustWindowRectEx(rect, window.dwStyle, FALSE, window.dwExStyle);

        SetWindowPos(window.handle, NULL, RECT.leftGet(rect), RECT.topGet(rect), 0, 0,
                SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
    }

    @Override
    public void getWindowSize(GLFWwindowWin window, IntBuffer width, IntBuffer height) {
        APIBuffer __buffer = apiBuffer();
        nGetClientRect(window.handle, __buffer.address());

        if (width != null)
            width.put(width.position(), RECT.rightGet(__buffer.buffer()));

        if (height != null)
            height.put(height.position(), RECT.bottomGet(__buffer.buffer()));
    }

    @Override
    public void setWindowSize(GLFWwindowWin window, int width, int height) {
        if (window.monitor != null) {
            setVideoMode(window.monitor, window.videoMode);
            GLFWvidmode mode = getVideoMode(window.monitor);

            SetWindowPos(window.handle, HWND_TOP, 0, 0, mode.width, mode.height, SWP_NOMOVE);
        } else {
            APIBuffer __buffer = apiBuffer();
            ByteBuffer rect = __buffer.buffer();

            getFullWindowSize(window, width, height, rect);

            // Calculate width and height of full window
            int fullWidth = RECT.rightGet(rect) - RECT.leftGet(rect);
            int fullHeight = RECT.bottomGet(rect) - RECT.topGet(rect);

            SetWindowPos(window.handle, HWND_TOP, 0, 0, fullWidth, fullHeight,
                    SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
        }
    }

    @Override
    public void iconifyWindow(GLFWwindowWin window) {
        ShowWindow(window.handle, SW_MINIMIZE);
    }

    @Override
    public void restoreWindow(GLFWwindowWin window) {
        ShowWindow(window.handle, SW_RESTORE);
    }

    @Override
    public void pollEvents() {
        APIBuffer __buffer = apiBuffer();

        ByteBuffer msg = __buffer.buffer();
        while (PeekMessage(msg, NULL, 0, 0, PM_REMOVE) != 0) {
            if (MSG.messageGet(msg) == WM_QUIT) {
                // Treat WM_QUIT as a close on all windows

                for (GLFWwindow w : windows)
                    inputWindowCloseRequest(w);
            } else {
                TranslateMessage(msg);
                DispatchMessage(msg);
            }
        }

        GLFWwindowWin window = (GLFWwindowWin) focusedWindow;
        if (window != null) {
            // LSHIFT/RSHIFT fixup (keys tend to "stick" without this fix)
            // This is the only async event handling in GLFW, but it solves some
            // nasty problems
            {
                // Get current state of left and right shift keys
                boolean lshift_up = ((GetAsyncKeyState(VK_LSHIFT) >> 15) & 1) == 0;
                boolean rshift_up = ((GetAsyncKeyState(VK_RSHIFT) >> 15) & 1) == 0;

                // See if this differs from our belief of what has happened
                // (we only have to check for lost key up events)
                if (lshift_up && window.keys[GLFW_KEY_LEFT_SHIFT] == 1)
                    inputKey(window, GLFW_KEY_LEFT_SHIFT, GLFW_RELEASE);

                if (rshift_up && window.keys[GLFW_KEY_RIGHT_SHIFT] == 1)
                    inputKey(window, GLFW_KEY_RIGHT_SHIFT, GLFW_RELEASE);
            }

            // Did the cursor move in an focused window that has captured the cursor
            if (window.cursorMode == GLFW_CURSOR_CAPTURED && !window.cursorCentered) {
                ByteBuffer rect = __buffer.buffer();
                GetClientRect(window.handle, rect);

                int width = RECT.rightGet(rect);
                int height = RECT.bottomGet(rect);

                setCursorPos(window, width / 2, height / 2);
                window.cursorCentered = true;
            }
        }
    }

    @Override
    public void waitEvents() {
        WaitMessage();
        pollEvents();
    }

    @Override
    public void setCursorPos(GLFWwindowWin window, double centerPosX, double centerPosY) {
        APIBuffer __buffer = apiBuffer();
        ByteBuffer pos = __buffer.buffer();

        POINT.xSet(pos, (int) centerPosX);
        POINT.ySet(pos, (int) centerPosY);
        ClientToScreen(window.handle, pos);
        SetCursorPos(POINT.xGet(pos), POINT.yGet(pos));
    }

    @Override
    public void setCursorMode(GLFWwindowWin window, int mode) {
        switch (mode) {
        case GLFW_CURSOR_NORMAL:
            showCursor(window);
            break;
        case GLFW_CURSOR_HIDDEN:
            hideCursor(window);
            break;
        case GLFW_CURSOR_CAPTURED:
            captureCursor(window);
            break;
        }
    }

    @Override
    public int getJoystickParam(int joy, int param) {
        if (!isJoystickPresent(joy))
            return 0;

        // We got this far, the joystick is present
        if (param == GLFW_PRESENT)
            return GL11.GL_TRUE;

        // Get joystick capabilities
        ByteBuffer jc = apiBuffer().buffer();
        joyGetDevCaps(joy - GLFW_JOYSTICK_1, jc, JOYCAPS.SIZEOF);

        int caps = JOYCAPS.capsGet(jc);
        int hats = (caps & JOYCAPS_HASPOV) != 0 && (caps & JOYCAPS_POV4DIR) != 0 ? 1 : 0;

        switch (param) {
        case GLFW_AXES:
            // Return number of joystick axes
            return JOYCAPS.numAxesGet(jc);

        case GLFW_BUTTONS:
            // Return number of joystick buttons
            return JOYCAPS.numButtonsGet(jc) + hats * 4;

        default:
            break;
        }

        return 0;
    }

    @Override
    public int getJoystickAxes(int joy, FloatBuffer axes) {
        int axis;

        if (!isJoystickPresent(joy))
            return 0;

        // This is a big awkward, but we avoid allocations.
        APIBuffer __buffer = apiBuffer();
        __buffer.bufferParam(JOYCAPS.SIZEOF); // Make sure we have enough
        int info = __buffer.bufferParam(JOYINFOEX.SIZEOF); // space for both structs

        // We'll store both struct in the same buffer and do accesses for the 2nd buffer manually.
        ByteBuffer jci = apiBuffer().buffer();

        // Get joystick capabilities
        joyGetDevCaps(joy - GLFW_JOYSTICK_1, jci, JOYCAPS.SIZEOF);

        // Get joystick state
        jci.putInt(info + JOYINFOEX.SIZE, JOYINFOEX.SIZEOF);
        jci.putInt(info + JOYINFOEX.FLAGS,
                JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNR | JOY_RETURNU | JOY_RETURNV);
        njoyGetPosEx(joy - GLFW_JOYSTICK_1, __buffer.address() + info);

        // Get position values for all axes
        axis = 0;
        if (axis < axes.remaining())
            axes.put(axes.position() + axis++,
                    calcJoystickPos(jci.getInt(info + JOYINFOEX.XPOS), JOYCAPS.xminGet(jci), JOYCAPS.xmaxGet(jci)));

        if (axis < axes.remaining())
            axes.put(axes.position() + axis++, -calcJoystickPos(jci.getInt(info + JOYINFOEX.YPOS),
                    JOYCAPS.yminGet(jci), JOYCAPS.ymaxGet(jci)));

        int caps = JOYCAPS.capsGet(jci);
        if (axis < axes.remaining() && (caps & JOYCAPS_HASZ) != 0)
            axes.put(axes.position() + axis++,
                    calcJoystickPos(jci.getInt(info + JOYINFOEX.ZPOS), JOYCAPS.zminGet(jci), JOYCAPS.zmaxGet(jci)));

        if (axis < axes.remaining() && (caps & JOYCAPS_HASR) != 0)
            axes.put(axes.position() + axis++,
                    calcJoystickPos(jci.getInt(info + JOYINFOEX.RPOS), JOYCAPS.rminGet(jci), JOYCAPS.rmaxGet(jci)));

        if (axis < axes.remaining() && (caps & JOYCAPS_HASU) != 0)
            axes.put(axes.position() + axis++,
                    calcJoystickPos(jci.getInt(info + JOYINFOEX.UPOS), JOYCAPS.uminGet(jci), JOYCAPS.umaxGet(jci)));

        if (axis < axes.remaining() && (caps & JOYCAPS_HASV) != 0)
            axes.put(axes.position() + axis++, -calcJoystickPos(jci.getInt(info + JOYINFOEX.VPOS),
                    JOYCAPS.vminGet(jci), JOYCAPS.vmaxGet(jci)));

        return axis;
    }

    // Bit fields of button presses for each direction, including nil
    private static final int[] directions = { 1, 3, 2, 6, 4, 12, 8, 9, 0 };

    @Override
    public int getJoystickButtons(int joy, ByteBuffer buttons) {
        if (!isJoystickPresent(joy))
            return 0;

        // This is a big awkward, but we avoid allocations.
        APIBuffer __buffer = apiBuffer();
        __buffer.bufferParam(JOYCAPS.SIZEOF); // Make sure we have enough
        int info = __buffer.bufferParam(JOYINFOEX.SIZEOF); // space for both structs

        // We'll store both struct in the same buffer and do accesses for the 2nd buffer manually.
        ByteBuffer jci = apiBuffer().buffer();

        // Get joystick capabilities
        joyGetDevCaps(joy - GLFW_JOYSTICK_1, jci, JOYCAPS.SIZEOF);

        // Get joystick state
        jci.putInt(info + JOYINFOEX.SIZE, JOYINFOEX.SIZEOF);
        jci.putInt(info + JOYINFOEX.FLAGS, JOY_RETURNBUTTONS | JOY_RETURNPOV);
        njoyGetPosEx(joy - GLFW_JOYSTICK_1, __buffer.address() + info);

        // Get states of all requested buttons
        int button;
        for (button = 0; button < buttons.remaining() && button < JOYCAPS.numButtonsGet(jci); button++) {
            buttons.put(buttons.position() + button,
                    (byte) ((jci.getInt(info + JOYINFOEX.BUTTONS) & (1 << button)) != 0 ? GLFW_PRESS
                            : GLFW_RELEASE));
        }

        // Virtual buttons - Inject data from hats
        // Each hat is exposed as 4 buttons which exposes 8 directions with
        // concurrent button presses
        // NOTE: this API exposes only one hat

        int caps = JOYCAPS.capsGet(jci);
        int hats = (caps & JOYCAPS_HASPOV) != 0 && (caps & JOYCAPS_POV4DIR) != 0 ? 1 : 0;

        if (hats > 0) {
            int value = jci.getInt(info + JOYINFOEX.POV) / 100 / 45;
            if (value < 0 || value > 8)
                value = 8;

            for (int j = 0; j < 4 && button < buttons.limit(); j++) {
                buttons.put(buttons.position() + button,
                        (byte) ((directions[value] & (1 << j)) != 0 ? GLFW_PRESS : GLFW_RELEASE));
                button++;
            }
        }

        return button;
    }

    @Override
    public String getJoystickName(int joy) {
        ByteBuffer jc = apiBuffer().buffer();
        int i = joy - GLFW_JOYSTICK_1;

        if (!isJoystickPresent(joy))
            return null;

        joyGetDevCaps(i, jc, JOYCAPS.SIZEOF);

        return JOYCAPS.pnameGets(jc);
    }

    @Override
    public void setClipboardString(GLFWwindowWin window, CharSequence string) {
        int bytes = string.length() * 2 + 2;

        long stringHandle = GlobalAlloc(GMEM_MOVEABLE, bytes);
        if (stringHandle == NULL) {
            inputError(GLFW_PLATFORM_ERROR, "Win32: Failed to allocate global handle for clipboard");
            return;
        }

        ByteBuffer handleBuffer = memByteBuffer(GlobalLock(stringHandle), bytes);
        memEncodeUTF16(string, true, handleBuffer);
        GlobalUnlock(stringHandle);

        if (OpenClipboard(window.handle) == FALSE) {
            GlobalFree(stringHandle);
            inputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard");
            return;
        }

        EmptyClipboard();
        SetClipboardData(CF_UNICODETEXT, stringHandle);
        CloseClipboard();
    }

    @Override
    public String getClipboardString(GLFWwindowWin window) {
        if (IsClipboardFormatAvailable(CF_UNICODETEXT) == FALSE) {
            inputError(GLFW_FORMAT_UNAVAILABLE, null);
            return null;
        }

        if (OpenClipboard(window.handle) == FALSE) {
            inputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard");
            return null;
        }

        long stringHandle = GetClipboardData(CF_UNICODETEXT);
        if (stringHandle == NULL) {
            CloseClipboard();

            inputError(GLFW_PLATFORM_ERROR, "Win32: Failed to retrieve clipboard data");
            return null;
        }

        String string = memDecodeUTF16(memByteBufferNT2(GlobalLock(stringHandle)));

        GlobalUnlock(stringHandle);
        CloseClipboard();

        return string;
    }

    @Override
    public double getTime() {
        return timer.getTime();
    }

    @Override
    public void setTime(double time) {
        timer.setTime(time);
    }

    // ---------------- IMPLEMENTATION STUFF ------------------

    private boolean initLibraries() {
        try {
            user32 = new WindowsFunctionProvider("user32.dll");
        } catch (Exception e) {
            // ignore silently
            user32 = new DummyFunctionProvider();
        }

        try {
            dwmapi = new WindowsFunctionProvider("dwmapi.dll");
        } catch (Exception e) {
            // ignore silently
            dwmapi = new DummyFunctionProvider();
        }

        user32Funcs = new User32.Functions(user32);
        dwmapiFuncs = new Dwmapi.Functions(dwmapi);

        return true;
    }

    private boolean createWindowImpl(GLFWwindowWin window, GLFWwndconfig wndconfig, GLFWfbconfig fbconfig) {
        window.dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
        window.dwExStyle = WS_EX_APPWINDOW;

        int xpos, ypos, fullWidth, fullHeight;

        if (window.monitor != null) {
            window.dwStyle |= WS_POPUP;

            IntBuffer xposOut = BufferUtils.createIntBuffer(1);
            IntBuffer yposOut = BufferUtils.createIntBuffer(1);

            getMonitorPos(wndconfig.monitor, xposOut, yposOut);

            xpos = xposOut.get(0);
            ypos = yposOut.get(0);

            fullWidth = wndconfig.width;
            fullHeight = wndconfig.height;
        } else {
            if (wndconfig.decorated) {
                window.dwStyle |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;

                if (wndconfig.resizable) {
                    window.dwStyle |= WS_MAXIMIZEBOX | WS_SIZEBOX;
                    window.dwExStyle |= WS_EX_WINDOWEDGE;
                }
            } else {
                window.dwStyle = WS_POPUP;
                window.dwExStyle = 0;
            }

            xpos = CW_USEDEFAULT;
            ypos = CW_USEDEFAULT;

            APIBuffer __buffer = apiBuffer();
            ByteBuffer rect = __buffer.buffer();

            getFullWindowSize(window, wndconfig.width, wndconfig.height, rect);

            // Calculate width and height of full window
            fullWidth = RECT.rightGet(rect) - RECT.leftGet(rect);
            fullHeight = RECT.bottomGet(rect) - RECT.topGet(rect);
        }

        window.wndprocRef = memGlobalRefNew(window.wndproc);
        window.handle = CreateWindowEx(window.dwExStyle, GLFW_WNDCLASSNAME, wndconfig.title, window.dwStyle, xpos,
                ypos, fullWidth, fullHeight, NULL, NULL, nGetModuleHandle(NULL), window.wndprocRef);

        if (window.handle == NULL) {
            memGlobalRefDelete(window.wndprocRef);
            inputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create window");
            return false;
        }

        ByteBuffer cursorPos = POINT.malloc();
        GetCursorPos(cursorPos);
        ScreenToClient(window.handle, cursorPos);
        window.cursorPosX = window.oldCursorX = POINT.xGet(cursorPos);
        window.cursorPosY = window.oldCursorY = POINT.yGet(cursorPos);

        return createContext(window, wndconfig, fbconfig);
    }

    private static long registerWindowClass() {
        long moduleHandle = nGetModuleHandle(NULL);
        long icon = LoadIcon(moduleHandle, "GLFW_ICON");
        if (icon == NULL)
            icon = nLoadIcon(moduleHandle, IDI_WINLOGO);

        ByteBuffer in = WNDCLASSEX.malloc(WNDCLASSEX.SIZEOF, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, // Redraw on...
                WindowProc.CALLBACK, // Message handler
                0, // No extra class data
                Pointer.POINTER_SIZE + 4, // WNDPROC reference + reserver int
                moduleHandle, icon, nLoadCursor(NULL, IDC_ARROW), NULL, NULL,
                memAddress(memEncodeUTF16(GLFW_WNDCLASSNAME)), NULL);

        long classAtom = RegisterClassEx(in);
        if (classAtom == NULL)
            inputError(GLFW_PLATFORM_ERROR, "Win32: Failed to register window class");

        return classAtom;
    }

    boolean setVideoMode(GLFWmonitor monitor, GLFWvidmode desired) {
        GLFWvidmode best = chooseVideoMode(monitor, desired);
        GLFWvidmode current = getVideoMode(monitor);

        if (current.equals(best))
            return true;

        ByteBuffer dm = DEVMODE.malloc();

        DEVMODE.sizeSet(dm, DEVMODE.SIZEOF);
        DEVMODE.fieldsSet(dm, DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL);
        DEVMODE.pelsWidthSet(dm, best.width);
        DEVMODE.pelsHeightSet(dm, best.height);

        int bpp = best.redBits + best.greenBits + best.blueBits;
        DEVMODE.bitsPerPelSet(dm, bpp < 15 || 24 <= bpp ? 32 : bpp);

        if (ChangeDisplaySettingsEx(monitor.getName(), dm, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL) {
            inputError(GLFW_PLATFORM_ERROR, "Win32: Failed to set video mode");
            return false;
        }

        return true;
    }

    private static void getFullWindowSize(GLFWwindowWin window, int clientWidth, int clientHeight,
            ByteBuffer rect) {
        RECT.leftSet(rect, 0);
        RECT.topSet(rect, 0);
        RECT.rightSet(rect, clientWidth);
        RECT.bottomSet(rect, clientHeight);

        // Adjust according to window styles
        AdjustWindowRectEx(rect, window.dwStyle, FALSE, window.dwExStyle);
    }

    private static void setWGLattrib(IntBuffer attribs, int attribName, int attribValue) {
        attribs.put(attribName);
        attribs.put(attribValue);
    }

    private boolean createContext(GLFWwindowWin window, GLFWwndconfig wndconfig, GLFWfbconfig fbconfig) {
        long share = wndconfig.share == null ? NULL
                : wndconfig.share.<GLFWwindowWin>asPlatformWindow().context.getHandle();

        window.dc = GetDC(window.handle);
        if (window.dc == NULL) {
            inputError(GLFW_PLATFORM_ERROR, "Win32: Failed to retrieve DC for window");
            return false;
        }

        IntBuffer attribs = null;
        int pixelFormat;
        ByteBuffer pfd = PIXELFORMATDESCRIPTOR.malloc();

        if (window.ARB_pixel_format) {
            IntBuffer count = BufferUtils.createIntBuffer(1);

            attribs = BufferUtils.createIntBuffer(40);

            setWGLattrib(attribs, WGL_SUPPORT_OPENGL_ARB, TRUE);
            setWGLattrib(attribs, WGL_DRAW_TO_WINDOW_ARB, TRUE);
            setWGLattrib(attribs, WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB);
            setWGLattrib(attribs, WGL_DOUBLE_BUFFER_ARB, TRUE);

            if (fbconfig.redBits != 0)
                setWGLattrib(attribs, WGL_RED_BITS_ARB, fbconfig.redBits);
            if (fbconfig.greenBits != 0)
                setWGLattrib(attribs, WGL_GREEN_BITS_ARB, fbconfig.greenBits);
            if (fbconfig.blueBits != 0)
                setWGLattrib(attribs, WGL_BLUE_BITS_ARB, fbconfig.blueBits);
            if (fbconfig.alphaBits != 0)
                setWGLattrib(attribs, WGL_ALPHA_BITS_ARB, fbconfig.alphaBits);

            if (fbconfig.depthBits != 0)
                setWGLattrib(attribs, WGL_DEPTH_BITS_ARB, fbconfig.depthBits);
            if (fbconfig.stencilBits != 0)
                setWGLattrib(attribs, WGL_STENCIL_BITS_ARB, fbconfig.stencilBits);

            if (fbconfig.auxBuffers != 0)
                setWGLattrib(attribs, WGL_AUX_BUFFERS_ARB, fbconfig.auxBuffers);

            if (fbconfig.accumRedBits != 0)
                setWGLattrib(attribs, WGL_ACCUM_RED_BITS_ARB, fbconfig.accumRedBits);
            if (fbconfig.accumGreenBits != 0)
                setWGLattrib(attribs, WGL_ACCUM_GREEN_BITS_ARB, fbconfig.accumGreenBits);
            if (fbconfig.accumBlueBits != 0)
                setWGLattrib(attribs, WGL_ACCUM_BLUE_BITS_ARB, fbconfig.accumBlueBits);
            if (fbconfig.accumAlphaBits != 0)
                setWGLattrib(attribs, WGL_ACCUM_BLUE_BITS_ARB, fbconfig.accumAlphaBits);

            if (fbconfig.stereo != 0)
                setWGLattrib(attribs, WGL_STEREO_ARB, TRUE);

            if (window.ARB_multisample) {
                if (fbconfig.samples != 0) {
                    setWGLattrib(attribs, WGL_SAMPLE_BUFFERS_ARB, 1);
                    setWGLattrib(attribs, WGL_SAMPLES_ARB, fbconfig.samples);
                }
            }

            if (window.ARB_framebuffer_sRGB) {
                if (fbconfig.sRGB)
                    setWGLattrib(attribs, WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, TRUE);
            }

            setWGLattrib(attribs, 0, 0);
            attribs.flip();

            IntBuffer pixelFormatOut = BufferUtils.createIntBuffer(1);
            if (wglChoosePixelFormatARB(window.dc, attribs, null, pixelFormatOut, count) == FALSE) {
                inputError(GLFW_PLATFORM_ERROR, "WGL: Failed to find a suitable pixel format");
                return false;
            }

            pixelFormat = pixelFormatOut.get(0);
        } else {
            PIXELFORMATDESCRIPTOR.sizeSet(pfd, PIXELFORMATDESCRIPTOR.SIZEOF);
            PIXELFORMATDESCRIPTOR.versionSet(pfd, 1);

            int flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;

            if (fbconfig.stereo != 0)
                flags |= PFD_STEREO;

            PIXELFORMATDESCRIPTOR.flagsSet(pfd, flags);

            PIXELFORMATDESCRIPTOR.pixelTypeSet(pfd, PFD_TYPE_RGBA);
            PIXELFORMATDESCRIPTOR.colorBitsSet(pfd, fbconfig.redBits + fbconfig.greenBits + fbconfig.blueBits);
            PIXELFORMATDESCRIPTOR.alphaBitsSet(pfd, fbconfig.alphaBits);
            PIXELFORMATDESCRIPTOR.accumBitsSet(pfd,
                    fbconfig.accumRedBits + fbconfig.accumGreenBits + fbconfig.accumBlueBits);
            PIXELFORMATDESCRIPTOR.depthBitsSet(pfd, fbconfig.depthBits);
            PIXELFORMATDESCRIPTOR.stencilBitsSet(pfd, fbconfig.stencilBits);
            PIXELFORMATDESCRIPTOR.auxBuffersSet(pfd, fbconfig.auxBuffers);

            pixelFormat = ChoosePixelFormat(window.dc, pfd);
            if (pixelFormat == 0) {
                inputError(GLFW_PLATFORM_ERROR, "WGL: Failed to find a suitable pixel format");
                return false;
            }
        }

        if (DescribePixelFormat(window.dc, pixelFormat, pfd) == FALSE) {
            inputError(GLFW_PLATFORM_ERROR, "Win32: Failed to retrieve PFD for selected pixel format");
            return false;
        }

        int flags = PIXELFORMATDESCRIPTOR.flagsGet(pfd);
        if ((flags & PFD_GENERIC_ACCELERATED) == 0 && (flags & PFD_GENERIC_FORMAT) != 0) {
            inputError(GLFW_PLATFORM_ERROR, "Win32: Failed to find an accelerated pixel format");
            return false;
        }

        if (SetPixelFormat(window.dc, pixelFormat, pfd) == FALSE) {
            inputError(GLFW_PLATFORM_ERROR, "Win32: Failed to set selected pixel format");
            return false;
        }

        long context;
        if (window.ARB_create_context) {
            int mask = 0, strategy = 0;
            flags = 0;

            if (wndconfig.glForward)
                flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;

            if (wndconfig.glDebug)
                flags |= WGL_CONTEXT_DEBUG_BIT_ARB;

            if (wndconfig.glProfile != 0) {
                if (wndconfig.glProfile == GLFW_OPENGL_CORE_PROFILE)
                    mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
                else if (wndconfig.glProfile == GLFW_OPENGL_COMPAT_PROFILE)
                    mask |= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
            }

            if (wndconfig.glRobustness != 0) {
                if (window.ARB_create_context_robustness) {
                    if (wndconfig.glRobustness == GLFW_NO_RESET_NOTIFICATION)
                        strategy = WGL_NO_RESET_NOTIFICATION_ARB;
                    else if (wndconfig.glRobustness == GLFW_LOSE_CONTEXT_ON_RESET)
                        strategy = WGL_LOSE_CONTEXT_ON_RESET_ARB;

                    flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;
                }
            }

            attribs.clear();
            if (wndconfig.glMajor != 1 || wndconfig.glMinor != 0) {
                setWGLattrib(attribs, WGL_CONTEXT_MAJOR_VERSION_ARB, wndconfig.glMajor);
                setWGLattrib(attribs, WGL_CONTEXT_MINOR_VERSION_ARB, wndconfig.glMinor);
            }

            if (flags != 0)
                setWGLattrib(attribs, WGL_CONTEXT_FLAGS_ARB, flags);

            if (mask != 0)
                setWGLattrib(attribs, WGL_CONTEXT_PROFILE_MASK_ARB, mask);

            if (strategy != 0)
                setWGLattrib(attribs, WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, strategy);

            setWGLattrib(attribs, 0, 0);
            attribs.flip();

            context = wglCreateContextAttribsARB(window.dc, share, attribs);
            if (context == NULL) {
                inputError(GLFW_VERSION_UNAVAILABLE, "WGL: Failed to create OpenGL context");
                return false;
            }
        } else {
            context = wglCreateContext(window.dc);
            if (context == NULL) {
                inputError(GLFW_PLATFORM_ERROR, "WGL: Failed to create OpenGL context");
                return false;
            }

            if (share != NULL) {
                if (wglShareLists(share, context) == FALSE) {
                    inputError(GLFW_PLATFORM_ERROR, "WGL: Failed to enable sharing with specified OpenGL context");
                    return false;
                }
            }
        }

        //makeContextCurrent(window);
        wglMakeCurrent(window.dc, context);
        currentWindow.set(window);
        window.context = new WindowsGLContext(GL.createCapabilities(window.glForward), context);

        initWGLExtensions(window);

        return true;
    }

    private static void initWGLExtensions(GLFWwindowWin window) {
        ContextCapabilities caps = window.context.getCapabilities();

        if (!(caps.WGL_ARB_extensions_string || caps.WGL_EXT_extensions_string))
            return;

        window.ARB_multisample = caps.WGL_ARB_multisample;
        window.ARB_framebuffer_sRGB = caps.WGL_ARB_framebuffer_sRGB || caps.WGL_EXT_framebuffer_sRGB;

        window.ARB_create_context = caps.WGL_ARB_create_context;
        window.ARB_create_context_profile = caps.WGL_ARB_create_context_profile;

        window.EXT_create_context_es2_profile = caps.WGL_EXT_create_context_es_profile;

        window.ARB_create_context_robustness = caps.WGL_ARB_create_context_robustness;

        window.EXT_swap_control = caps.WGL_EXT_swap_control;

        window.ARB_pixel_format = caps.WGL_ARB_pixel_format;
    }

    private static int analyzeContext(GLFWwindowWin window, GLFWwndconfig wndconfig, GLFWfbconfig fbconfig) {
        boolean required = false;

        if (wndconfig.glForward) {
            if (!window.ARB_create_context) {
                inputError(GLFW_VERSION_UNAVAILABLE,
                        "WGL: A forward compatible OpenGL context requested but WGL_ARB_create_context is unavailable");
                return _GLFW_RECREATION_IMPOSSIBLE;
            }

            required = true;
        }

        if (wndconfig.glProfile != 0) {
            if (!window.ARB_create_context_profile) {
                inputError(GLFW_VERSION_UNAVAILABLE,
                        "WGL: OpenGL profile requested but WGL_ARB_create_context_profile is unavailable");
                return _GLFW_RECREATION_IMPOSSIBLE;
            }

            required = true;
        }

        if (wndconfig.glMajor != 1 || wndconfig.glMinor != 0) {
            if (window.ARB_create_context)
                required = true;
        }

        if (wndconfig.glDebug) {
            if (window.ARB_create_context)
                required = true;
        }

        if (fbconfig.samples > 0) {
            // We want FSAA, but can we get it?
            // FSAA is not a hard constraint, so otherwise we just don't care

            if (window.ARB_multisample && window.ARB_pixel_format) {
                // We appear to have both the extension and the means to ask for it
                required = true;
            }
        }

        if (required)
            return _GLFW_RECREATION_REQUIRED;

        return _GLFW_RECREATION_NOT_NEEDED;
    }

    private static void destroyWindowImpl(GLFWwindowWin window) {
        destroyContext(window);

        if (window.handle != NULL) {
            DestroyWindow(window.handle);
            window.handle = NULL;
            memGlobalRefDelete(window.wndprocRef);
        }
    }

    private static void destroyContext(GLFWwindowWin window) {
        if (window.context != null) {
            window.context.destroy();
            window.context = null;
        }

        if (window.dc != NULL) {
            ReleaseDC(window.handle, window.dc);
            window.dc = NULL;
        }
    }

    static void restoreVideoMode(GLFWmonitor monitor) {
        ChangeDisplaySettingsEx(monitor.getName(), null, NULL, CDS_FULLSCREEN, NULL);
    }

    boolean isCompositionEnabled() {
        if (dwmapiFuncs.DwmIsCompositionEnabled == NULL)
            return false;

        APIBuffer __buffer = apiBuffer();
        DwmIsCompositionEnabled(__buffer.buffer(), dwmapiFuncs.DwmIsCompositionEnabled);
        return __buffer.intValue(0) == WinError.S_OK;
    }

    static void updateClipRect(GLFWwindowWin window) {
        APIBuffer __buffer = apiBuffer();
        ByteBuffer clipRect = __buffer.buffer();

        GetClientRect(window.handle, clipRect);
        nClientToScreen(window.handle, __buffer.address() + RECT.LEFT);
        nClientToScreen(window.handle, __buffer.address() + RECT.RIGHT);
        ClipCursor(clipRect);
    }

    // Hide mouse cursor
    static void hideCursor(GLFWwindowWin window) {
        ReleaseCapture();
        ClipCursor(null);

        if (window.cursorHidden) {
            ShowCursor(TRUE);
            window.cursorHidden = false;
        }

        APIBuffer __buffer = apiBuffer();
        ByteBuffer pos = __buffer.buffer();
        if (GetCursorPos(pos) != 0) {
            if (WindowFromPoint(pos) == window.handle)
                SetCursor(NULL);
        }
    }

    // Capture mouse cursor
    static void captureCursor(GLFWwindowWin window) {
        if (!window.cursorHidden) {
            ShowCursor(FALSE);
            window.cursorHidden = true;
        }

        updateClipRect(window);
        SetCapture(window.handle);
    }

    // Show mouse cursor
    static void showCursor(GLFWwindowWin window) {
        ReleaseCapture();
        ClipCursor(null);

        if (window.cursorHidden) {
            ShowCursor(TRUE);
            window.cursorHidden = false;
        }

        APIBuffer __buffer = apiBuffer();
        ByteBuffer pos = __buffer.buffer();
        if (GetCursorPos(pos) != 0) {
            if (WindowFromPoint(pos) == window.handle)
                SetCursor(nLoadCursor(NULL, IDC_ARROW));
        }
    }

    private static class DummyFunctionProvider implements FunctionProvider {

        @Override
        public long getFunctionAddress(String functionName) {
            return NULL;
        }

        @Override
        public void destroy() {
        }

    }

}