From 3249f812d6f6a500ec6b6529d2ee40a21298e6f2 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Tue, 7 Sep 2010 17:34:51 +0200 Subject: [PATCH] Initial import of 2.7 Lite. --- CMake/CheckX11Extensions.cmake | 88 ++ CMakeLists.txt | 163 +++ COPYING.txt | 21 + cmake_uninstall.cmake.in | 22 + docs/Makefile | 57 + docs/cleanup.bat | 22 + docs/glfwdoc.sty | 80 ++ docs/glfwrm.tex | 2128 ++++++++++++++++++++++++++++++++ docs/glfwug.tex | 1287 +++++++++++++++++++ docs/readme.txt | 52 + examples/CMakeLists.txt | 37 + examples/boing.c | 615 +++++++++ examples/gears.c | 373 ++++++ examples/listmodes.c | 48 + examples/mipmaps.c | 122 ++ examples/mipmaps.tga | Bin 0 -> 66322 bytes examples/particles.c | 1152 +++++++++++++++++ examples/pong3d.c | 854 +++++++++++++ examples/pong3d_field.tga | Bin 0 -> 17816 bytes examples/pong3d_instr.tga | Bin 0 -> 21279 bytes examples/pong3d_menu.tga | Bin 0 -> 1835 bytes examples/pong3d_title.tga | Bin 0 -> 106516 bytes examples/pong3d_winner1.tga | Bin 0 -> 861 bytes examples/pong3d_winner2.tga | Bin 0 -> 891 bytes examples/splitview.c | 514 ++++++++ examples/triangle.c | 94 ++ examples/wave.c | 399 ++++++ include/GL/glfw.h | 446 +++++++ lib/carbon/CMakeLists.txt | 38 + lib/carbon/carbon_enable.c | 43 + lib/carbon/carbon_fullscreen.c | 127 ++ lib/carbon/carbon_glext.c | 53 + lib/carbon/carbon_init.c | 159 +++ lib/carbon/carbon_joystick.c | 51 + lib/carbon/carbon_time.c | 62 + lib/carbon/carbon_window.c | 1309 ++++++++++++++++++++ lib/carbon/libglfw.pc.cmake | 11 + lib/carbon/platform.h | 268 ++++ lib/cocoa/CMakeLists.txt | 44 + lib/cocoa/cocoa_enable.m | 51 + lib/cocoa/cocoa_fullscreen.m | 102 ++ lib/cocoa/cocoa_glext.m | 63 + lib/cocoa/cocoa_init.m | 250 ++++ lib/cocoa/cocoa_joystick.m | 65 + lib/cocoa/cocoa_time.m | 53 + lib/cocoa/cocoa_window.m | 886 +++++++++++++ lib/cocoa/libglfw.pc.cmake | 11 + lib/cocoa/platform.h | 189 +++ lib/enable.c | 310 +++++ lib/fullscreen.c | 94 ++ lib/glext.c | 235 ++++ lib/init.c | 110 ++ lib/input.c | 280 +++++ lib/internal.h | 221 ++++ lib/joystick.c | 102 ++ lib/time.c | 68 + lib/win32/CMakeLists.txt | 55 + lib/win32/glfwdll.def | 46 + lib/win32/glfwdll_mgw1.def | 46 + lib/win32/glfwdll_mgw2.def | 46 + lib/win32/libglfw.pc.cmake | 11 + lib/win32/platform.h | 480 +++++++ lib/win32/win32_dllmain.c | 49 + lib/win32/win32_enable.c | 155 +++ lib/win32/win32_fullscreen.c | 320 +++++ lib/win32/win32_glext.c | 82 ++ lib/win32/win32_init.c | 281 +++++ lib/win32/win32_joystick.c | 234 ++++ lib/win32/win32_time.c | 118 ++ lib/win32/win32_window.c | 1817 +++++++++++++++++++++++++++ lib/window.c | 1030 ++++++++++++++++ lib/x11/CMakeLists.txt | 36 + lib/x11/libglfw.pc.cmake | 11 + lib/x11/platform.h | 433 +++++++ lib/x11/x11_config.h.cmake | 14 + lib/x11/x11_enable.c | 64 + lib/x11/x11_fullscreen.c | 572 +++++++++ lib/x11/x11_glext.c | 90 ++ lib/x11/x11_init.c | 206 ++++ lib/x11/x11_joystick.c | 367 ++++++ lib/x11/x11_keysym2unicode.c | 901 ++++++++++++++ lib/x11/x11_time.c | 89 ++ lib/x11/x11_window.c | 1875 ++++++++++++++++++++++++++++ readme.html | 821 ++++++++++++ tests/CMakeLists.txt | 36 + tests/accuracy.c | 103 ++ tests/defaults.c | 98 ++ tests/events.c | 309 +++++ tests/fsaa.c | 105 ++ tests/getopt.c | 253 ++++ tests/getopt.h | 63 + tests/iconify.c | 158 +++ tests/joysticks.c | 136 ++ tests/peter.c | 137 ++ tests/reopen.c | 170 +++ tests/tearing.c | 84 ++ tests/version.c | 253 ++++ 97 files changed, 25983 insertions(+) create mode 100644 CMake/CheckX11Extensions.cmake create mode 100644 CMakeLists.txt create mode 100644 COPYING.txt create mode 100644 cmake_uninstall.cmake.in create mode 100644 docs/Makefile create mode 100644 docs/cleanup.bat create mode 100644 docs/glfwdoc.sty create mode 100644 docs/glfwrm.tex create mode 100644 docs/glfwug.tex create mode 100644 docs/readme.txt create mode 100644 examples/CMakeLists.txt create mode 100644 examples/boing.c create mode 100644 examples/gears.c create mode 100644 examples/listmodes.c create mode 100644 examples/mipmaps.c create mode 100644 examples/mipmaps.tga create mode 100644 examples/particles.c create mode 100644 examples/pong3d.c create mode 100644 examples/pong3d_field.tga create mode 100644 examples/pong3d_instr.tga create mode 100644 examples/pong3d_menu.tga create mode 100644 examples/pong3d_title.tga create mode 100644 examples/pong3d_winner1.tga create mode 100644 examples/pong3d_winner2.tga create mode 100644 examples/splitview.c create mode 100644 examples/triangle.c create mode 100644 examples/wave.c create mode 100644 include/GL/glfw.h create mode 100644 lib/carbon/CMakeLists.txt create mode 100644 lib/carbon/carbon_enable.c create mode 100644 lib/carbon/carbon_fullscreen.c create mode 100644 lib/carbon/carbon_glext.c create mode 100644 lib/carbon/carbon_init.c create mode 100644 lib/carbon/carbon_joystick.c create mode 100644 lib/carbon/carbon_time.c create mode 100644 lib/carbon/carbon_window.c create mode 100644 lib/carbon/libglfw.pc.cmake create mode 100644 lib/carbon/platform.h create mode 100644 lib/cocoa/CMakeLists.txt create mode 100644 lib/cocoa/cocoa_enable.m create mode 100644 lib/cocoa/cocoa_fullscreen.m create mode 100644 lib/cocoa/cocoa_glext.m create mode 100644 lib/cocoa/cocoa_init.m create mode 100644 lib/cocoa/cocoa_joystick.m create mode 100644 lib/cocoa/cocoa_time.m create mode 100644 lib/cocoa/cocoa_window.m create mode 100644 lib/cocoa/libglfw.pc.cmake create mode 100644 lib/cocoa/platform.h create mode 100644 lib/enable.c create mode 100644 lib/fullscreen.c create mode 100644 lib/glext.c create mode 100644 lib/init.c create mode 100644 lib/input.c create mode 100644 lib/internal.h create mode 100644 lib/joystick.c create mode 100644 lib/time.c create mode 100644 lib/win32/CMakeLists.txt create mode 100644 lib/win32/glfwdll.def create mode 100644 lib/win32/glfwdll_mgw1.def create mode 100644 lib/win32/glfwdll_mgw2.def create mode 100644 lib/win32/libglfw.pc.cmake create mode 100644 lib/win32/platform.h create mode 100644 lib/win32/win32_dllmain.c create mode 100644 lib/win32/win32_enable.c create mode 100644 lib/win32/win32_fullscreen.c create mode 100644 lib/win32/win32_glext.c create mode 100644 lib/win32/win32_init.c create mode 100644 lib/win32/win32_joystick.c create mode 100644 lib/win32/win32_time.c create mode 100644 lib/win32/win32_window.c create mode 100644 lib/window.c create mode 100644 lib/x11/CMakeLists.txt create mode 100644 lib/x11/libglfw.pc.cmake create mode 100644 lib/x11/platform.h create mode 100644 lib/x11/x11_config.h.cmake create mode 100644 lib/x11/x11_enable.c create mode 100644 lib/x11/x11_fullscreen.c create mode 100644 lib/x11/x11_glext.c create mode 100644 lib/x11/x11_init.c create mode 100644 lib/x11/x11_joystick.c create mode 100644 lib/x11/x11_keysym2unicode.c create mode 100644 lib/x11/x11_time.c create mode 100644 lib/x11/x11_window.c create mode 100644 readme.html create mode 100644 tests/CMakeLists.txt create mode 100644 tests/accuracy.c create mode 100644 tests/defaults.c create mode 100644 tests/events.c create mode 100644 tests/fsaa.c create mode 100644 tests/getopt.c create mode 100644 tests/getopt.h create mode 100644 tests/iconify.c create mode 100644 tests/joysticks.c create mode 100644 tests/peter.c create mode 100644 tests/reopen.c create mode 100644 tests/tearing.c create mode 100644 tests/version.c diff --git a/CMake/CheckX11Extensions.cmake b/CMake/CheckX11Extensions.cmake new file mode 100644 index 00000000..a6f9b18b --- /dev/null +++ b/CMake/CheckX11Extensions.cmake @@ -0,0 +1,88 @@ +# - Check if X11 RandR extension is available +# Check if the X11 extension RandR is available. +# This macro defines : +# - X11_RANDR_FOUND, If set to NO RandR is not available. +# - X11_RANDR_INCLUDE_DIR, includes directory containing the RandR header. +# - X11_RANDR_LIBRARIES, libraries to link in the library to use RandR. +# +# Created by Olivier Delannoy. +macro(CHECK_X11_XRANDR) + message(STATUS "Checking for X11 extension XRandR") + set(X11_XRANDR_FOUND "NO") + find_path(X11_XRANDR_INCLUDE_DIR "X11/extensions/Xrandr.h" + PATHS + /usr/local/include + /usr/local/X11/include + /usr/local/X11R6/include + /usr/include + /usr/X11/include + /usr/X11R6/include) + + find_library(X11_XRANDR_LIBRARIES NAMES Xrandr + PATHS + /usr/local/lib + /usr/local/X11/lib + /usr/local/X11R6/lib + /usr/lib + /usr/X11/lib + /usr/X11R6/lib) + # Create check if file compiles with randr + + if (X11_XRANDR_LIBRARIES AND X11_XRANDR_INCLUDE_DIR) + set(X11_XRANDR_FOUND "YES") + endif (X11_XRANDR_LIBRARIES AND X11_XRANDR_INCLUDE_DIR) + + if (X11_XRANDR_FOUND) + message(STATUS "Checking for X11 extension XRandR -- found") + else (X11_XRANDR_FOUND) + message(STATUS "Checking for X11 extension XRandR -- not found") + endif (X11_XRANDR_FOUND) + + mark_as_advanced(X11_XRANDR_LIBRARIES X11_XRANDR_INCLUDE_DIR) +endmacro(CHECK_X11_XRANDR) + + +# - Check if X11 VidMod extension is available +# Check if the X11 extension VidMod is available. +# This macro defines : +# - X11_VIDMOD_FOUND, If set to NO VidMod is not available. +# - X11_VIDMOD_INCLUDE_DIR, includes directory containing the headers. +# - X11_VIDMOD_LIBRARIES, libraries to link in the libraries. +# +# Created by Olivier Delannoy. +macro(CHECK_X11_XF86VIDMODE) + message(STATUS "Checking for X11 extension xf86vidmode") + set(X11_XF86VIDMODE_FOUND "NO") + find_path(X11_XF86VIDMODE_INCLUDE_DIR "X11/extensions/xf86vmode.h" + PATHS + /usr/local/include + /usr/local/X11/include + /usr/local/X11R6/include + /usr/include + /usr/X11/include + /usr/X11R6/include) + + find_library(X11_XF86VIDMODE_LIBRARIES NAMES Xxf86vm PATHS + /usr/local/lib + /usr/local/X11/lib + /usr/local/X11R6/lib + /usr/lib + /usr/X11/lib + /usr/X11R6/lib) + # Add a test case here + if (X11_XF86VIDMODE_LIBRARIES AND X11_XF86VIDMODE_INCLUDE_DIR) + set(X11_XF86VIDMODE_FOUND "YES") + endif (X11_XF86VIDMODE_LIBRARIES AND X11_XF86VIDMODE_INCLUDE_DIR) + + if (X11_XF86VIDMODE_FOUND) + message(STATUS "Checking for X11 extension xf86vidmode -- found") + else (X11_XF86VIDMODE_FOUND) + message(STATUS "Checking for X11 extension xf86vidmode -- not found") + endif(X11_XF86VIDMODE_FOUND) + + mark_as_advanced( + X11_XF86VIDMODE_LIBRARIES + X11_XF86VIDMODE_INCLUDE_DIR + ) + +endmacro(CHECK_X11_XF86VIDMODE) diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..7acc5af1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,163 @@ +project(GLFW C) + +set(GLFW_VERSION_MAJOR "2") +set(GLFW_VERSION_MINOR "7") +set(GLFW_VERSION_PATCH "0") +set(GLFW_VERSION_EXTRA "") +set(GLFW_VERSION "${GLFW_VERSION_MAJOR}.${GLFW_VERSION_MINOR}") +set(GLFW_VERSION_FULL + "${GLFW_VERSION}.${GLFW_VERSION_PATCH}${GLFW_VERSION_EXTRA}") + +cmake_minimum_required(VERSION 2.4) + +include(CheckFunctionExists) +include(CheckSymbolExists) + +# Stuff common to all platform +find_package(OpenGL REQUIRED) + +set(common_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/lib/enable.c + ${CMAKE_CURRENT_SOURCE_DIR}/lib/fullscreen.c + ${CMAKE_CURRENT_SOURCE_DIR}/lib/glext.c + ${CMAKE_CURRENT_SOURCE_DIR}/lib/init.c + ${CMAKE_CURRENT_SOURCE_DIR}/lib/input.c + ${CMAKE_CURRENT_SOURCE_DIR}/lib/joystick.c + ${CMAKE_CURRENT_SOURCE_DIR}/lib/time.c + ${CMAKE_CURRENT_SOURCE_DIR}/lib/window.c +) + +# Stuff specific to WGL on Win32 +if (WIN32) + message(STATUS "Building GLFW for WGL on a Win32 system") + + # Set up library and include paths + set(CMAKE_REQUIRED_LIBRARIES ${OPENGL_gl_LIBRARY}) + list(APPEND GLFW_INCLUDE_DIR ${OPENGL_INCLUDE_DIR}) + list(APPEND GLFW_LIBRARIES ${OPENGL_gl_LIBRARY}) + + # Select platform specific code + add_subdirectory(lib/win32) +endif (WIN32) + +# Stuff specific to GLX on Unix-like platforms +if (UNIX AND NOT APPLE AND NOT CYGWIN) + message(STATUS "Building GLFW for GLX on a Unix-like system") + + # Set up library and include paths + set(CMAKE_REQUIRED_LIBRARIES ${X11_X11_LIB} ${OPENGL_gl_LIBRARY}) + list(APPEND GLFW_INCLUDE_DIR ${X11_X11_INCLUDE_PATH}) + list(APPEND GLFW_LIBRARIES ${X11_X11_LIB}) + list(APPEND GLFW_INCLUDE_DIR ${OPENGL_INCLUDE_DIR}) + list(APPEND GLFW_LIBRARIES ${OPENGL_gl_LIBRARY}) + + # Detect X11 extension + include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/CheckX11Extensions.cmake) + + # Check for XRandR (modern resolution switching extension) + CHECK_X11_XRANDR() + if (X11_XRANDR_FOUND) + set(_GLFW_HAS_XRANDR 1) + list(APPEND GLFW_INCLUDE_DIR ${X11_XRANDR_INCLUDE_DIR}) + list(APPEND GLFW_LIBRARIES ${X11_XRANDR_LIBRARIES}) + endif(X11_XRANDR_FOUND) + + # Check for xf86vidmode (fallback legacy resolution switching extension) + if (NOT X11_XRANDR_FOUND) + CHECK_X11_XF86VIDMODE() + if (X11_XF86VIDMODE_FOUND) + set(_GLFW_HAS_XF86VIDMODE 1) + list(APPEND GLFW_INCLUDE_DIR ${X11_XF86VIDMODE_INCLUDE_DIR}) + list(APPEND GLFW_LIBRARIES ${X11_XF86VIDMODE_LIBRARIES}) + endif(X11_XF86VIDMODE_FOUND) + endif (NOT X11_XRANDR_FOUND) + + CHECK_FUNCTION_EXISTS(glXGetProcAddress _GLFW_HAS_GLXGETPROCADDRESS) + + if (NOT _GLFW_HAS_GLXGETPROCADDRESS) + CHECK_FUNCTION_EXISTS(glXGetProcAddressARB _GLFW_HAS_GLXGETPROCADDRESSARB) + endif (NOT _GLFW_HAS_GLXGETPROCADDRESS) + + if (NOT _GLFW_HAS_GLXGETPROCADDRESS AND NOT _GLFW_HAS_GLXGETPROCADDRESSARB) + CHECK_FUNCTION_EXISTS(glXGetProcAddressEXT _GLFW_HAS_GLXGETPROCADDRESSEXT) + endif (NOT _GLFW_HAS_GLXGETPROCADDRESS AND NOT _GLFW_HAS_GLXGETPROCADDRESSARB) + + if (NOT _GLFW_HAS_GLXGETPROCADDRESS AND + NOT _GLFW_HAS_GLXGETPROCADDRESSARB AND + NOT _GLFW_HAS_GLXGETPROCADDRESSEXT) + message(WARNING "No glXGetProcAddressXXX variant found") + endif (NOT _GLFW_HAS_GLXGETPROCADDRESS AND + NOT _GLFW_HAS_GLXGETPROCADDRESSARB AND + NOT _GLFW_HAS_GLXGETPROCADDRESSEXT) + + # Select platform specific code + add_subdirectory(lib/x11) +endif(UNIX AND NOT APPLE AND NOT CYGWIN) + +# Stuff specific to AGL and CGL on Mac OS X +if (UNIX AND APPLE) + message(STATUS "Building GLFW for NSOpenGL on Mac OS X") + + # Universal build, decent set of warning flags... + set(CMAKE_OSX_ARCHITECTURES ppc;i386;ppc64;x86_64) + set(CMAKE_OSX_SYSROOT /Developer/SDKs/MacOSX10.5.sdk) + set(CMAKE_C_FLAGS "-mmacosx-version-min=10.5 -Wall -Wextra -Wno-unused-parameter -Werror") + + # Set up library and include paths + find_library(COCOA_FRAMEWORK Cocoa) + list(APPEND GLFW_LIBRARIES ${COCOA_FRAMEWORK}) + list(APPEND GLFW_LIBRARIES ${OPENGL_gl_LIBRARY}) + + # Select platform specific code + add_subdirectory(lib/cocoa) +endif(UNIX AND APPLE) + +add_subdirectory(examples) +add_subdirectory(tests) +#add_subdirectory(docs/doxygen) +#add_subdirectory(docs/manuals) + + +#-------------------------------------------------------------------- +# -- Install standard files +#-------------------------------------------------------------------- + +# Install the GLFW header file +install(DIRECTORY include/ DESTINATION include + PATTERN ".svn" EXCLUDE + PATTERN "include/*" +) + +# Install documentation +install( + FILES + COPYING.txt + readme.html + DESTINATION + share/doc/glfw-${GLFW_VERSION_FULL}/ +) + +#-------------------------------------------------------------------- +# -- Additional stuff +#-------------------------------------------------------------------- + +#-------------------------------------------------------------------- +# -- Documentation generation +#-------------------------------------------------------------------- +#include("${CMAKE_CURRENT_SOURCE_DIR}/documentation.cmake") +#configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in" +# "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" +# IMMEDIATE @ONLY) +#add_doxygen_target("${CMAKE_CURRENT_BINARY_DIR}/Doxyfile") +#add_subdirectory(docs) + +#-------------------------------------------------------------------- +# -- Uninstall operation +# ------------------------------------------------------------------- +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) +add_custom_target(uninstall + "${CMAKE_COMMAND}" -P + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") + diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 00000000..0a69446d --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,21 @@ +Copyright (c) 2002-2007 Camilla Berglund + +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. + diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in new file mode 100644 index 00000000..17376d2e --- /dev/null +++ b/cmake_uninstall.cmake.in @@ -0,0 +1,22 @@ +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") +FOREACH(file ${files}) + MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + IF(EXISTS "$ENV{DESTDIR}${file}") + EXEC_PROGRAM( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + ENDIF(NOT "${rm_retval}" STREQUAL 0) + ELSE(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + ENDIF(EXISTS "$ENV{DESTDIR}${file}") +ENDFOREACH(file) + diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..6bc7da29 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,57 @@ +########################################################################## +# Makefile for the GLFW documentation. +########################################################################## + +PDFDOCS = glfwrm.pdf glfwug.pdf +DVIDOCS = glfwrm.dvi glfwug.dvi + + +########################################################################## +# Build macros +########################################################################## +default: pdf +pdf: $(PDFDOCS) +dvi: $(DVIDOCS) + + +########################################################################## +# Clean macros +########################################################################## +clean: + rm -f glfwrm.dvi glfwrm.aux glfwrm.log glfwrm.out glfwrm.pdf glfwrm.toc glfwrm.lot + rm -f glfwug.dvi glfwug.aux glfwug.log glfwug.out glfwug.pdf glfwug.toc + +clean-win: + @.\\cleanup.bat + + +########################################################################## +# Rules for building the GLFW Reference Manual +########################################################################## + +glfwrm.pdf: glfwrm.tex glfwrm.toc glfwrm.lot glfwdoc.sty + pdflatex glfwrm.tex + +glfwrm.dvi: glfwrm.tex glfwrm.toc glfwrm.lot glfwdoc.sty + latex glfwrm.tex + +glfwrm.toc: glfwrm.tex glfwdoc.sty + latex glfwrm.tex + +glfwrm.lot: glfwrm.tex glfwdoc.sty + latex glfwrm.tex + + +########################################################################## +# Rules for building the GLFW Users Guide +########################################################################## + +glfwug.pdf: glfwug.tex glfwug.toc glfwdoc.sty + pdflatex glfwug.tex + +glfwug.dvi: glfwug.tex glfwug.toc glfwdoc.sty + latex glfwug.tex + +glfwug.toc: glfwug.tex glfwdoc.sty + latex glfwug.tex + diff --git a/docs/cleanup.bat b/docs/cleanup.bat new file mode 100644 index 00000000..268a27b6 --- /dev/null +++ b/docs/cleanup.bat @@ -0,0 +1,22 @@ +@echo off + +REM ---------------------------------------------------------------------- +REM Windows cleanup batch file for the GLFW documentation. +REM ---------------------------------------------------------------------- + +REM GLFW Reference Manual +if exist glfwrm.dvi del glfwrm.dvi +if exist glfwrm.aux del glfwrm.aux +if exist glfwrm.log del glfwrm.log +if exist glfwrm.out del glfwrm.out +if exist glfwrm.pdf del glfwrm.pdf +if exist glfwrm.toc del glfwrm.toc +if exist glfwrm.lot del glfwrm.lot + +REM GLFW Users Guide +if exist glfwug.dvi del glfwug.dvi +if exist glfwug.aux del glfwug.aux +if exist glfwug.log del glfwug.log +if exist glfwug.out del glfwug.out +if exist glfwug.pdf del glfwug.pdf +if exist glfwug.toc del glfwug.toc diff --git a/docs/glfwdoc.sty b/docs/glfwdoc.sty new file mode 100644 index 00000000..b7e7b97d --- /dev/null +++ b/docs/glfwdoc.sty @@ -0,0 +1,80 @@ +%------------------------------------------------------------------------- +% Common document formatting and macros for GLFW manuals +%------------------------------------------------------------------------- + +% Misc. document info +\date{\today} + +% Packages +\usepackage{fancyhdr} +\usepackage{titling} +\usepackage{lastpage} +\usepackage{listings} +\usepackage{color} +\usepackage[overload]{textcase} +\usepackage{needspace} +\usepackage{times} + +% Logo macros +\newcommand{\OpenGL}[1][0]{\textbf{OpenGL}\texttrademark} +\newcommand{\GLFW}[1][0]{\textbf{GLFW}} + +% Encoding +\usepackage[latin1]{inputenc} +\usepackage[T1]{fontenc} + +% Page formatting +\usepackage[hmargin=2.5cm]{geometry} +\raggedright +\raggedbottom +\sloppy +\usepackage{parskip} + +% Header and footer +\pagestyle{fancy} +%\lhead{\textit{GLFW Reference Manual}} +\lhead{\textit{GLFW \glfwdoctype}} +\chead{API version \glfwapiver} +\rhead{Page \thepage/\pageref{LastPage}} +\lfoot{} +\cfoot{} +\rfoot{} +\renewcommand{\headrulewidth}{0.4pt} +\renewcommand{\footrulewidth}{0.0pt} + +% Titlepage +\newcommand{\glfwmaketitle}{\begin{titlepage}\ \\% + \begin{center}% + \vspace{7.0cm}{\Huge\textbf{GLFW}}\\% + \rule{10.0cm}{0.5pt}\\% + \vspace{0.5cm}{\LARGE\textbf{\glfwdoctype}}\\% + \vspace{0.8cm}{\large\textbf{API version \glfwapiver}}\\% + \textit{\today}\\% + \vspace{1.5cm}\textbf{\textcopyright2002-2007 Camilla Berglund}\\% + \end{center}\end{titlepage}\newpage} + +% Colors +\definecolor{code}{rgb}{0.9,0.9,1.0} +\definecolor{link}{rgb}{0.6,0.0,0.0} +\definecolor{codeA}{rgb}{0.9,1.0,0.9} +\definecolor{codeB}{rgb}{1.0,0.9,0.9} + +% Code listings +\lstset{frame=single,frameround=tttt,backgroundcolor=\color{code},% + language=C,basicstyle={\ttfamily},% + breaklines,breakindent=0pt,postbreak=\space\space\space\space} + + +% A simple hack for keeping lines together +\newenvironment{mysamepage}[1][2]{\begin{samepage}\needspace{#1\baselineskip}}{\end{samepage}} + +% Macros for automating function reference entries +\newenvironment{refparameters}[1][0]{\begin{mysamepage}\textbf{Parameters}\\}{\end{mysamepage}\bigskip} +\newenvironment{refreturn}[1][0]{\begin{mysamepage}\textbf{Return values}\\}{\end{mysamepage}\bigskip} +\newenvironment{refdescription}[1][0]{\begin{mysamepage}\textbf{Description}\\}{\end{mysamepage}\bigskip} +\newenvironment{refnotes}[1][0]{\begin{mysamepage}\textbf{Notes}\\}{\end{mysamepage}\bigskip} + +% hyperref (bookmarks, links etc) - use this package last +\usepackage[colorlinks=true,linkcolor=link,bookmarks=true,bookmarksopen=true,% + pdfhighlight=/N,bookmarksnumbered=true,bookmarksopenlevel=1,% + pdfview=FitH,pdfstartview=FitH]{hyperref} diff --git a/docs/glfwrm.tex b/docs/glfwrm.tex new file mode 100644 index 00000000..72f87934 --- /dev/null +++ b/docs/glfwrm.tex @@ -0,0 +1,2128 @@ +%------------------------------------------------------------------------- +% GLFW Reference Manual +% API Version: 2.7 +%------------------------------------------------------------------------- + +% Document class +\documentclass[a4paper,11pt,oneside]{report} + +% Document title and API version +\newcommand{\glfwdoctype}[1][0]{Reference Manual} +\newcommand{\glfwapiver}[1][0]{2.7} + +% Common document settings and macros +\input{glfwdoc.sty} + +% PDF specific document settings +\hypersetup{pdftitle={GLFW Reference Manual}} +\hypersetup{pdfauthor={Marcus Geelnard}} +\hypersetup{pdfkeywords={GLFW,OpenGL,reference,manual}} + + +%------------------------------------------------------------------------- +% Document body +%------------------------------------------------------------------------- + +\begin{document} + +\pagestyle{plain} + +% Title page +\glfwmaketitle + +% Summary, trademarks and table of contents +\pagenumbering{roman} +\setcounter{page}{1} + +%------------------------------------------------------------------------- +% Summary and Trademarks +%------------------------------------------------------------------------- +\chapter*{Summary} + +This document is primarily a function reference manual for the \GLFW\ API. +For a description of how to use \GLFW\ you should refer to the +\textit{GLFW Users Guide}. +\vspace{10cm} + +\large +Trademarks + +\small +OpenGL and IRIX are registered trademarks of Silicon Graphics, Inc.\linebreak +Microsoft and Windows are registered trademarks of Microsoft Corporation.\linebreak +Mac OS is a registered trademark of Apple Computer, Inc.\linebreak +Linux is a registered trademark of Linus Torvalds.\linebreak +FreeBSD is a registered trademark of Wind River Systems, Inc.\linebreak +Solaris is a trademark of Sun Microsystems, Inc.\linebreak +UNIX is a registered trademark of The Open Group.\linebreak +X Window System is a trademark of The Open Group.\linebreak +POSIX is a trademark of IEEE.\linebreak +Truevision, TARGA and TGA are registered trademarks of Truevision, Inc.\linebreak + +All other trademarks mentioned in this document are the property of their respective owners. +\normalsize + + +%------------------------------------------------------------------------- +% Table of contents +%------------------------------------------------------------------------- +\tableofcontents + +%------------------------------------------------------------------------- +% List of tables +%------------------------------------------------------------------------- +\listoftables +\pagebreak + + +% Document chapters starts here... +\pagenumbering{arabic} +\setcounter{page}{1} + +\pagestyle{fancy} + + +%------------------------------------------------------------------------- +% Introduction +%------------------------------------------------------------------------- +\chapter{Introduction} +\thispagestyle{fancy} + +\GLFW\ is a portable API (Application Program Interface) that handles +operating system specific tasks related to \OpenGL\ programming. While +\OpenGL\ in general is portable, easy to use and often results in tidy and +compact code, the operating system specific mechanisms that are required +to set up and manage an \OpenGL\ window are quite the opposite. \GLFW\ tries +to remedy this by providing the following functionality: + +\begin{itemize} +\item Opening and managing an \OpenGL\ window. +\item Keyboard, mouse and joystick input. +\item A high precision timer. +\item Support for querying and using \OpenGL\ extensions. +\end{itemize} +\vspace{18pt} + +All this functionality is implemented as a set of easy-to-use functions, +which makes it possible to write an \OpenGL\ application framework in just a +few lines of code. The \GLFW\ API is completely operating system and +platform independent, which makes it very simple to port \GLFW\ based \OpenGL\ +applications to a variety of platforms. + +Currently supported platforms are: +\begin{itemize} +\item Microsoft Windows\textsuperscript{\textregistered} 95/98/ME/NT/2000/XP/Vista. +\item Unix\textsuperscript{\textregistered} or Unix­-like systems running the +X Window System\texttrademark, e.g. Linux\textsuperscript{\textregistered}, +IRIX\textsuperscript{\textregistered}, FreeBSD\textsuperscript{\textregistered}, +Solaris\texttrademark, QNX\textsuperscript{\textregistered} and +Mac OS\textsuperscript{\textregistered} X. +\item Mac OS\textsuperscript{\textregistered} X (Carbon)\footnote{Support for joysticks missing at the time of writing.} +\end{itemize} + + + +%------------------------------------------------------------------------- +% GLFW Operation +%------------------------------------------------------------------------- +\chapter{GLFW Operation Overview} +\thispagestyle{fancy} + + +%------------------------------------------------------------------------- +\section{The GLFW Window} +\GLFW\ only supports one opened window at a time. The window can be either +a normal desktop window or a fullscreen window. The latter is completely +undecorated, without window borders, and covers the entire monitor. With +a fullscreen window, it is also possible to select which video mode to use. + +When a window is opened, an \OpenGL\ rendering context is created and +attached to the entire client area of the window. When the window is closed, +the \OpenGL\ rendering context is detached and destroyed. + +Through a window it is possible to receive user input in the form of +keyboard and mouse input. User input is exposed through the \GLFW\ API via +callback functions. There are different callback functions for dealing with +different kinds of user input. Also, \GLFW\ stores most user input as +internal state that can be queried through different \GLFW\ API functions +(for instance it is possible to query the position of the mouse cursor +with the \textbf{glfwGetMousePos} function). + +As for user input, it is possible to receive information about window +state changes, such as window resize or close events, through callback +functions. It is also possible to query different kinds of window +information through different \GLFW\ API functions. + + +%------------------------------------------------------------------------- +\section{The GLFW Event Loop} +The \GLFW\ event loop is an open loop, which means that it is up to the +programmer to design the loop. Events are processed by calling specific +\GLFW\ functions, which in turn query the system for new input and window +events, and reports these events back to the program through callback +functions. + +The programmer decides when to call the event processing functions, and +when to abort the event loop. + +In pseudo language, a typical event loop might look like this: + +\begin{lstlisting} + repeat until window is closed + { + poll events + draw OpenGL graphics + } +\end{lstlisting} + +There are two ways to handle events in \GLFW : + +\begin{itemize} + \item Block the event loop while waiting for new events. + \item Poll for new events, and continue the loop regardless if there are + any new events or not. +\end{itemize} + +The first method is useful for interactive applications that do not +need to refresh the \OpenGL\ display unless the user interacts with the +application through user input. Typical applications are CAD software +and other kinds of editors. + +The second method is useful for applications that need to refresh the +\OpenGL\ display constantly, regardless of user input, such as games, +demos, 3D animations, screen savers and so on. + + +%------------------------------------------------------------------------- +\section{Callback Functions} +Using callback functions can be a good method for receiving up to date +information about window state and user input. When a window has been +opened, it is possible to register custom callback functions that will +be called when certain events occur. + +Callback functions are called from any of the event polling functions +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers}. + +Callback functions should \emph{only} be used to gather information. Since +the callback functions are called from within the internal \GLFW\ event +polling loops, they should not call any \GLFW\ functions that might +result in considerable \GLFW\ state changes, nor stall the event polling +loop for a lengthy period of time. + +In other words, most or all \OpenGL\ rendering should be called from the +main application event loop, not from any of the \GLFW\ callback +functions. Also, the only \GLFW\ functions that may be safely called from +callback functions are the different Get functions (e.g. +\textbf{glfwGetKey}, \textbf{glfwGetTime}, \textbf{glfwGetWindowParam} +etc). + + +%------------------------------------------------------------------------- +% Function Reference +%------------------------------------------------------------------------- +\chapter{Function Reference} +\thispagestyle{fancy} + +%------------------------------------------------------------------------- +\section{GLFW Initialization and Termination} +Before any \GLFW\ functions can be used, \GLFW\ must be initialized to +ensure proper functionality, and before a program terminates, \GLFW\ has to +be terminated in order to free up resources etc. + + +%------------------------------------------------------------------------- +\subsection{glfwInit} + +\textbf{C language syntax} +\begin{lstlisting} +int glfwInit( void ) +\end{lstlisting} + +\begin{refparameters} +none +\end{refparameters} + +\begin{refreturn} +If the function succeeds, GL\_TRUE is returned.\\ +If the function fails, GL\_FALSE is returned. +\end{refreturn} + +\begin{refdescription} +The glfwInit function initializes \GLFW. No other \GLFW\ functions may be +used before this function has been called. +\end{refdescription} + +\begin{refnotes} +This function may take several seconds to complete on some systems, while +on other systems it may take only a fraction of a second to complete. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwTerminate} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwTerminate( void ) +\end{lstlisting} + +\begin{refparameters} +none +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function terminates \GLFW. Among other things it closes the window, +if it is opened. This function must be called before a program exits. +\end{refdescription} + + +%------------------------------------------------------------------------- +\subsection{glfwGetVersion} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwGetVersion( int *major, int *minor, int *rev ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{major}]\ \\ + Pointer to an integer that will hold the major version number. +\item [\textit{minor}]\ \\ + Pointer to an integer that will hold the minor version number. +\item [\textit{rev}]\ \\ + Pointer to an integer that will hold the revision. +\end{description} +\end{refparameters} + +\begin{refreturn} +The function returns the major and minor version numbers and the revision +for the currently linked \GLFW\ library. +\end{refreturn} + +\begin{refdescription} +The function returns the \GLFW\ library version. +\end{refdescription} + + +%------------------------------------------------------------------------- +\pagebreak +\section{Window Handling} +The main functionality of \GLFW\ is to provide a simple interface to +\OpenGL\ window management. \GLFW\ can open one window, which can be +either a normal desktop window or a fullscreen window. + + +%------------------------------------------------------------------------- +\subsection{glfwOpenWindow} + +\textbf{C language syntax} +\begin{lstlisting} +int glfwOpenWindow( int width, int height, int redbits, + int greenbits, int bluebits, int alphabits, int depthbits, + int stencilbits, int mode ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{width}]\ \\ + The width of the window. If \textit{width} is zero, it will be + calculated as ${width=\frac{4}{3}height}$, if \textit{height} is not + zero. If both \textit{width} and \textit{height} are zero, then + \textit{width} will be set to 640. +\item [\textit{height}]\ \\ + The height of the window. If \textit{height} is zero, it will be + calculated as ${height=\frac{3}{4}width}$, if \textit{width} is not + zero. If both \textit{width} and \textit{height} are zero, then + \textit{height} will be set to 480. +\item [\textit{redbits, greenbits, bluebits}]\ \\ + The number of bits to use for each color component of the color buffer + (0 means default color depth). For instance, setting \textit{redbits=5, + greenbits=6, and bluebits=5} will generate a 16-­bit color buffer, if + possible. +\item [\textit{alphabits}]\ \\ + The number of bits to use for the alpha buffer (0 means no alpha + buffer). +\item [\textit{depthbits}]\ \\ + The number of bits to use for the depth buffer (0 means no depth + buffer). +\item [\textit{stencilbits}]\ \\ + The number of bits to use for the stencil buffer (0 means no stencil + buffer). +\item [\textit{mode}]\ \\ + Selects which type of \OpenGL\ window to use. \textit{mode} can be + either GLFW\_WINDOW, which will generate a normal desktop window, or + GLFW\_FULLSCREEN, which will generate a window which covers the entire + screen. When GLFW\_FULLSCREEN is selected, the video mode will be + changed to the resolution that closest matches the \textit{width} and + \textit{height} parameters. +\end{description} +\end{refparameters} + +\begin{refreturn} +If the function succeeds, GL\_TRUE is returned.\\ +If the function fails, GL\_FALSE is returned. +\end{refreturn} + +\begin{refdescription} +The function opens a window that best matches the parameters given to the +function. How well the resulting window matches the desired window depends +mostly on the available hardware and \OpenGL\ drivers. In general, +selecting a fullscreen mode has better chances of generating a close match +than does a normal desktop window, since \GLFW\ can freely select from all +the available video modes. A desktop window is normally restricted to the +video mode of the desktop. +\end{refdescription} + +\begin{refnotes} +For additional control of window properties, see +\textbf{glfwOpenWindowHint}. + +In fullscreen mode the mouse cursor is hidden by default, and any system +screensavers are prohibited from starting. In windowed mode the mouse +cursor is visible, and screensavers are allowed to start. To change the +visibility of the mouse cursor, use \textbf{glfwEnable} or +\textbf{glfwDisable} with the argument GLFW\_MOUSE\_CURSOR. + +In order to determine the actual properties of an opened window, use +\textbf{glfwGetWindowParam} and \textbf{glfwGetWindowSize} (or +\textbf{glfwSetWindowSizeCallback}). +\end{refnotes} + + +%------------------------------------------------------------------------- +\begin{table}[p] +\begin{center} +\begin{tabular}{|l|l|p{7.0cm}|} \hline \raggedright +\textbf{Name} & \textbf{Default} & \textbf{Description} \\ \hline +GLFW\_REFRESH\_RATE & 0 & Vertical monitor refresh rate in Hz (only used for fullscreen windows). Zero means system default.\\ \hline +GLFW\_ACCUM\_RED\_BITS & 0 & Number of bits for the red channel of the accumulator buffer.\\ \hline +GLFW\_ACCUM\_GREEN\_BITS & 0 & Number of bits for the green channel of the accumulator buffer.\\ \hline +GLFW\_ACCUM\_BLUE\_BITS & 0 & Number of bits for the blue channel of the accumulator buffer.\\ \hline +GLFW\_ACCUM\_ALPHA\_BITS & 0 & Number of bits for the alpha channel of the accumulator buffer.\\ \hline +GLFW\_AUX\_BUFFERS & 0 & Number of auxiliary buffers.\\ \hline +GLFW\_STEREO & GL\_FALSE & Specify if stereo rendering should be supported (can be GL\_TRUE or GL\_FALSE).\\ \hline +GLFW\_WINDOW\_NO\_RESIZE & GL\_FALSE & Specify whether the window can be resized (not used for fullscreen windows).\\ \hline +GLFW\_FSAA\_SAMPLES & 0 & Number of samples to use for the multisampling buffer. Zero disables multisampling.\\ \hline +GLFW\_OPENGL\_VERSION\_MAJOR & 0 & Major number of the desired OpenGL version. + The default requests the highest OpenGL version equal to or lower than 2.1.\\ \hline +GLFW\_OPENGL\_VERSION\_MINOR & 0 & Minor number of the desired OpenGL version. + The default requests the highest OpenGL version equal to or lower than 2.1.\\ \hline +GLFW\_OPENGL\_FORWARD\_COMPAT & GL\_FALSE & Specify whether the OpenGL context should be forward compatible (i.e. disallow legacy functionality). + This hint is ignored for OpenGL version 2.1 and below.\\ \hline +\end{tabular} +\end{center} +\caption{Targets for \textbf{glfwOpenWindowHint}} +\label{tab:winhints} +\end{table} + + +%------------------------------------------------------------------------- +\subsection{glfwOpenWindowHint} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwOpenWindowHint( int target, int hint ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{target}]\ \\ + Can be any of the constants in the table \ref{tab:winhints}. +\item [\textit{hint}]\ \\ + An integer giving the value of the corresponding target (see table + \ref{tab:winhints}). +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function sets additional properties for a window that is to be opened. +For a hint to be registered, the function must be called before calling +\textbf{glfwOpenWindow}. When the \textbf{glfwOpenWindow} function is +called, any hints that were registered with the \textbf{glfwOpenWindowHint} +function are used for setting the corresponding window properties, and +then all hints are reset to their default values. +\end{refdescription} + +\begin{refnotes} +In order to determine the actual properties of an opened window, use +\textbf{glfwGetWindowParam} (after the window has been opened). + +GLFW\_STEREO is a hard constraint. If stereo rendering is requested, but +no stereo rendering capable pixel formats / visuals are available, +\textbf{glfwOpenWindow} will fail. + +The GLFW\_REFRESH\_RATE property should be used with caution. Most +systems have default values for monitor refresh rates that are optimal +for the specific system. Specifying the refresh rate can override these +settings, which can result in suboptimal operation. The monitor may be +unable to display the resulting video signal, or in the worst case it may +even be damaged! + +If you want to create a context with OpenGL version 3.0 or above you have to +set the GLFW\_OPENGL\_VERSION\_MAJOR and GLFW\_OPENGL\_VERSION\_MINOR hints +accordingly. If you don't do this, the highest OpenGL version available for a +context is 2.1 or lower. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwCloseWindow} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwCloseWindow( void ) +\end{lstlisting} + +\begin{refparameters} +none +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function closes an opened window and destroys the associated \OpenGL\ +context. +\end{refdescription} + + +%------------------------------------------------------------------------- +\subsection{glfwSetWindowCloseCallback} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetWindowCloseCallback( GLFWwindowclosefun cbfun ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{cbfun}]\ \\ + Pointer to a callback function that will be called when a user requests + that the window should be closed, typically by clicking the window close + icon (e.g. the cross in the upper right corner of a window under + Microsoft Windows). The function should have the following C language + prototype: + + \texttt{int GLFWCALL functionname( void );} + + Where \textit{functionname} is the name of the callback function. The + return value of the callback function indicates wether or not the window + close action should continue. If the function returns GL\_TRUE, the + window will be closed. If the function returns GL\_FALSE, the window + will not be closed. + + If \textit{cbfun} is NULL, any previously selected callback function + will be deselected. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function selects which function to be called upon a window close +event. + +A window has to be opened for this function to have any effect. +\end{refdescription} + +\begin{refnotes} +Window close events are recorded continuously, but only reported when +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers} is called. + +The \OpenGL\ context is still valid when this function is called. + +Note that the window close callback function is not called when +\textbf{glfwCloseWindow} is called, but only when the close request +comes from the window manager. + +Do \emph{not} call \textbf{glfwCloseWindow} from a window close +callback function. Close the window by returning GL\_TRUE from the +function. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSetWindowTitle} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetWindowTitle( const char *title ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{title}]\ \\ + Pointer to a null terminated ISO~8859-1 (8-bit Latin~1) string that + holds the title of the window. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function changes the title of the opened window. +\end{refdescription} + +\begin{refnotes} +The title property of a window is often used in situations other than for +the window title, such as the title of an application icon when it is in +iconified state. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSetWindowSize} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetWindowSize( int width, int height ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{width}]\ \\ + Width of the window. +\item [\textit{height}]\ \\ + Height of the window. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function changes the size of an opened window. The \textit{width} and +\textit{height} parameters denote the size of the client area of the +window (i.e. excluding any window borders and decorations). + +If the window is in fullscreen mode, the video mode will be changed to a +resolution that closest matches the width and height parameters (the +number of color bits will not be changed). +\end{refdescription} + +\begin{refnotes} +The \OpenGL\ context is guaranteed to be preserved after calling +\textbf{glfwSetWindowSize}, even if the video mode is changed. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSetWindowPos} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetWindowPos( int x, int y ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{x}]\ \\ + Horizontal position of the window, relative to the upper left corner + of the desktop. +\item [\textit{y}]\ \\ + Vertical position of the window, relative to the upper left corner of + the desktop. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function changes the position of an opened window. It does not have +any effect on a fullscreen window. +\end{refdescription} + + +%------------------------------------------------------------------------- +\subsection{glfwGetWindowSize} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwGetWindowSize( int *width, int *height ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{width}]\ \\ + Pointer to an integer that will hold the width of the window. +\item [\textit{height}]\ \\ + Pointer to an integer that will hold the height of the window. +\end{description} +\end{refparameters} + +\begin{refreturn} +The current width and height of the opened window is returned in the +\textit{width} and \textit{height} parameters, respectively. +\end{refreturn} + +\begin{refdescription} +The function is used for determining the size of an opened window. +The returned values are dimensions of the client area of the window +(i.e. excluding any window borders and decorations). +\end{refdescription} + +\begin{refnotes} +Even if the size of a fullscreen window does not change once the window +has been opened, it does not necessarily have to be the same as the size +that was requested using \textbf{glfwOpenWindow}. Therefor it is wise to +use this function to determine the true size of the window once it has +been opened. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSetWindowSizeCallback} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetWindowSizeCallback( GLFWwindowsizefun cbfun ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{cbfun}]\ \\ + Pointer to a callback function that will be called every time the + window size changes. The function should have the following C language + prototype: + + \texttt{void GLFWCALL functionname( int width, int height );} + + Where \textit{functionname} is the name of the callback function, and + \textit{width} and \textit{height} are the dimensions of the window + client area. + + If \textit{cbfun} is NULL, any previously selected callback function + will be deselected. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function selects which function to be called upon a window size +change event. + +A window has to be opened for this function to have any effect. +\end{refdescription} + +\begin{refnotes} +Window size changes are recorded continuously, but only reported when +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers} is called. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwIconifyWindow} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwIconifyWindow( void ) +\end{lstlisting} + +\begin{refparameters} +none +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +Iconify a window. If the window is in fullscreen mode, then the desktop +video mode will be restored. +\end{refdescription} + + +%------------------------------------------------------------------------- +\subsection{glfwRestoreWindow} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwRestoreWindow( void ) +\end{lstlisting} + +\begin{refparameters} +none +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +Restore an iconified window. If the window that is restored is in +fullscreen mode, then the fullscreen video mode will be restored. +\end{refdescription} + + +%------------------------------------------------------------------------- +\begin{table}[p] +\begin{center} +\begin{tabular}{|l|p{9.5cm}|} \hline \raggedright +\textbf{Name} & \textbf{Description} \\ \hline +GLFW\_OPENED & GL\_TRUE if window is opened, else GL\_FALSE.\\ \hline +GLFW\_ACTIVE & GL\_TRUE if window has focus, else GL\_FALSE.\\ \hline +GLFW\_ICONIFIED & GL\_TRUE if window is iconified, else GL\_FALSE.\\ \hline +GLFW\_ACCELERATED & GL\_TRUE if window is hardware accelerated, else GL\_FALSE.\\ \hline +GLFW\_RED\_BITS & Number of bits for the red color component.\\ \hline +GLFW\_GREEN\_BITS & Number of bits for the green color component.\\ \hline +GLFW\_BLUE\_BITS & Number of bits for the blue color component.\\ \hline +GLFW\_ALPHA\_BITS & Number of bits for the alpha buffer.\\ \hline +GLFW\_DEPTH\_BITS & Number of bits for the depth buffer.\\ \hline +GLFW\_STENCIL\_BITS & Number of bits for the stencil buffer.\\ \hline +GLFW\_REFRESH\_RATE & Vertical monitor refresh rate in Hz. Zero indicates an unknown or a default refresh rate.\\ \hline +GLFW\_ACCUM\_RED\_BITS & Number of bits for the red channel of the accumulator buffer.\\ \hline +GLFW\_ACCUM\_GREEN\_BITS & Number of bits for the green channel of the accumulator buffer.\\ \hline +GLFW\_ACCUM\_BLUE\_BITS & Number of bits for the blue channel of the accumulator buffer.\\ \hline +GLFW\_ACCUM\_ALPHA\_BITS & Number of bits for the alpha channel of the accumulator buffer.\\ \hline +GLFW\_AUX\_BUFFERS & Number of auxiliary buffers.\\ \hline +GLFW\_STEREO & GL\_TRUE if stereo rendering is supported, else GL\_FALSE.\\ \hline +GLFW\_WINDOW\_NO\_RESIZE & GL\_TRUE if the window cannot be resized, else GL\_FALSE.\\ \hline +GLFW\_FSAA\_SAMPLES & Number of multisampling buffer samples. Zero indicated multisampling is disabled.\\ \hline +GLFW\_OPENGL\_VERSION\_MAJOR & Major number of the desired OpenGL version.\\ \hline +GLFW\_OPENGL\_VERSION\_MINOR & Minor number of the desired OpenGL version.\\ \hline +GLFW\_OPENGL\_FORWARD\_COMPAT & GL\_TRUE if the OpenGL context is forward compatible (i.e. disallows legacy functionality), else GL\_FALSE. +This is always GL\_FALSE for OpenGL version 2.1 and below.\\ \hline +\end{tabular} +\end{center} +\caption{Window parameters for \textbf{glfwGetWindowParam}} +\label{tab:winparams} +\end{table} + + +%------------------------------------------------------------------------- +\subsection{glfwGetWindowParam} + +\textbf{C language syntax} +\begin{lstlisting} +int glfwGetWindowParam( int param ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{param}]\ \\ + A token selecting which parameter the function should return (see + table \ref{tab:winparams}). +\end{description} +\end{refparameters} + +\begin{refreturn} +The function returns different parameters depending on the value of +\textit{param}. Table \ref{tab:winparams} lists valid \textit{param} +values, and their corresponding return values. +\end{refreturn} + +\begin{refdescription} +The function is used for acquiring various properties of an opened window. +\end{refdescription} + +\begin{refnotes} +GLFW\_ACCELERATED is only supported under Windows. Other systems will +always return GL\_TRUE. Under Windows, GLFW\_ACCELERATED means that the +\OpenGL\ renderer is a 3rd party renderer, rather than the fallback +Microsoft software \OpenGL\ renderer. In other words, it is not a real +guarantee that the \OpenGL\ renderer is actually hardware accelerated. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSwapBuffers} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSwapBuffers( void ) +\end{lstlisting} + +\begin{refparameters} +none +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function swaps the back and front color buffers of the window. If +GLFW\_AUTO\_POLL\_EVENTS is enabled (which is the default), +\textbf{glfwPollEvents} is called before swapping the front and back +buffers. +\end{refdescription} + + +%------------------------------------------------------------------------- +\subsection{glfwSwapInterval} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSwapInterval( int interval ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{interval}]\ \\ + Minimum number of monitor vertical retraces between each buffer swap + performed by \textbf{glfwSwapBuffers}. If \textit{interval} is zero, + buffer swaps will not be synchronized to the vertical refresh of the + monitor (also known as 'VSync off'). +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function selects the minimum number of monitor vertical retraces that +should occur between two buffer swaps. If the selected swap interval is +one, the rate of buffer swaps will never be higher than the vertical +refresh rate of the monitor. If the selected swap interval is zero, the +rate of buffer swaps is only limited by the speed of the software and +the hardware. +\end{refdescription} + +\begin{refnotes} +This function will only have an effect on hardware and drivers that +support user selection of the swap interval. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSetWindowRefreshCallback} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetWindowRefreshCallback( GLFWwindowrefreshfun cbfun ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{cbfun}]\ \\ + Pointer to a callback function that will be called when the window client + area needs to be refreshed. The function should have the following C + language prototype: + + \texttt{void GLFWCALL functionname( void );} + + Where \textit{functionname} is the name of the callback function. + + If \textit{cbfun} is NULL, any previously selected callback function + will be deselected. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function selects which function to be called upon a window refresh +event, which occurs when any part of the window client area has been +damaged, and needs to be repainted (for instance, if a part of the window +that was previously occluded by another window has become visible). + +A window has to be opened for this function to have any effect. +\end{refdescription} + +\begin{refnotes} +Window refresh events are recorded continuously, but only reported when +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers} is called. +\end{refnotes} + + +%------------------------------------------------------------------------- +\pagebreak +\section{Video Modes} +Since \GLFW\ supports video mode changes when using a fullscreen window, +it also provides functionality for querying which video modes are +supported on a system. + + +%------------------------------------------------------------------------- +\subsection{glfwGetVideoModes} + +\textbf{C language syntax} +\begin{lstlisting} +int glfwGetVideoModes( GLFWvidmode *list, int maxcount ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{list}]\ \\ + A vector of \textit{GLFWvidmode} structures, which will be filled out + by the function. +\item [\textit{maxcount}]\ \\ + Maximum number of video modes that \textit{list} vector can hold. +\end{description} +\end{refparameters} + +\begin{refreturn} +The function returns the number of detected video modes (this number +will never exceed \textit{maxcount}). The \textit{list} vector is +filled out with the video modes that are supported by the system. +\end{refreturn} + +\begin{refdescription} +The function returns a list of supported video modes. Each video mode is +represented by a \textit{GLFWvidmode} structure, which has the following +definition: + +\begin{lstlisting} +typedef struct { + int Width, Height; // Video resolution + int RedBits; // Number of red bits + int GreenBits; // Number of green bits + int BlueBits; // Number of blue bits +} GLFWvidmode; +\end{lstlisting} +\end{refdescription} + +\begin{refnotes} +The returned list is sorted, first by color depth ($RedBits + GreenBits + +BlueBits$), and then by resolution ($Width \times Height$), with the +lowest resolution, fewest bits per pixel mode first. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwGetDesktopMode} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwGetDesktopMode( GLFWvidmode *mode ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{mode}]\ \\ + Pointer to a \textit{GLFWvidmode} structure, which will be filled out + by the function. +\end{description} +\end{refparameters} + +\begin{refreturn} +The \textit{GLFWvidmode} structure pointed to by \textit{mode} is filled +out with the desktop video mode. +\end{refreturn} + +\begin{refdescription} +The function returns the desktop video mode in a \textit{GLFWvidmode} +structure. See \textbf{glfwGetVideoModes} for a definition of the +\textit{GLFWvidmode} structure. +\end{refdescription} + +\begin{refnotes} +The color depth of the desktop display is always reported as the number +of bits for each individual color component (red, green and blue), even +if the desktop is not using an RGB or RGBA color format. For instance, an +indexed 256 color display may report \textit{RedBits} = 3, +\textit{GreenBits} = 3 and \textit{BlueBits} = 2, which adds up to 8 bits +in total. + +The desktop video mode is the video mode used by the desktop, \textit{not} +the current video mode (which may differ from the desktop video mode if +the \GLFW\ window is a fullscreen window). +\end{refnotes} + + +%------------------------------------------------------------------------- +\pagebreak +\section{Input Handling} +\GLFW\ supports three channels of user input: keyboard input, mouse input +and joystick input. + +Keyboard and mouse input can be treated either as events, using callback +functions, or as state, using functions for polling specific keyboard and +mouse states. Regardless of which method is used, all keyboard and mouse +input is collected using window event polling. + +Joystick input is asynchronous to the keyboard and mouse input, and does +not require event polling for keeping up to date joystick information. +Also, joystick input is independent of any window, so a window does not +have to be opened for joystick input to be used. + + +%------------------------------------------------------------------------- +\subsection{glfwPollEvents} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwPollEvents( void ) +\end{lstlisting} + +\begin{refparameters} +none +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function is used for polling for events, such as user input and +window resize events. Upon calling this function, all window states, +keyboard states and mouse states are updated. If any related callback +functions are registered, these are called during the call to +\textbf{glfwPollEvents}. +\end{refdescription} + +\begin{refnotes} +\textbf{glfwPollEvents} is called implicitly from \textbf{glfwSwapBuffers} +if GLFW\_AUTO\_POLL\_EVENTS is enabled (default). Thus, if +\textbf{glfwSwapBuffers} is called frequently, which is normally the case, +there is no need to call \textbf{glfwPollEvents}. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwWaitEvents} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwWaitEvents( void ) +\end{lstlisting} + +\begin{refparameters} +none +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function is used for waiting for events, such as user input and +window resize events. Upon calling this function, the calling thread will +be put to sleep until any event appears in the event queue. When events +are ready, the events will be processed just as they are processed by +\textbf{glfwPollEvents}. + +If there are any events in the queue when the function is called, the +function will behave exactly like \textbf{glfwPollEvents} (i.e. process +all messages and then return, without blocking the calling thread). +\end{refdescription} + +\begin{refnotes} +It is guaranteed that \textbf{glfwWaitEvents} will wake up on any event +that can be processed by \textbf{glfwPollEvents}. However, +\textbf{glfwWaitEvents} may wake up on events that are \emph{not} +processed or reported by \textbf{glfwPollEvents} too, and the function +may behave differently on different systems. Do not make any assumptions +about when or why \textbf{glfwWaitEvents} will return. +\end{refnotes} + + +%------------------------------------------------------------------------- +\begin{table}[p] +\begin{center} +\begin{tabular}{|l|l|} \hline \raggedright +\textbf{Name} & \textbf{Description} \\ \hline +GLFW\_KEY\_SPACE & Space\\ \hline +GLFW\_KEY\_ESC & Escape\\ \hline +GLFW\_KEY\_F\textit{n} & Function key \textit{n} (\textit{n} can be in the range 1..25)\\ \hline +GLFW\_KEY\_UP & Cursor up\\ \hline +GLFW\_KEY\_DOWN & Cursor down\\ \hline +GLFW\_KEY\_LEFT & Cursor left\\ \hline +GLFW\_KEY\_RIGHT & Cursor right\\ \hline +GLFW\_KEY\_LSHIFT & Left shift key\\ \hline +GLFW\_KEY\_RSHIFT & Right shift key\\ \hline +GLFW\_KEY\_LCTRL & Left control key\\ \hline +GLFW\_KEY\_RCTRL & Right control key\\ \hline +GLFW\_KEY\_LALT & Left alternate function key\\ \hline +GLFW\_KEY\_RALT & Right alternate function key\\ \hline +GLFW\_KEY\_TAB & Tabulator\\ \hline +GLFW\_KEY\_ENTER & Enter\\ \hline +GLFW\_KEY\_BACKSPACE & Backspace\\ \hline +GLFW\_KEY\_INSERT & Insert\\ \hline +GLFW\_KEY\_DEL & Delete\\ \hline +GLFW\_KEY\_PAGEUP & Page up\\ \hline +GLFW\_KEY\_PAGEDOWN & Page down\\ \hline +GLFW\_KEY\_HOME & Home\\ \hline +GLFW\_KEY\_END & End\\ \hline +GLFW\_KEY\_KP\_\textit{n} & Keypad numeric key \textit{n} (\textit{n} can be in the range 0..9)\\ \hline +GLFW\_KEY\_KP\_DIVIDE & Keypad divide ($\div$)\\ \hline +GLFW\_KEY\_KP\_MULTIPLY & Keypad multiply ($\times$)\\ \hline +GLFW\_KEY\_KP\_SUBTRACT & Keypad subtract ($-$)\\ \hline +GLFW\_KEY\_KP\_ADD & Keypad add ($+$)\\ \hline +GLFW\_KEY\_KP\_DECIMAL & Keypad decimal (. or ,)\\ \hline +GLFW\_KEY\_KP\_EQUAL & Keypad equal (=)\\ \hline +GLFW\_KEY\_KP\_ENTER & Keypad enter\\ \hline +\end{tabular} +\end{center} +\caption{Special key identifiers} +\label{tab:keys} +\end{table} + + +%------------------------------------------------------------------------- +\begin{table}[p] +\begin{center} +\begin{tabular}{|l|l|} \hline \raggedright +\textbf{Name} & \textbf{Description} \\ \hline +GLFW\_MOUSE\_BUTTON\_LEFT & Left mouse button (button 1) \\ \hline +GLFW\_MOUSE\_BUTTON\_RIGHT & Right mouse button (button 2) \\ \hline +GLFW\_MOUSE\_BUTTON\_MIDDLE & Middle mouse button (button 3) \\ \hline +GLFW\_MOUSE\_BUTTON\_\textit{n} & Mouse button \textit{n} (\textit{n} can be in the range 1..8)\\ \hline +\end{tabular} +\end{center} +\caption{Valid mouse button identifiers} +\label{tab:mousebuttons} +\end{table} + + +%------------------------------------------------------------------------- +\subsection{glfwGetKey} + +\textbf{C language syntax} +\begin{lstlisting} +int glfwGetKey( int key ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{key}]\ \\ + A keyboard key identifier, which can be either an uppercase printable + ISO~8859-1 (Latin~1) character (e.g. 'A', '3' or '.'), or a special key + identifier. Table \ref{tab:keys} lists valid special key identifiers. +\end{description} +\end{refparameters} + +\begin{refreturn} +The function returns GLFW\_PRESS if the key is held down, or GLFW\_RELEASE +if the key is not held down. +\end{refreturn} + +\begin{refdescription} +The function queries the current state of a specific keyboard key. The +physical location of each key depends on the system keyboard layout +setting. +\end{refdescription} + +\begin{refnotes} +The constant GLFW\_KEY\_SPACE is equal to 32, which is the ISO~8859-1 code +for space. + +Not all key codes are supported on all systems. Also, while some keys are +available on some keyboard layouts, they may not be available on other +keyboard layouts. + +For systems that do not distinguish between left and right versions of +modifier keys (shift, alt and control), the left version is used (e.g. +GLFW\_KEY\_LSHIFT). + +A window must be opened for the function to have any effect, and +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers} must be called before any keyboard events are +recorded and reported by \textbf{glfwGetKey}. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwGetMouseButton} + +\textbf{C language syntax} +\begin{lstlisting} +int glfwGetMouseButton( int button ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{button}]\ \\ + A mouse button identifier, which can be one of the mouse button + identifiers listed in table \ref{tab:mousebuttons}. +\end{description} +\end{refparameters} + +\begin{refreturn} +The function returns GLFW\_PRESS if the mouse button is held down, or +GLFW\_RELEASE if the mouse button is not held down. +\end{refreturn} + +\begin{refdescription} +The function queries the current state of a specific mouse button. +\end{refdescription} + +\begin{refnotes} +A window must be opened for the function to have any effect, and +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers} must be called before any mouse button events +are recorded and reported by \textbf{glfwGetMouseButton}. + +GLFW\_MOUSE\_BUTTON\_LEFT is equal to GLFW\_MOUSE\_BUTTON\_1. +GLFW\_MOUSE\_BUTTON\_RIGHT is equal to GLFW\_MOUSE\_BUTTON\_2. +GLFW\_MOUSE\_BUTTON\_MIDDLE is equal to GLFW\_MOUSE\_BUTTON\_3. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwGetMousePos} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwGetMousePos( int *xpos, int *ypos ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{xpos}]\ \\ + Pointer to an integer that will be filled out with the horizontal + position of the mouse. +\item [\textit{ypos}]\ \\ + Pointer to an integer that will be filled out with the vertical + position of the mouse. +\end{description} +\end{refparameters} + +\begin{refreturn} +The function returns the current mouse position in \textit{xpos} and +\textit{ypos}. +\end{refreturn} + +\begin{refdescription} +The function returns the current mouse position. If the cursor is not +hidden, the mouse position is the cursor position, relative to the upper +left corner of the window and limited to the client area of the window. +If the cursor is hidden, the mouse position is a virtual absolute +position, not limited to any boundaries except to those implied by the +maximum number that can be represented by a signed integer (normally +-2147483648 to +2147483647). +\end{refdescription} + +\begin{refnotes} +A window must be opened for the function to have any effect, and +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers} must be called before any mouse movements are +recorded and reported by \textbf{glfwGetMousePos}. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSetMousePos} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetMousePos( int xpos, int ypos ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{xpos}]\ \\ + Horizontal position of the mouse. +\item [\textit{ypos}]\ \\ + Vertical position of the mouse. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function changes the position of the mouse. If the cursor is +visible (not disabled), the cursor will be moved to the specified +position, relative to the upper left corner of the window client area. +If the cursor is hidden (disabled), only the mouse position that is +reported by \GLFW\ is changed. +\end{refdescription} + + +%------------------------------------------------------------------------- +\subsection{glfwGetMouseWheel} + +\textbf{C language syntax} +\begin{lstlisting} +int glfwGetMouseWheel( void ) +\end{lstlisting} + +\begin{refparameters} +none +\end{refparameters} + +\begin{refreturn} +The function returns the current mouse wheel position. +\end{refreturn} + +\begin{refdescription} +The function returns the current mouse wheel position. The mouse wheel can +be thought of as a third mouse axis, which is available as a separate +wheel or up/down stick on some mice. +\end{refdescription} + +\begin{refnotes} +A window must be opened for the function to have any effect, and +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers} must be called before any mouse wheel movements +are recorded and reported by \textbf{glfwGetMouseWheel}. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSetMouseWheel} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetMouseWheel( int pos ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{pos}]\ \\ + Position of the mouse wheel. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function changes the position of the mouse wheel. +\end{refdescription} + + +%------------------------------------------------------------------------- +\subsection{glfwSetKeyCallback} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetKeyCallback( GLFWkeyfun cbfun ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{cbfun}]\ \\ + Pointer to a callback function that will be called every time a key is + pressed or released. The function should have the following C language + prototype: + + \texttt{void GLFWCALL functionname( int key, int action );} + + Where \textit{functionname} is the name of the callback function, + \textit{key} is a key identifier, which is an uppercase printable + ISO~8859-1 character or a special key identifier (see table + \ref{tab:keys}), and \textit{action} is either GLFW\_PRESS or + GLFW\_RELEASE. + + If \textit{cbfun} is NULL, any previously selected callback function + will be deselected. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function selects which function to be called upon a keyboard key +event. The callback function is called every time the state of a single +key is changed (from released to pressed or vice versa). The reported keys +are unaffected by any modifiers (such as shift or alt). + +A window has to be opened for this function to have any effect. +\end{refdescription} + +\begin{refnotes} +Keyboard events are recorded continuously, but only reported when +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers} is called. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSetCharCallback} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetCharCallback( GLFWcharfun cbfun ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{cbfun}]\ \\ + Pointer to a callback function that will be called every time a + printable character is generated by the keyboard. The function should + have the following C language prototype: + + \texttt{void GLFWCALL functionname( int character, int action );} + + Where \textit{functionname} is the name of the callback function, + \textit{character} is a Unicode (ISO~10646) character, and + \textit{action} is either GLFW\_PRESS or GLFW\_RELEASE. + + If \textit{cbfun} is NULL, any previously selected callback function + will be deselected. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function selects which function to be called upon a keyboard character +event. The callback function is called every time a key that results in a +printable Unicode character is pressed or released. Characters are +affected by modifiers (such as shift or alt). + +A window has to be opened for this function to have any effect. +\end{refdescription} + +\begin{refnotes} +Character events are recorded continuously, but only reported when +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers} is called. + +Control characters, such as tab and carriage return, are not reported to +the character callback function, since they are not part of the Unicode +character set. Use the key callback function for such events (see +\textbf{glfwSetKeyCallback}). + +The Unicode character set supports character codes above 255, so never +cast a Unicode character to an eight bit data type (e.g. the C language +'char' type) without first checking that the character code is less than +256. Also note that Unicode character codes 0 to 255 are equal to +ISO~8859-1 (Latin~1). +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSetMouseButtonCallback} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetMouseButtonCallback( GLFWmousebuttonfun cbfun ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{cbfun}]\ \\ + Pointer to a callback function that will be called every time a mouse + button is pressed or released. The function should have the following C + language prototype: + + \texttt{void GLFWCALL functionname( int button, int action );} + + Where \textit{functionname} is the name of the callback function, + \textit{button} is a mouse button identifier (see table + \ref{tab:mousebuttons} on page \pageref{tab:mousebuttons}), and + \textit{action} is either GLFW\_PRESS or GLFW\_RELEASE. + + If \textit{cbfun} is NULL, any previously selected callback function + will be deselected. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function selects which function to be called upon a mouse button +event. + +A window has to be opened for this function to have any effect. +\end{refdescription} + +\begin{refnotes} +Mouse button events are recorded continuously, but only reported when +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers} is called. + +GLFW\_MOUSE\_BUTTON\_LEFT is equal to GLFW\_MOUSE\_BUTTON\_1. +GLFW\_MOUSE\_BUTTON\_RIGHT is equal to GLFW\_MOUSE\_BUTTON\_2. +GLFW\_MOUSE\_BUTTON\_MIDDLE is equal to GLFW\_MOUSE\_BUTTON\_3. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSetMousePosCallback} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetMousePosCallback( GLFWmouseposfun cbfun ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{cbfun}]\ \\ + Pointer to a callback function that will be called every time the mouse + is moved. The function should have the following C language prototype: + + \texttt{void GLFWCALL functionname( int x, int y );} + + Where \textit{functionname} is the name of the callback function, and + \textit{x} and \textit{y} are the mouse coordinates (see + \textbf{glfwGetMousePos} for more information on mouse coordinates). + + If \textit{cbfun} is NULL, any previously selected callback function + will be deselected. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function selects which function to be called upon a mouse motion event. + +A window has to be opened for this function to have any effect. +\end{refdescription} + +\begin{refnotes} +Mouse motion events are recorded continuously, but only reported when +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers} is called. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSetMouseWheelCallback} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetMouseWheelCallback( GLFWmousewheelfun cbfun ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{cbfun}]\ \\ + Pointer to a callback function that will be called every time the mouse + wheel is moved. The function should have the following C language + prototype: + + \texttt{void GLFWCALL functionname( int pos );} + + Where \textit{functionname} is the name of the callback function, and + \textit{pos} is the mouse wheel position. + + If \textit{cbfun} is NULL, any previously selected callback function + will be deselected. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function selects which function to be called upon a mouse wheel event. + +A window has to be opened for this function to have any effect. +\end{refdescription} + +\begin{refnotes} +Mouse wheel events are recorded continuously, but only reported when +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers} is called. +\end{refnotes} + + +%------------------------------------------------------------------------- +\begin{table}[p] +\begin{center} +\begin{tabular}{|l|l|}\hline \raggedright +\textbf{Name} & \textbf{Return value}\\ \hline +GLFW\_PRESENT & GL\_TRUE if the joystick is connected, else GL\_FALSE.\\ \hline +GLFW\_AXES & Number of axes supported by the joystick.\\ \hline +GLFW\_BUTTONS & Number of buttons supported by the joystick.\\ \hline +\end{tabular} +\end{center} +\caption{Joystick parameters for \textbf{glfwGetJoystickParam}} +\label{tab:joyparams} +\end{table} + + +%------------------------------------------------------------------------- +\subsection{glfwGetJoystickParam} + +\textbf{C language syntax} +\begin{lstlisting} +int glfwGetJoystickParam( int joy, int param ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{joy}]\ \\ + A joystick identifier, which should be GLFW\_JOYSTICK\_\textit{n}, where + \textit{n} is in the range 1 to 16. +\item [\textit{param}]\ \\ + A token selecting which parameter the function should return (see table + \ref{tab:joyparams}). +\end{description} +\end{refparameters} + +\begin{refreturn} +The function returns different parameters depending on the value of +\textit{param}. Table \ref{tab:joyparams} lists valid \textit{param} +values, and their corresponding return values. +\end{refreturn} + +\begin{refdescription} +The function is used for acquiring various properties of a joystick. +\end{refdescription} + +\begin{refnotes} +The joystick information is updated every time the function is called. + +No window has to be opened for joystick information to be valid. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwGetJoystickPos} + +\textbf{C language syntax} +\begin{lstlisting} +int glfwGetJoystickPos( int joy, float *pos, int numaxes ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{joy}]\ \\ + A joystick identifier, which should be GLFW\_JOYSTICK\_\textit{n}, where + \textit{n} is in the range 1 to 16. +\item [\textit{pos}]\ \\ + An array that will hold the positional values for all requested axes. +\item [\textit{numaxes}]\ \\ + Specifies how many axes should be returned. +\end{description} +\end{refparameters} + +\begin{refreturn} +The function returns the number of actually returned axes. This is the +minimum of \textit{numaxes} and the number of axes supported by the +joystick. If the joystick is not supported or connected, the function will +return 0 (zero). +\end{refreturn} + +\begin{refdescription} +The function queries the current position of one or more axes of a +joystick. The positional values are returned in an array, where the first +element represents the first axis of the joystick (normally the X axis). +Each position is in the range -1.0 to 1.0. Where applicable, the positive +direction of an axis is right, forward or up, and the negative direction +is left, back or down. + +If \textit{numaxes} exceeds the number of axes supported by the joystick, +or if the joystick is not available, the unused elements in the +\textit{pos} array will be set to 0.0 (zero). +\end{refdescription} + +\begin{refnotes} +The joystick state is updated every time the function is called, so there +is no need to call \textbf{glfwPollEvents} or \textbf{glfwWaitEvents} for +joystick state to be updated. + +Use \textbf{glfwGetJoystickParam} to retrieve joystick capabilities, such +as joystick availability and number of supported axes. + +No window has to be opened for joystick input to be valid. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwGetJoystickButtons} + +\textbf{C language syntax} +\begin{lstlisting} +int glfwGetJoystickButtons( int joy, unsigned char *buttons, + int numbuttons ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{joy}]\ \\ + A joystick identifier, which should be GLFW\_JOYSTICK\_\textit{n}, where + \textit{n} is in the range 1 to 16. +\item [\textit{buttons}]\ \\ + An array that will hold the button states for all requested buttons. +\item [\textit{numbuttons}]\ \\ + Specifies how many buttons should be returned. +\end{description} +\end{refparameters} + +\begin{refreturn} +The function returns the number of actually returned buttons. This is the +minimum of \textit{numbuttons} and the number of buttons supported by the +joystick. If the joystick is not supported or connected, the function will +return 0 (zero). +\end{refreturn} + +\begin{refdescription} +The function queries the current state of one or more buttons of a +joystick. The button states are returned in an array, where the first +element represents the first button of the joystick. Each state can be +either GLFW\_PRESS or GLFW\_RELEASE. + +If \textit{numbuttons} exceeds the number of buttons supported by the +joystick, or if the joystick is not available, the unused elements in the +\textit{buttons} array will be set to GLFW\_RELEASE. +\end{refdescription} + +\begin{refnotes} +The joystick state is updated every time the function is called, so there +is no need to call \textbf{glfwPollEvents} or \textbf{glfwWaitEvents} for +joystick state to be updated. + +Use \textbf{glfwGetJoystickParam} to retrieve joystick capabilities, such +as joystick availability and number of supported buttons. + +No window has to be opened for joystick input to be valid. +\end{refnotes} + + +%------------------------------------------------------------------------- +\pagebreak +\section{Timing} + +%------------------------------------------------------------------------- +\subsection{glfwGetTime} + +\textbf{C language syntax} +\begin{lstlisting} +double glfwGetTime( void ) +\end{lstlisting} + +\begin{refparameters} +none +\end{refparameters} + +\begin{refreturn} +The function returns the value of the high precision timer. The time is +measured in seconds, and is returned as a double precision floating point +value. +\end{refreturn} + +\begin{refdescription} +The function returns the state of a high precision timer. Unless the timer +has been set by the \textbf{glfwSetTime} function, the time is measured as +the number of seconds that have passed since \textbf{glfwInit} was called. +\end{refdescription} + +\begin{refnotes} +The resolution of the timer depends on which system the program is running +on. The worst case resolution is somewhere in the order of $10~ms$, while +for most systems the resolution should be better than $1~\mu s$. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwSetTime} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwSetTime( double time ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{time}]\ \\ + Time (in seconds) that the timer should be set to. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +The function sets the current time of the high precision timer to the +specified time. Subsequent calls to \textbf{glfwGetTime} will be relative +to this time. The time is given in seconds. +\end{refdescription} + + +%------------------------------------------------------------------------- +\pagebreak +\section{OpenGL Extension Support} +One of the great features of \OpenGL\ is its support for extensions, which +allow independent vendors to supply non-standard functionality in their +\OpenGL\ implementations. Using extensions is different under different +systems, which is why \GLFW\ has provided an operating system independent +interface to querying and using \OpenGL\ extensions. + + +%------------------------------------------------------------------------- +\subsection{glfwExtensionSupported} + +\textbf{C language syntax} +\begin{lstlisting} +int glfwExtensionSupported( const char *extension ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{extension}]\ \\ + A null terminated ISO~8859-1 string containing the name of an \OpenGL\ + extension. +\end{description} +\end{refparameters} + +\begin{refreturn} +The function returns GL\_TRUE if the extension is supported. Otherwise it +returns GL\_FALSE. +\end{refreturn} + +\begin{refdescription} +The function does a string search in the list of supported \OpenGL\ +extensions to find if the specified extension is listed. +\end{refdescription} + +\begin{refnotes} +An \OpenGL\ context must be created before this function can be called +(i.e. an \OpenGL\ window must have been opened with +\textbf{glfwOpenWindow}). + +In addition to checking for \OpenGL\ extensions, \GLFW\ also checks for +extensions in the operating system ``glue API'', such as WGL extensions +under Windows and glX extensions under the X Window System. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwGetProcAddress} + +\textbf{C language syntax} +\begin{lstlisting} +void * glfwGetProcAddress( const char *procname ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{procname}]\ \\ + A null terminated ISO~8859-1 string containing the name of an \OpenGL\ + extension function. +\end{description} +\end{refparameters} + +\begin{refreturn} +The function returns the pointer to the specified \OpenGL\ function if it +is supported, otherwise NULL is returned. +\end{refreturn} + +\begin{refdescription} +The function acquires the pointer to an \OpenGL\ extension function. Some +(but not all) \OpenGL\ extensions define new API functions, which are +usually not available through normal linking. It is therefore necessary to +get access to those API functions at runtime. +\end{refdescription} + +\begin{refnotes} +An \OpenGL\ context must be created before this function can be called +(i.e. an \OpenGL\ window must have been opened with +\textbf{glfwOpenWindow}). + +Some systems do not support dynamic function pointer retrieval, in which +case \textbf{glfwGetProcAddress} will always return NULL. +\end{refnotes} + + +%------------------------------------------------------------------------- +\subsection{glfwGetGLVersion} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwGetGLVersion( int *major, int *minor, int *rev ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{major}]\ \\ + Pointer to an integer that will hold the major version number. +\item [\textit{minor}]\ \\ + Pointer to an integer that will hold the minor version number. +\item [\textit{rev}]\ \\ + Pointer to an integer that will hold the revision. +\end{description} +\end{refparameters} + +\begin{refreturn} +The function returns the major and minor version numbers and the revision +for the currently used \OpenGL\ implementation. +\end{refreturn} + +\begin{refdescription} +The function returns the \OpenGL\ implementation version. This is a +convenient function that parses the version number information from the +string returned by calling \texttt{glGetString(~GL\_VERSION~)}. The +\OpenGL\ version information can be used to determine what functionality +is supported by the used \OpenGL\ implementation. +\end{refdescription} + +\begin{refnotes} +An \OpenGL\ context must be created before this function can be called +(i.e. an \OpenGL\ window must have been opened with +\textbf{glfwOpenWindow}). +\end{refnotes} + + +%------------------------------------------------------------------------- +\pagebreak +\section{Miscellaneous} + + +%------------------------------------------------------------------------- +\subsection{glfwEnable/glfwDisable} + +\textbf{C language syntax} +\begin{lstlisting} +void glfwEnable( int token ) +void glfwDisable( int token ) +\end{lstlisting} + +\begin{refparameters} +\begin{description} +\item [\textit{token}]\ \\ + A value specifying a feature to enable or disable. Valid tokens are + listed in table \ref{tab:enable}. +\end{description} +\end{refparameters} + +\begin{refreturn} +none +\end{refreturn} + +\begin{refdescription} +\textbf{glfwEnable} is used to enable a certain feature, while +\textbf{glfwDisable} is used to disable it. Below follows a description of +each feature. +\end{refdescription} + + +\begin{table}[p] +\begin{center} +\begin{tabular}{|l|p{5.0cm}|p{3.0cm}|} \hline \raggedright +\textbf{Name} & \textbf{Controls} & \textbf{Default}\\ \hline +\hyperlink{lnk:autopollevents}{GLFW\_AUTO\_POLL\_EVENTS} & Automatic event polling when \textbf{glfwSwapBuffers} is called & Enabled\\ \hline +\hyperlink{lnk:keyrepeat}{GLFW\_KEY\_REPEAT} & Keyboard key repeat & Disabled\\ \hline +\hyperlink{lnk:mousecursor}{GLFW\_MOUSE\_CURSOR} & Mouse cursor visibility & Enabled in windowed mode. Disabled in fullscreen mode.\\ \hline +\hyperlink{lnk:stickykeys}{GLFW\_STICKY\_KEYS} & Keyboard key ``stickiness'' & Disabled\\ \hline +\hyperlink{lnk:stickymousebuttons}{GLFW\_STICKY\_MOUSE\_BUTTONS} & Mouse button ``stickiness'' & Disabled\\ \hline +\hyperlink{lnk:systemkeys}{GLFW\_SYSTEM\_KEYS} & Special system key actions & Enabled\\ \hline +\end{tabular} +\end{center} +\caption{Tokens for \textbf{glfwEnable}/\textbf{glfwDisable}} +\label{tab:enable} +\end{table} + + +\bigskip\begin{mysamepage}\hypertarget{lnk:autopollevents}{} +\textbf{GLFW\_AUTO\_POLL\_EVENTS}\\ +When GLFW\_AUTO\_POLL\_EVENTS is enabled, \textbf{glfwPollEvents} is +automatically called each time that \textbf{glfwSwapBuffers} is called. + +When GLFW\_AUTO\_POLL\_EVENTS is disabled, calling +\textbf{glfwSwapBuffers} will not result in a call to +\textbf{glfwPollEvents}. This can be useful if \textbf{glfwSwapBuffers} +needs to be called from within a callback function, since calling +\textbf{glfwPollEvents} from a callback function is not allowed. +\end{mysamepage} + + +\bigskip\begin{mysamepage}\hypertarget{lnk:keyrepeat}{} +\textbf{GLFW\_KEY\_REPEAT}\\ +When GLFW\_KEY\_REPEAT is enabled, the key and character callback +functions are called repeatedly when a key is held down long enough +(according to the system key repeat configuration). + +When GLFW\_KEY\_REPEAT is disabled, the key and character callback +functions are only called once when a key is pressed (and once when it is +released). +\end{mysamepage} + + +\bigskip\begin{mysamepage}\hypertarget{lnk:mousecursor}{} +\textbf{GLFW\_MOUSE\_CURSOR}\\ +When GLFW\_MOUSE\_CURSOR is enabled, the mouse cursor is visible, and +mouse coordinates are relative to the upper left corner of the client area +of the \GLFW\ window. The coordinates are limited to the client area of +the window. + +When GLFW\_MOUSE\_CURSOR is disabled, the mouse cursor is invisible, and +mouse coordinates are not limited to the drawing area of the window. It is +as if the mouse coordinates are recieved directly from the mouse, without +being restricted or manipulated by the windowing system. +\end{mysamepage} + + +\bigskip\begin{mysamepage}\hypertarget{lnk:stickykeys}{} +\textbf{GLFW\_STICKY\_KEYS}\\ +When GLFW\_STICKY\_KEYS is enabled, keys which are pressed will not be +released until they are physically released and checked with +\textbf{glfwGetKey}. This behavior makes it possible to catch keys that +were pressed and then released again between two calls to +\textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers}, which would otherwise have been reported as +released. Care should be taken when using this mode, since keys that are +not checked with \textbf{glfwGetKey} will never be released. Note also +that enabling GLFW\_STICKY\_KEYS does not affect the behavior of the +keyboard callback functionality. + +When GLFW\_STICKY\_KEYS is disabled, the status of a key that is reported +by \textbf{glfwGetKey} is always the physical state of the key. Disabling +GLFW\_STICKY\_KEYS also clears the sticky information for all keys. +\end{mysamepage} + + +\bigskip\begin{mysamepage}\hypertarget{lnk:stickymousebuttons}{} +\textbf{GLFW\_STICKY\_MOUSE\_BUTTONS}\\ +When GLFW\_STICKY\_MOUSE\_BUTTONS is enabled, mouse buttons that are +pressed will not be released until they are physically released and +checked with \textbf{glfwGetMouseButton}. This behavior makes it +possible to catch mouse buttons which were pressed and then released again +between two calls to \textbf{glfwPollEvents}, \textbf{glfwWaitEvents} or +\textbf{glfwSwapBuffers}, which would otherwise have been reported as +released. Care should be taken when using this mode, since mouse buttons +that are not checked with \textbf{glfwGetMouseButton} will never be +released. Note also that enabling GLFW\_STICKY\_MOUSE\_BUTTONS does not +affect the behavior of the mouse button callback functionality. + +When GLFW\_STICKY\_MOUSE\_BUTTONS is disabled, the status of a mouse +button that is reported by \textbf{glfwGetMouseButton} is always the +physical state of the mouse button. Disabling GLFW\_STICKY\_MOUSE\_BUTTONS +also clears the sticky information for all mouse buttons. +\end{mysamepage} + + +\bigskip\begin{mysamepage}\hypertarget{lnk:systemkeys}{} +\textbf{GLFW\_SYSTEM\_KEYS}\\ +When GLFW\_SYSTEM\_KEYS is enabled, pressing standard system key +combinations, such as \texttt{ALT+TAB} under Windows, will give the normal +behavior. Note that when \texttt{ALT+TAB} is issued under Windows in this +mode so that the \GLFW\ application is deselected when \GLFW\ is operating +in fullscreen mode, the \GLFW\ application window will be minimized and +the video mode will be set to the original desktop mode. When the \GLFW\ +application is re-selected, the video mode will be set to the \GLFW\ video +mode again. + +When GLFW\_SYSTEM\_KEYS is disabled, pressing standard system key +combinations will have no effect, since those key combinations are blocked +by \GLFW . This mode can be useful in situations when the \GLFW\ program +must not be interrupted (normally for games in fullscreen mode). +\end{mysamepage} + + +\end{document} diff --git a/docs/glfwug.tex b/docs/glfwug.tex new file mode 100644 index 00000000..32a0359b --- /dev/null +++ b/docs/glfwug.tex @@ -0,0 +1,1287 @@ +%------------------------------------------------------------------------- +% GLFW Users Guide +% API Version: 2.7 +%------------------------------------------------------------------------- + +% Document class +\documentclass[a4paper,11pt,oneside]{report} + +% Document title and API version +\newcommand{\glfwdoctype}[1][0]{Users Guide} +\newcommand{\glfwapiver}[1][0]{2.7} + +% Common document settings and macros +\input{glfwdoc.sty} + +% PDF specific document settings +\hypersetup{pdftitle={GLFW Users Guide}} +\hypersetup{pdfauthor={Marcus Geelnard}} +\hypersetup{pdfkeywords={GLFW,OpenGL,guide,manual}} + + +%------------------------------------------------------------------------- +% Document body +%------------------------------------------------------------------------- + +\begin{document} + +\pagestyle{plain} + +% Title page +\glfwmaketitle + +% Summary, trademarks and table of contents +\pagenumbering{roman} +\setcounter{page}{1} + +%------------------------------------------------------------------------- +% Summary and Trademarks +%------------------------------------------------------------------------- +\chapter*{Summary} + +This document is a users guide for the \GLFW\ API that gives a practical +introduction to using \GLFW . For a more detailed description of the +\GLFW\ API you should refer to the \textit{GLFW Reference Manual}. +\vspace{10cm} + +\large +Trademarks + +\small +OpenGL and IRIX are registered trademarks of Silicon Graphics, Inc.\linebreak +Microsoft and Windows are registered trademarks of Microsoft Corporation.\linebreak +Mac OS is a registered trademark of Apple Computer, Inc.\linebreak +Linux is a registered trademark of Linus Torvalds.\linebreak +FreeBSD is a registered trademark of Wind River Systems, Inc.\linebreak +Solaris is a trademark of Sun Microsystems, Inc.\linebreak +UNIX is a registered trademark of The Open Group.\linebreak +X Window System is a trademark of The Open Group.\linebreak +POSIX is a trademark of IEEE.\linebreak +Truevision, TARGA and TGA are registered trademarks of Truevision, Inc.\linebreak +IBM is a registered trademark of IBM Corporation.\linebreak + +All other trademarks mentioned in this document are the property of their respective owners. +\normalsize + + +%------------------------------------------------------------------------- +% Table of contents +%------------------------------------------------------------------------- +\tableofcontents +\pagebreak + + +% Document chapters starts here... +\pagenumbering{arabic} +\setcounter{page}{1} + +\pagestyle{fancy} + + +%------------------------------------------------------------------------- +% Introduction +%------------------------------------------------------------------------- +\chapter{Introduction} +\thispagestyle{fancy} +\GLFW\ is a portable API (Application Program Interface) that handles +operating system specific tasks related to \OpenGL\ programming. While +\OpenGL\ in general is portable, easy to use and often results in tidy and +compact code, the operating system specific mechanisms that are required +to set up and manage an \OpenGL\ window are quite the opposite. \GLFW\ tries +to remedy this by providing the following functionality: + +\begin{itemize} +\item Opening and managing an \OpenGL\ window. +\item Keyboard, mouse and joystick input. +\item A high precision timer. +\item Support for querying and using \OpenGL\ extensions. +\end{itemize} +\vspace{18pt} + +All this functionality is implemented as a set of easy-to-use functions, +which makes it possible to write an \OpenGL\ application framework in just a +few lines of code. The \GLFW\ API is completely operating system and +platform independent, which makes it very simple to port \GLFW\ based \OpenGL\ +applications to a variety of platforms. + +Currently supported platforms are: +\begin{itemize} +\item Microsoft Windows\textsuperscript{\textregistered} 95/98/ME/NT/2000/XP/.NET Server. +\item Unix\textsuperscript{\textregistered} or Unix­-like systems running the +X Window System\texttrademark, e.g. Linux\textsuperscript{\textregistered}, +IRIX\textsuperscript{\textregistered}, FreeBSD\textsuperscript{\textregistered}, +Solaris\texttrademark, QNX\textsuperscript{\textregistered} and +Mac OS\textsuperscript{\textregistered} X. +\item Mac OS\textsuperscript{\textregistered} X (Carbon)\footnote{Support for joysticks missing at the time of writing.} +\end{itemize} + + +%------------------------------------------------------------------------- +% Getting Started +%------------------------------------------------------------------------- +\chapter{Getting Started} +\thispagestyle{fancy} +In this chapter you will learn how to write a simple \OpenGL\ application +using \GLFW . We start by initializing \GLFW , then we open a window and +read some user keyboard input. + + +%------------------------------------------------------------------------- +\section{Initializing GLFW} +Before using any of the \GLFW\ functions, it is necessary to call +\textbf{glfwInit}. It initializes internal working variables that are used +by other \GLFW\ functions. The C syntax is: + +\begin{lstlisting} +int glfwInit( void ) +\end{lstlisting} + +\textbf{glfwInit} returns GL\_TRUE if initialization succeeded, or +GL\_FALSE if it failed. + +When your application is done using \GLFW , typically at the very end of +the program, you should call \textbf{glfwTerminate}, which makes a clean +up and places \GLFW\ in a non-initialized state (i.e. it is necessary to +call \textbf{glfwInit} again before using any \GLFW\ functions). The C +syntax is: + +\begin{lstlisting} +void glfwTerminate( void ) +\end{lstlisting} + +Among other things, \textbf{glfwTerminate} closes the \OpenGL\ window +unless it was closed manually. + + +%------------------------------------------------------------------------- +\section{Opening An OpenGL Window} +Opening an \OpenGL\ window is done with the function +\textbf{glfwOpenWindow}. The function takes nine arguments, which are used +to describe the following properties of the window to open: + +\begin{itemize} +\item Window dimensions (width and height) in pixels. +\item Color and alpha buffer depth. +\item Depth buffer (Z-buffer) depth. +\item Stencil buffer depth. +\item Fullscreen or windowed mode. +\end{itemize} + +The C language syntax for \textbf{glfwOpenWindow} is: +\begin{lstlisting} +int glfwOpenWindow( int width, int height, + int redbits, int greenbits, int bluebits, + int alphabits, int depthbits, int stencilbits, + int mode ) +\end{lstlisting} + +\textbf{glfwOpenWindow} returns GL\_TRUE if the window was opened +correctly, or GL\_FALSE if \GLFW\ failed to open the window. + +\GLFW\ tries to open a window that best matches the requested parameters. +Some parameters may be omitted by setting them to zero, which will result +in \GLFW\ either using a default value, or the related functionality to be +disabled. For instance, if \textit{width} and \textit{height} are both +zero, \GLFW\ will use a window resolution of 640x480. If +\textit{depthbits} is zero, the opened window may not have a depth buffer. + +The \textit{mode} argument is used to specify if the window is to be a +s.c. fullscreen window, or a regular window. + +If \textit{mode} is GLFW\_FULLSCREEN, the window will cover the entire +screen and no window borders will be visible. If possible, the video mode +will be changed to the mode that closest matches the \textit{width}, +\textit{height}, \textit{redbits}, \textit{greenbits}, \textit{bluebits} +and \textit{alphabits} arguments. Furthermore, the mouse pointer will be +hidden, and screensavers are prohibited. This is usually the best mode for +games and demos. + +If \textit{mode} is GLFW\_WINDOW, the window will be opened as a normal +window on the desktop. The mouse pointer will not be hidden, and +screensavers are allowed to be activated. + +To close the window, you can either use \textbf{glfwTerminate}, as +described earlier, or you can use the more explicit approach by calling +\textbf{glfwCloseWindow}, which has the C syntax: + +\begin{lstlisting} +void glfwCloseWindow( void ) +\end{lstlisting} + + +%------------------------------------------------------------------------- +\section{Using Keyboard Input} +\GLFW\ provides several means for receiving user input, which will be +discussed in more detail in chapter \ref{par:inputhandling}. One of the +simplest ways of checking for keyboard input is to use the function +\textbf{glfwGetKey}: + +\begin{lstlisting} +int glfwGetKey( int key ) +\end{lstlisting} + +It queries the current status of individual keyboard keys. The argument +\textit{key} specifies which key to check, and it can be either an +uppercase printable ISO 8859-1 (Latin 1) character (e.g. `A', `3' or `.'), +or a special key identifier (see the \textit{GLFW Reference Manual} for a +list of special key identifiers). \textbf{glfwGetKey} returns GLFW\_PRESS +(or 1) if the key is currently held down, or GLFW\_RELEASE (or 0) if the +key is not being held down. For example: + +\begin{lstlisting} +A_pressed = glfwGetKey( 'A' ); +esc_pressed = glfwGetKey( GLFW_KEY_ESC ); +\end{lstlisting} + +In order for \textbf{glfwGetKey} to have any effect, you need to poll for +input events on a regular basis. This can be done in one of two ways: + +\begin{enumerate} +\item Implicitly by calling \textbf{glfwSwapBuffers} often. +\item Explicitly by calling \textbf{glfwPollEvents} often. +\end{enumerate} + +In general you do not have to care about this, since you will normally +call \textbf{glfwSwapBuffers} to swap front and back rendering buffers +every animation frame anyway. If, however, this is not the case, you +should call \textbf{glfwPollEvents} in the order of 10-100 times per +second in order for \GLFW\ to maintain an up to date input state. + + +%------------------------------------------------------------------------- +\section{Putting It Together: A Minimal GLFW Application} +Now that you know how to initialize \GLFW , open a window and poll for +keyboard input, let us exemplify this with a simple \OpenGL\ program. In +the following example some error-checking has been omitted for the sake of +brevity: + +\begin{lstlisting} +#include + +int main( void ) +{ + int running = GL_TRUE; + + // Initialize GLFW + glfwInit(); + + // Open an OpenGL window + if( !glfwOpenWindow( 300,300, 0,0,0,0,0,0, GLFW_WINDOW ) ) + { + glfwTerminate(); + return 0; + } + + // Main loop + while( running ) + { + // OpenGL rendering goes here... + glClear( GL_COLOR_BUFFER_BIT ); + + // Swap front and back rendering buffers + glfwSwapBuffers(); + + // Check if ESC key was pressed or window was closed + running = !glfwGetKey( GLFW_KEY_ESC ) && + glfwGetWindowParam( GLFW_OPENED ); + } + + // Close window and terminate GLFW + glfwTerminate(); + + // Exit program + return 0; +} +\end{lstlisting} + +The program opens a 300x300 window and runs in a loop until the escape key +is pressed, or the window was closed. All the \OpenGL\ ``rendering'' that +is done in this example is to clear the window. + + +%------------------------------------------------------------------------- +% Window Operations +%------------------------------------------------------------------------- +\chapter{Window Operations} +\thispagestyle{fancy} +In this chapter, you will learn more about window related \GLFW\ +functionality, including: setting and getting window properties, buffer +swap control and video mode querying. + + +%------------------------------------------------------------------------- +\section{Setting Window Properties} +In the previous chapter the \textbf{glfwOpenWindow} function was +described, which specifies the sizes of the color, alpha, depth and +stencil buffers. It is also possible to request an accumulator buffer, +auxiliary buffers and stereo rendering by using the +\textbf{glfwOpenWindowHint} function: + +\begin{lstlisting} +void glfwOpenWindowHint( int target, int hint ) +\end{lstlisting} + +The \textit{target} argument can be one of the constants listed in table~ +\ref{tab:winhints}, and \textit{hint} is the value to assign to the +specified target. + +%------------------------------------------------------------------------- +\begin{table}[p] +\begin{center} +\begin{tabular}{|l|l|p{7.0cm}|} \hline \raggedright +\textbf{Name} & \textbf{Default} & \textbf{Description} \\ \hline +GLFW\_REFRESH\_RATE & 0 & Vertical monitor refresh rate in Hz (only used for fullscreen windows). Zero means system default.\\ \hline +GLFW\_ACCUM\_RED\_BITS & 0 & Number of bits for the red channel of the accumulator buffer.\\ \hline +GLFW\_ACCUM\_GREEN\_BITS & 0 & Number of bits for the green channel of the accumulator buffer.\\ \hline +GLFW\_ACCUM\_BLUE\_BITS & 0 & Number of bits for the blue channel of the accumulator buffer.\\ \hline +GLFW\_ACCUM\_ALPHA\_BITS & 0 & Number of bits for the alpha channel of the accumulator buffer.\\ \hline +GLFW\_AUX\_BUFFERS & 0 & Number of auxiliary buffers.\\ \hline +GLFW\_STEREO & GL\_FALSE & Specify if stereo rendering should be supported (can be GL\_TRUE or GL\_FALSE).\\ \hline +GLFW\_WINDOW\_NO\_RESIZE & GL\_FALSE & Specify whether the window can be resized (not used for fullscreen windows).\\ \hline +GLFW\_FSAA\_SAMPLES & 0 & Number of samples to use for the multisampling buffer. Zero disables multisampling.\\ \hline +GLFW\_OPENGL\_VERSION\_MAJOR & 0 & Major number of the desired OpenGL version. + The default requests the highest OpenGL version equal to or lower than 2.1.\\ \hline +GLFW\_OPENGL\_VERSION\_MINOR & 0 & Minor number of the desired OpenGL version. + The default requests the highest OpenGL version equal to or lower than 2.1.\\ \hline +GLFW\_OPENGL\_FORWARD\_COMPAT & GL\_FALSE & Specify whether the OpenGL context should be forward compatible (i.e. disallow legacy functionality). + This hint is ignored for OpenGL version 2.1 and below.\\ \hline +\end{tabular} +\end{center} +\caption{Targets for \textbf{glfwOpenWindowHint}} +\label{tab:winhints} +\end{table} +%------------------------------------------------------------------------- + +For a hint to have any effect, the \textbf{glfwOpenWindowHint} function +must be called before opening the window with the \textbf{glfwOpenWindow} +function. + +To request an accumulator buffer, set the GLFW\_ACCUM\_x\_BITS targets to +values greater than zero (usually eight or sixteen bits per component). +To request auxiliary buffers, set the GLFW\_AUX\_BUFFERS target to a value +greater than zero. To request a stereo rendering capable window, set the +GLFW\_STEREO target to GL\_TRUE. + +If you want to enable fullscreen antialiasing, set the GLFW\_FSAA\_SAMPLES +target to a value greater than zero. If the windowing system is unable to +fulfil the request, \GLFW\ will degrade gracefully and disable FSAA if necessary. + +The GLFW\_REFRESH\_RATE target should be used with caution, since it may +result in suboptimal operation, or even a blank or damaged screen. + +If you want to create a context with OpenGL version 3.0 or above you have to +set the GLFW\_OPENGL\_VERSION\_MAJOR and GLFW\_OPENGL\_VERSION\_MINOR hints +accordingly. If you don't do this, the highest OpenGL version available for a +context is 2.1 or lower. + +Besides the parameters that are given with the \textbf{glfwOpenWindow} and +\textbf{glfwOpenWindowHint} functions, a few more properties of a window +can be changed after the window has been opened, namely the window title, +window size, and window position. + +To change the window title of an open window, use the +\textbf{glfwSetWindowTitle} function: + +\begin{lstlisting} +void glfwSetWindowTitle( const char *title ) +\end{lstlisting} + +\textit{title} is a null terminated ISO~8859-1 (8-bit Latin~1) string that +will be used as the window title. It will also be used as the application +name (for instance in the application list when using \texttt{ALT+TAB} +under Windows, or as the icon name when the window is iconified under +the X Window System). The default window name is ``GLFW Window'', which +will be used unless \textbf{glfwSetWindowTitle} is called after the window +has been opened. + +To change the size of a window, call \textbf{glfwSetWindowSize}: + +\begin{lstlisting} +void glfwSetWindowSize( int width, int height ) +\end{lstlisting} + +Where \textit{width} and \textit{height} are the new dimensions of the +window. + +To change the position of a window, call \textbf{glfwSetWindowPos}: + +\begin{lstlisting} +void glfwSetWindowPos( int x, int y ) +\end{lstlisting} + +Where \textit{x} and \textit{y} are the new desktop coordinates of the +window. This function does not have any effect when in fullscreen mode. + + +%------------------------------------------------------------------------- +\section{Getting Window Properties} +When opening a window, the opened window will not necessarily have the +requested properties, so you should always check the parameters that your +application relies on (e.g. number of stencil bits) using +\textbf{glfwGetWindowParam}, which has the C syntax: + +\begin{lstlisting} +int glfwGetWindowParam( int param ) +\end{lstlisting} + +The argument \textit{param} can be one of the tokens listed in table +\ref{tab:winparams}, and the return value is an integer holding the +requested value. + +%------------------------------------------------------------------------- +\begin{table}[p] +\begin{center} +\begin{tabular}{|l|p{9.5cm}|} \hline \raggedright +\textbf{Name} & \textbf{Description} \\ \hline +GLFW\_OPENED & GL\_TRUE if window is opened, else GL\_FALSE.\\ \hline +GLFW\_ACTIVE & GL\_TRUE if window has focus, else GL\_FALSE.\\ \hline +GLFW\_ICONIFIED & GL\_TRUE if window is iconified, else GL\_FALSE.\\ \hline +GLFW\_ACCELERATED & GL\_TRUE if window is hardware accelerated, else GL\_FALSE.\\ \hline +GLFW\_RED\_BITS & Number of bits for the red color component.\\ \hline +GLFW\_GREEN\_BITS & Number of bits for the green color component.\\ \hline +GLFW\_BLUE\_BITS & Number of bits for the blue color component.\\ \hline +GLFW\_ALPHA\_BITS & Number of bits for the alpha buffer.\\ \hline +GLFW\_DEPTH\_BITS & Number of bits for the depth buffer.\\ \hline +GLFW\_STENCIL\_BITS & Number of bits for the stencil buffer.\\ \hline +GLFW\_REFRESH\_RATE & Vertical monitor refresh rate in Hz. Zero indicates an unknown or a default refresh rate.\\ \hline +GLFW\_ACCUM\_RED\_BITS & Number of bits for the red channel of the accumulator buffer.\\ \hline +GLFW\_ACCUM\_GREEN\_BITS & Number of bits for the green channel of the accumulator buffer.\\ \hline +GLFW\_ACCUM\_BLUE\_BITS & Number of bits for the blue channel of the accumulator buffer.\\ \hline +GLFW\_ACCUM\_ALPHA\_BITS & Number of bits for the alpha channel of the accumulator buffer.\\ \hline +GLFW\_AUX\_BUFFERS & Number of auxiliary buffers.\\ \hline +GLFW\_STEREO & GL\_TRUE if stereo rendering is supported, else GL\_FALSE.\\ \hline +GLFW\_WINDOW\_NO\_RESIZE & GL\_TRUE if the window cannot be resized, else GL\_FALSE.\\ \hline +GLFW\_FSAA\_SAMPLES & Number of multisampling buffer samples. Zero indicated multisampling is disabled.\\ \hline +GLFW\_OPENGL\_VERSION\_MAJOR & Major number of the desired OpenGL version.\\ \hline +GLFW\_OPENGL\_VERSION\_MINOR & Minor number of the desired OpenGL version.\\ \hline +GLFW\_OPENGL\_FORWARD\_COMPAT & GL\_TRUE if the OpenGL context is forward compatible (i.e. disallows legacy functionality), else GL\_FALSE. +This is always GL\_FALSE for OpenGL version 2.1 and below.\\ \hline +\end{tabular} +\end{center} +\caption{Window parameters for \textbf{glfwGetWindowParam}} +\label{tab:winparams} +\end{table} +%------------------------------------------------------------------------- + +Another useful function is \textbf{glfwSetWindowSizeCallback}, which +specifies a user function that will be called every time the window size +has changed. The C syntax is: + +\begin{lstlisting} +void glfwSetWindowSizeCallback( GLFWwindowsizefun cbfun ) +\end{lstlisting} + +The user function \textit{fun} should be of the type: + +\begin{lstlisting} +void GLFWCALL fun( int width, int height ) +\end{lstlisting} + +The first argument passed to the user function is the width of the window, +and the second argument is the height of the window. Here is an example +of how to use a window size callback function: + +\begin{lstlisting} +int WinWidth, WinHeight; + +void GLFWCALL WindowResize( int width, int height ) +{ + WinWidth = width; + WinHeight = height; +} + +int main( void ) +{ + ... + glfwSetWindowSizeCallback( WindowResize ); + ... +} +\end{lstlisting} + +Using a callback function for getting the window size is mostly useful for +windowed applications, since the window size may be changed at any time by +the user. It can also be used to determine the actual fullscreen +resolution. + +An alternative to using a callback function for getting the window size, +is to use the function \textbf{glfwGetWindowSize}: + +\begin{lstlisting} +void glfwGetWindowSize( int *width, int *height ) +\end{lstlisting} + +The \textit{width} and \textit{height} arguments are filled out with the +current window dimensions. + + +%------------------------------------------------------------------------- +\section{Buffer Swapping} +\GLFW\ windows are always double buffered. That means that you have two +rendering buffers; a front buffer and a back buffer. The front buffer is +the buffer that is being displayed, and the back buffer is not displayed. +\OpenGL\ lets you select which of these two buffers you want to render to +(with the \textbf{glDrawBuffer} command), but the default (and preferred) +rendering buffer is the back buffer. This way you will avoid flickering +and artifacts caused by graphics being only partly drawn at the same time +as the video raster beam is displaying the graphics on the monitor. + +When an entire frame has been rendered to the back buffer, it is time to +swap the back and the front buffers in order to display the rendered +frame, and begin rendering a new frame. This is done with the command +\textbf{glfwSwapBuffers}. The C syntax is: + +\begin{lstlisting} +void glfwSwapBuffers( void ) +\end{lstlisting} + +Besides swapping the front and back rendering buffers, +\textbf{glfwSwapBuffers} also calls \textbf{glfwPollEvents}\footnote{This +behavior can be disabled by calling \textbf{glfwDisable} with the argument +GLFW\_AUTO\_POLL\_EVENTS.}. This is to ensure frequent polling of events, +such as keyboard and mouse input, and window reshaping events. + +Sometimes it can be useful to select when the buffer swap will occur. With +the function \textbf{glfwSwapInterval} it is possible to select the +minimum number of vertical retraces the video raster line should do before +swapping the buffers: + +\begin{lstlisting} +void glfwSwapInterval( int interval ) +\end{lstlisting} + +If \textit{interval} is zero, the swap will take place immediately when +\textbf{glfwSwapBuffers} is called, without waiting for a vertical retrace +(also known as ``vsync off''). Otherwise at least \textit{interval} +retraces will pass between each buffer swap (also known as ``vsync on''). +Using a swap interval of zero can be useful for benchmarking purposes, +when it is not desirable to measure the time it takes to wait for the +vertical retrace. However, a swap interval of 1 generally gives better +visual quality. + +It should be noted that not all \OpenGL\ implementations and hardware +support this function, in which case \textbf{glfwSwapInterval} will have +no effect. Sometimes it is only possible to affect the swap interval +through driver settings (e.g. the display settings under Windows, or as an +environment variable setting under Unix). + + +%------------------------------------------------------------------------- +\section{Querying Video Modes} +Although \GLFW\ generally does a good job at selecting a suitable video +mode for you when you open a fullscreen window, it is sometimes useful to +know exactly which modes are available on a certain system. For example, +you may want to present the user with a list of video modes to select +from. To get a list of available video modes, you can use the function +\textbf{glfwGetVideoModes}: + +\begin{lstlisting} +int glfwGetVideoModes( GLFWvidmode *list, int maxcount ) +\end{lstlisting} + +The argument \textit{list} is a vector of GLFWvidmode structures, and +\textit{maxcount} is the maximum number of video modes that your vector +can hold. \textbf{glfwGetVideoModes} will return the actual number of +video modes detected on the system. + +The GLFWvidmode structure looks like this: + +\begin{lstlisting} +typedef struct { + int Width, Height; // Video resolution + int RedBits; // Red bits per pixel + int GreenBits; // Green bits per pixel + int BlueBits; // Blue bits per pixel +} GLFWvidmode; +\end{lstlisting} + +Here is an example of retrieving all available video modes: + +\begin{lstlisting} +int nummodes; +GLFWvidmode list[ 200 ]; +nummodes = glfwGetVideoModes( list, 200 ); +\end{lstlisting} + +The returned list is sorted, first by color depth ($RedBits + GreenBits + +BlueBits$), and then by resolution ($Width\times Height$), with the +lowest resolution, fewest bits per pixel mode first. + +To get the desktop video mode, use the function +\textbf{glfwGetDesktopMode}: + +\begin{lstlisting} +void glfwGetDesktopMode( GLFWvidmode *mode ) +\end{lstlisting} + +The function returns the resolution and color depth of the user desktop in +the mode structure. Note that the user desktop mode is independent of the +current video mode if a \GLFW\ fullscreen window has been opened. + + +%------------------------------------------------------------------------- +% Input Handling +%------------------------------------------------------------------------- +\chapter{Input Handling} +\label{par:inputhandling} +\thispagestyle{fancy} +In this chapter you will learn how to use keyboard, mouse and joystick +input, using either polling or callback functions. + + +%------------------------------------------------------------------------- +\section{Event Polling} +The first thing to know about input handling in \GLFW\ is that all +keyboard and mouse input is collected by checking for input events. This +has do be done manually by calling either \textbf{glfwPollEvents} or +\textbf{glfwSwapBuffers} (which implicitly calls \textbf{glfwPollEvents} +for you). Normally this does not have to be a concern, since +\textbf{glfwSwapBuffers} is called every frame, which should be often +enough (about 10-100 times per second for a normal \OpenGL\ application). +One exception is when rendering is paused, and then the program waits for +input to begin animation again. In this case \textbf{glfwPollEvents} has +to be called repeatedly until any new input events arrive. + +If it is not desirable that \textbf{glfwPollEvents is} called implicitly +from \textbf{glfwSwapBuffers}, call \textbf{glfwDisable} with the argument +GLFW\_AUTO\_POLL\_EVENTS. + +Note that event polling is not needed for joystick input, since all +relevant joystick state is gathered every time a joystick function is +called. + + +%------------------------------------------------------------------------- +\section{Keyboard Input} +\GLFW\ gives three options for getting keyboard input: + +\begin{itemize} +\item Manually polling the state of individual keys. +\item Automatically receive new key state for any key, using a callback + function. +\item Automatically receive characters, using a callback function. +\end{itemize} + +Depending on what the keyboard input will be used for, either of the +methods may be more suitable. The main difference between the two last +options is that while characters are affected by modifier keys (such as +shift), key state is independent of any modifier keys. Also, special keys +(such as function keys, cursor keys and modifier keys) are not reported to +the character callback function. + +%------------------------------------------------------------------------- +\subsection{Key state} +To check if a key is held down or not at any given moment, use the +function \textbf{glfwGetKey}: + +\begin{lstlisting} +int glfwGetKey( int key ) +\end{lstlisting} + +It queries the current status of individual keyboard keys. The argument +\textit{key} specifies which key to check, and it can be either an +uppercase ISO~8859-1 character, or a special key identifier. +\textbf{glfwGetKey} returns GLFW\_PRESS (or 1) if the key is currently +held down, or GLFW\_RELEASE (or 0) if the key is not being held down. + +In most situations, it may be useful to know if a key has been pressed and +released between two calls to \textbf{glfwGetKey} (especially if the +animation is fairly slow, which may allow the user to press and release a +key between two calls to \textbf{glfwGetKey}). This can be accomplished by +enabling sticky keys, which is done by calling \textbf{glfwEnable} with +the argument GLFW\_STICKY\_KEYS, as in the following example: + +\begin{lstlisting} +glfwEnable( GLFW_STICKY_KEYS ); +\end{lstlisting} + +When sticky keys are enabled, a key will not be released until it is +checked with \textbf{glfwGetKey}. To disable sticky keys, call +\textbf{glfwDisable} witht the argument GLFW\_STICKY\_KEYS. Then all keys +that are not currently held down will be released, and future key releases +will take place immediately when the user releases the key, without +waiting for \textbf{glfwGetKey} to check the key. By default sticky keys +are disabled. + +Sticky keys are often very useful and should be used in most cases where +\textbf{glfwGetKey} is used. There is however a danger involved with +enabling sticky keys, and that is that keys that are pressed by the user +but are not checked with \textbf{glfwGetKey}, may remain ``pressed'' for a +very long time. A typical situation where this may be dangerous is in a +program that consists of two or more sections (e.g. a menu section and a +game section). If the first section enables sticky keys but does not check +for keys which the second section checks for, there is a potential of +recording many key presses in the first section that will be detected in +the second section. To avoid this problem, always disable sticky keys +before leaving a section of a program. + +An alternative to using \textbf{glfwGetKey} is to register a keyboard +input callback function with \textbf{glfwSetKeyCallback}: + +\begin{lstlisting} +void glfwSetKeyCallback( GLFWkeyfun cbfun ) +\end{lstlisting} + +The argument \textit{fun} is a pointer to a callback function. The +callback function shall take two integer arguments. The first is the key +identifier, and the second is the new key state, which can be GLFW\_PRESS +or GLFW\_RELEASE. To unregister a callback function, call +\textbf{glfwSetKeyCallback} with \textit{fun} = NULL. + +A callback function can be useful in some situations. For instance it can +replace multiple \textbf{glfwGetKey} calls with a switch/case statement. + +%------------------------------------------------------------------------- +\subsection{Character input} +If the keyboard is to be used as a text input device (e.g. in a user +dialog) rather than as a set of independent buttons, a character callback +function is more suitable. To register a character callback function, use +\textbf{glfwSetCharCallback}: + +\begin{lstlisting} +void glfwSetCharCallback( GLFWcharfun cbfun ) +\end{lstlisting} + +The argument \textit{fun} is a pointer to a callback function. The +callback function shall take two integer arguments. The first is a Unicode +character code, and the second is GLFW\_PRESS if the key that generated +the character was pressed, or GLFW\_RELEASE if it was released. To +unregister a callback function, call \textbf{glfwSetCharCallback} with +\textit{fun} = NULL. + +The Unicode character set is an international standard for encoding +characters. It is much more comprehensive than seven or eight bit +character sets (e.g. US-ASCII and Latin~1), and includes characters for +most written languages in the world. It should be noted that Unicode +character codes 0 to 255 are the same as for ISO~8859-1 (Latin~1), so as +long as a proper range check is performed on the Unicode character code, +it can be used just as an eight bit Latin~1 character code (which can be +useful if full Unicode support is not possible). + + +%------------------------------------------------------------------------- +\subsection{Key repeat} +By default, \GLFW\ does not report key repeats when a key is held down. +To activate key repeat, call \textbf{glfwEnable} with the argument +GLFW\_KEY\_REPEAT: + +\begin{lstlisting} +glfwEnable( GLFW_KEY_REPEAT ); +\end{lstlisting} + +This will let a registered key or character callback function receive key +repeat events when a key is held down. + + +%------------------------------------------------------------------------- +\subsection{Special system keys} +On most systems there are some special system keys that are normally not +intercepted by an application. For instance, under Windows it is possible +to switch programs by pressing \texttt{ALT+TAB}, which brings up a list of +running programs to select from. In certain situations it can be desirable +to prevent such special system keys from interfering with the program. +With \GLFW\ it is possible to do by calling \textbf{glfwDisable} with the +argument GLFW\_SYSTEM\_KEYS: + +\begin{lstlisting} +glfwDisable( GLFW_SYSTEM_KEYS ); +\end{lstlisting} + +By doing so, most system keys will have no effect and will not interfere +with your program. System keys can be re-enabled by calling +\textbf{glfwEnable} with the argument GLFW\_SYSTEM\_KEYS. By default, +system keys are enabled. + + +%------------------------------------------------------------------------- +\section{Mouse Input} +Just like for keyboard input, mouse input can be realized with either +polling or callback functions. + + +%------------------------------------------------------------------------- +\subsection{Mouse position} +To read the mouse position, you can use the function +\textbf{glfwGetMousePos}: + +\begin{lstlisting} +void glfwGetMousePos( int *x, int *y ) +\end{lstlisting} + +The arguments \textit{x} and \textit{y} point to integer variables that +will be updated with the current absolute mouse position. An alternative +is to use a callback function instead, which can be set with +\textbf{glfwSetMousePosCallback}: + +\begin{lstlisting} +void glfwSetMousePosCallback( GLFWmouseposfun cbfun ) +\end{lstlisting} + +The function that \textit{fun} points to will be called every time the +mouse position changes. The first argument to the callback function is +the mouse x position, and the second argument is the mouse y position. + + +%------------------------------------------------------------------------- +\subsection{Mouse buttons} +To query the state of a mouse button, call \textbf{glfwGetMouseButton}: + +\begin{lstlisting} +int glfwGetMouseButton( int button ) +\end{lstlisting} + +The argument \textit{button} can be one of the following mouse button +identifiers: GLFW\_MOUSE\_BUTTON\_LEFT, GLFW\_MOUSE\_BUTTON\_RIGHT or +GLFW\_MOUSE\_BUTTON\_MIDDLE. \textbf{glfwGetMouseButton} will return +GLFW\_PRESS (which is a non-zero value) if the corresponding mouse +button is held down, otherwise it will return GLFW\_RELEASE (which is +equal to zero). + +Just as it is possible to make keys ``sticky'', it is also possible to +make mouse buttons appear as held down until the button is checked for +with \textbf{glfwGetMouseButton}. To enable sticky mouse buttons, call +\textbf{glfwEnable} with the argument GLFW\_STICKY\_MOUSE\_BUTTONS. + +When sticky mouse buttons are enabled, a mouse button will not be released +until it is checked with \textbf{glfwGetMouseButton}. To disable sticky +mouse buttons, call \textbf{glfwDisable} with the argument +GLFW\_STICKY\_MOUSE\_BUTTONS. Then all mouse buttons that are not +currently held down will be released, and future mouse button releases +will take place immediately when the user releases the mouse button, +without waiting for \textbf{glfwGetMouseButton} to check for the mouse +button. By default sticky mouse buttons are disabled. + +There is also a callback function for mouse button activities, which can +be set with \textbf{glfwSetMouseButtonCallback}: + +\begin{lstlisting} +void glfwSetMouseButtonCallback( GLFWmousebuttonfun fun ) +\end{lstlisting} + +The argument \textit{fun} specifies a function that will be called +whenever a mouse button is pressed or released, or NULL to unregister a +callback function. The first argument to the callback function is a mouse +button identifier, and the second is either GLFW\_PRESS or GLFW\_RELEASE, +depending on the new state of the corresponding mouse button. + + +%------------------------------------------------------------------------- +\subsection{Mouse wheel} +Some mice have a mouse wheel, which can be thought of as a third mouse +axis. To get the position of the mouse wheel, call +\textbf{glfwGetMouseWheel}: + +\begin{lstlisting} +int glfwGetMouseWheel( void ) +\end{lstlisting} + +The function returns an integer that represents the position of the mouse +wheel. When the user turns the wheel, the wheel position will increase or +decrease. + +It is also possible to register a callback function for mouse wheel events +with the \textbf{glfwSetMouseWheelCallback} function: + +\begin{lstlisting} +void glfwSetMouseWheelCallback( GLFWmousewheelfun fun ) +\end{lstlisting} + +The argument \textit{fun} specifies a function that will be called +whenever the mouse wheel is moved, or NULL to unregister a callback +function. The argument to the callback function is the position of the +mouse wheel. + + +%------------------------------------------------------------------------- +\subsection{Hiding the mouse cursor} +It is possible to hide the mouse cursor with the function call: + +\begin{lstlisting} +glfwDisable( GLFW_MOUSE_CURSOR ); +\end{lstlisting} + +Hiding the mouse cursor has three effects: + +\begin{enumerate} +\item The cursor becomes invisible. +\item The cursor is guaranteed to be confined to the window. +\item Mouse coordinates are not limited to the window size. +\end{enumerate} + +To show the mouse cursor again, call \textbf{glfwEnable} with the +argument GLFW\_MOUSE\_CURSOR: + +\begin{lstlisting} +glfwEnable( GLFW_MOUSE_CURSOR ); +\end{lstlisting} + +By default the mouse cursor is hidden if a window is opened in fullscreen +mode, otherwise it is not hidden. + + +%------------------------------------------------------------------------- +\section{Joystick Input} +\GLFW\ has support for up to sixteen joysticks, and an infinite\footnote{% +There are of course actual limitations posed by the underlying hardware, +drivers and operation system.} number of axes and buttons per joystick. +Unlike keyboard and mouse input, joystick input does not need an opened +window, and \textbf{glfwPollEvents} or \textbf{glfwSwapBuffers} does not +have to be called in order for joystick state to be updated. + + +%------------------------------------------------------------------------- +\subsection{Joystick capabilities} +First, it is often necessary to determine if a joystick is connected, and +what its capabilities are. To get this information the function +\textbf{glfwGetJoystickParam} can be used: + +\begin{lstlisting} +int glfwGetJoystickParam( int joy, int param ) +\end{lstlisting} + +The \textit{joy} argument specifies which joystick to retrieve the +parameter from, and it should be GLFW\_JOYSTICK\_\textit{n}, where +\textit{n} is in the range 1 to 16. The \textit{param} argument specifies +which parameter to retrieve. To determine if a joystick is connected, +\textit{param} should be GLFW\_PRESENT, which will cause the function to +return GL\_TRUE if the joystick is connected, or GL\_FALSE if it is not. +To determine the number of axes or buttons that are supported by the +joystick, \textit{param} should be GLFW\_AXES or GLFW\_BUTTONS, +respectively. + + +%------------------------------------------------------------------------- +\subsection{Joystick position} +To get the current axis positions of the joystick, the +\textbf{glfwGetJoystickPos} is used: + +\begin{lstlisting} +int glfwGetJoystickPos( int joy, float *pos, int numaxes ) +\end{lstlisting} + +As with \textbf{glfwGetJoystickParam}, the \textit{joy} argument +specifies which joystick to retrieve information from. The +\textit{numaxes} argument specifies how many axes to return, and the +\textit{pos} argument specifies an array in which all the axis positions +are stored. The function returns the actual number of axes that were +returned, which could be less than \textit{numaxes} if the joystick does +not support all the requested axes, or if the joystick is not connected. + +For instance, to get the position of the first two axes (the X and Y axes) +of joystick 1, the following code can be used: + +\begin{lstlisting} +float position[ 2 ]; + +glfwGetJoystickPos( GLFW_JOYSTICK_1, position, 2 ); +\end{lstlisting} + +After this call, the first element of the \textit{position} array will +hold the X axis position of the joystick, and the second element will hold +the Y axis position. In this example we do not use the information about +how many axes were really returned. + +The position of an axis can be in the range -1.0 to 1.0, where positive +values represent right, forward or up directions, while negative values +represent left, back or down directions. If a requested axis is not +supported by the joystick, the corresponding array element will be set +to zero. The same goes for the situation when the joystick is not +connected (all axes are treated as unsupported). + + +%------------------------------------------------------------------------- +\subsection{Joystick buttons} +A function similar to the \textbf{glfwGetJoystickPos} function is +available for querying the state of joystick buttons, namely the +\textbf{glfwGetJoystickButtons} function: + +\begin{lstlisting} +int glfwGetJoystickButtons( int joy, unsigned char *buttons, + int numbuttons ) +\end{lstlisting} + +The function works just like the \textbf{glfwGetJoystickAxis} function, +except that it returns the state of joystick buttons instead of axis +positions. Each button in the array specified by the \textit{buttons} +argument can be either GLFW\_PRESS or GLFW\_RELEASE, telling if the +corresponding button is currently held down or not. Unsupported buttons +will have the value GLFW\_RELEASE. + + +%------------------------------------------------------------------------- +% Timing +%------------------------------------------------------------------------- +\chapter{Timing} +\thispagestyle{fancy} + +%------------------------------------------------------------------------- +\section{High Resolution Timer} +In most applications, it is useful to know exactly how much time has +passed between point $A$ and point $B$ in a program. A typical situation +is in a game, where you need to know how much time has passed between two +rendered frames in order to calculate the correct movement and physics +etc. Another example is when you want to benchmark a certain piece of +code. + +\GLFW\ provides a high-resolution timer, which reports a double precision +floating point value representing the number of seconds that have passed +since \textbf{glfwInit} was called. The timer is accessed with the +function \textbf{glfwGetTime}: + +\begin{lstlisting} +double glfwGetTime( void ) +\end{lstlisting} + +The precision of the timer depends on which computer and operating +system you are running, but it is almost guaranteed to be better than +$10~ms$, and in most cases it is much better than $1~ms$ (on a modern PC +you can get resolutions in the order of $1~ns$). + +It is possible to set the value of the high precision timer using the +\textbf{glfwSetTime} function: + +\begin{lstlisting} +void glfwSetTime( double time ) +\end{lstlisting} + +The argument \textit{time} is the time, in seconds, that the timer should +be set to. + + +%------------------------------------------------------------------------- +% OpenGL Extension Support +%------------------------------------------------------------------------- +\chapter{OpenGL Extension Support} +\thispagestyle{fancy} +One of the benefits of \OpenGL\ is that it is extensible. Independent +hardware vendors (IHVs) may include functionality in their \OpenGL\ +implementations that exceed that of the \OpenGL\ standard. + +An extension is defined by: + +\begin{enumerate} +\item An extension name (e.g. GL\_ARB\_multitexture). +\item New OpenGL tokens (e.g. GL\_TEXTURE1\_ARB). +\item New OpenGL functions (e.g. \textbf{glActiveTextureARB}). +\end{enumerate} + +A list of official extensions, together with their definitions, can be +found at the \textit{OpenGL Extension Registry} +(\url{http://oss.sgi.com/projects/ogl-sample/registry/}). + +To use a certain extension, the following steps must be performed: + +\begin{enumerate} +\item A compile time check for the support of the extension. +\item A run time check for the support of the extension. +\item Fetch function pointers for the extended \OpenGL\ functions (done at + run time). +\end{enumerate} + +How this is done using \GLFW\ is described in the following sections. +Please note that this chapter covers some advanced topics, and is quite +specific to the C programming language. + + +%------------------------------------------------------------------------- +\section{Compile Time Check} +The compile time check is necessary to perform in order to know if the +compiler include files have defined the necessary tokens. It is very easy +to do. The include file \texttt{GL/gl.h} will define a constant with the +same name as the extension, if all the extension tokens are defined. Here +is an example of how to check for the extension GL\_ARB\_multitexture: + +\begin{lstlisting} +#ifdef GL_ARB_multitexture + // Extension is supported by the include files +#else + // Extension is not supported by the include files + // Update your file! +#endif +\end{lstlisting} + + +%------------------------------------------------------------------------- +\section{Runtime Check} +Even if the compiler include files have defined all the necessary tokens, +the target system may not support the extension (perhaps it has a +different graphic card with a different \OpenGL\ implementation, or it has +an older driver). That is why it is necessary to do a run time check for +the extension support as well. This is done with the \GLFW\ function +\textbf{glfwExtensionSupported}, which has the C syntax: + +\begin{lstlisting} +int glfwExtensionSupported( const char *extension ) +\end{lstlisting} + +The argument \textit{extension} is a null terminated ISO~8859-1 string +with the extension name. \textbf{glfwExtensionSupported} returns GL\_TRUE +if the extension is supported, otherwise it returns GL\_FALSE. + +Let us extend the previous example of checking for support of the +extension GL\_ARB\_multitexture. This time we add a run time check, and a +variable which we set to GL\_TRUE if the extension is supported, or +GL\_FALSE if it is not supported. + +\begin{lstlisting} +int multitexture_supported; + +#ifdef GL_ARB_multitexture + // Check if extension is supported at run time + multitexture_supported = + glfwExtensionSupported( "GL_ARB_multitexture" ); +#else + // Extension is not supported by the include files + // Update your file! + multitexture_supported = GL_FALSE; +#endif +\end{lstlisting} + +Now it is easy to check for the extension within the program, simply do: + +\begin{lstlisting} + if( multitexture_supported ) + { + // Use multi texturing + } + else + { + // Use some other solution (or fail) + } +\end{lstlisting} + + +%------------------------------------------------------------------------- +\section{Fetching Function Pointers} +Some extensions (not all) require the use of new \OpenGL\ functions, which +are not necessarily defined by your link libraries. Thus it is necessary +to get the function pointers dynamically at run time. This is done with +the \GLFW\ function \textbf{glfwGetProcAddress}: + +\begin{lstlisting} +void * glfwGetProcAddress( const char *procname ) +\end{lstlisting} + +The argument \textit{procname} is a null terminated ISO~8859-1 string +holding the name of the \OpenGL\ function. \textbf{glfwGetProcAddress} +returns the address to the function if the function is available, +otherwise NULL is returned. + +Obviously, fetching the function pointer is trivial. For instance, if we +want to obtain the pointer to \textbf{glActiveTextureARB}, we simply call: + +\begin{lstlisting} +glActiveTextureARB = glfwGetProcAddress( "glActiveTextureARB" ); +\end{lstlisting} + +However, there are many possible naming and type definition conflicts +involved with such an operation, which may result in compiler warnings or +errors. My proposed solution is the following: + +\begin{itemize} +\item Do not use the function name for the variable name. Use something + similar (perhaps with a prefix or suffix), and then use + \texttt{\#define} to map the function name to your variable. +\item The standard type definition naming convention for function pointers + is \texttt{PFN\textit{xxxx}PROC}, where \texttt{\textit{xxxx}} is + the uppercase version of the function name (e.g. + \texttt{PFNGLACTIVETEXTUREARBPROC}). Either make sure that a + compatible \texttt{gl.h} and/or \texttt{glext.h} file is used by + your compiler and rely on it to do the type definitions for you, or + use a custom type definition naming convention (e.g. + \texttt{\textit{xxxx}\_T} or something) and do the type definitions + yourself. +\end{itemize} + +Here is an example of how to do it (here we use our own function pointer +type defintion): + +\begin{lstlisting} +// Type definition of the function pointer +typedef void (APIENTRY * GLACTIVETEXTUREARB_T) (GLenum texture); + +// Function pointer +GLACTIVETEXTUREARB_T _ActiveTextureARB; +#define glActiveTextureARB _ActiveTextureARB + +// Extension availability flag +int multitexture_supported; + +#ifdef GL_ARB_multitexture + // Check if extension is supported at run time + if( glfwExtensionSupported( "GL_ARB_multitexture" ) ) + { + // Get the function pointer + glActiveTextureARB = (GLACTIVETEXTUREARB_T) + glfwGetProcAddress( "glActiveTextureARB" ); + + multitexture_supported = GL_TRUE; + } + else + { + multitexture_supported = GL_FALSE; + } +#else + // Extension is not supported by the include files + multitexture_supported = GL_FALSE; +#endif +\end{lstlisting} + +Please note that the code example is not 100\% complete. First of all, +the GL\_ARB\_multitexture extension defines many more functions than the +single function that the code example defines. Secondly, checking if an +extension is supported using \textbf{glfwExtensionSupported} is not enough +to ensure that the corresponding functions will be valid. You also need to +check if the function pointers returned by \textbf{glfwGetProcAddress} are +non-NULL values. + + +%------------------------------------------------------------------------- +\subsection{Function pointer type definitions} +To make a function pointer type definition, you need to know the function +prototype. This can often be found in the extension definitions (e.g. at +the \textit{OpenGL Extension Registry}). All the functions that are +defined for an extension are listed with their C prototype definitions +under the section \textit{New Procedures and Functions} in the extension +definition. + +For instance, if we look at the definition of the +GL\_ARB\_texture\_compression extension, we find a list of new functions. +One of the functions looks like this: + +\begin{lstlisting} +void GetCompressedTexImageARB(enum target, int lod, void *img); +\end{lstlisting} + +Like in most official \OpenGL\ documentation, all the \texttt{GL} and +\texttt{gl} prefixes have been left out. In other words, the real function +prototype would look like this: + +\begin{lstlisting} +void glGetCompressedTexImageARB(GLenum target, GLint lod, void *img); +\end{lstlisting} + +All we have to do to turn this prototype definition into a function +pointer type definition, is to replace the function name with +\texttt{(APIENTRY * \textit{xxxx}\_T)}, where \textit{xxxx} is the +uppercase version of the name (according to the proposed naming +convention). The keyword \texttt{APIENTRY} is needed to be compatible +between different platforms. The \GLFW\ include file \texttt{GL/glfw.h} +always makes sure that \texttt{APIENTRY} is properly defined, regardless +of which platform the program is compiled on. + +In other words, for the function \textbf{glGetCompressedTexImageARB} we +get: + +\begin{lstlisting} +typedef void (APIENTRY * GLGETCOMPRESSEDTEXIMAGEARB_T) + (GLenum target, GLint level, void *img); +\end{lstlisting} + + + +%------------------------------------------------------------------------- +% Index +%------------------------------------------------------------------------- +% ... + +\end{document} diff --git a/docs/readme.txt b/docs/readme.txt new file mode 100644 index 00000000..f34521a2 --- /dev/null +++ b/docs/readme.txt @@ -0,0 +1,52 @@ +Introduction +------------ + +The GLFW documentation is written in LaTeX. Besides being powerful, LaTeX is +also very attractive since all the necessary tools for dealing with LaTeX +documentation are both free and ported to a wide variety of platforms. Another +advantage is that the LaTeX files are written in plain text, which means that +version control systems such as CVS handle them perfectly without having to +treat the documents as binary files. + + +The documents +------------- + +There are two main documents: + + glfwrm.tex - The GLFW Reference Manual + glfwug.tex - The GLFW Users Guide + +In addition, there is a common LaTeX style file that sets up things +such as page formatting and useful macros: + + glfwdoc.sty - Common GLFW document styles and macros + + +Requirements +------------ + +Of course you need LaTeX installed on your system in order to compile the GLFW +documentation. If you are using a Unix-like operating system, then your +package system most likely has a version of LaTeX adapted for your system. If +not, the easiest way to get a full LaTeX system is to download/get the TeXLive +CD from http://www.tug.org/texlive/. It has all the necessary software for +Windows, Mac OS X and most popular Unix-like operating systems. + +A number of LaTeX packages have to be installed in order to compile the +GLFW documentation successfully: + + color + fancyhdr + hyperref + lastpage + listings + needspace + textcase + times + titling + +These packages are all available on the TeXLive CD. Just make sure that +you have checked all these packages when installing TeXLive, or get them +in some other way if you do not have the TeXLive CD. + diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..a6282a14 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,37 @@ +# This line is used to link with static libraries +# Note that the library list should be updated to be obtained from +# the main CMakeLists.txt +link_libraries(libglfwStatic ${GLFW_LIBRARIES} ${OPENGL_glu_LIBRARY}) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include ${OPENGL_INCLUDE_DIR}) + +add_executable(listmodes listmodes.c) + +if(APPLE) + # Set fancy names for bundles + add_executable(Boing MACOSX_BUNDLE boing.c) + add_executable(Gears MACOSX_BUNDLE gears.c) + add_executable("Split View" MACOSX_BUNDLE splitview.c) + add_executable(Triangle MACOSX_BUNDLE triangle.c) + add_executable(Wave MACOSX_BUNDLE wave.c) +else(APPLE) + # Set boring names for executables + add_executable(boing WIN32 boing.c) + add_executable(gears WIN32 gears.c) + add_executable(splitview WIN32 splitview.c) + add_executable(triangle WIN32 triangle.c) + add_executable(wave WIN32 wave.c) +endif(APPLE) + +if(MSVC) + # Tell MSVC to use main instead of WinMain for Windows subsystem executables + set_target_properties(boing gears splitview triangle wave PROPERTIES LINK_FLAGS "/ENTRY:mainCRTStartup") +endif(MSVC) + +if(CYGWIN) + # Set cross-compile and subsystem compile and link flags + set_target_properties(boing gears listmodes splitview triangle wave PROPERTIES COMPILE_FLAGS "-mno-cygwin") + set_target_properties(boing gears splitview triangle wave PROPERTIES LINK_FLAGS "-mno-cygwin -mwindows") + set_target_properties(listmodes PROPERTIES LINK_FLAGS "-mno-cygwin -mconsole") +endif(CYGWIN) + diff --git a/examples/boing.c b/examples/boing.c new file mode 100644 index 00000000..451445b0 --- /dev/null +++ b/examples/boing.c @@ -0,0 +1,615 @@ +/***************************************************************************** + * Title: GLBoing + * Desc: Tribute to Amiga Boing. + * Author: Jim Brooks + * Original Amiga authors were R.J. Mical and Dale Luck. + * GLFW conversion by Marcus Geelnard + * Notes: - 360' = 2*PI [radian] + * + * - Distances between objects are created by doing a relative + * Z translations. + * + * - Although OpenGL enticingly supports alpha-blending, + * the shadow of the original Boing didn't affect the color + * of the grid. + * + * - [Marcus] Changed timing scheme from interval driven to frame- + * time based animation steps (which results in much smoother + * movement) + * + * History of Amiga Boing: + * + * Boing was demonstrated on the prototype Amiga (codenamed "Lorraine") in + * 1985. According to legend, it was written ad-hoc in one night by + * R. J. Mical and Dale Luck. Because the bouncing ball animation was so fast + * and smooth, attendees did not believe the Amiga prototype was really doing + * the rendering. Suspecting a trick, they began looking around the booth for + * a hidden computer or VCR. + *****************************************************************************/ + +#include +#include +#include +#include + + +/***************************************************************************** + * Various declarations and macros + *****************************************************************************/ + +/* Prototypes */ +void init( void ); +void display( void ); +void reshape( int w, int h ); +void DrawBoingBall( void ); +void BounceBall( double dt ); +void DrawBoingBallBand( GLfloat long_lo, GLfloat long_hi ); +void DrawGrid( void ); + +#define RADIUS 70.f +#define STEP_LONGITUDE 22.5f /* 22.5 makes 8 bands like original Boing */ +#define STEP_LATITUDE 22.5f + +#define DIST_BALL (RADIUS * 2.f + RADIUS * 0.1f) + +#define VIEW_SCENE_DIST (DIST_BALL * 3.f + 200.f)/* distance from viewer to middle of boing area */ +#define GRID_SIZE (RADIUS * 4.5f) /* length (width) of grid */ +#define BOUNCE_HEIGHT (RADIUS * 2.1f) +#define BOUNCE_WIDTH (RADIUS * 2.1f) + +#define SHADOW_OFFSET_X -20.f +#define SHADOW_OFFSET_Y 10.f +#define SHADOW_OFFSET_Z 0.f + +#define WALL_L_OFFSET 0.f +#define WALL_R_OFFSET 5.f + +/* Animation speed (50.0 mimics the original GLUT demo speed) */ +#define ANIMATION_SPEED 50.f + +/* Maximum allowed delta time per physics iteration */ +#define MAX_DELTA_T 0.02f + +/* Draw ball, or its shadow */ +typedef enum { DRAW_BALL, DRAW_BALL_SHADOW } DRAW_BALL_ENUM; + +/* Vertex type */ +typedef struct {float x; float y; float z;} vertex_t; + +/* Global vars */ +GLfloat deg_rot_y = 0.f; +GLfloat deg_rot_y_inc = 2.f; +GLfloat ball_x = -RADIUS; +GLfloat ball_y = -RADIUS; +GLfloat ball_x_inc = 1.f; +GLfloat ball_y_inc = 2.f; +DRAW_BALL_ENUM drawBallHow; +double t; +double t_old = 0.f; +double dt; + +/* Random number generator */ +#ifndef RAND_MAX + #define RAND_MAX 4095 +#endif + +/* PI */ +#ifndef M_PI + #define M_PI 3.1415926535897932384626433832795 +#endif + + +/***************************************************************************** + * Truncate a degree. + *****************************************************************************/ +GLfloat TruncateDeg( GLfloat deg ) +{ + if ( deg >= 360.f ) + return (deg - 360.f); + else + return deg; +} + +/***************************************************************************** + * Convert a degree (360-based) into a radian. + * 360' = 2 * PI + *****************************************************************************/ +double deg2rad( double deg ) +{ + return deg / 360 * (2 * M_PI); +} + +/***************************************************************************** + * 360' sin(). + *****************************************************************************/ +double sin_deg( double deg ) +{ + return sin( deg2rad( deg ) ); +} + +/***************************************************************************** + * 360' cos(). + *****************************************************************************/ +double cos_deg( double deg ) +{ + return cos( deg2rad( deg ) ); +} + +/***************************************************************************** + * Compute a cross product (for a normal vector). + * + * c = a x b + *****************************************************************************/ +void CrossProduct( vertex_t a, vertex_t b, vertex_t c, vertex_t *n ) +{ + GLfloat u1, u2, u3; + GLfloat v1, v2, v3; + + u1 = b.x - a.x; + u2 = b.y - a.y; + u3 = b.y - a.z; + + v1 = c.x - a.x; + v2 = c.y - a.y; + v3 = c.z - a.z; + + n->x = u2 * v3 - v2 * v3; + n->y = u3 * v1 - v3 * u1; + n->z = u1 * v2 - v1 * u2; +} + +/***************************************************************************** + * Calculate the angle to be passed to gluPerspective() so that a scene + * is visible. This function originates from the OpenGL Red Book. + * + * Parms : size + * The size of the segment when the angle is intersected at "dist" + * (ie at the outermost edge of the angle of vision). + * + * dist + * Distance from viewpoint to scene. + *****************************************************************************/ +GLfloat PerspectiveAngle( GLfloat size, + GLfloat dist ) +{ + GLfloat radTheta, degTheta; + + radTheta = 2.f * (GLfloat) atan2( size / 2.f, dist ); + degTheta = (180.f * radTheta) / (GLfloat) M_PI; + return degTheta; +} + + + +#define BOING_DEBUG 0 + + +/***************************************************************************** + * init() + *****************************************************************************/ +void init( void ) +{ + /* + * Clear background. + */ + glClearColor( 0.55f, 0.55f, 0.55f, 0.f ); + + glShadeModel( GL_FLAT ); +} + + +/***************************************************************************** + * display() + *****************************************************************************/ +void display(void) +{ + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + glPushMatrix(); + + drawBallHow = DRAW_BALL_SHADOW; + DrawBoingBall(); + + DrawGrid(); + + drawBallHow = DRAW_BALL; + DrawBoingBall(); + + glPopMatrix(); + glFlush(); +} + + +/***************************************************************************** + * reshape() + *****************************************************************************/ +void reshape( int w, int h ) +{ + glViewport( 0, 0, (GLsizei)w, (GLsizei)h ); + + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + + gluPerspective( PerspectiveAngle( RADIUS * 2, 200 ), + (GLfloat)w / (GLfloat)h, + 1.0, + VIEW_SCENE_DIST ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + gluLookAt( 0.0, 0.0, VIEW_SCENE_DIST,/* eye */ + 0.0, 0.0, 0.0, /* center of vision */ + 0.0, -1.0, 0.0 ); /* up vector */ +} + + +/***************************************************************************** + * Draw the Boing ball. + * + * The Boing ball is sphere in which each facet is a rectangle. + * Facet colors alternate between red and white. + * The ball is built by stacking latitudinal circles. Each circle is composed + * of a widely-separated set of points, so that each facet is noticably large. + *****************************************************************************/ +void DrawBoingBall( void ) +{ + GLfloat lon_deg; /* degree of longitude */ + double dt_total, dt2; + + glPushMatrix(); + glMatrixMode( GL_MODELVIEW ); + + /* + * Another relative Z translation to separate objects. + */ + glTranslatef( 0.0, 0.0, DIST_BALL ); + + /* Update ball position and rotation (iterate if necessary) */ + dt_total = dt; + while( dt_total > 0.0 ) + { + dt2 = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total; + dt_total -= dt2; + BounceBall( dt2 ); + deg_rot_y = TruncateDeg( deg_rot_y + deg_rot_y_inc*((float)dt2*ANIMATION_SPEED) ); + } + + /* Set ball position */ + glTranslatef( ball_x, ball_y, 0.0 ); + + /* + * Offset the shadow. + */ + if ( drawBallHow == DRAW_BALL_SHADOW ) + { + glTranslatef( SHADOW_OFFSET_X, + SHADOW_OFFSET_Y, + SHADOW_OFFSET_Z ); + } + + /* + * Tilt the ball. + */ + glRotatef( -20.0, 0.0, 0.0, 1.0 ); + + /* + * Continually rotate ball around Y axis. + */ + glRotatef( deg_rot_y, 0.0, 1.0, 0.0 ); + + /* + * Set OpenGL state for Boing ball. + */ + glCullFace( GL_FRONT ); + glEnable( GL_CULL_FACE ); + glEnable( GL_NORMALIZE ); + + /* + * Build a faceted latitude slice of the Boing ball, + * stepping same-sized vertical bands of the sphere. + */ + for ( lon_deg = 0; + lon_deg < 180; + lon_deg += STEP_LONGITUDE ) + { + /* + * Draw a latitude circle at this longitude. + */ + DrawBoingBallBand( lon_deg, + lon_deg + STEP_LONGITUDE ); + } + + glPopMatrix(); + + return; +} + + +/***************************************************************************** + * Bounce the ball. + *****************************************************************************/ +void BounceBall( double dt ) +{ + GLfloat sign; + GLfloat deg; + + /* Bounce on walls */ + if ( ball_x > (BOUNCE_WIDTH/2 + WALL_R_OFFSET ) ) + { + ball_x_inc = -0.5f - 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX; + deg_rot_y_inc = -deg_rot_y_inc; + } + if ( ball_x < -(BOUNCE_HEIGHT/2 + WALL_L_OFFSET) ) + { + ball_x_inc = 0.5f + 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX; + deg_rot_y_inc = -deg_rot_y_inc; + } + + /* Bounce on floor / roof */ + if ( ball_y > BOUNCE_HEIGHT/2 ) + { + ball_y_inc = -0.75f - 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX; + } + if ( ball_y < -BOUNCE_HEIGHT/2*0.85 ) + { + ball_y_inc = 0.75f + 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX; + } + + /* Update ball position */ + ball_x += ball_x_inc * ((float)dt*ANIMATION_SPEED); + ball_y += ball_y_inc * ((float)dt*ANIMATION_SPEED); + + /* + * Simulate the effects of gravity on Y movement. + */ + if ( ball_y_inc < 0 ) sign = -1.0; else sign = 1.0; + + deg = (ball_y + BOUNCE_HEIGHT/2) * 90 / BOUNCE_HEIGHT; + if ( deg > 80 ) deg = 80; + if ( deg < 10 ) deg = 10; + + ball_y_inc = sign * 4.f * (float) sin_deg( deg ); +} + + +/***************************************************************************** + * Draw a faceted latitude band of the Boing ball. + * + * Parms: long_lo, long_hi + * Low and high longitudes of slice, resp. + *****************************************************************************/ +void DrawBoingBallBand( GLfloat long_lo, + GLfloat long_hi ) +{ + vertex_t vert_ne; /* "ne" means south-east, so on */ + vertex_t vert_nw; + vertex_t vert_sw; + vertex_t vert_se; + vertex_t vert_norm; + GLfloat lat_deg; + static int colorToggle = 0; + + /* + * Iterate thru the points of a latitude circle. + * A latitude circle is a 2D set of X,Z points. + */ + for ( lat_deg = 0; + lat_deg <= (360 - STEP_LATITUDE); + lat_deg += STEP_LATITUDE ) + { + /* + * Color this polygon with red or white. + */ + if ( colorToggle ) + glColor3f( 0.8f, 0.1f, 0.1f ); + else + glColor3f( 0.95f, 0.95f, 0.95f ); +#if 0 + if ( lat_deg >= 180 ) + if ( colorToggle ) + glColor3f( 0.1f, 0.8f, 0.1f ); + else + glColor3f( 0.5f, 0.5f, 0.95f ); +#endif + colorToggle = ! colorToggle; + + /* + * Change color if drawing shadow. + */ + if ( drawBallHow == DRAW_BALL_SHADOW ) + glColor3f( 0.35f, 0.35f, 0.35f ); + + /* + * Assign each Y. + */ + vert_ne.y = vert_nw.y = (float) cos_deg(long_hi) * RADIUS; + vert_sw.y = vert_se.y = (float) cos_deg(long_lo) * RADIUS; + + /* + * Assign each X,Z with sin,cos values scaled by latitude radius indexed by longitude. + * Eg, long=0 and long=180 are at the poles, so zero scale is sin(longitude), + * while long=90 (sin(90)=1) is at equator. + */ + vert_ne.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE )); + vert_se.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo )); + vert_nw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE )); + vert_sw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo )); + + vert_ne.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE )); + vert_se.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo )); + vert_nw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE )); + vert_sw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo )); + + /* + * Draw the facet. + */ + glBegin( GL_POLYGON ); + + CrossProduct( vert_ne, vert_nw, vert_sw, &vert_norm ); + glNormal3f( vert_norm.x, vert_norm.y, vert_norm.z ); + + glVertex3f( vert_ne.x, vert_ne.y, vert_ne.z ); + glVertex3f( vert_nw.x, vert_nw.y, vert_nw.z ); + glVertex3f( vert_sw.x, vert_sw.y, vert_sw.z ); + glVertex3f( vert_se.x, vert_se.y, vert_se.z ); + + glEnd(); + +#if BOING_DEBUG + printf( "----------------------------------------------------------- \n" ); + printf( "lat = %f long_lo = %f long_hi = %f \n", lat_deg, long_lo, long_hi ); + printf( "vert_ne x = %.8f y = %.8f z = %.8f \n", vert_ne.x, vert_ne.y, vert_ne.z ); + printf( "vert_nw x = %.8f y = %.8f z = %.8f \n", vert_nw.x, vert_nw.y, vert_nw.z ); + printf( "vert_se x = %.8f y = %.8f z = %.8f \n", vert_se.x, vert_se.y, vert_se.z ); + printf( "vert_sw x = %.8f y = %.8f z = %.8f \n", vert_sw.x, vert_sw.y, vert_sw.z ); +#endif + + } + + /* + * Toggle color so that next band will opposite red/white colors than this one. + */ + colorToggle = ! colorToggle; + + /* + * This circular band is done. + */ + return; +} + + +/***************************************************************************** + * Draw the purple grid of lines, behind the Boing ball. + * When the Workbench is dropped to the bottom, Boing shows 12 rows. + *****************************************************************************/ +void DrawGrid( void ) +{ + int row, col; + const int rowTotal = 12; /* must be divisible by 2 */ + const int colTotal = rowTotal; /* must be same as rowTotal */ + const GLfloat widthLine = 2.0; /* should be divisible by 2 */ + const GLfloat sizeCell = GRID_SIZE / rowTotal; + const GLfloat z_offset = -40.0; + GLfloat xl, xr; + GLfloat yt, yb; + + glPushMatrix(); + glDisable( GL_CULL_FACE ); + + /* + * Another relative Z translation to separate objects. + */ + glTranslatef( 0.0, 0.0, DIST_BALL ); + + /* + * Draw vertical lines (as skinny 3D rectangles). + */ + for ( col = 0; col <= colTotal; col++ ) + { + /* + * Compute co-ords of line. + */ + xl = -GRID_SIZE / 2 + col * sizeCell; + xr = xl + widthLine; + + yt = GRID_SIZE / 2; + yb = -GRID_SIZE / 2 - widthLine; + + glBegin( GL_POLYGON ); + + glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */ + + glVertex3f( xr, yt, z_offset ); /* NE */ + glVertex3f( xl, yt, z_offset ); /* NW */ + glVertex3f( xl, yb, z_offset ); /* SW */ + glVertex3f( xr, yb, z_offset ); /* SE */ + + glEnd(); + } + + /* + * Draw horizontal lines (as skinny 3D rectangles). + */ + for ( row = 0; row <= rowTotal; row++ ) + { + /* + * Compute co-ords of line. + */ + yt = GRID_SIZE / 2 - row * sizeCell; + yb = yt - widthLine; + + xl = -GRID_SIZE / 2; + xr = GRID_SIZE / 2 + widthLine; + + glBegin( GL_POLYGON ); + + glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */ + + glVertex3f( xr, yt, z_offset ); /* NE */ + glVertex3f( xl, yt, z_offset ); /* NW */ + glVertex3f( xl, yb, z_offset ); /* SW */ + glVertex3f( xr, yb, z_offset ); /* SE */ + + glEnd(); + } + + glPopMatrix(); + + return; +} + + +/*======================================================================* + * main() + *======================================================================*/ + +int main( void ) +{ + int running; + + /* Init GLFW */ + if( !glfwInit() ) + { + fprintf( stderr, "Failed to initialize GLFW\n" ); + exit( EXIT_FAILURE ); + } + + if( !glfwOpenWindow( 400,400, 0,0,0,0, 16,0, GLFW_WINDOW ) ) + { + fprintf( stderr, "Failed to open GLFW window\n" ); + glfwTerminate(); + exit( EXIT_FAILURE ); + } + + glfwSetWindowTitle( "Boing (classic Amiga demo)" ); + glfwSetWindowSizeCallback( reshape ); + glfwEnable( GLFW_STICKY_KEYS ); + glfwSwapInterval( 1 ); + glfwSetTime( 0.0 ); + + init(); + + /* Main loop */ + do + { + /* Timing */ + t = glfwGetTime(); + dt = t - t_old; + t_old = t; + + /* Draw one frame */ + display(); + + /* Swap buffers */ + glfwSwapBuffers(); + + /* Check if we are still running */ + running = !glfwGetKey( GLFW_KEY_ESC ) && + glfwGetWindowParam( GLFW_OPENED ); + } + while( running ); + + glfwTerminate(); + exit( EXIT_SUCCESS ); +} + diff --git a/examples/gears.c b/examples/gears.c new file mode 100644 index 00000000..5ad484db --- /dev/null +++ b/examples/gears.c @@ -0,0 +1,373 @@ +/* + * 3-D gear wheels. This program is in the public domain. + * + * Command line options: + * -info print GL implementation information + * -exit automatically exit after 30 seconds + * + * + * Brian Paul + * + * + * Marcus Geelnard: + * - Conversion to GLFW + * - Time based rendering (frame rate independent) + * - Slightly modified camera that should work better for stereo viewing + * + * + * Camilla Berglund: + * - Removed FPS counter (this is not a benchmark) + * - Added a few comments + * - Enabled vsync + */ + + +#include +#include +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.141592654 +#endif + +/* The program exits when this is zero. + */ +static int running = 1; + +/* If non-zero, the program exits after that many seconds + */ +static int autoexit = 0; + +/** + + Draw a gear wheel. You'll probably want to call this function when + building a display list since we do a lot of trig here. + + Input: inner_radius - radius of hole at center + outer_radius - radius at center of teeth + width - width of gear teeth - number of teeth + tooth_depth - depth of tooth + + **/ + +static void +gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width, + GLint teeth, GLfloat tooth_depth) +{ + GLint i; + GLfloat r0, r1, r2; + GLfloat angle, da; + GLfloat u, v, len; + + r0 = inner_radius; + r1 = outer_radius - tooth_depth / 2.f; + r2 = outer_radius + tooth_depth / 2.f; + + da = 2.f * (float) M_PI / teeth / 4.f; + + glShadeModel(GL_FLAT); + + glNormal3f(0.f, 0.f, 1.f); + + /* draw front face */ + glBegin(GL_QUAD_STRIP); + for (i = 0; i <= teeth; i++) { + angle = i * 2.f * (float) M_PI / teeth; + glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), width * 0.5f); + glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), width * 0.5f); + if (i < teeth) { + glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), width * 0.5f); + glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), width * 0.5f); + } + } + glEnd(); + + /* draw front sides of teeth */ + glBegin(GL_QUADS); + da = 2.f * (float) M_PI / teeth / 4.f; + for (i = 0; i < teeth; i++) { + angle = i * 2.f * (float) M_PI / teeth; + + glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), width * 0.5f); + glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), width * 0.5f); + glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), width * 0.5f); + glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), width * 0.5f); + } + glEnd(); + + glNormal3f(0.0, 0.0, -1.0); + + /* draw back face */ + glBegin(GL_QUAD_STRIP); + for (i = 0; i <= teeth; i++) { + angle = i * 2.f * (float) M_PI / teeth; + glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), -width * 0.5f); + glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), -width * 0.5f); + if (i < teeth) { + glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), -width * 0.5f); + glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), -width * 0.5f); + } + } + glEnd(); + + /* draw back sides of teeth */ + glBegin(GL_QUADS); + da = 2.f * (float) M_PI / teeth / 4.f; + for (i = 0; i < teeth; i++) { + angle = i * 2.f * (float) M_PI / teeth; + + glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), -width * 0.5f); + glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), -width * 0.5f); + glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), -width * 0.5f); + glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), -width * 0.5f); + } + glEnd(); + + /* draw outward faces of teeth */ + glBegin(GL_QUAD_STRIP); + for (i = 0; i < teeth; i++) { + angle = i * 2.f * (float) M_PI / teeth; + + glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), width * 0.5f); + glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), -width * 0.5f); + u = r2 * (float) cos(angle + da) - r1 * (float) cos(angle); + v = r2 * (float) sin(angle + da) - r1 * (float) sin(angle); + len = (float) sqrt(u * u + v * v); + u /= len; + v /= len; + glNormal3f(v, -u, 0.0); + glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), width * 0.5f); + glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), -width * 0.5f); + glNormal3f((float) cos(angle), (float) sin(angle), 0.f); + glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), width * 0.5f); + glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), -width * 0.5f); + u = r1 * (float) cos(angle + 3 * da) - r2 * (float) cos(angle + 2 * da); + v = r1 * (float) sin(angle + 3 * da) - r2 * (float) sin(angle + 2 * da); + glNormal3f(v, -u, 0.f); + glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), width * 0.5f); + glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), -width * 0.5f); + glNormal3f((float) cos(angle), (float) sin(angle), 0.f); + } + + glVertex3f(r1 * (float) cos(0), r1 * (float) sin(0), width * 0.5f); + glVertex3f(r1 * (float) cos(0), r1 * (float) sin(0), -width * 0.5f); + + glEnd(); + + glShadeModel(GL_SMOOTH); + + /* draw inside radius cylinder */ + glBegin(GL_QUAD_STRIP); + for (i = 0; i <= teeth; i++) { + angle = i * 2.f * (float) M_PI / teeth; + glNormal3f(-(float) cos(angle), -(float) sin(angle), 0.f); + glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), -width * 0.5f); + glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), width * 0.5f); + } + glEnd(); + +} + + +static GLfloat view_rotx = 20.f, view_roty = 30.f, view_rotz = 0.f; +static GLint gear1, gear2, gear3; +static GLfloat angle = 0.f; + +/* OpenGL draw function & timing */ +static void draw(void) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix(); + glRotatef(view_rotx, 1.0, 0.0, 0.0); + glRotatef(view_roty, 0.0, 1.0, 0.0); + glRotatef(view_rotz, 0.0, 0.0, 1.0); + + glPushMatrix(); + glTranslatef(-3.0, -2.0, 0.0); + glRotatef(angle, 0.0, 0.0, 1.0); + glCallList(gear1); + glPopMatrix(); + + glPushMatrix(); + glTranslatef(3.1f, -2.f, 0.f); + glRotatef(-2.f * angle - 9.f, 0.f, 0.f, 1.f); + glCallList(gear2); + glPopMatrix(); + + glPushMatrix(); + glTranslatef(-3.1f, 4.2f, 0.f); + glRotatef(-2.f * angle - 25.f, 0.f, 0.f, 1.f); + glCallList(gear3); + glPopMatrix(); + + glPopMatrix(); +} + + +/* update animation parameters */ +static void animate(void) +{ + angle = 100.f * (float) glfwGetTime(); +} + + +/* change view angle, exit upon ESC */ +void key( int k, int action ) +{ + if( action != GLFW_PRESS ) return; + + switch (k) { + case 'Z': + if( glfwGetKey( GLFW_KEY_LSHIFT ) ) + view_rotz -= 5.0; + else + view_rotz += 5.0; + break; + case GLFW_KEY_ESC: + running = 0; + break; + case GLFW_KEY_UP: + view_rotx += 5.0; + break; + case GLFW_KEY_DOWN: + view_rotx -= 5.0; + break; + case GLFW_KEY_LEFT: + view_roty += 5.0; + break; + case GLFW_KEY_RIGHT: + view_roty -= 5.0; + break; + default: + return; + } +} + + +/* new window size */ +void reshape( int width, int height ) +{ + GLfloat h = (GLfloat) height / (GLfloat) width; + GLfloat xmax, znear, zfar; + + znear = 5.0f; + zfar = 30.0f; + xmax = znear * 0.5f; + + glViewport( 0, 0, (GLint) width, (GLint) height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glFrustum( -xmax, xmax, -xmax*h, xmax*h, znear, zfar ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + glTranslatef( 0.0, 0.0, -20.0 ); +} + + +/* program & OpenGL initialization */ +static void init(int argc, char *argv[]) +{ + static GLfloat pos[4] = {5.f, 5.f, 10.f, 0.f}; + static GLfloat red[4] = {0.8f, 0.1f, 0.f, 1.f}; + static GLfloat green[4] = {0.f, 0.8f, 0.2f, 1.f}; + static GLfloat blue[4] = {0.2f, 0.2f, 1.f, 1.f}; + GLint i; + + glLightfv(GL_LIGHT0, GL_POSITION, pos); + glEnable(GL_CULL_FACE); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_DEPTH_TEST); + + /* make the gears */ + gear1 = glGenLists(1); + glNewList(gear1, GL_COMPILE); + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); + gear(1.f, 4.f, 1.f, 20, 0.7f); + glEndList(); + + gear2 = glGenLists(1); + glNewList(gear2, GL_COMPILE); + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green); + gear(0.5f, 2.f, 2.f, 10, 0.7f); + glEndList(); + + gear3 = glGenLists(1); + glNewList(gear3, GL_COMPILE); + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue); + gear(1.3f, 2.f, 0.5f, 10, 0.7f); + glEndList(); + + glEnable(GL_NORMALIZE); + + for ( i=1; i +#include + +// Maximum number of modes that we want to list +#define MAX_NUM_MODES 400 + + +//======================================================================== +// main() +//======================================================================== + +int main( void ) +{ + GLFWvidmode dtmode, modes[ MAX_NUM_MODES ]; + int modecount, i; + + // Initialize GLFW + if( !glfwInit() ) + { + return 0; + } + + // Show desktop video mode + glfwGetDesktopMode( &dtmode ); + printf( "Desktop mode: %d x %d x %d\n\n", + dtmode.Width, dtmode.Height, dtmode.RedBits + + dtmode.GreenBits + dtmode.BlueBits ); + + // List available video modes + modecount = glfwGetVideoModes( modes, MAX_NUM_MODES ); + printf( "Available modes:\n" ); + for( i = 0; i < modecount; i ++ ) + { + printf( "%3d: %d x %d x %d\n", i, + modes[i].Width, modes[i].Height, modes[i].RedBits + + modes[i].GreenBits + modes[i].BlueBits ); + } + + // Terminate GLFW + glfwTerminate(); + + return 0; +} diff --git a/examples/mipmaps.c b/examples/mipmaps.c new file mode 100644 index 00000000..59bbef2e --- /dev/null +++ b/examples/mipmaps.c @@ -0,0 +1,122 @@ +//======================================================================== +// This is an example program for the GLFW library +// +// It shows texture loading with mipmap generation and rendering with +// trilienar texture filtering +//======================================================================== + +#include +#include + +#include + +int main( void ) +{ + int width, height, x; + double time; + GLboolean running; + GLuint textureID; + char* texturePath = "mipmaps.tga"; + + // Initialise GLFW + if( !glfwInit() ) + { + fprintf( stderr, "Failed to initialize GLFW\n" ); + exit( EXIT_FAILURE ); + } + + // Open OpenGL window + if( !glfwOpenWindow( 640, 480, 0,0,0,0, 0,0, GLFW_WINDOW ) ) + { + fprintf( stderr, "Failed to open GLFW window\n" ); + glfwTerminate(); + exit( EXIT_FAILURE ); + } + + glfwSetWindowTitle( "Trilinear interpolation" ); + + // Enable sticky keys + glfwEnable( GLFW_STICKY_KEYS ); + + // Enable vertical sync (on cards that support it) + glfwSwapInterval( 1 ); + + // Generate and bind our texture ID + glGenTextures( 1, &textureID ); + glBindTexture( GL_TEXTURE_2D, textureID ); + + // Load texture from file into video memory, including mipmap levels + if( !glfwLoadTexture2D( texturePath, GLFW_BUILD_MIPMAPS_BIT ) ) + { + fprintf( stderr, "Failed to load texture %s\n", texturePath ); + glfwTerminate(); + exit( EXIT_FAILURE ); + } + + // Use trilinear interpolation (GL_LINEAR_MIPMAP_LINEAR) + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + GL_LINEAR ); + + // Enable plain 2D texturing + glEnable( GL_TEXTURE_2D ); + + running = GL_TRUE; + while( running ) + { + // Get time and mouse position + time = glfwGetTime(); + glfwGetMousePos( &x, NULL ); + + // Get window size (may be different than the requested size) + glfwGetWindowSize( &width, &height ); + height = height > 0 ? height : 1; + + // Set viewport + glViewport( 0, 0, width, height ); + + // Clear color buffer + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f); + glClear( GL_COLOR_BUFFER_BIT ); + + // Select and setup the projection matrix + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + gluPerspective( 65.0f, (GLfloat)width / (GLfloat)height, 1.0f, + 50.0f ); + + // Select and setup the modelview matrix + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + gluLookAt( 0.0f, 3.0f, -20.0f, // Eye-position + 0.0f, -4.0f, -11.0f, // View-point + 0.0f, 1.0f, 0.0f ); // Up-vector + + // Draw a textured quad + glRotatef( 0.05f * (GLfloat)x + (GLfloat)time * 5.0f, 0.0f, 1.0f, 0.0f ); + glBegin( GL_QUADS ); + glTexCoord2f( -20.0f, 20.0f ); + glVertex3f( -50.0f, 0.0f, -50.0f ); + glTexCoord2f( 20.0f, 20.0f ); + glVertex3f( 50.0f, 0.0f, -50.0f ); + glTexCoord2f( 20.0f, -20.0f ); + glVertex3f( 50.0f, 0.0f, 50.0f ); + glTexCoord2f( -20.0f, -20.0f ); + glVertex3f( -50.0f, 0.0f, 50.0f ); + glEnd(); + + // Swap buffers + glfwSwapBuffers(); + + // Check if the ESC key was pressed or the window was closed + running = !glfwGetKey( GLFW_KEY_ESC ) && + glfwGetWindowParam( GLFW_OPENED ); + } + + // Close OpenGL window and terminate GLFW + glfwTerminate(); + + exit( EXIT_SUCCESS ); +} + diff --git a/examples/mipmaps.tga b/examples/mipmaps.tga new file mode 100644 index 0000000000000000000000000000000000000000..55f913b0fa48f69b163664da1db6f9b1fe80e2ed GIT binary patch literal 66322 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 +#include +#include +#include +#include + +// Define tokens for GL_EXT_separate_specular_color if not already defined +#ifndef GL_EXT_separate_specular_color +#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 +#define GL_SINGLE_COLOR_EXT 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA +#endif // GL_EXT_separate_specular_color + +// Some 's do not define M_PI +#ifndef M_PI +#define M_PI 3.141592654 +#endif + +// Desired fullscreen resolution +#define WIDTH 640 +#define HEIGHT 480 + + +//======================================================================== +// Type definitions +//======================================================================== + +typedef struct { float x,y,z; } VEC; + +// This structure is used for interleaved vertex arrays (see the +// DrawParticles function) - Note: This structure SHOULD be packed on most +// systems. It uses 32-bit fields on 32-bit boundaries, and is a multiple +// of 64 bits in total (6x32=3x64). If it does not work, try using pragmas +// or whatever to force the structure to be packed. +typedef struct { + GLfloat s, t; // Texture coordinates + GLuint rgba; // Color (four ubytes packed into an uint) + GLfloat x, y, z; // Vertex coordinates +} VERTEX; + + +//======================================================================== +// Program control global variables +//======================================================================== + +// "Running" flag (true if program shall continue to run) +int running; + +// Window dimensions +int width, height; + +// "wireframe" flag (true if we use wireframe view) +int wireframe; + +// "multithreading" flag (true if we use multithreading) +int multithreading; + +// Thread synchronization +struct { + double t; // Time (s) + float dt; // Time since last frame (s) + int p_frame; // Particle physics frame number + int d_frame; // Particle draw frame number + GLFWcond p_done; // Condition: particle physics done + GLFWcond d_done; // Condition: particle draw done + GLFWmutex particles_lock; // Particles data sharing mutex +} thread_sync; + + +//======================================================================== +// Texture declarations (we hard-code them into the source code, since +// they are so simple) +//======================================================================== + +#define P_TEX_WIDTH 8 // Particle texture dimensions +#define P_TEX_HEIGHT 8 +#define F_TEX_WIDTH 16 // Floor texture dimensions +#define F_TEX_HEIGHT 16 + +// Texture object IDs +GLuint particle_tex_id, floor_tex_id; + +// Particle texture (a simple spot) +const unsigned char particle_texture[ P_TEX_WIDTH * P_TEX_HEIGHT ] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x11, 0x22, 0x22, 0x11, 0x00, 0x00, + 0x00, 0x11, 0x33, 0x88, 0x77, 0x33, 0x11, 0x00, + 0x00, 0x22, 0x88, 0xff, 0xee, 0x77, 0x22, 0x00, + 0x00, 0x22, 0x77, 0xee, 0xff, 0x88, 0x22, 0x00, + 0x00, 0x11, 0x33, 0x77, 0x88, 0x33, 0x11, 0x00, + 0x00, 0x00, 0x11, 0x33, 0x22, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +// Floor texture (your basic checkered floor) +const unsigned char floor_texture[ F_TEX_WIDTH * F_TEX_HEIGHT ] = { + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0xff, 0xf0, 0xcc, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0xf0, 0xcc, 0xee, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0x66, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xee, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0xf0, 0xf0, 0xf0, 0xf0, 0xcc, 0xf0, 0xf0, 0xf0, 0x30, 0x30, 0x55, 0x30, 0x30, 0x44, 0x30, 0x30, + 0xf0, 0xdd, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0x30, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x33, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, 0xf0, 0xff, 0xf0, 0xf0, 0xdd, 0xf0, 0xf0, 0xff, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x55, 0x33, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xf0, + 0x30, 0x44, 0x66, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xaa, 0xf0, 0xf0, 0xcc, 0xf0, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xff, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xdd, 0xf0, + 0x30, 0x30, 0x30, 0x77, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, +}; + + +//======================================================================== +// These are fixed constants that control the particle engine. In a +// modular world, these values should be variables... +//======================================================================== + +// Maximum number of particles +#define MAX_PARTICLES 3000 + +// Life span of a particle (in seconds) +#define LIFE_SPAN 8.0f + +// A new particle is born every [BIRTH_INTERVAL] second +#define BIRTH_INTERVAL (LIFE_SPAN/(float)MAX_PARTICLES) + +// Particle size (meters) +#define PARTICLE_SIZE 0.7f + +// Gravitational constant (m/s^2) +#define GRAVITY 9.8f + +// Base initial velocity (m/s) +#define VELOCITY 8.0f + +// Bounce friction (1.0 = no friction, 0.0 = maximum friction) +#define FRICTION 0.75f + +// "Fountain" height (m) +#define FOUNTAIN_HEIGHT 3.0f + +// Fountain radius (m) +#define FOUNTAIN_RADIUS 1.6f + +// Minimum delta-time for particle phisics (s) +#define MIN_DELTA_T (BIRTH_INTERVAL * 0.5f) + + +//======================================================================== +// Particle system global variables +//======================================================================== + +// This structure holds all state for a single particle +typedef struct { + float x,y,z; // Position in space + float vx,vy,vz; // Velocity vector + float r,g,b; // Color of particle + float life; // Life of particle (1.0 = newborn, < 0.0 = dead) + int active; // Tells if this particle is active +} PARTICLE; + +// Global vectors holding all particles. We use two vectors for double +// buffering. +static PARTICLE particles[ MAX_PARTICLES ]; + +// Global variable holding the age of the youngest particle +static float min_age; + +// Color of latest born particle (used for fountain lighting) +static float glow_color[4]; + +// Position of latest born particle (used for fountain lighting) +static float glow_pos[4]; + + +//======================================================================== +// Object material and fog configuration constants +//======================================================================== + +const GLfloat fountain_diffuse[4] = {0.7f,1.0f,1.0f,1.0f}; +const GLfloat fountain_specular[4] = {1.0f,1.0f,1.0f,1.0f}; +const GLfloat fountain_shininess = 12.0f; +const GLfloat floor_diffuse[4] = {1.0f,0.6f,0.6f,1.0f}; +const GLfloat floor_specular[4] = {0.6f,0.6f,0.6f,1.0f}; +const GLfloat floor_shininess = 18.0f; +const GLfloat fog_color[4] = {0.1f, 0.1f, 0.1f, 1.0f}; + + +//======================================================================== +// InitParticle() - Initialize a new particle +//======================================================================== + +void InitParticle( PARTICLE *p, double t ) +{ + float xy_angle, velocity; + + // Start position of particle is at the fountain blow-out + p->x = 0.0f; + p->y = 0.0f; + p->z = FOUNTAIN_HEIGHT; + + // Start velocity is up (Z)... + p->vz = 0.7f + (0.3f/4096.f) * (float) (rand() & 4095); + + // ...and a randomly chosen X/Y direction + xy_angle = (2.f * (float)M_PI / 4096.f) * (float) (rand() & 4095); + p->vx = 0.4f * (float) cos( xy_angle ); + p->vy = 0.4f * (float) sin( xy_angle ); + + // Scale velocity vector according to a time-varying velocity + velocity = VELOCITY*(0.8f + 0.1f*(float)(sin( 0.5*t )+sin( 1.31*t ))); + p->vx *= velocity; + p->vy *= velocity; + p->vz *= velocity; + + // Color is time-varying + p->r = 0.7f + 0.3f * (float) sin( 0.34*t + 0.1 ); + p->g = 0.6f + 0.4f * (float) sin( 0.63*t + 1.1 ); + p->b = 0.6f + 0.4f * (float) sin( 0.91*t + 2.1 ); + + // Store settings for fountain glow lighting + glow_pos[0] = 0.4f * (float) sin( 1.34*t ); + glow_pos[1] = 0.4f * (float) sin( 3.11*t ); + glow_pos[2] = FOUNTAIN_HEIGHT + 1.0f; + glow_pos[3] = 1.0f; + glow_color[0] = p->r; + glow_color[1] = p->g; + glow_color[2] = p->b; + glow_color[3] = 1.0f; + + // The particle is new-born and active + p->life = 1.0f; + p->active = 1; +} + + +//======================================================================== +// UpdateParticle() - Update a particle +//======================================================================== + +#define FOUNTAIN_R2 (FOUNTAIN_RADIUS+PARTICLE_SIZE/2)*(FOUNTAIN_RADIUS+PARTICLE_SIZE/2) + +void UpdateParticle( PARTICLE *p, float dt ) +{ + // If the particle is not active, we need not do anything + if( !p->active ) + { + return; + } + + // The particle is getting older... + p->life = p->life - dt * (1.0f / LIFE_SPAN); + + // Did the particle die? + if( p->life <= 0.0f ) + { + p->active = 0; + return; + } + + // Update particle velocity (apply gravity) + p->vz = p->vz - GRAVITY * dt; + + // Update particle position + p->x = p->x + p->vx * dt; + p->y = p->y + p->vy * dt; + p->z = p->z + p->vz * dt; + + // Simple collision detection + response + if( p->vz < 0.0f ) + { + // Particles should bounce on the fountain (with friction) + if( (p->x*p->x + p->y*p->y) < FOUNTAIN_R2 && + p->z < (FOUNTAIN_HEIGHT + PARTICLE_SIZE/2) ) + { + p->vz = -FRICTION * p->vz; + p->z = FOUNTAIN_HEIGHT + PARTICLE_SIZE/2 + + FRICTION * (FOUNTAIN_HEIGHT + + PARTICLE_SIZE/2 - p->z); + } + + // Particles should bounce on the floor (with friction) + else if( p->z < PARTICLE_SIZE/2 ) + { + p->vz = -FRICTION * p->vz; + p->z = PARTICLE_SIZE/2 + + FRICTION * (PARTICLE_SIZE/2 - p->z); + } + + } +} + + +//======================================================================== +// ParticleEngine() - The main frame for the particle engine. Called once +// per frame. +//======================================================================== + +void ParticleEngine( double t, float dt ) +{ + int i; + float dt2; + + // Update particles (iterated several times per frame if dt is too + // large) + while( dt > 0.0f ) + { + // Calculate delta time for this iteration + dt2 = dt < MIN_DELTA_T ? dt : MIN_DELTA_T; + + // Update particles + for( i = 0; i < MAX_PARTICLES; i ++ ) + { + UpdateParticle( &particles[ i ], dt2 ); + } + + // Increase minimum age + min_age += dt2; + + // Should we create any new particle(s)? + while( min_age >= BIRTH_INTERVAL ) + { + min_age -= BIRTH_INTERVAL; + + // Find a dead particle to replace with a new one + for( i = 0; i < MAX_PARTICLES; i ++ ) + { + if( !particles[ i ].active ) + { + InitParticle( &particles[ i ], t + min_age ); + UpdateParticle( &particles[ i ], min_age ); + break; + } + } + } + + // Decrease frame delta time + dt -= dt2; + } +} + + +//======================================================================== +// DrawParticles() - Draw all active particles. We use OpenGL 1.1 vertex +// arrays for this in order to accelerate the drawing. +//======================================================================== + +#define BATCH_PARTICLES 70 // Number of particles to draw in each batch + // (70 corresponds to 7.5 KB = will not blow + // the L1 data cache on most CPUs) +#define PARTICLE_VERTS 4 // Number of vertices per particle + +void DrawParticles( double t, float dt ) +{ + int i, particle_count; + VERTEX vertex_array[ BATCH_PARTICLES * PARTICLE_VERTS ], *vptr; + float alpha; + GLuint rgba; + VEC quad_lower_left, quad_lower_right; + GLfloat mat[ 16 ]; + PARTICLE *pptr; + + // Here comes the real trick with flat single primitive objects (s.c. + // "billboards"): We must rotate the textured primitive so that it + // always faces the viewer (is coplanar with the view-plane). + // We: + // 1) Create the primitive around origo (0,0,0) + // 2) Rotate it so that it is coplanar with the view plane + // 3) Translate it according to the particle position + // Note that 1) and 2) is the same for all particles (done only once). + + // Get modelview matrix. We will only use the upper left 3x3 part of + // the matrix, which represents the rotation. + glGetFloatv( GL_MODELVIEW_MATRIX, mat ); + + // 1) & 2) We do it in one swift step: + // Although not obvious, the following six lines represent two matrix/ + // vector multiplications. The matrix is the inverse 3x3 rotation + // matrix (i.e. the transpose of the same matrix), and the two vectors + // represent the lower left corner of the quad, PARTICLE_SIZE/2 * + // (-1,-1,0), and the lower right corner, PARTICLE_SIZE/2 * (1,-1,0). + // The upper left/right corners of the quad is always the negative of + // the opposite corners (regardless of rotation). + quad_lower_left.x = (-PARTICLE_SIZE/2) * (mat[0] + mat[1]); + quad_lower_left.y = (-PARTICLE_SIZE/2) * (mat[4] + mat[5]); + quad_lower_left.z = (-PARTICLE_SIZE/2) * (mat[8] + mat[9]); + quad_lower_right.x = (PARTICLE_SIZE/2) * (mat[0] - mat[1]); + quad_lower_right.y = (PARTICLE_SIZE/2) * (mat[4] - mat[5]); + quad_lower_right.z = (PARTICLE_SIZE/2) * (mat[8] - mat[9]); + + // Don't update z-buffer, since all particles are transparent! + glDepthMask( GL_FALSE ); + + // Enable blending + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE ); + + // Select particle texture + if( !wireframe ) + { + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, particle_tex_id ); + } + + // Set up vertex arrays. We use interleaved arrays, which is easier to + // handle (in most situations) and it gives a linear memeory access + // access pattern (which may give better performance in some + // situations). GL_T2F_C4UB_V3F means: 2 floats for texture coords, + // 4 ubytes for color and 3 floats for vertex coord (in that order). + // Most OpenGL cards / drivers are optimized for this format. + glInterleavedArrays( GL_T2F_C4UB_V3F, 0, vertex_array ); + + // Is particle physics carried out in a separate thread? + if( multithreading ) + { + // Wait for particle physics thread to be done + glfwLockMutex( thread_sync.particles_lock ); + while( running && thread_sync.p_frame <= thread_sync.d_frame ) + { + glfwWaitCond( thread_sync.p_done, thread_sync.particles_lock, + 0.1 ); + } + + // Store the frame time and delta time for the physics thread + thread_sync.t = t; + thread_sync.dt = dt; + + // Update frame counter + thread_sync.d_frame ++; + } + else + { + // Perform particle physics in this thread + ParticleEngine( t, dt ); + } + + // Loop through all particles and build vertex arrays. + particle_count = 0; + vptr = vertex_array; + pptr = particles; + for( i = 0; i < MAX_PARTICLES; i ++ ) + { + if( pptr->active ) + { + // Calculate particle intensity (we set it to max during 75% + // of its life, then it fades out) + alpha = 4.0f * pptr->life; + if( alpha > 1.0f ) + { + alpha = 1.0f; + } + + // Convert color from float to 8-bit (store it in a 32-bit + // integer using endian independent type casting) + ((GLubyte *)&rgba)[0] = (GLubyte)(pptr->r * 255.0f); + ((GLubyte *)&rgba)[1] = (GLubyte)(pptr->g * 255.0f); + ((GLubyte *)&rgba)[2] = (GLubyte)(pptr->b * 255.0f); + ((GLubyte *)&rgba)[3] = (GLubyte)(alpha * 255.0f); + + // 3) Translate the quad to the correct position in modelview + // space and store its parameters in vertex arrays (we also + // store texture coord and color information for each vertex). + + // Lower left corner + vptr->s = 0.0f; + vptr->t = 0.0f; + vptr->rgba = rgba; + vptr->x = pptr->x + quad_lower_left.x; + vptr->y = pptr->y + quad_lower_left.y; + vptr->z = pptr->z + quad_lower_left.z; + vptr ++; + + // Lower right corner + vptr->s = 1.0f; + vptr->t = 0.0f; + vptr->rgba = rgba; + vptr->x = pptr->x + quad_lower_right.x; + vptr->y = pptr->y + quad_lower_right.y; + vptr->z = pptr->z + quad_lower_right.z; + vptr ++; + + // Upper right corner + vptr->s = 1.0f; + vptr->t = 1.0f; + vptr->rgba = rgba; + vptr->x = pptr->x - quad_lower_left.x; + vptr->y = pptr->y - quad_lower_left.y; + vptr->z = pptr->z - quad_lower_left.z; + vptr ++; + + // Upper left corner + vptr->s = 0.0f; + vptr->t = 1.0f; + vptr->rgba = rgba; + vptr->x = pptr->x - quad_lower_right.x; + vptr->y = pptr->y - quad_lower_right.y; + vptr->z = pptr->z - quad_lower_right.z; + vptr ++; + + // Increase count of drawable particles + particle_count ++; + } + + // If we have filled up one batch of particles, draw it as a set + // of quads using glDrawArrays. + if( particle_count >= BATCH_PARTICLES ) + { + // The first argument tells which primitive type we use (QUAD) + // The second argument tells the index of the first vertex (0) + // The last argument is the vertex count + glDrawArrays( GL_QUADS, 0, PARTICLE_VERTS * particle_count ); + particle_count = 0; + vptr = vertex_array; + } + + // Next particle + pptr ++; + } + + // We are done with the particle data: Unlock mutex and signal physics + // thread + if( multithreading ) + { + glfwUnlockMutex( thread_sync.particles_lock ); + glfwSignalCond( thread_sync.d_done ); + } + + // Draw final batch of particles (if any) + glDrawArrays( GL_QUADS, 0, PARTICLE_VERTS * particle_count ); + + // Disable vertex arrays (Note: glInterleavedArrays implicitly called + // glEnableClientState for vertex, texture coord and color arrays) + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + glDisableClientState( GL_COLOR_ARRAY ); + + // Disable texturing and blending + glDisable( GL_TEXTURE_2D ); + glDisable( GL_BLEND ); + + // Allow Z-buffer updates again + glDepthMask( GL_TRUE ); +} + + +//======================================================================== +// Fountain geometry specification +//======================================================================== + +#define FOUNTAIN_SIDE_POINTS 14 +#define FOUNTAIN_SWEEP_STEPS 32 + +static const float fountain_side[ FOUNTAIN_SIDE_POINTS*2 ] = { + 1.2f, 0.0f, 1.0f, 0.2f, 0.41f, 0.3f, 0.4f, 0.35f, + 0.4f, 1.95f, 0.41f, 2.0f, 0.8f, 2.2f, 1.2f, 2.4f, + 1.5f, 2.7f, 1.55f,2.95f, 1.6f, 3.0f, 1.0f, 3.0f, + 0.5f, 3.0f, 0.0f, 3.0f +}; + +static const float fountain_normal[ FOUNTAIN_SIDE_POINTS*2 ] = { + 1.0000f, 0.0000f, 0.6428f, 0.7660f, 0.3420f, 0.9397f, 1.0000f, 0.0000f, + 1.0000f, 0.0000f, 0.3420f,-0.9397f, 0.4226f,-0.9063f, 0.5000f,-0.8660f, + 0.7660f,-0.6428f, 0.9063f,-0.4226f, 0.0000f,1.00000f, 0.0000f,1.00000f, + 0.0000f,1.00000f, 0.0000f,1.00000f +}; + + +//======================================================================== +// DrawFountain() - Draw a fountain +//======================================================================== + +void DrawFountain( void ) +{ + static GLuint fountain_list = 0; + double angle; + float x, y; + int m, n; + + // The first time, we build the fountain display list + if( !fountain_list ) + { + // Start recording of a new display list + fountain_list = glGenLists( 1 ); + glNewList( fountain_list, GL_COMPILE_AND_EXECUTE ); + + // Set fountain material + glMaterialfv( GL_FRONT, GL_DIFFUSE, fountain_diffuse ); + glMaterialfv( GL_FRONT, GL_SPECULAR, fountain_specular ); + glMaterialf( GL_FRONT, GL_SHININESS, fountain_shininess ); + + // Build fountain using triangle strips + for( n = 0; n < FOUNTAIN_SIDE_POINTS-1; n ++ ) + { + glBegin( GL_TRIANGLE_STRIP ); + for( m = 0; m <= FOUNTAIN_SWEEP_STEPS; m ++ ) + { + angle = (double) m * (2.0*M_PI/(double)FOUNTAIN_SWEEP_STEPS); + x = (float) cos( angle ); + y = (float) sin( angle ); + + // Draw triangle strip + glNormal3f( x * fountain_normal[ n*2+2 ], + y * fountain_normal[ n*2+2 ], + fountain_normal[ n*2+3 ] ); + glVertex3f( x * fountain_side[ n*2+2 ], + y * fountain_side[ n*2+2 ], + fountain_side[ n*2+3 ] ); + glNormal3f( x * fountain_normal[ n*2 ], + y * fountain_normal[ n*2 ], + fountain_normal[ n*2+1 ] ); + glVertex3f( x * fountain_side[ n*2 ], + y * fountain_side[ n*2 ], + fountain_side[ n*2+1 ] ); + } + glEnd(); + } + + // End recording of display list + glEndList(); + } + else + { + // Playback display list + glCallList( fountain_list ); + } +} + + +//======================================================================== +// TesselateFloor() - Recursive function for building variable tesselated +// floor +//======================================================================== + +void TesselateFloor( float x1, float y1, float x2, float y2, + int recursion ) +{ + float delta, x, y; + + // Last recursion? + if( recursion >= 5 ) + { + delta = 999999.0f; + } + else + { + x = (float) (fabs(x1) < fabs(x2) ? fabs(x1) : fabs(x2)); + y = (float) (fabs(y1) < fabs(y2) ? fabs(y1) : fabs(y2)); + delta = x*x + y*y; + } + + // Recurse further? + if( delta < 0.1f ) + { + x = (x1+x2) * 0.5f; + y = (y1+y2) * 0.5f; + TesselateFloor( x1,y1, x, y, recursion + 1 ); + TesselateFloor( x,y1, x2, y, recursion + 1 ); + TesselateFloor( x1, y, x,y2, recursion + 1 ); + TesselateFloor( x, y, x2,y2, recursion + 1 ); + } + else + { + glTexCoord2f( x1*30.0f, y1*30.0f ); + glVertex3f( x1*80.0f, y1*80.0f , 0.0f ); + glTexCoord2f( x2*30.0f, y1*30.0f ); + glVertex3f( x2*80.0f, y1*80.0f , 0.0f ); + glTexCoord2f( x2*30.0f, y2*30.0f ); + glVertex3f( x2*80.0f, y2*80.0f , 0.0f ); + glTexCoord2f( x1*30.0f, y2*30.0f ); + glVertex3f( x1*80.0f, y2*80.0f , 0.0f ); + } +} + + +//======================================================================== +// DrawFloor() - Draw floor. We builde the floor recursively, and let the +// tesselation in the centre (near x,y=0,0) be high, while the selleation +// around the edges be low. +//======================================================================== + +void DrawFloor( void ) +{ + static GLuint floor_list = 0; + + // Select floor texture + if( !wireframe ) + { + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, floor_tex_id ); + } + + // The first time, we build the floor display list + if( !floor_list ) + { + // Start recording of a new display list + floor_list = glGenLists( 1 ); + glNewList( floor_list, GL_COMPILE_AND_EXECUTE ); + + // Set floor material + glMaterialfv( GL_FRONT, GL_DIFFUSE, floor_diffuse ); + glMaterialfv( GL_FRONT, GL_SPECULAR, floor_specular ); + glMaterialf( GL_FRONT, GL_SHININESS, floor_shininess ); + + // Draw floor as a bunch of triangle strips (high tesselation + // improves lighting) + glNormal3f( 0.0f, 0.0f, 1.0f ); + glBegin( GL_QUADS ); + TesselateFloor( -1.0f,-1.0f, 0.0f,0.0f, 0 ); + TesselateFloor( 0.0f,-1.0f, 1.0f,0.0f, 0 ); + TesselateFloor( 0.0f, 0.0f, 1.0f,1.0f, 0 ); + TesselateFloor( -1.0f, 0.0f, 0.0f,1.0f, 0 ); + glEnd(); + + // End recording of display list + glEndList(); + } + else + { + // Playback display list + glCallList( floor_list ); + } + + glDisable( GL_TEXTURE_2D ); + +} + + +//======================================================================== +// SetupLights() - Position and configure light sources +//======================================================================== + +void SetupLights( void ) +{ + float l1pos[4], l1amb[4], l1dif[4], l1spec[4]; + float l2pos[4], l2amb[4], l2dif[4], l2spec[4]; + + // Set light source 1 parameters + l1pos[0] = 0.0f; l1pos[1] = -9.0f; l1pos[2] = 8.0f; l1pos[3] = 1.0f; + l1amb[0] = 0.2f; l1amb[1] = 0.2f; l1amb[2] = 0.2f; l1amb[3] = 1.0f; + l1dif[0] = 0.8f; l1dif[1] = 0.4f; l1dif[2] = 0.2f; l1dif[3] = 1.0f; + l1spec[0] = 1.0f; l1spec[1] = 0.6f; l1spec[2] = 0.2f; l1spec[3] = 0.0f; + + // Set light source 2 parameters + l2pos[0] = -15.0f; l2pos[1] = 12.0f; l2pos[2] = 1.5f; l2pos[3] = 1.0f; + l2amb[0] = 0.0f; l2amb[1] = 0.0f; l2amb[2] = 0.0f; l2amb[3] = 1.0f; + l2dif[0] = 0.2f; l2dif[1] = 0.4f; l2dif[2] = 0.8f; l2dif[3] = 1.0f; + l2spec[0] = 0.2f; l2spec[1] = 0.6f; l2spec[2] = 1.0f; l2spec[3] = 0.0f; + + // Configure light sources in OpenGL + glLightfv( GL_LIGHT1, GL_POSITION, l1pos ); + glLightfv( GL_LIGHT1, GL_AMBIENT, l1amb ); + glLightfv( GL_LIGHT1, GL_DIFFUSE, l1dif ); + glLightfv( GL_LIGHT1, GL_SPECULAR, l1spec ); + glLightfv( GL_LIGHT2, GL_POSITION, l2pos ); + glLightfv( GL_LIGHT2, GL_AMBIENT, l2amb ); + glLightfv( GL_LIGHT2, GL_DIFFUSE, l2dif ); + glLightfv( GL_LIGHT2, GL_SPECULAR, l2spec ); + glLightfv( GL_LIGHT3, GL_POSITION, glow_pos ); + glLightfv( GL_LIGHT3, GL_DIFFUSE, glow_color ); + glLightfv( GL_LIGHT3, GL_SPECULAR, glow_color ); + + // Enable light sources + glEnable( GL_LIGHT1 ); + glEnable( GL_LIGHT2 ); + glEnable( GL_LIGHT3 ); +} + + +//======================================================================== +// Draw() - Main rendering function +//======================================================================== + +void Draw( double t ) +{ + double xpos, ypos, zpos, angle_x, angle_y, angle_z; + static double t_old = 0.0; + float dt; + + // Calculate frame-to-frame delta time + dt = (float)(t-t_old); + t_old = t; + + // Setup viewport + glViewport( 0, 0, width, height ); + + // Clear color and Z-buffer + glClearColor( 0.1f, 0.1f, 0.1f, 1.0f ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // Setup projection + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + gluPerspective( 65.0, (double)width/(double)height, 1.0, 60.0 ); + + // Setup camera + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Rotate camera + angle_x = 90.0 - 10.0; + angle_y = 10.0 * sin( 0.3 * t ); + angle_z = 10.0 * t; + glRotated( -angle_x, 1.0, 0.0, 0.0 ); + glRotated( -angle_y, 0.0, 1.0, 0.0 ); + glRotated( -angle_z, 0.0, 0.0, 1.0 ); + + // Translate camera + xpos = 15.0 * sin( (M_PI/180.0) * angle_z ) + + 2.0 * sin( (M_PI/180.0) * 3.1 * t ); + ypos = -15.0 * cos( (M_PI/180.0) * angle_z ) + + 2.0 * cos( (M_PI/180.0) * 2.9 * t ); + zpos = 4.0 + 2.0 * cos( (M_PI/180.0) * 4.9 * t ); + glTranslated( -xpos, -ypos, -zpos ); + + // Enable face culling + glFrontFace( GL_CCW ); + glCullFace( GL_BACK ); + glEnable( GL_CULL_FACE ); + + // Enable lighting + SetupLights(); + glEnable( GL_LIGHTING ); + + // Enable fog (dim details far away) + glEnable( GL_FOG ); + glFogi( GL_FOG_MODE, GL_EXP ); + glFogf( GL_FOG_DENSITY, 0.05f ); + glFogfv( GL_FOG_COLOR, fog_color ); + + // Draw floor + DrawFloor(); + + // Enable Z-buffering + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LEQUAL ); + glDepthMask( GL_TRUE ); + + // Draw fountain + DrawFountain(); + + // Disable fog & lighting + glDisable( GL_LIGHTING ); + glDisable( GL_FOG ); + + // Draw all particles (must be drawn after all solid objects have been + // drawn!) + DrawParticles( t, dt ); + + // Z-buffer not needed anymore + glDisable( GL_DEPTH_TEST ); +} + + +//======================================================================== +// Resize() - GLFW window resize callback function +//======================================================================== + +void GLFWCALL Resize( int x, int y ) +{ + width = x; + height = y > 0 ? y : 1; // Prevent division by zero in aspect calc. +} + + +//======================================================================== +// Input callback functions +//======================================================================== + +void GLFWCALL KeyFun( int key, int action ) +{ + if( action == GLFW_PRESS ) + { + switch( key ) + { + case GLFW_KEY_ESC: + running = 0; + break; + case 'W': + wireframe = !wireframe; + glPolygonMode( GL_FRONT_AND_BACK, + wireframe ? GL_LINE : GL_FILL ); + break; + default: + break; + } + } +} + + +//======================================================================== +// PhysicsThreadFun() - Thread for updating particle physics +//======================================================================== + +void GLFWCALL PhysicsThreadFun( void *arg ) +{ + while( running ) + { + // Lock mutex + glfwLockMutex( thread_sync.particles_lock ); + + // Wait for particle drawing to be done + while( running && thread_sync.p_frame > thread_sync.d_frame ) + { + glfwWaitCond( thread_sync.d_done, thread_sync.particles_lock, + 0.1 ); + } + + // No longer running? + if( !running ) + { + break; + } + + // Update particles + ParticleEngine( thread_sync.t, thread_sync.dt ); + + // Update frame counter + thread_sync.p_frame ++; + + // Unlock mutex and signal drawing thread + glfwUnlockMutex( thread_sync.particles_lock ); + glfwSignalCond( thread_sync.p_done ); + } +} + + +//======================================================================== +// main() +//======================================================================== + +int main( int argc, char **argv ) +{ + int i, frames, benchmark; + double t0, t; + GLFWthread physics_thread = 0; + + // Use multithreading by default, but don't benchmark + multithreading = 1; + benchmark = 0; + + // Check command line arguments + for( i = 1; i < argc; i ++ ) + { + // Use benchmarking? + if( strcmp( argv[i], "-b" ) == 0 ) + { + benchmark = 1; + } + + // Force multithreading off? + else if( strcmp( argv[i], "-s" ) == 0 ) + { + multithreading = 0; + } + + // With a Finder launch on Mac OS X we get a bogus -psn_0_46268417 + // kind of argument (actual numbers vary). Ignore it. + else if( strncmp( argv[i], "-psn_", 5) == 0 ); + + // Usage + else + { + if( strcmp( argv[i], "-?" ) != 0 ) + { + printf( "Unknonwn option %s\n\n", argv[ i ] ); + } + printf( "Usage: %s [options]\n", argv[ 0 ] ); + printf( "\n"); + printf( "Options:\n" ); + printf( " -b Benchmark (run program for 60 s)\n" ); + printf( " -s Run program as single thread (default is to use two threads)\n" ); + printf( " -? Display this text\n" ); + printf( "\n"); + printf( "Program runtime controls:\n" ); + printf( " w Toggle wireframe mode\n" ); + printf( " ESC Exit program\n" ); + exit( 0 ); + } + } + + // Initialize GLFW + if( !glfwInit() ) + { + fprintf( stderr, "Failed to initialize GLFW\n" ); + exit( EXIT_FAILURE ); + } + + // Open OpenGL fullscreen window + if( !glfwOpenWindow( WIDTH, HEIGHT, 0,0,0,0, 16,0, GLFW_FULLSCREEN ) ) + { + fprintf( stderr, "Failed to open GLFW window\n" ); + glfwTerminate(); + exit( EXIT_FAILURE ); + } + + // Set window title + glfwSetWindowTitle( "Particle engine" ); + + // Disable VSync (we want to get as high FPS as possible!) + glfwSwapInterval( 0 ); + + // Window resize callback function + glfwSetWindowSizeCallback( Resize ); + + // Set keyboard input callback function + glfwSetKeyCallback( KeyFun ); + + // Upload particle texture + glGenTextures( 1, &particle_tex_id ); + glBindTexture( GL_TEXTURE_2D, particle_tex_id ); + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE, P_TEX_WIDTH, P_TEX_HEIGHT, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, particle_texture ); + + // Upload floor texture + glGenTextures( 1, &floor_tex_id ); + glBindTexture( GL_TEXTURE_2D, floor_tex_id ); + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE, F_TEX_WIDTH, F_TEX_HEIGHT, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, floor_texture ); + + // Check if we have GL_EXT_separate_specular_color, and if so use it + if( glfwExtensionSupported( "GL_EXT_separate_specular_color" ) ) + { + glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL_EXT, + GL_SEPARATE_SPECULAR_COLOR_EXT ); + } + + // Set filled polygon mode as default (not wireframe) + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + wireframe = 0; + + // Clear particle system + for( i = 0; i < MAX_PARTICLES; i ++ ) + { + particles[ i ].active = 0; + } + min_age = 0.0f; + + // Set "running" flag + running = 1; + + // Set initial times + thread_sync.t = 0.0; + thread_sync.dt = 0.001f; + + // Init threading + if( multithreading ) + { + thread_sync.p_frame = 0; + thread_sync.d_frame = 0; + thread_sync.particles_lock = glfwCreateMutex(); + thread_sync.p_done = glfwCreateCond(); + thread_sync.d_done = glfwCreateCond(); + physics_thread = glfwCreateThread( PhysicsThreadFun, NULL ); + } + + // Main loop + t0 = glfwGetTime(); + frames = 0; + while( running ) + { + // Get frame time + t = glfwGetTime() - t0; + + // Draw... + Draw( t ); + + // Swap buffers + glfwSwapBuffers(); + + // Check if window was closed + running = running && glfwGetWindowParam( GLFW_OPENED ); + + // Increase frame count + frames ++; + + // End of benchmark? + if( benchmark && t >= 60.0 ) + { + running = 0; + } + } + t = glfwGetTime() - t0; + + // Wait for particle physics thread to die + if( multithreading ) + { + glfwWaitThread( physics_thread, GLFW_WAIT ); + } + + // Display profiling information + printf( "%d frames in %.2f seconds = %.1f FPS", frames, t, + (double)frames / t ); + printf( " (multithreading %s)\n", multithreading ? "on" : "off" ); + + // Terminate OpenGL + glfwTerminate(); + + exit( EXIT_SUCCESS ); +} + diff --git a/examples/pong3d.c b/examples/pong3d.c new file mode 100644 index 00000000..1d1afd1f --- /dev/null +++ b/examples/pong3d.c @@ -0,0 +1,854 @@ +//======================================================================== +// This is a small test application for GLFW. +// This is an OpenGL port of the famous "PONG" game (the first computer +// game ever?). It is very simple, and could be improved alot. It was +// created in order to show off the gaming capabilities of GLFW. +//======================================================================== + +#include +#include +#include +#include + + +//======================================================================== +// Constants +//======================================================================== + +// Screen resolution +#define WIDTH 640 +#define HEIGHT 480 + +// Player size (units) +#define PLAYER_XSIZE 0.05f +#define PLAYER_YSIZE 0.15f + +// Ball size (units) +#define BALL_SIZE 0.02f + +// Maximum player movement speed (units / second) +#define MAX_SPEED 1.5f + +// Player movement acceleration (units / seconds^2) +#define ACCELERATION 4.0f + +// Player movement deceleration (units / seconds^2) +#define DECELERATION 2.0f + +// Ball movement speed (units / second) +#define BALL_SPEED 0.4f + +// Menu options +#define MENU_NONE 0 +#define MENU_PLAY 1 +#define MENU_QUIT 2 + +// Game events +#define NOBODY_WINS 0 +#define PLAYER1_WINS 1 +#define PLAYER2_WINS 2 + +// Winner ID +#define NOBODY 0 +#define PLAYER1 1 +#define PLAYER2 2 + +// Camera positions +#define CAMERA_CLASSIC 0 +#define CAMERA_ABOVE 1 +#define CAMERA_SPECTATOR 2 +#define CAMERA_DEFAULT CAMERA_CLASSIC + + +//======================================================================== +// Textures +//======================================================================== + +#define TEX_TITLE 0 +#define TEX_MENU 1 +#define TEX_INSTR 2 +#define TEX_WINNER1 3 +#define TEX_WINNER2 4 +#define TEX_FIELD 5 +#define NUM_TEXTURES 6 + +// Texture names +char * tex_name[ NUM_TEXTURES ] = { + "pong3d_title.tga", + "pong3d_menu.tga", + "pong3d_instr.tga", + "pong3d_winner1.tga", + "pong3d_winner2.tga", + "pong3d_field.tga" +}; + +// OpenGL texture object IDs +GLuint tex_id[ NUM_TEXTURES ]; + + +//======================================================================== +// Global variables +//======================================================================== + +// Display information +int width, height; + +// Frame information +double thistime, oldtime, dt, starttime; + +// Camera information +int camerapos; + +// Player information +struct { + double ypos; // -1.0 to +1.0 + double yspeed; // -MAX_SPEED to +MAX_SPEED +} player1, player2; + +// Ball information +struct { + double xpos, ypos; + double xspeed, yspeed; +} ball; + +// And the winner is... +int winner; + +// Lighting configuration +const GLfloat env_ambient[4] = {1.0f,1.0f,1.0f,1.0f}; +const GLfloat light1_position[4] = {-3.0f,3.0f,2.0f,1.0f}; +const GLfloat light1_diffuse[4] = {1.0f,1.0f,1.0f,0.0f}; +const GLfloat light1_ambient[4] = {0.0f,0.0f,0.0f,0.0f}; + +// Object material properties +const GLfloat player1_diffuse[4] = {1.0f,0.3f,0.3f,1.0f}; +const GLfloat player1_ambient[4] = {0.3f,0.1f,0.0f,1.0f}; +const GLfloat player2_diffuse[4] = {0.3f,1.0f,0.3f,1.0f}; +const GLfloat player2_ambient[4] = {0.1f,0.3f,0.1f,1.0f}; +const GLfloat ball_diffuse[4] = {1.0f,1.0f,0.5f,1.0f}; +const GLfloat ball_ambient[4] = {0.3f,0.3f,0.1f,1.0f}; +const GLfloat border_diffuse[4] = {0.3f,0.3f,1.0f,1.0f}; +const GLfloat border_ambient[4] = {0.1f,0.1f,0.3f,1.0f}; +const GLfloat floor_diffuse[4] = {1.0f,1.0f,1.0f,1.0f}; +const GLfloat floor_ambient[4] = {0.3f,0.3f,0.3f,1.0f}; + + +//======================================================================== +// LoadTextures() - Load textures from disk and upload to OpenGL card +//======================================================================== + +GLboolean LoadTextures( void ) +{ + int i; + + // Generate texture objects + glGenTextures( NUM_TEXTURES, tex_id ); + + // Load textures + for( i = 0; i < NUM_TEXTURES; i ++ ) + { + // Select texture object + glBindTexture( GL_TEXTURE_2D, tex_id[ i ] ); + + // Set texture parameters + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + + // Upload texture from file to texture memory + if( !glfwLoadTexture2D( tex_name[ i ], 0 ) ) + { + fprintf( stderr, "Failed to load texture %s\n", tex_name[ i ] ); + return GL_FALSE; + } + } + + return GL_TRUE; +} + + +//======================================================================== +// DrawImage() - Draw a 2D image as a texture +//======================================================================== + +void DrawImage( int texnum, float x1, float x2, float y1, float y2 ) +{ + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, tex_id[ texnum ] ); + glBegin( GL_QUADS ); + glTexCoord2f( 0.0f, 1.0f ); + glVertex2f( x1, y1 ); + glTexCoord2f( 1.0f, 1.0f ); + glVertex2f( x2, y1 ); + glTexCoord2f( 1.0f, 0.0f ); + glVertex2f( x2, y2 ); + glTexCoord2f( 0.0f, 0.0f ); + glVertex2f( x1, y2 ); + glEnd(); + glDisable( GL_TEXTURE_2D ); +} + + +//======================================================================== +// GameMenu() - Game menu (returns menu option) +//======================================================================== + +int GameMenu( void ) +{ + int option; + + // Enable sticky keys + glfwEnable( GLFW_STICKY_KEYS ); + + // Wait for a game menu key to be pressed + do + { + // Get window size + glfwGetWindowSize( &width, &height ); + + // Set viewport + glViewport( 0, 0, width, height ); + + // Clear display + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + glClear( GL_COLOR_BUFFER_BIT ); + + // Setup projection matrix + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f ); + + // Setup modelview matrix + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Display title + glColor3f( 1.0f, 1.0f, 1.0f ); + DrawImage( TEX_TITLE, 0.1f, 0.9f, 0.0f, 0.3f ); + + // Display menu + glColor3f( 1.0f, 1.0f, 0.0f ); + DrawImage( TEX_MENU, 0.38f, 0.62f, 0.35f, 0.5f ); + + // Display instructions + glColor3f( 0.0f, 1.0f, 1.0f ); + DrawImage( TEX_INSTR, 0.32f, 0.68f, 0.65f, 0.85f ); + + // Swap buffers + glfwSwapBuffers(); + + // Check for keys + if( glfwGetKey( 'Q' ) || !glfwGetWindowParam( GLFW_OPENED ) ) + { + option = MENU_QUIT; + } + else if( glfwGetKey( GLFW_KEY_F1 ) ) + { + option = MENU_PLAY; + } + else + { + option = MENU_NONE; + } + + // To avoid horrible busy waiting, sleep for at least 20 ms + glfwSleep( 0.02 ); + } + while( option == MENU_NONE ); + + // Disable sticky keys + glfwDisable( GLFW_STICKY_KEYS ); + + return option; +} + + +//======================================================================== +// NewGame() - Initialize a new game +//======================================================================== + +void NewGame( void ) +{ + // Frame information + starttime = thistime = glfwGetTime(); + + // Camera information + camerapos = CAMERA_DEFAULT; + + // Player 1 information + player1.ypos = 0.0; + player1.yspeed = 0.0; + + // Player 2 information + player2.ypos = 0.0; + player2.yspeed = 0.0; + + // Ball information + ball.xpos = -1.0 + PLAYER_XSIZE; + ball.ypos = player1.ypos; + ball.xspeed = 1.0; + ball.yspeed = 1.0; +} + + +//======================================================================== +// PlayerControl() - Player control +//======================================================================== + +void PlayerControl( void ) +{ + float joy1pos[ 2 ], joy2pos[ 2 ]; + + // Get joystick X & Y axis positions + glfwGetJoystickPos( GLFW_JOYSTICK_1, joy1pos, 2 ); + glfwGetJoystickPos( GLFW_JOYSTICK_2, joy2pos, 2 ); + + // Player 1 control + if( glfwGetKey( 'A' ) || joy1pos[ 1 ] > 0.2f ) + { + player1.yspeed += dt * ACCELERATION; + if( player1.yspeed > MAX_SPEED ) + { + player1.yspeed = MAX_SPEED; + } + } + else if( glfwGetKey( 'Z' ) || joy1pos[ 1 ] < -0.2f ) + { + player1.yspeed -= dt * ACCELERATION; + if( player1.yspeed < -MAX_SPEED ) + { + player1.yspeed = -MAX_SPEED; + } + } + else + { + player1.yspeed /= exp( DECELERATION * dt ); + } + + // Player 2 control + if( glfwGetKey( 'K' ) || joy2pos[ 1 ] > 0.2f ) + { + player2.yspeed += dt * ACCELERATION; + if( player2.yspeed > MAX_SPEED ) + { + player2.yspeed = MAX_SPEED; + } + } + else if( glfwGetKey( 'M' ) || joy2pos[ 1 ] < -0.2f ) + { + player2.yspeed -= dt * ACCELERATION; + if( player2.yspeed < -MAX_SPEED ) + { + player2.yspeed = -MAX_SPEED; + } + } + else + { + player2.yspeed /= exp( DECELERATION * dt ); + } + + // Update player 1 position + player1.ypos += dt * player1.yspeed; + if( player1.ypos > 1.0 - PLAYER_YSIZE ) + { + player1.ypos = 1.0 - PLAYER_YSIZE; + player1.yspeed = 0.0; + } + else if( player1.ypos < -1.0 + PLAYER_YSIZE ) + { + player1.ypos = -1.0 + PLAYER_YSIZE; + player1.yspeed = 0.0; + } + + // Update player 2 position + player2.ypos += dt * player2.yspeed; + if( player2.ypos > 1.0 - PLAYER_YSIZE ) + { + player2.ypos = 1.0 - PLAYER_YSIZE; + player2.yspeed = 0.0; + } + else if( player2.ypos < -1.0 + PLAYER_YSIZE ) + { + player2.ypos = -1.0 + PLAYER_YSIZE; + player2.yspeed = 0.0; + } +} + + +//======================================================================== +// BallControl() - Ball control +//======================================================================== + +int BallControl( void ) +{ + int event; + double ballspeed; + + // Calculate new ball speed + ballspeed = BALL_SPEED * (1.0 + 0.02*(thistime-starttime)); + ball.xspeed = ball.xspeed > 0 ? ballspeed : -ballspeed; + ball.yspeed = ball.yspeed > 0 ? ballspeed : -ballspeed; + ball.yspeed *= 0.74321; + + // Update ball position + ball.xpos += dt * ball.xspeed; + ball.ypos += dt * ball.yspeed; + + // Did the ball hit a top/bottom wall? + if( ball.ypos >= 1.0 ) + { + ball.ypos = 2.0 - ball.ypos; + ball.yspeed = -ball.yspeed; + } + else if( ball.ypos <= -1.0 ) + { + ball.ypos = -2.0 - ball.ypos; + ball.yspeed = -ball.yspeed; + } + + // Did the ball hit/miss a player? + event = NOBODY_WINS; + + // Is the ball entering the player 1 goal? + if( ball.xpos < -1.0 + PLAYER_XSIZE ) + { + // Did player 1 catch the ball? + if( ball.ypos > (player1.ypos-PLAYER_YSIZE) && + ball.ypos < (player1.ypos+PLAYER_YSIZE) ) + { + ball.xpos = -2.0 + 2.0*PLAYER_XSIZE - ball.xpos; + ball.xspeed = -ball.xspeed; + } + else + { + event = PLAYER2_WINS; + } + } + + // Is the ball entering the player 2 goal? + if( ball.xpos > 1.0 - PLAYER_XSIZE ) + { + // Did player 2 catch the ball? + if( ball.ypos > (player2.ypos-PLAYER_YSIZE) && + ball.ypos < (player2.ypos+PLAYER_YSIZE) ) + { + ball.xpos = 2.0 - 2.0*PLAYER_XSIZE - ball.xpos; + ball.xspeed = -ball.xspeed; + } + else + { + event = PLAYER1_WINS; + } + } + + return event; +} + + +//======================================================================== +// DrawBox() - Draw a 3D box +//======================================================================== + +#define TEX_SCALE 4.0f + + +void DrawBox( float x1, float y1, float z1, float x2, float y2, float z2 ) +{ + // Draw six sides of a cube + glBegin( GL_QUADS ); + // Side 1 (down) + glNormal3f( 0.0f, 0.0f, -1.0f ); + glTexCoord2f( 0.0f, 0.0f ); + glVertex3f( x1,y2,z1 ); + glTexCoord2f( TEX_SCALE, 0.0f ); + glVertex3f( x2,y2,z1 ); + glTexCoord2f( TEX_SCALE, TEX_SCALE ); + glVertex3f( x2,y1,z1 ); + glTexCoord2f( 0.0f, TEX_SCALE ); + glVertex3f( x1,y1,z1 ); + // Side 2 (up) + glNormal3f( 0.0f, 0.0f, 1.0f ); + glTexCoord2f( 0.0f, 0.0f ); + glVertex3f( x1,y1,z2 ); + glTexCoord2f( TEX_SCALE, 0.0f ); + glVertex3f( x2,y1,z2 ); + glTexCoord2f( TEX_SCALE, TEX_SCALE ); + glVertex3f( x2,y2,z2 ); + glTexCoord2f( 0.0f, TEX_SCALE ); + glVertex3f( x1,y2,z2 ); + // Side 3 (backward) + glNormal3f( 0.0f, -1.0f, 0.0f ); + glTexCoord2f( 0.0f, 0.0f ); + glVertex3f( x1,y1,z1 ); + glTexCoord2f( TEX_SCALE, 0.0f ); + glVertex3f( x2,y1,z1 ); + glTexCoord2f( TEX_SCALE, TEX_SCALE ); + glVertex3f( x2,y1,z2 ); + glTexCoord2f( 0.0f, TEX_SCALE ); + glVertex3f( x1,y1,z2 ); + // Side 4 (forward) + glNormal3f( 0.0f, 1.0f, 0.0f ); + glTexCoord2f( 0.0f, 0.0f ); + glVertex3f( x1,y2,z2 ); + glTexCoord2f( TEX_SCALE, 0.0f ); + glVertex3f( x2,y2,z2 ); + glTexCoord2f( TEX_SCALE, TEX_SCALE ); + glVertex3f( x2,y2,z1 ); + glTexCoord2f( 0.0f, TEX_SCALE ); + glVertex3f( x1,y2,z1 ); + // Side 5 (left) + glNormal3f( -1.0f, 0.0f, 0.0f ); + glTexCoord2f( 0.0f, 0.0f ); + glVertex3f( x1,y1,z2 ); + glTexCoord2f( TEX_SCALE, 0.0f ); + glVertex3f( x1,y2,z2 ); + glTexCoord2f( TEX_SCALE, TEX_SCALE ); + glVertex3f( x1,y2,z1 ); + glTexCoord2f( 0.0f, TEX_SCALE ); + glVertex3f( x1,y1,z1 ); + // Side 6 (right) + glNormal3f( 1.0f, 0.0f, 0.0f ); + glTexCoord2f( 0.0f, 0.0f ); + glVertex3f( x2,y1,z1 ); + glTexCoord2f( TEX_SCALE, 0.0f ); + glVertex3f( x2,y2,z1 ); + glTexCoord2f( TEX_SCALE, TEX_SCALE ); + glVertex3f( x2,y2,z2 ); + glTexCoord2f( 0.0f, TEX_SCALE ); + glVertex3f( x2,y1,z2 ); + glEnd(); +} + + +//======================================================================== +// UpdateDisplay() - Draw graphics (all game related OpenGL stuff goes +// here) +//======================================================================== + +void UpdateDisplay( void ) +{ + // Get window size + glfwGetWindowSize( &width, &height ); + + // Set viewport + glViewport( 0, 0, width, height ); + + // Clear display + glClearColor( 0.02f, 0.02f, 0.02f, 0.0f ); + glClearDepth( 1.0f ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // Setup projection matrix + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + gluPerspective( + 55.0f, // Angle of view + (GLfloat)width/(GLfloat)height, // Aspect + 1.0f, // Near Z + 100.0f // Far Z + ); + + // Setup modelview matrix + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + switch( camerapos ) + { + default: + case CAMERA_CLASSIC: + gluLookAt( + 0.0f, 0.0f, 2.5f, + 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f + ); + break; + case CAMERA_ABOVE: + gluLookAt( + 0.0f, 0.0f, 2.5f, + (float)ball.xpos, (float)ball.ypos, 0.0f, + 0.0f, 1.0f, 0.0f + ); + break; + case CAMERA_SPECTATOR: + gluLookAt( + 0.0f, -2.0, 1.2f, + (float)ball.xpos, (float)ball.ypos, 0.0f, + 0.0f, 0.0f, 1.0f + ); + break; + } + + // Enable depth testing + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LEQUAL ); + + // Enable lighting + glEnable( GL_LIGHTING ); + glLightModelfv( GL_LIGHT_MODEL_AMBIENT, env_ambient ); + glLightModeli( GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE ); + glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE ); + glLightfv( GL_LIGHT1, GL_POSITION, light1_position ); + glLightfv( GL_LIGHT1, GL_DIFFUSE, light1_diffuse ); + glLightfv( GL_LIGHT1, GL_AMBIENT, light1_ambient ); + glEnable( GL_LIGHT1 ); + + // Front face is counter-clock-wise + glFrontFace( GL_CCW ); + + // Enable face culling (not necessary, but speeds up rendering) + glCullFace( GL_BACK ); + glEnable( GL_CULL_FACE ); + + // Draw Player 1 + glMaterialfv( GL_FRONT, GL_DIFFUSE, player1_diffuse ); + glMaterialfv( GL_FRONT, GL_AMBIENT, player1_ambient ); + DrawBox( -1.f, (GLfloat)player1.ypos-PLAYER_YSIZE, 0.f, + -1.f+PLAYER_XSIZE, (GLfloat)player1.ypos+PLAYER_YSIZE, 0.1f ); + + // Draw Player 2 + glMaterialfv( GL_FRONT, GL_DIFFUSE, player2_diffuse ); + glMaterialfv( GL_FRONT, GL_AMBIENT, player2_ambient ); + DrawBox( 1.f-PLAYER_XSIZE, (GLfloat)player2.ypos-PLAYER_YSIZE, 0.f, + 1.f, (GLfloat)player2.ypos+PLAYER_YSIZE, 0.1f ); + + // Draw Ball + glMaterialfv( GL_FRONT, GL_DIFFUSE, ball_diffuse ); + glMaterialfv( GL_FRONT, GL_AMBIENT, ball_ambient ); + DrawBox( (GLfloat)ball.xpos-BALL_SIZE, (GLfloat)ball.ypos-BALL_SIZE, 0.f, + (GLfloat)ball.xpos+BALL_SIZE, (GLfloat)ball.ypos+BALL_SIZE, BALL_SIZE*2 ); + + // Top game field border + glMaterialfv( GL_FRONT, GL_DIFFUSE, border_diffuse ); + glMaterialfv( GL_FRONT, GL_AMBIENT, border_ambient ); + DrawBox( -1.1f, 1.0f, 0.0f, 1.1f, 1.1f, 0.1f ); + // Bottom game field border + glColor3f( 0.0f, 0.0f, 0.7f ); + DrawBox( -1.1f, -1.1f, 0.0f, 1.1f, -1.0f, 0.1f ); + // Left game field border + DrawBox( -1.1f, -1.0f, 0.0f, -1.0f, 1.0f, 0.1f ); + // Left game field border + DrawBox( 1.0f, -1.0f, 0.0f, 1.1f, 1.0f, 0.1f ); + + // Enable texturing + glEnable( GL_TEXTURE_2D ); + glBindTexture( GL_TEXTURE_2D, tex_id[ TEX_FIELD ] ); + + // Game field floor + glMaterialfv( GL_FRONT, GL_DIFFUSE, floor_diffuse ); + glMaterialfv( GL_FRONT, GL_AMBIENT, floor_ambient ); + DrawBox( -1.01f, -1.01f, -0.01f, 1.01f, 1.01f, 0.0f ); + + // Disable texturing + glDisable( GL_TEXTURE_2D ); + + // Disable face culling + glDisable( GL_CULL_FACE ); + + // Disable lighting + glDisable( GL_LIGHTING ); + + // Disable depth testing + glDisable( GL_DEPTH_TEST ); +} + + +//======================================================================== +// GameOver() +//======================================================================== + +void GameOver( void ) +{ + // Enable sticky keys + glfwEnable( GLFW_STICKY_KEYS ); + + // Until the user presses ESC or SPACE + while( !glfwGetKey( GLFW_KEY_ESC ) && !glfwGetKey( ' ' ) && + glfwGetWindowParam( GLFW_OPENED ) ) + { + // Draw display + UpdateDisplay(); + + // Setup projection matrix + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f ); + + // Setup modelview matrix + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Enable blending + glEnable( GL_BLEND ); + + // Dim background + glBlendFunc( GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA ); + glColor4f( 0.3f, 0.3f, 0.3f, 0.3f ); + glBegin( GL_QUADS ); + glVertex2f( 0.0f, 0.0f ); + glVertex2f( 1.0f, 0.0f ); + glVertex2f( 1.0f, 1.0f ); + glVertex2f( 0.0f, 1.0f ); + glEnd(); + + // Display winner text + glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_COLOR ); + if( winner == PLAYER1 ) + { + glColor4f( 1.0f, 0.5f, 0.5f, 1.0f ); + DrawImage( TEX_WINNER1, 0.35f, 0.65f, 0.46f, 0.54f ); + } + else if( winner == PLAYER2 ) + { + glColor4f( 0.5f, 1.0f, 0.5f, 1.0f ); + DrawImage( TEX_WINNER2, 0.35f, 0.65f, 0.46f, 0.54f ); + } + + // Disable blending + glDisable( GL_BLEND ); + + // Swap buffers + glfwSwapBuffers(); + } + + // Disable sticky keys + glfwDisable( GLFW_STICKY_KEYS ); +} + + +//======================================================================== +// GameLoop() - Game loop +//======================================================================== + +void GameLoop( void ) +{ + int playing, event; + + // Initialize a new game + NewGame(); + + // Enable sticky keys + glfwEnable( GLFW_STICKY_KEYS ); + + // Loop until the game ends + playing = GL_TRUE; + while( playing && glfwGetWindowParam( GLFW_OPENED ) ) + { + // Frame timer + oldtime = thistime; + thistime = glfwGetTime(); + dt = thistime - oldtime; + + // Get user input and update player positions + PlayerControl(); + + // Move the ball, and check if a player hits/misses the ball + event = BallControl(); + + // Did we have a winner? + switch( event ) + { + case PLAYER1_WINS: + winner = PLAYER1; + playing = GL_FALSE; + break; + case PLAYER2_WINS: + winner = PLAYER2; + playing = GL_FALSE; + break; + default: + break; + } + + // Did the user press ESC? + if( glfwGetKey( GLFW_KEY_ESC ) ) + { + playing = GL_FALSE; + } + + // Did the user change camera view? + if( glfwGetKey( '1' ) ) + { + camerapos = CAMERA_CLASSIC; + } + else if( glfwGetKey( '2' ) ) + { + camerapos = CAMERA_ABOVE; + } + else if( glfwGetKey( '3' ) ) + { + camerapos = CAMERA_SPECTATOR; + } + + // Draw display + UpdateDisplay(); + + // Swap buffers + glfwSwapBuffers(); + } + + // Disable sticky keys + glfwDisable( GLFW_STICKY_KEYS ); + + // Show winner + GameOver(); +} + + +//======================================================================== +// main() - Program entry point +//======================================================================== + +int main( void ) +{ + int menuoption; + + // Initialize GLFW + if( !glfwInit() ) + { + fprintf( stderr, "Failed to initialize GLFW\n" ); + exit( EXIT_FAILURE ); + } + + // Open OpenGL window + if( !glfwOpenWindow( WIDTH, HEIGHT, 0,0,0,0, 16,0, GLFW_FULLSCREEN ) ) + { + fprintf( stderr, "Failed to open GLFW window\n" ); + glfwTerminate(); + exit( EXIT_FAILURE ); + } + + glfwSwapInterval( 1 ); + + // Load all textures + if( !LoadTextures() ) + { + glfwTerminate(); + exit( EXIT_FAILURE ); + } + + // Main loop + do + { + // Get menu option + menuoption = GameMenu(); + + // If the user wants to play, let him... + if( menuoption == MENU_PLAY ) + { + GameLoop(); + } + } + while( menuoption != MENU_QUIT ); + + // Unload all textures + if( glfwGetWindowParam( GLFW_OPENED ) ) + { + glDeleteTextures( NUM_TEXTURES, tex_id ); + } + + // Terminate GLFW + glfwTerminate(); + + exit( EXIT_SUCCESS ); +} + diff --git a/examples/pong3d_field.tga b/examples/pong3d_field.tga new file mode 100644 index 0000000000000000000000000000000000000000..cc20bbdb82434faa3bbc01d27fd199d5214c8b2f GIT binary patch literal 17816 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 T6XevNs>&3`i95eA{_g(;MdbFK literal 0 HcmV?d00001 diff --git a/examples/pong3d_instr.tga b/examples/pong3d_instr.tga new file mode 100644 index 0000000000000000000000000000000000000000..758eb447a97187cef812cb9100aa94631954c256 GIT binary patch literal 21279 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 literal 0 HcmV?d00001 diff --git a/examples/pong3d_menu.tga b/examples/pong3d_menu.tga new file mode 100644 index 0000000000000000000000000000000000000000..d0d6c5a431687bfd6442e43c3878a9eb905c48b8 GIT binary patch literal 1835 zcmbVMTawc-5FFd7KrLK{i*bQLe#C)-km6&1*q~+M0-Qi+$_YgGjAbh+zSu1}vefFC z>1kQyjg-Il@=jjMRenoIj~IXChe+&m7gLVsZQ6^Zkej3-2jP$yvZRY#urXlCQ#Jn=lGQ*?msDM%?fom_#3D0WLYd>| zrM8HEH4zdvDSlgLUu?J06w%uHU$F5BH?%^)6{nIMA=d zk04{L6gsNXLvPU$y7bcLR=F~R;{Yz~AoE6761IR~EZy-Sc90#?@jjEKjdZ2xgd?t2 zqtTm)Fw3kYxE=nmQqN{37J-&5_=7y4JqUx5<790bksq4`P6Xm?W2J#gsv94$c80Ep zhB(C(Tcv$%4nwfg>5lD?ncV@L-j6_|Q=WiD2HsmBQ7_k*Bwh}mIAS5_(&Zv)0+JQ*BV=)qzNL$W#l<{{t=av1 zh6fQJ3*Rf(fcY7y8DcVufs`P8r)X65U6x`6n^qTRM5LcL_vtFnz@hK-l+BKK+wK>L@83So!V-2z3xXZm}7`ZB*l$gvAC5tvMEd)H$-L j=(lM?g-Vm}Fwx!OsnJ{IV_$0Jn){}t&ktU$$5s9UqXSX} literal 0 HcmV?d00001 diff --git a/examples/pong3d_title.tga b/examples/pong3d_title.tga new file mode 100644 index 0000000000000000000000000000000000000000..d0d8e36d74011c25b908647632b4997dc0cf6456 GIT binary patch literal 106516 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 literal 0 HcmV?d00001 diff --git a/examples/pong3d_winner1.tga b/examples/pong3d_winner1.tga new file mode 100644 index 0000000000000000000000000000000000000000..f963720c5b897c01b8694a0fb2d76ad617877a7d GIT binary patch literal 861 zcmZvaO-{ow5QV29sw!Ag>q$zL$Sb2xaDZHc?={d~z#x?;&rAh7an>TNM%UG8<_V`T}Av+dQR1Q!U7Qc6{sSH8wNZP39q9qy#b$XF9@I6ez)S<6n zq_ehk;iMZ&fsh(meZGobq4LV7e~pc@d~^GlelDh;wKF}zCi`6*Je6~M>is%9SNEbW LDfk1czN>u!GHiyO literal 0 HcmV?d00001 diff --git a/examples/pong3d_winner2.tga b/examples/pong3d_winner2.tga new file mode 100644 index 0000000000000000000000000000000000000000..ea8266dea9f6ca7b261f2163b4f2bc4777a59027 GIT binary patch literal 891 zcmZvbOHRWu5Qe8Asw!Ag>q)E8}th{PDhL`jrJ%{=Jv6BL#k|RH6{>RMJ?8(;O zi#^-HZuV}m>~grW8Ozl^%t9=dzKDzE#2U(`*Z?%X-syv|S(? zK)sHv$a>wtB4Eb?Ma3Y>!sKURS)Tb0r3+%vTaEo6DJm{if#^X&j{?aT=fbk|<~&_O zt+PmDbft=sKHTt?QQd&&`CbHe)uYgASx)wtida}Bc}9?zn%(@SUw-irZBG_CWXznz z=XiOM7P58fg0$VeHnc3XW7$gDg_J8@PnncbAuX(+Ga!3cnA!amNonh?123}NqEvQz zr;kDo0gOh0x+DVBrpbD_p`wQ*Phz^)ltEEbz;t}sX=m;%O{Moj&@6$p=8wfP;J5)? zkhFgfjw0#^%tT0ME$!44Ga|_7pj0Ep>Z9on#mYG5)@84;kMi2DpOfl)jnK+)SAA~d cBD_m`U#oV84}EF9M`aSmW&0a^ddAJZ0j)ZbKmY&$ literal 0 HcmV?d00001 diff --git a/examples/splitview.c b/examples/splitview.c new file mode 100644 index 00000000..a26cd2c5 --- /dev/null +++ b/examples/splitview.c @@ -0,0 +1,514 @@ +//======================================================================== +// This is an example program for the GLFW library +// +// The program uses a "split window" view, rendering four views of the +// same scene in one window (e.g. uesful for 3D modelling software). This +// demo uses scissors to separete the four different rendering areas from +// each other. +// +// (If the code seems a little bit strange here and there, it may be +// because I am not a friend of orthogonal projections) +//======================================================================== + +#include +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + + +//======================================================================== +// Global variables +//======================================================================== + +// Mouse position +static int xpos = 0, ypos = 0; + +// Window size +static int width, height; + +// Active view: 0 = none, 1 = upper left, 2 = upper right, 3 = lower left, +// 4 = lower right +static int active_view = 0; + +// Rotation around each axis +static int rot_x = 0, rot_y = 0, rot_z = 0; + +// Do redraw? +static int do_redraw = 1; + + +//======================================================================== +// Draw a solid torus (use a display list for the model) +//======================================================================== + +#define TORUS_MAJOR 1.5 +#define TORUS_MINOR 0.5 +#define TORUS_MAJOR_RES 32 +#define TORUS_MINOR_RES 32 + +static void drawTorus( void ) +{ + static GLuint torus_list = 0; + int i, j, k; + double s, t, x, y, z, nx, ny, nz, scale, twopi; + + if( !torus_list ) + { + // Start recording displaylist + torus_list = glGenLists( 1 ); + glNewList( torus_list, GL_COMPILE_AND_EXECUTE ); + + // Draw torus + twopi = 2.0 * M_PI; + for( i = 0; i < TORUS_MINOR_RES; i++ ) + { + glBegin( GL_QUAD_STRIP ); + for( j = 0; j <= TORUS_MAJOR_RES; j++ ) + { + for( k = 1; k >= 0; k-- ) + { + s = (i + k) % TORUS_MINOR_RES + 0.5; + t = j % TORUS_MAJOR_RES; + + // Calculate point on surface + x = (TORUS_MAJOR+TORUS_MINOR*cos(s*twopi/TORUS_MINOR_RES))*cos(t*twopi/TORUS_MAJOR_RES); + y = TORUS_MINOR * sin(s * twopi / TORUS_MINOR_RES); + z = (TORUS_MAJOR+TORUS_MINOR*cos(s*twopi/TORUS_MINOR_RES))*sin(t*twopi/TORUS_MAJOR_RES); + + // Calculate surface normal + nx = x - TORUS_MAJOR*cos(t*twopi/TORUS_MAJOR_RES); + ny = y; + nz = z - TORUS_MAJOR*sin(t*twopi/TORUS_MAJOR_RES); + scale = 1.0 / sqrt( nx*nx + ny*ny + nz*nz ); + nx *= scale; + ny *= scale; + nz *= scale; + + glNormal3f( (float)nx, (float)ny, (float)nz ); + glVertex3f( (float)x, (float)y, (float)z ); + } + } + glEnd(); + } + + // Stop recording displaylist + glEndList(); + } + else + { + // Playback displaylist + glCallList( torus_list ); + } +} + + +//======================================================================== +// Draw the scene (a rotating torus) +//======================================================================== + +static void drawScene( void ) +{ + const GLfloat model_diffuse[4] = {1.0f, 0.8f, 0.8f, 1.0f}; + const GLfloat model_specular[4] = {0.6f, 0.6f, 0.6f, 1.0f}; + const GLfloat model_shininess = 20.0f; + + glPushMatrix(); + + // Rotate the object + glRotatef( (GLfloat)rot_x*0.5f, 1.0f, 0.0f, 0.0f ); + glRotatef( (GLfloat)rot_y*0.5f, 0.0f, 1.0f, 0.0f ); + glRotatef( (GLfloat)rot_z*0.5f, 0.0f, 0.0f, 1.0f ); + + // Set model color (used for orthogonal views, lighting disabled) + glColor4fv( model_diffuse ); + + // Set model material (used for perspective view, lighting enabled) + glMaterialfv( GL_FRONT, GL_DIFFUSE, model_diffuse ); + glMaterialfv( GL_FRONT, GL_SPECULAR, model_specular ); + glMaterialf( GL_FRONT, GL_SHININESS, model_shininess ); + + // Draw torus + drawTorus(); + + glPopMatrix(); +} + + +//======================================================================== +// Draw a 2D grid (used for orthogonal views) +//======================================================================== + +static void drawGrid( float scale, int steps ) +{ + int i; + float x, y; + + glPushMatrix(); + + // Set background to some dark bluish grey + glClearColor( 0.05f, 0.05f, 0.2f, 0.0f); + glClear( GL_COLOR_BUFFER_BIT ); + + // Setup modelview matrix (flat XY view) + glLoadIdentity(); + gluLookAt( 0.0, 0.0, 1.0, + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0 ); + + // We don't want to update the Z-buffer + glDepthMask( GL_FALSE ); + + // Set grid color + glColor3f( 0.0f, 0.5f, 0.5f ); + + glBegin( GL_LINES ); + + // Horizontal lines + x = scale * 0.5f * (float)(steps-1); + y = -scale * 0.5f * (float)(steps-1); + for( i = 0; i < steps; i ++ ) + { + glVertex3f( -x, y, 0.0f ); + glVertex3f( x, y, 0.0f ); + y += scale; + } + + // Vertical lines + x = -scale * 0.5f * (float)(steps-1); + y = scale * 0.5f * (float)(steps-1); + for( i = 0; i < steps; i ++ ) + { + glVertex3f( x, -y, 0.0f ); + glVertex3f( x, y, 0.0f ); + x += scale; + } + + glEnd(); + + // Enable Z-buffer writing again + glDepthMask( GL_TRUE ); + + glPopMatrix(); +} + + +//======================================================================== +// Draw all views +//======================================================================== + +static void drawAllViews( void ) +{ + const GLfloat light_position[4] = {0.0f, 8.0f, 8.0f, 1.0f}; + const GLfloat light_diffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + const GLfloat light_specular[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + const GLfloat light_ambient[4] = {0.2f, 0.2f, 0.3f, 1.0f}; + double aspect; + + // Calculate aspect of window + if( height > 0 ) + { + aspect = (double)width / (double)height; + } + else + { + aspect = 1.0; + } + + // Clear screen + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // Enable scissor test + glEnable( GL_SCISSOR_TEST ); + + // Enable depth test + glEnable( GL_DEPTH_TEST ); + glDepthFunc( GL_LEQUAL ); + + + // ** ORTHOGONAL VIEWS ** + + // For orthogonal views, use wireframe rendering + glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + + // Enable line anti-aliasing + glEnable( GL_LINE_SMOOTH ); + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + + // Setup orthogonal projection matrix + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( -3.0*aspect, 3.0*aspect, -3.0, 3.0, 1.0, 50.0 ); + + // Upper left view (TOP VIEW) + glViewport( 0, height/2, width/2, height/2 ); + glScissor( 0, height/2, width/2, height/2 ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + gluLookAt( 0.0f, 10.0f, 1e-3f, // Eye-position (above) + 0.0f, 0.0f, 0.0f, // View-point + 0.0f, 1.0f, 0.0f ); // Up-vector + drawGrid( 0.5, 12 ); + drawScene(); + + // Lower left view (FRONT VIEW) + glViewport( 0, 0, width/2, height/2 ); + glScissor( 0, 0, width/2, height/2 ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + gluLookAt( 0.0f, 0.0f, 10.0f, // Eye-position (in front of) + 0.0f, 0.0f, 0.0f, // View-point + 0.0f, 1.0f, 0.0f ); // Up-vector + drawGrid( 0.5, 12 ); + drawScene(); + + // Lower right view (SIDE VIEW) + glViewport( width/2, 0, width/2, height/2 ); + glScissor( width/2, 0, width/2, height/2 ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + gluLookAt( 10.0f, 0.0f, 0.0f, // Eye-position (to the right) + 0.0f, 0.0f, 0.0f, // View-point + 0.0f, 1.0f, 0.0f ); // Up-vector + drawGrid( 0.5, 12 ); + drawScene(); + + // Disable line anti-aliasing + glDisable( GL_LINE_SMOOTH ); + glDisable( GL_BLEND ); + + + // ** PERSPECTIVE VIEW ** + + // For perspective view, use solid rendering + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + + // Enable face culling (faster rendering) + glEnable( GL_CULL_FACE ); + glCullFace( GL_BACK ); + glFrontFace( GL_CW ); + + // Setup perspective projection matrix + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + gluPerspective( 65.0f, aspect, 1.0f, 50.0f ); + + // Upper right view (PERSPECTIVE VIEW) + glViewport( width/2, height/2, width/2, height/2 ); + glScissor( width/2, height/2, width/2, height/2 ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + gluLookAt( 3.0f, 1.5f, 3.0f, // Eye-position + 0.0f, 0.0f, 0.0f, // View-point + 0.0f, 1.0f, 0.0f ); // Up-vector + + // Configure and enable light source 1 + glLightfv( GL_LIGHT1, GL_POSITION, light_position ); + glLightfv( GL_LIGHT1, GL_AMBIENT, light_ambient ); + glLightfv( GL_LIGHT1, GL_DIFFUSE, light_diffuse ); + glLightfv( GL_LIGHT1, GL_SPECULAR, light_specular ); + glEnable( GL_LIGHT1 ); + glEnable( GL_LIGHTING ); + + // Draw scene + drawScene(); + + // Disable lighting + glDisable( GL_LIGHTING ); + + // Disable face culling + glDisable( GL_CULL_FACE ); + + // Disable depth test + glDisable( GL_DEPTH_TEST ); + + // Disable scissor test + glDisable( GL_SCISSOR_TEST ); + + + // Draw a border around the active view + if( active_view > 0 && active_view != 2 ) + { + glViewport( 0, 0, width, height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0.0, 2.0, 0.0, 2.0, 0.0, 1.0 ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + glColor3f( 1.0f, 1.0f, 0.6f ); + glTranslatef( (GLfloat) ((active_view - 1) & 1), (GLfloat) (1 - (active_view - 1) / 2), 0.0f ); + glBegin( GL_LINE_STRIP ); + glVertex2i( 0, 0 ); + glVertex2i( 1, 0 ); + glVertex2i( 1, 1 ); + glVertex2i( 0, 1 ); + glVertex2i( 0, 0 ); + glEnd(); + } +} + + +//======================================================================== +// Window size callback function +//======================================================================== + +static void windowSizeFun( int w, int h ) +{ + width = w; + height = h > 0 ? h : 1; + do_redraw = 1; +} + + +//======================================================================== +// Window refresh callback function +//======================================================================== + +static void windowRefreshFun( void ) +{ + do_redraw = 1; +} + + +//======================================================================== +// Mouse position callback function +//======================================================================== + +static void mousePosFun( int x, int y ) +{ + // Depending on which view was selected, rotate around different axes + switch( active_view ) + { + case 1: + rot_x += y - ypos; + rot_z += x - xpos; + do_redraw = 1; + break; + case 3: + rot_x += y - ypos; + rot_y += x - xpos; + do_redraw = 1; + break; + case 4: + rot_y += x - xpos; + rot_z += y - ypos; + do_redraw = 1; + break; + default: + // Do nothing for perspective view, or if no view is selected + break; + } + + // Remember mouse position + xpos = x; + ypos = y; +} + + +//======================================================================== +// Mouse button callback function +//======================================================================== + +static void mouseButtonFun( int button, int action ) +{ + // Button clicked? + if( ( button == GLFW_MOUSE_BUTTON_LEFT ) && action == GLFW_PRESS ) + { + // Detect which of the four views was clicked + active_view = 1; + if( xpos >= width/2 ) + { + active_view += 1; + } + if( ypos >= height/2 ) + { + active_view += 2; + } + } + + // Button released? + else if( button == GLFW_MOUSE_BUTTON_LEFT ) + { + // Deselect any previously selected view + active_view = 0; + } + + do_redraw = 1; +} + + +//======================================================================== +// main() +//======================================================================== + +int main( void ) +{ + // Initialise GLFW + if( !glfwInit() ) + { + fprintf( stderr, "Failed to initialize GLFW\n" ); + exit( EXIT_FAILURE ); + } + + // Open OpenGL window + if( !glfwOpenWindow( 500, 500, 0,0,0,0, 16,0, GLFW_WINDOW ) ) + { + fprintf( stderr, "Failed to open GLFW window\n" ); + glfwTerminate(); + exit( EXIT_FAILURE ); + } + + // Enable vsync + glfwSwapInterval( 1 ); + + // Set window title + glfwSetWindowTitle( "Split view demo" ); + + // Enable sticky keys + glfwEnable( GLFW_STICKY_KEYS ); + + // Enable mouse cursor (only needed for fullscreen mode) + glfwEnable( GLFW_MOUSE_CURSOR ); + + // Disable automatic event polling + glfwDisable( GLFW_AUTO_POLL_EVENTS ); + + // Set callback functions + glfwSetWindowSizeCallback( windowSizeFun ); + glfwSetWindowRefreshCallback( windowRefreshFun ); + glfwSetMousePosCallback( mousePosFun ); + glfwSetMouseButtonCallback( mouseButtonFun ); + + // Main loop + do + { + // Only redraw if we need to + if( do_redraw ) + { + // Draw all views + drawAllViews(); + + // Swap buffers + glfwSwapBuffers(); + + do_redraw = 0; + } + + // Wait for new events + glfwWaitEvents(); + + } // Check if the ESC key was pressed or the window was closed + while( glfwGetKey( GLFW_KEY_ESC ) != GLFW_PRESS && + glfwGetWindowParam( GLFW_OPENED ) ); + + // Close OpenGL window and terminate GLFW + glfwTerminate(); + + exit( EXIT_SUCCESS ); +} + diff --git a/examples/triangle.c b/examples/triangle.c new file mode 100644 index 00000000..a8b737be --- /dev/null +++ b/examples/triangle.c @@ -0,0 +1,94 @@ +//======================================================================== +// This is a small test application for GLFW. +// The program opens a window (640x480), and renders a spinning colored +// triangle (it is controlled with both the GLFW timer and the mouse). +//======================================================================== + +#include +#include +#include + + +int main( void ) +{ + int width, height, x; + double t; + + // Initialise GLFW + if( !glfwInit() ) + { + fprintf( stderr, "Failed to initialize GLFW\n" ); + exit( EXIT_FAILURE ); + } + + // Open a window and create its OpenGL context + if( !glfwOpenWindow( 640, 480, 0,0,0,0, 0,0, GLFW_WINDOW ) ) + { + fprintf( stderr, "Failed to open GLFW window\n" ); + + glfwTerminate(); + exit( EXIT_FAILURE ); + } + + glfwSetWindowTitle( "Spinning Triangle" ); + + // Ensure we can capture the escape key being pressed below + glfwEnable( GLFW_STICKY_KEYS ); + + // Enable vertical sync (on cards that support it) + glfwSwapInterval( 1 ); + + do + { + t = glfwGetTime(); + glfwGetMousePos( &x, NULL ); + + // Get window size (may be different than the requested size) + glfwGetWindowSize( &width, &height ); + + // Special case: avoid division by zero below + height = height > 0 ? height : 1; + + glViewport( 0, 0, width, height ); + + // Clear color buffer to black + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + glClear( GL_COLOR_BUFFER_BIT ); + + // Select and setup the projection matrix + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + gluPerspective( 65.0f, (GLfloat)width/(GLfloat)height, 1.0f, 100.0f ); + + // Select and setup the modelview matrix + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + gluLookAt( 0.0f, 1.0f, 0.0f, // Eye-position + 0.0f, 20.0f, 0.0f, // View-point + 0.0f, 0.0f, 1.0f ); // Up-vector + + // Draw a rotating colorful triangle + glTranslatef( 0.0f, 14.0f, 0.0f ); + glRotatef( 0.3f*(GLfloat)x + (GLfloat)t*100.0f, 0.0f, 0.0f, 1.0f ); + glBegin( GL_TRIANGLES ); + glColor3f( 1.0f, 0.0f, 0.0f ); + glVertex3f( -5.0f, 0.0f, -4.0f ); + glColor3f( 0.0f, 1.0f, 0.0f ); + glVertex3f( 5.0f, 0.0f, -4.0f ); + glColor3f( 0.0f, 0.0f, 1.0f ); + glVertex3f( 0.0f, 0.0f, 6.0f ); + glEnd(); + + // Swap buffers + glfwSwapBuffers(); + + } // Check if the ESC key was pressed or the window was closed + while( glfwGetKey( GLFW_KEY_ESC ) != GLFW_PRESS && + glfwGetWindowParam( GLFW_OPENED ) ); + + // Close OpenGL window and terminate GLFW + glfwTerminate(); + + exit( EXIT_SUCCESS ); +} + diff --git a/examples/wave.c b/examples/wave.c new file mode 100644 index 00000000..571cc9d5 --- /dev/null +++ b/examples/wave.c @@ -0,0 +1,399 @@ +/***************************************************************************** + * Wave Simulation in OpenGL + * (C) 2002 Jakob Thomsen + * http://home.in.tum.de/~thomsen + * Modified for GLFW by Sylvain Hellegouarch - sh@programmationworld.com + * Modified for variable frame rate by Marcus Geelnard + * 2003-Jan-31: Minor cleanups and speedups / MG + *****************************************************************************/ + +#include +#include +#include +#include + +#ifndef M_PI + #define M_PI 3.1415926535897932384626433832795 +#endif + +/* Maximum delta T to allow for differential calculations */ +#define MAX_DELTA_T 0.01 + +/* Animation speed (10.0 looks good) */ +#define ANIMATION_SPEED 10.0 + + +GLfloat alpha = 210.0f, beta = -70.0f; +GLfloat zoom = 2.0f; + +int running = 1; + +struct Vertex +{ + GLfloat x,y,z; + GLfloat r,g,b; +}; + +#define GRIDW 50 +#define GRIDH 50 +#define VERTEXNUM (GRIDW*GRIDH) + +#define QUADW (GRIDW-1) +#define QUADH (GRIDH-1) +#define QUADNUM (QUADW*QUADH) + +GLuint quad[4*QUADNUM]; +struct Vertex vertex[VERTEXNUM]; + +/* The grid will look like this: + * + * 3 4 5 + * *---*---* + * | | | + * | 0 | 1 | + * | | | + * *---*---* + * 0 1 2 + */ + +void initVertices( void ) +{ + int x,y,p; + + /* place the vertices in a grid */ + for(y=0;y1) zoom-=1; + break; + case GLFW_KEY_PAGEDOWN: + zoom+=1; + break; + default: + break; + } +} + + +/* Callback function for window resize events */ +void handle_resize( int width, int height ) +{ + float ratio = 1.0f; + + if( height > 0 ) + { + ratio = (float) width / (float) height; + } + + /* Setup viewport (Place where the stuff will appear in the main window). */ + glViewport(0, 0, width, height); + + /* + * Change to the projection matrix and set + * our viewing volume. + */ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(60.0, ratio, 1.0, 1024.0); +} + + +/* Program entry point */ +int main(int argc, char* argv[]) +{ + /* Dimensions of our window. */ + int width, height; + /* Style of our window. */ + int mode; + /* Frame time */ + double t, t_old, dt_total; + + /* Initialize GLFW */ + if(glfwInit() == GL_FALSE) + { + fprintf(stderr, "GLFW initialization failed\n"); + exit(-1); + } + + /* Desired window properties */ + width = 640; + height = 480; + mode = GLFW_WINDOW; + + /* Open window */ + if( glfwOpenWindow(width,height,0,0,0,0,16,0,mode) == GL_FALSE ) + { + fprintf(stderr, "Could not open window\n"); + glfwTerminate(); + exit(-1); + } + + /* Set title */ + glfwSetWindowTitle( "Wave Simulation" ); + + glfwSwapInterval( 1 ); + + /* Keyboard handler */ + glfwSetKeyCallback( handle_key_down ); + glfwEnable( GLFW_KEY_REPEAT ); + + /* Window resize handler */ + glfwSetWindowSizeCallback( handle_resize ); + + /* Initialize OpenGL */ + setup_opengl(); + + /* Initialize simulation */ + initVertices(); + initSurface(); + adjustGrid(); + + /* Initialize timer */ + t_old = glfwGetTime() - 0.01; + + /* Main loop */ + while(running) + { + /* Timing */ + t = glfwGetTime(); + dt_total = t - t_old; + t_old = t; + + /* Safety - iterate if dt_total is too large */ + while( dt_total > 0.0f ) + { + /* Select iteration time step */ + dt = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total; + dt_total -= dt; + + /* Calculate wave propagation */ + calc(); + } + + /* Compute height of each vertex */ + adjustGrid(); + + /* Draw wave grid to OpenGL display */ + draw_screen(); + + /* Still running? */ + running = running && glfwGetWindowParam( GLFW_OPENED ); + } + + glfwTerminate(); + + return 0; +} diff --git a/include/GL/glfw.h b/include/GL/glfw.h new file mode 100644 index 00000000..63313db3 --- /dev/null +++ b/include/GL/glfw.h @@ -0,0 +1,446 @@ +/************************************************************************ + * GLFW - An OpenGL framework + * API version: 2.7 + * WWW: http://www.glfw.org/ + *------------------------------------------------------------------------ + * Copyright (c) 2002-2006 Marcus Geelnard + * Copyright (c) 2006-2010 Camilla Berglund + * + * 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 __glfw_h_ +#define __glfw_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* + * Global definitions + *************************************************************************/ + +/* We need a NULL pointer from time to time */ +#ifndef NULL + #ifdef __cplusplus + #define NULL 0 + #else + #define NULL ((void *)0) + #endif +#endif /* NULL */ + + +/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */ + +/* Please report any probles that you find with your compiler, which may + * be solved in this section! There are several compilers that I have not + * been able to test this file with yet. + * + * First: If we are we on Windows, we want a single define for it (_WIN32) + * (Note: For Cygwin the compiler flag -mwin32 should be used, but to + * make sure that things run smoothly for Cygwin users, we add __CYGWIN__ + * to the list of "valid Win32 identifiers", which removes the need for + * -mwin32) + */ +#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__)) + #define _WIN32 +#endif /* _WIN32 */ + +/* In order for extension support to be portable, we need to define an + * OpenGL function call method. We use the keyword APIENTRY, which is + * defined for Win32. (Note: Windows also needs this for ) + */ +#ifndef APIENTRY + #ifdef _WIN32 + #define APIENTRY __stdcall + #else + #define APIENTRY + #endif + #define GL_APIENTRY_DEFINED +#endif /* APIENTRY */ + + +/* The following three defines are here solely to make some Windows-based + * files happy. Theoretically we could include , but + * it has the major drawback of severely polluting our namespace. + */ + +/* Under Windows, we need WINGDIAPI defined */ +#if !defined(WINGDIAPI) && defined(_WIN32) + #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__) + /* Microsoft Visual C++, Borland C++ Builder and Pelles C */ + #define WINGDIAPI __declspec(dllimport) + #elif defined(__LCC__) + /* LCC-Win32 */ + #define WINGDIAPI __stdcall + #else + /* Others (e.g. MinGW, Cygwin) */ + #define WINGDIAPI extern + #endif + #define GL_WINGDIAPI_DEFINED +#endif /* WINGDIAPI */ + +/* Some files also need CALLBACK defined */ +#if !defined(CALLBACK) && defined(_WIN32) + #if defined(_MSC_VER) + /* Microsoft Visual C++ */ + #if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS) + #define CALLBACK __stdcall + #else + #define CALLBACK + #endif + #else + /* Other Windows compilers */ + #define CALLBACK __stdcall + #endif + #define GLU_CALLBACK_DEFINED +#endif /* CALLBACK */ + +/* Microsoft Visual C++, Borland C++ and Pelles C needs wchar_t */ +#if defined(_WIN32) && (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__)) && !defined(_WCHAR_T_DEFINED) + typedef unsigned short wchar_t; + #define _WCHAR_T_DEFINED +#endif /* _WCHAR_T_DEFINED */ + + +/* ---------------- GLFW related system specific defines ----------------- */ + +#if defined(_WIN32) && defined(GLFW_BUILD_DLL) + + /* We are building a Win32 DLL */ + #define GLFWAPI __declspec(dllexport) + +#elif defined(_WIN32) && defined(GLFW_DLL) + + /* We are calling a Win32 DLL */ + #if defined(__LCC__) + #define GLFWAPI extern + #else + #define GLFWAPI __declspec(dllimport) + #endif + +#else + + /* We are either building/calling a static lib or we are non-win32 */ + #define GLFWAPI + +#endif + +/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ + +/* Include standard OpenGL headers: GLFW uses GL_FALSE/GL_TRUE, and it is + * convenient for the user to only have to include . This also + * solves the problem with Windows and needing some + * special defines which normally requires the user to include + * (which is not a nice solution for portable programs). + */ +#if defined(__APPLE_CC__) + #include + #ifndef GLFW_NO_GLU + #include + #endif +#else + #include + #ifndef GLFW_NO_GLU + #include + #endif +#endif + + +/************************************************************************* + * GLFW version + *************************************************************************/ + +#define GLFW_VERSION_MAJOR 2 +#define GLFW_VERSION_MINOR 7 +#define GLFW_VERSION_REVISION 0 + + +/************************************************************************* + * Input handling definitions + *************************************************************************/ + +/* Key and button state/action definitions */ +#define GLFW_RELEASE 0 +#define GLFW_PRESS 1 + +/* Keyboard key definitions: 8-bit ISO-8859-1 (Latin 1) encoding is used + * for printable keys (such as A-Z, 0-9 etc), and values above 256 + * represent special (non-printable) keys (e.g. F1, Page Up etc). + */ +#define GLFW_KEY_UNKNOWN -1 +#define GLFW_KEY_SPACE 32 +#define GLFW_KEY_SPECIAL 256 +#define GLFW_KEY_ESC (GLFW_KEY_SPECIAL+1) +#define GLFW_KEY_F1 (GLFW_KEY_SPECIAL+2) +#define GLFW_KEY_F2 (GLFW_KEY_SPECIAL+3) +#define GLFW_KEY_F3 (GLFW_KEY_SPECIAL+4) +#define GLFW_KEY_F4 (GLFW_KEY_SPECIAL+5) +#define GLFW_KEY_F5 (GLFW_KEY_SPECIAL+6) +#define GLFW_KEY_F6 (GLFW_KEY_SPECIAL+7) +#define GLFW_KEY_F7 (GLFW_KEY_SPECIAL+8) +#define GLFW_KEY_F8 (GLFW_KEY_SPECIAL+9) +#define GLFW_KEY_F9 (GLFW_KEY_SPECIAL+10) +#define GLFW_KEY_F10 (GLFW_KEY_SPECIAL+11) +#define GLFW_KEY_F11 (GLFW_KEY_SPECIAL+12) +#define GLFW_KEY_F12 (GLFW_KEY_SPECIAL+13) +#define GLFW_KEY_F13 (GLFW_KEY_SPECIAL+14) +#define GLFW_KEY_F14 (GLFW_KEY_SPECIAL+15) +#define GLFW_KEY_F15 (GLFW_KEY_SPECIAL+16) +#define GLFW_KEY_F16 (GLFW_KEY_SPECIAL+17) +#define GLFW_KEY_F17 (GLFW_KEY_SPECIAL+18) +#define GLFW_KEY_F18 (GLFW_KEY_SPECIAL+19) +#define GLFW_KEY_F19 (GLFW_KEY_SPECIAL+20) +#define GLFW_KEY_F20 (GLFW_KEY_SPECIAL+21) +#define GLFW_KEY_F21 (GLFW_KEY_SPECIAL+22) +#define GLFW_KEY_F22 (GLFW_KEY_SPECIAL+23) +#define GLFW_KEY_F23 (GLFW_KEY_SPECIAL+24) +#define GLFW_KEY_F24 (GLFW_KEY_SPECIAL+25) +#define GLFW_KEY_F25 (GLFW_KEY_SPECIAL+26) +#define GLFW_KEY_UP (GLFW_KEY_SPECIAL+27) +#define GLFW_KEY_DOWN (GLFW_KEY_SPECIAL+28) +#define GLFW_KEY_LEFT (GLFW_KEY_SPECIAL+29) +#define GLFW_KEY_RIGHT (GLFW_KEY_SPECIAL+30) +#define GLFW_KEY_LSHIFT (GLFW_KEY_SPECIAL+31) +#define GLFW_KEY_RSHIFT (GLFW_KEY_SPECIAL+32) +#define GLFW_KEY_LCTRL (GLFW_KEY_SPECIAL+33) +#define GLFW_KEY_RCTRL (GLFW_KEY_SPECIAL+34) +#define GLFW_KEY_LALT (GLFW_KEY_SPECIAL+35) +#define GLFW_KEY_RALT (GLFW_KEY_SPECIAL+36) +#define GLFW_KEY_TAB (GLFW_KEY_SPECIAL+37) +#define GLFW_KEY_ENTER (GLFW_KEY_SPECIAL+38) +#define GLFW_KEY_BACKSPACE (GLFW_KEY_SPECIAL+39) +#define GLFW_KEY_INSERT (GLFW_KEY_SPECIAL+40) +#define GLFW_KEY_DEL (GLFW_KEY_SPECIAL+41) +#define GLFW_KEY_PAGEUP (GLFW_KEY_SPECIAL+42) +#define GLFW_KEY_PAGEDOWN (GLFW_KEY_SPECIAL+43) +#define GLFW_KEY_HOME (GLFW_KEY_SPECIAL+44) +#define GLFW_KEY_END (GLFW_KEY_SPECIAL+45) +#define GLFW_KEY_KP_0 (GLFW_KEY_SPECIAL+46) +#define GLFW_KEY_KP_1 (GLFW_KEY_SPECIAL+47) +#define GLFW_KEY_KP_2 (GLFW_KEY_SPECIAL+48) +#define GLFW_KEY_KP_3 (GLFW_KEY_SPECIAL+49) +#define GLFW_KEY_KP_4 (GLFW_KEY_SPECIAL+50) +#define GLFW_KEY_KP_5 (GLFW_KEY_SPECIAL+51) +#define GLFW_KEY_KP_6 (GLFW_KEY_SPECIAL+52) +#define GLFW_KEY_KP_7 (GLFW_KEY_SPECIAL+53) +#define GLFW_KEY_KP_8 (GLFW_KEY_SPECIAL+54) +#define GLFW_KEY_KP_9 (GLFW_KEY_SPECIAL+55) +#define GLFW_KEY_KP_DIVIDE (GLFW_KEY_SPECIAL+56) +#define GLFW_KEY_KP_MULTIPLY (GLFW_KEY_SPECIAL+57) +#define GLFW_KEY_KP_SUBTRACT (GLFW_KEY_SPECIAL+58) +#define GLFW_KEY_KP_ADD (GLFW_KEY_SPECIAL+59) +#define GLFW_KEY_KP_DECIMAL (GLFW_KEY_SPECIAL+60) +#define GLFW_KEY_KP_EQUAL (GLFW_KEY_SPECIAL+61) +#define GLFW_KEY_KP_ENTER (GLFW_KEY_SPECIAL+62) +#define GLFW_KEY_KP_NUM_LOCK (GLFW_KEY_SPECIAL+63) +#define GLFW_KEY_CAPS_LOCK (GLFW_KEY_SPECIAL+64) +#define GLFW_KEY_SCROLL_LOCK (GLFW_KEY_SPECIAL+65) +#define GLFW_KEY_PAUSE (GLFW_KEY_SPECIAL+66) +#define GLFW_KEY_LSUPER (GLFW_KEY_SPECIAL+67) +#define GLFW_KEY_RSUPER (GLFW_KEY_SPECIAL+68) +#define GLFW_KEY_MENU (GLFW_KEY_SPECIAL+69) +#define GLFW_KEY_LAST GLFW_KEY_MENU + +/* Mouse button definitions */ +#define GLFW_MOUSE_BUTTON_1 0 +#define GLFW_MOUSE_BUTTON_2 1 +#define GLFW_MOUSE_BUTTON_3 2 +#define GLFW_MOUSE_BUTTON_4 3 +#define GLFW_MOUSE_BUTTON_5 4 +#define GLFW_MOUSE_BUTTON_6 5 +#define GLFW_MOUSE_BUTTON_7 6 +#define GLFW_MOUSE_BUTTON_8 7 +#define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8 + +/* Mouse button aliases */ +#define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1 +#define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2 +#define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3 + +/* Joystick identifiers */ +#define GLFW_JOYSTICK_1 0 +#define GLFW_JOYSTICK_2 1 +#define GLFW_JOYSTICK_3 2 +#define GLFW_JOYSTICK_4 3 +#define GLFW_JOYSTICK_5 4 +#define GLFW_JOYSTICK_6 5 +#define GLFW_JOYSTICK_7 6 +#define GLFW_JOYSTICK_8 7 +#define GLFW_JOYSTICK_9 8 +#define GLFW_JOYSTICK_10 9 +#define GLFW_JOYSTICK_11 10 +#define GLFW_JOYSTICK_12 11 +#define GLFW_JOYSTICK_13 12 +#define GLFW_JOYSTICK_14 13 +#define GLFW_JOYSTICK_15 14 +#define GLFW_JOYSTICK_16 15 +#define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16 + + +/************************************************************************* + * Other definitions + *************************************************************************/ + +/* glfwOpenWindow modes */ +#define GLFW_WINDOW 0x00010001 +#define GLFW_FULLSCREEN 0x00010002 + +/* glfwGetWindowParam tokens */ +#define GLFW_OPENED 0x00020001 +#define GLFW_ACTIVE 0x00020002 +#define GLFW_ICONIFIED 0x00020003 +#define GLFW_ACCELERATED 0x00020004 +#define GLFW_RED_BITS 0x00020005 +#define GLFW_GREEN_BITS 0x00020006 +#define GLFW_BLUE_BITS 0x00020007 +#define GLFW_ALPHA_BITS 0x00020008 +#define GLFW_DEPTH_BITS 0x00020009 +#define GLFW_STENCIL_BITS 0x0002000A + +/* The following constants are used for both glfwGetWindowParam + * and glfwOpenWindowHint + */ +#define GLFW_REFRESH_RATE 0x0002000B +#define GLFW_ACCUM_RED_BITS 0x0002000C +#define GLFW_ACCUM_GREEN_BITS 0x0002000D +#define GLFW_ACCUM_BLUE_BITS 0x0002000E +#define GLFW_ACCUM_ALPHA_BITS 0x0002000F +#define GLFW_AUX_BUFFERS 0x00020010 +#define GLFW_STEREO 0x00020011 +#define GLFW_WINDOW_NO_RESIZE 0x00020012 +#define GLFW_FSAA_SAMPLES 0x00020013 +#define GLFW_OPENGL_VERSION_MAJOR 0x00020014 +#define GLFW_OPENGL_VERSION_MINOR 0x00020015 +#define GLFW_OPENGL_FORWARD_COMPAT 0x00020016 +#define GLFW_OPENGL_DEBUG_CONTEXT 0x00020017 +#define GLFW_OPENGL_PROFILE 0x00020018 + +/* GLFW_OPENGL_PROFILE tokens */ +#define GLFW_OPENGL_CORE_PROFILE 0x00050001 +#define GLFW_OPENGL_COMPAT_PROFILE 0x00050002 + +/* glfwEnable/glfwDisable tokens */ +#define GLFW_MOUSE_CURSOR 0x00030001 +#define GLFW_STICKY_KEYS 0x00030002 +#define GLFW_STICKY_MOUSE_BUTTONS 0x00030003 +#define GLFW_SYSTEM_KEYS 0x00030004 +#define GLFW_KEY_REPEAT 0x00030005 +#define GLFW_AUTO_POLL_EVENTS 0x00030006 + +/* glfwGetJoystickParam tokens */ +#define GLFW_PRESENT 0x00050001 +#define GLFW_AXES 0x00050002 +#define GLFW_BUTTONS 0x00050003 + + +/************************************************************************* + * Typedefs + *************************************************************************/ + +/* The video mode structure used by glfwGetVideoModes() */ +typedef struct { + int Width, Height; + int RedBits, BlueBits, GreenBits; +} GLFWvidmode; + +/* Function pointer types */ +typedef void (* GLFWwindowsizefun)(int,int); +typedef int (* GLFWwindowclosefun)(void); +typedef void (* GLFWwindowrefreshfun)(void); +typedef void (* GLFWmousebuttonfun)(int,int); +typedef void (* GLFWmouseposfun)(int,int); +typedef void (* GLFWmousewheelfun)(int); +typedef void (* GLFWkeyfun)(int,int); +typedef void (* GLFWcharfun)(int,int); + + +/************************************************************************* + * Prototypes + *************************************************************************/ + +/* GLFW initialization, termination and version querying */ +GLFWAPI int glfwInit( void ); +GLFWAPI void glfwTerminate( void ); +GLFWAPI void glfwGetVersion( int *major, int *minor, int *rev ); + +/* Window handling */ +GLFWAPI int glfwOpenWindow( int width, int height, int redbits, int greenbits, int bluebits, int alphabits, int depthbits, int stencilbits, int mode ); +GLFWAPI void glfwOpenWindowHint( int target, int hint ); +GLFWAPI void glfwCloseWindow( void ); +GLFWAPI void glfwSetWindowTitle( const char *title ); +GLFWAPI void glfwGetWindowSize( int *width, int *height ); +GLFWAPI void glfwSetWindowSize( int width, int height ); +GLFWAPI void glfwSetWindowPos( int x, int y ); +GLFWAPI void glfwIconifyWindow( void ); +GLFWAPI void glfwRestoreWindow( void ); +GLFWAPI void glfwSwapBuffers( void ); +GLFWAPI void glfwSwapInterval( int interval ); +GLFWAPI int glfwGetWindowParam( int param ); +GLFWAPI void glfwSetWindowSizeCallback( GLFWwindowsizefun cbfun ); +GLFWAPI void glfwSetWindowCloseCallback( GLFWwindowclosefun cbfun ); +GLFWAPI void glfwSetWindowRefreshCallback( GLFWwindowrefreshfun cbfun ); + +/* Video mode functions */ +GLFWAPI int glfwGetVideoModes( GLFWvidmode *list, int maxcount ); +GLFWAPI void glfwGetDesktopMode( GLFWvidmode *mode ); + +/* Input handling */ +GLFWAPI void glfwPollEvents( void ); +GLFWAPI void glfwWaitEvents( void ); +GLFWAPI int glfwGetKey( int key ); +GLFWAPI int glfwGetMouseButton( int button ); +GLFWAPI void glfwGetMousePos( int *xpos, int *ypos ); +GLFWAPI void glfwSetMousePos( int xpos, int ypos ); +GLFWAPI int glfwGetMouseWheel( void ); +GLFWAPI void glfwSetMouseWheel( int pos ); +GLFWAPI void glfwSetKeyCallback( GLFWkeyfun cbfun ); +GLFWAPI void glfwSetCharCallback( GLFWcharfun cbfun ); +GLFWAPI void glfwSetMouseButtonCallback( GLFWmousebuttonfun cbfun ); +GLFWAPI void glfwSetMousePosCallback( GLFWmouseposfun cbfun ); +GLFWAPI void glfwSetMouseWheelCallback( GLFWmousewheelfun cbfun ); + +/* Joystick input */ +GLFWAPI int glfwGetJoystickParam( int joy, int param ); +GLFWAPI int glfwGetJoystickPos( int joy, float *pos, int numaxes ); +GLFWAPI int glfwGetJoystickButtons( int joy, unsigned char *buttons, int numbuttons ); + +/* Time */ +GLFWAPI double glfwGetTime( void ); +GLFWAPI void glfwSetTime( double time ); + +/* Extension support */ +GLFWAPI int glfwExtensionSupported( const char *extension ); +GLFWAPI void* glfwGetProcAddress( const char *procname ); +GLFWAPI void glfwGetGLVersion( int *major, int *minor, int *rev ); + +/* Enable/disable functions */ +GLFWAPI void glfwEnable( int token ); +GLFWAPI void glfwDisable( int token ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __glfw_h_ */ + diff --git a/lib/carbon/CMakeLists.txt b/lib/carbon/CMakeLists.txt new file mode 100644 index 00000000..51c98545 --- /dev/null +++ b/lib/carbon/CMakeLists.txt @@ -0,0 +1,38 @@ + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/libglfw.pc.cmake + ${CMAKE_CURRENT_BINARY_DIR}/libglfw.pc @ONLY) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/lib + ${GLFW_INCLUDE_DIR}) + +set(libglfw_SOURCES + ${common_SOURCES} + carbon_enable.c + carbon_fullscreen.c + carbon_glext.c + carbon_init.c + carbon_joystick.c + carbon_time.c + carbon_window.c) + +add_library(libglfwStatic STATIC ${libglfw_SOURCES}) +add_library(libglfwShared SHARED ${libglfw_SOURCES}) +target_link_libraries(libglfwShared ${GLFW_LIBRARIES}) +set_target_properties(libglfwStatic libglfwShared PROPERTIES + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME glfw +) + +# Append -fno-common to the compile flags to work around a bug in the Apple GCC +get_target_property(CFLAGS libglfwShared COMPILE_FLAGS) +if(NOT CFLAGS) + set(CFLAGS "") +endif(NOT CFLAGS) +set_target_properties(libglfwShared PROPERTIES COMPILE_FLAGS "${CFLAGS} -fno-common") + +install(TARGETS libglfwStatic libglfwShared DESTINATION lib) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libglfw.pc DESTINATION lib/pkgconfig) + diff --git a/lib/carbon/carbon_enable.c b/lib/carbon/carbon_enable.c new file mode 100644 index 00000000..76b18fef --- /dev/null +++ b/lib/carbon/carbon_enable.c @@ -0,0 +1,43 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Carbon/AGL/CGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2003 Keith Bauer +// Copyright (c) 2003-2010 Camilla Berglund +// +// 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. +// +//======================================================================== + +void _glfwPlatformEnableSystemKeys( void ) +{ + // Nothing to do; event handling code checks the status of + // _glfwWin.sysKeysDisabled to ensure this behavior. +} + +void _glfwPlatformDisableSystemKeys( void ) +{ + // Nothing to do; event handling code checks the status of + // _glfwWin.sysKeysDisabled to ensure this behavior. +} + diff --git a/lib/carbon/carbon_fullscreen.c b/lib/carbon/carbon_fullscreen.c new file mode 100644 index 00000000..2fd54570 --- /dev/null +++ b/lib/carbon/carbon_fullscreen.c @@ -0,0 +1,127 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Carbon/AGL/CGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2003 Keith Bauer +// Copyright (c) 2003-2010 Camilla Berglund +// +// 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 "internal.h" + +//======================================================================== +// _glfwVideoModesEqual() - Compares two video modes +//======================================================================== + +static int _glfwVideoModesEqual( GLFWvidmode* first, + GLFWvidmode* second ) +{ + if( first->Width != second->Width ) + return 0; + + if( first->Height != second->Height ) + return 0; + + if( first->RedBits + first->GreenBits + first->BlueBits != + second->RedBits + second->GreenBits + second->BlueBits ) + return 0; + + return 1; +} + +//======================================================================== +// _glfwCGToGLFWVideoMode() - Converts a CG mode to a GLFW mode +//======================================================================== + +static void _glfwCGToGLFWVideoMode( CFDictionaryRef cgMode, + GLFWvidmode* glfwMode ) +{ + int bitsPerSample; + + CFNumberGetValue( CFDictionaryGetValue( cgMode, kCGDisplayWidth ), + kCFNumberIntType, + &(glfwMode->Width) ); + CFNumberGetValue( CFDictionaryGetValue( cgMode, kCGDisplayHeight ), + kCFNumberIntType, + &(glfwMode->Height) ); + + CFNumberGetValue( CFDictionaryGetValue( cgMode, kCGDisplayBitsPerSample ), + kCFNumberIntType, + &bitsPerSample ); + + glfwMode->RedBits = bitsPerSample; + glfwMode->GreenBits = bitsPerSample; + glfwMode->BlueBits = bitsPerSample; +} + +//======================================================================== +// _glfwPlatformGetVideoModes() - Get a list of available video modes +//======================================================================== + +int _glfwPlatformGetVideoModes( GLFWvidmode *list, int maxcount ) +{ + int i, j, maxModes, numModes; + GLFWvidmode mode; + CFArrayRef availableModes = CGDisplayAvailableModes( kCGDirectMainDisplay ); + CFIndex numberOfAvailableModes = CFArrayGetCount( availableModes ); + + numModes = 0; + maxModes = ( numberOfAvailableModes < maxcount ? + numberOfAvailableModes : + maxcount ); + + for( i = 0; i < maxModes; ++i ) + { + _glfwCGToGLFWVideoMode( CFArrayGetValueAtIndex( availableModes, i ), + &mode ); + + // Is it a valid mode? (only list depths >= 15 bpp) + if( mode.RedBits + mode.GreenBits + mode.BlueBits < 15 ) + continue; + + // Check for duplicate of current mode in target list + for( j = 0; j < numModes; ++j ) + { + if( _glfwVideoModesEqual( &mode, &(list[j]) ) ) + break; + } + + // If empty list or no match found + if( numModes == 0 || j == numModes ) + list[numModes++] = mode; + } + + return numModes; +} + +//======================================================================== +// glfwGetDesktopMode() - Get the desktop video mode +//======================================================================== + +void _glfwPlatformGetDesktopMode( GLFWvidmode *mode ) +{ + _glfwCGToGLFWVideoMode( _glfwDesktopVideoMode, mode ); +} + diff --git a/lib/carbon/carbon_glext.c b/lib/carbon/carbon_glext.c new file mode 100644 index 00000000..2a1018fc --- /dev/null +++ b/lib/carbon/carbon_glext.c @@ -0,0 +1,53 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Carbon/AGL/CGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2003 Keith Bauer +// Copyright (c) 2003-2010 Camilla Berglund +// +// 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 "internal.h" + +int _glfwPlatformExtensionSupported( const char *extension ) +{ + // There are no AGL, CGL or NSGL extensions. + return GL_FALSE; +} + +void * _glfwPlatformGetProcAddress( const char *procname ) +{ + CFStringRef symbolName = CFStringCreateWithCString( kCFAllocatorDefault, + procname, + kCFStringEncodingASCII ); + + void *symbol = CFBundleGetFunctionPointerForName( _glfwLibrary.Libs.OpenGLFramework, + symbolName ); + + CFRelease( symbolName ); + + return symbol; +} + diff --git a/lib/carbon/carbon_init.c b/lib/carbon/carbon_init.c new file mode 100644 index 00000000..6a6d09ad --- /dev/null +++ b/lib/carbon/carbon_init.c @@ -0,0 +1,159 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Carbon/AGL/CGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2003 Keith Bauer +// Copyright (c) 2003-2010 Camilla Berglund +// Copyright (c) 2006-2007 Robin Leffmann +// +// 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 "internal.h" + +#include + +//======================================================================== +// Global variables +//======================================================================== + +// KCHR resource pointer for keycode translation +void *KCHRPtr; + + +//======================================================================== +// Terminate GLFW when exiting application +//======================================================================== + +static void glfw_atexit( void ) +{ + glfwTerminate(); +} + + +#define NO_BUNDLE_MESSAGE \ + "Working in unbundled mode. " \ + "You should build a .app wrapper for your Mac OS X applications.\n" + +#define UNBUNDLED \ + fprintf(stderr, NO_BUNDLE_MESSAGE); \ + _glfwLibrary.Unbundled = 1; \ + return + +void _glfwChangeToResourcesDirectory( void ) +{ + CFBundleRef mainBundle = CFBundleGetMainBundle(); + if( mainBundle == NULL ) + { + UNBUNDLED; + } + + CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL( mainBundle ); + char resourcesPath[ _GLFW_MAX_PATH_LENGTH ]; + + CFStringRef lastComponent = CFURLCopyLastPathComponent( resourcesURL ); + if( kCFCompareEqualTo != CFStringCompare( + CFSTR( "Resources" ), + lastComponent, + 0 ) ) + { + UNBUNDLED; + } + + CFRelease( lastComponent ); + + if( !CFURLGetFileSystemRepresentation( resourcesURL, + TRUE, + (UInt8*)resourcesPath, + _GLFW_MAX_PATH_LENGTH ) ) + { + CFRelease( resourcesURL ); + UNBUNDLED; + } + + CFRelease( resourcesURL ); + + if( chdir( resourcesPath ) != 0 ) + { + UNBUNDLED; + } +} + +int _glfwPlatformInit( void ) +{ + struct timeval tv; + UInt32 nullDummy = 0; + + _glfwWin.window = NULL; + _glfwWin.aglContext = NULL; + _glfwWin.cglContext = NULL; + _glfwWin.windowUPP = NULL; + + _glfwInput.Modifiers = 0; + + _glfwLibrary.Unbundled = 0; + + _glfwLibrary.Libs.OpenGLFramework = + CFBundleGetBundleWithIdentifier( CFSTR( "com.apple.opengl" ) ); + if( _glfwLibrary.Libs.OpenGLFramework == NULL ) + { + fprintf( stderr, "glfwInit failing because you aren't linked to OpenGL\n" ); + return GL_FALSE; + } + + _glfwDesktopVideoMode = CGDisplayCurrentMode( kCGDirectMainDisplay ); + if( _glfwDesktopVideoMode == NULL ) + { + fprintf( stderr, "glfwInit failing because it kind find the desktop display mode\n" ); + return GL_FALSE; + } + + // Install atexit routine + atexit( glfw_atexit ); + + _glfwChangeToResourcesDirectory(); + + // Ugly hack to reduce the nasty jump that occurs at the first non- + // sys keypress, caused by OS X loading certain meta scripts used + // for lexical- and raw keycode translation - instead of letting + // this happen while our application is running, we do some blunt + // function calls in advance just to get the script caching out of + // the way BEFORE our window/screen is opened. These calls might + // generate err return codes, but we don't care in this case. + // NOTE: KCHRPtr is declared globally, because we need it later on. + KCHRPtr = (void *)GetScriptVariable( smCurrentScript, smKCHRCache ); + KeyTranslate( KCHRPtr, 0, &nullDummy ); + UppercaseText( (char *)&nullDummy, 0, smSystemScript ); + + gettimeofday( &tv, NULL ); + _glfwLibrary.Timer.t0 = tv.tv_sec + (double) tv.tv_usec / 1000000.0; + + return GL_TRUE; +} + +int _glfwPlatformTerminate( void ) +{ + return GL_TRUE; +} + diff --git a/lib/carbon/carbon_joystick.c b/lib/carbon/carbon_joystick.c new file mode 100644 index 00000000..3cf47ac5 --- /dev/null +++ b/lib/carbon/carbon_joystick.c @@ -0,0 +1,51 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Carbon/AGL/CGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2003 Keith Bauer +// Copyright (c) 2003-2010 Camilla Berglund +// +// 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 "internal.h" + +// TO DO: use HID manager to implement joystick support. + +int _glfwPlatformGetJoystickParam( int joy, int param ) +{ + // GL_FALSE == 0 + return 0; +} + +int _glfwPlatformGetJoystickPos( int joy, float *pos, int numaxes ) +{ + return 0; +} + +int _glfwPlatformGetJoystickButtons( int joy, unsigned char *buttons, int numbuttons ) +{ + return 0; +} + diff --git a/lib/carbon/carbon_time.c b/lib/carbon/carbon_time.c new file mode 100644 index 00000000..64de6d70 --- /dev/null +++ b/lib/carbon/carbon_time.c @@ -0,0 +1,62 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Carbon/AGL/CGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2003 Keith Bauer +// Copyright (c) 2003-2010 Camilla Berglund +// +// 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 "internal.h" + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Return timer value in seconds +//======================================================================== + +double _glfwPlatformGetTime( void ) +{ + struct timeval tv; + + gettimeofday( &tv, NULL ); + return tv.tv_sec + (double) tv.tv_usec / 1000000.0 - _glfwLibrary.Timer.t0; +} + + +//======================================================================== +// Set timer value in seconds +//======================================================================== + +void _glfwPlatformSetTime( double time ) +{ + struct timeval tv; + + gettimeofday( &tv, NULL ); + _glfwLibrary.Timer.t0 = tv.tv_sec + (double) tv.tv_usec / 1000000.0 - time; +} + diff --git a/lib/carbon/carbon_window.c b/lib/carbon/carbon_window.c new file mode 100644 index 00000000..4c709ede --- /dev/null +++ b/lib/carbon/carbon_window.c @@ -0,0 +1,1309 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Carbon/AGL/CGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2003 Keith Bauer +// Copyright (c) 2003-2010 Camilla Berglund +// Copyright (c) 2006-2007 Robin Leffmann +// +// 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 "internal.h" + +#define _glfwTestModifier( modifierMask, glfwKey ) \ +if ( changed & modifierMask ) \ +{ \ + _glfwInputKey( glfwKey, (modifiers & modifierMask ? GLFW_PRESS : GLFW_RELEASE) ); \ +} + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +static void handleMacModifierChange( UInt32 modifiers ) +{ + UInt32 changed = modifiers ^ _glfwInput.Modifiers; + + // The right *key variants below never actually occur + // There also isn't even a broken right command key constant + _glfwTestModifier( shiftKey, GLFW_KEY_LSHIFT ); + _glfwTestModifier( rightShiftKey, GLFW_KEY_RSHIFT ); + _glfwTestModifier( controlKey, GLFW_KEY_LCTRL ); + _glfwTestModifier( rightControlKey, GLFW_KEY_RCTRL ); + _glfwTestModifier( optionKey, GLFW_KEY_LALT ); + _glfwTestModifier( rightOptionKey, GLFW_KEY_RALT ); + _glfwTestModifier( cmdKey, GLFW_KEY_LSUPER ); + + _glfwInput.Modifiers = modifiers; +} + +static void handleMacKeyChange( UInt32 keyCode, int action ) +{ + switch ( keyCode ) + { + case MAC_KEY_ENTER: _glfwInputKey( GLFW_KEY_ENTER, action); break; + case MAC_KEY_RETURN: _glfwInputKey( GLFW_KEY_KP_ENTER, action); break; + case MAC_KEY_ESC: _glfwInputKey( GLFW_KEY_ESC, action); break; + case MAC_KEY_F1: _glfwInputKey( GLFW_KEY_F1, action); break; + case MAC_KEY_F2: _glfwInputKey( GLFW_KEY_F2, action); break; + case MAC_KEY_F3: _glfwInputKey( GLFW_KEY_F3, action); break; + case MAC_KEY_F4: _glfwInputKey( GLFW_KEY_F4, action); break; + case MAC_KEY_F5: _glfwInputKey( GLFW_KEY_F5, action); break; + case MAC_KEY_F6: _glfwInputKey( GLFW_KEY_F6, action); break; + case MAC_KEY_F7: _glfwInputKey( GLFW_KEY_F7, action); break; + case MAC_KEY_F8: _glfwInputKey( GLFW_KEY_F8, action); break; + case MAC_KEY_F9: _glfwInputKey( GLFW_KEY_F9, action); break; + case MAC_KEY_F10: _glfwInputKey( GLFW_KEY_F10, action); break; + case MAC_KEY_F11: _glfwInputKey( GLFW_KEY_F11, action); break; + case MAC_KEY_F12: _glfwInputKey( GLFW_KEY_F12, action); break; + case MAC_KEY_F13: _glfwInputKey( GLFW_KEY_F13, action); break; + case MAC_KEY_F14: _glfwInputKey( GLFW_KEY_F14, action); break; + case MAC_KEY_F15: _glfwInputKey( GLFW_KEY_F15, action); break; + case MAC_KEY_UP: _glfwInputKey( GLFW_KEY_UP, action); break; + case MAC_KEY_DOWN: _glfwInputKey( GLFW_KEY_DOWN, action); break; + case MAC_KEY_LEFT: _glfwInputKey( GLFW_KEY_LEFT, action); break; + case MAC_KEY_RIGHT: _glfwInputKey( GLFW_KEY_RIGHT, action); break; + case MAC_KEY_TAB: _glfwInputKey( GLFW_KEY_TAB, action); break; + case MAC_KEY_BACKSPACE: _glfwInputKey( GLFW_KEY_BACKSPACE, action); break; + case MAC_KEY_HELP: _glfwInputKey( GLFW_KEY_INSERT, action); break; + case MAC_KEY_DEL: _glfwInputKey( GLFW_KEY_DEL, action); break; + case MAC_KEY_PAGEUP: _glfwInputKey( GLFW_KEY_PAGEUP, action); break; + case MAC_KEY_PAGEDOWN: _glfwInputKey( GLFW_KEY_PAGEDOWN, action); break; + case MAC_KEY_HOME: _glfwInputKey( GLFW_KEY_HOME, action); break; + case MAC_KEY_END: _glfwInputKey( GLFW_KEY_END, action); break; + case MAC_KEY_KP_0: _glfwInputKey( GLFW_KEY_KP_0, action); break; + case MAC_KEY_KP_1: _glfwInputKey( GLFW_KEY_KP_1, action); break; + case MAC_KEY_KP_2: _glfwInputKey( GLFW_KEY_KP_2, action); break; + case MAC_KEY_KP_3: _glfwInputKey( GLFW_KEY_KP_3, action); break; + case MAC_KEY_KP_4: _glfwInputKey( GLFW_KEY_KP_4, action); break; + case MAC_KEY_KP_5: _glfwInputKey( GLFW_KEY_KP_5, action); break; + case MAC_KEY_KP_6: _glfwInputKey( GLFW_KEY_KP_6, action); break; + case MAC_KEY_KP_7: _glfwInputKey( GLFW_KEY_KP_7, action); break; + case MAC_KEY_KP_8: _glfwInputKey( GLFW_KEY_KP_8, action); break; + case MAC_KEY_KP_9: _glfwInputKey( GLFW_KEY_KP_9, action); break; + case MAC_KEY_KP_DIVIDE: _glfwInputKey( GLFW_KEY_KP_DIVIDE, action); break; + case MAC_KEY_KP_MULTIPLY: _glfwInputKey( GLFW_KEY_KP_MULTIPLY, action); break; + case MAC_KEY_KP_SUBTRACT: _glfwInputKey( GLFW_KEY_KP_SUBTRACT, action); break; + case MAC_KEY_KP_ADD: _glfwInputKey( GLFW_KEY_KP_ADD, action); break; + case MAC_KEY_KP_DECIMAL: _glfwInputKey( GLFW_KEY_KP_DECIMAL, action); break; + case MAC_KEY_KP_EQUAL: _glfwInputKey( GLFW_KEY_KP_EQUAL, action); break; + case MAC_KEY_KP_ENTER: _glfwInputKey( GLFW_KEY_KP_ENTER, action); break; + case MAC_KEY_NUMLOCK: _glfwInputKey( GLFW_KEY_KP_NUM_LOCK, action); break; + default: + { + extern void *KCHRPtr; + UInt32 state = 0; + char charCode = (char)KeyTranslate( KCHRPtr, keyCode, &state ); + UppercaseText( &charCode, 1, smSystemScript ); + _glfwInputKey( (unsigned char)charCode, action ); + } + break; + } +} + +// The set of event class/kind combinations supported by keyEventHandler +// This is used by installEventHandlers below +static const EventTypeSpec GLFW_KEY_EVENT_TYPES[] = +{ + { kEventClassKeyboard, kEventRawKeyDown }, + { kEventClassKeyboard, kEventRawKeyUp }, + { kEventClassKeyboard, kEventRawKeyRepeat }, + { kEventClassKeyboard, kEventRawKeyModifiersChanged } +}; + +static OSStatus keyEventHandler( EventHandlerCallRef handlerCallRef, + EventRef event, + void *userData ) +{ + UInt32 keyCode; + short int keyChar; + UInt32 modifiers; + + switch( GetEventKind( event ) ) + { + case kEventRawKeyRepeat: + case kEventRawKeyDown: + { + if( GetEventParameter( event, + kEventParamKeyCode, + typeUInt32, + NULL, + sizeof( UInt32 ), + NULL, + &keyCode ) == noErr ) + { + handleMacKeyChange( keyCode, GLFW_PRESS ); + } + if( GetEventParameter( event, + kEventParamKeyUnicodes, + typeUnicodeText, + NULL, + sizeof(keyChar), + NULL, + &keyChar) == noErr ) + { + _glfwInputChar( keyChar, GLFW_PRESS ); + } + return noErr; + } + + case kEventRawKeyUp: + { + if( GetEventParameter( event, + kEventParamKeyCode, + typeUInt32, + NULL, + sizeof( UInt32 ), + NULL, + &keyCode ) == noErr ) + { + handleMacKeyChange( keyCode, GLFW_RELEASE ); + } + if( GetEventParameter( event, + kEventParamKeyUnicodes, + typeUnicodeText, + NULL, + sizeof(keyChar), + NULL, + &keyChar) == noErr ) + { + _glfwInputChar( keyChar, GLFW_RELEASE ); + } + return noErr; + } + + case kEventRawKeyModifiersChanged: + { + if( GetEventParameter( event, + kEventParamKeyModifiers, + typeUInt32, + NULL, + sizeof( UInt32 ), + NULL, + &modifiers ) == noErr ) + { + handleMacModifierChange( modifiers ); + return noErr; + } + } + break; + } + + return eventNotHandledErr; +} + +// The set of event class/kind combinations supported by mouseEventHandler +// This is used by installEventHandlers below +static const EventTypeSpec GLFW_MOUSE_EVENT_TYPES[] = +{ + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseUp }, + { kEventClassMouse, kEventMouseMoved }, + { kEventClassMouse, kEventMouseDragged }, + { kEventClassMouse, kEventMouseWheelMoved }, +}; + +static OSStatus mouseEventHandler( EventHandlerCallRef handlerCallRef, + EventRef event, + void *userData ) +{ + switch( GetEventKind( event ) ) + { + case kEventMouseDown: + { + WindowRef window; + EventRecord oldStyleMacEvent; + ConvertEventRefToEventRecord( event, &oldStyleMacEvent ); + if( FindWindow ( oldStyleMacEvent.where, &window ) == inMenuBar ) + { + MenuSelect( oldStyleMacEvent.where ); + HiliteMenu(0); + return noErr; + } + else + { + EventMouseButton button; + if( GetEventParameter( event, + kEventParamMouseButton, + typeMouseButton, + NULL, + sizeof( EventMouseButton ), + NULL, + &button ) == noErr ) + { + button -= kEventMouseButtonPrimary; + if( button <= GLFW_MOUSE_BUTTON_LAST ) + { + _glfwInputMouseClick( button + GLFW_MOUSE_BUTTON_LEFT, + GLFW_PRESS ); + } + return noErr; + } + } + break; + } + + case kEventMouseUp: + { + EventMouseButton button; + if( GetEventParameter( event, + kEventParamMouseButton, + typeMouseButton, + NULL, + sizeof( EventMouseButton ), + NULL, + &button ) == noErr ) + { + button -= kEventMouseButtonPrimary; + if( button <= GLFW_MOUSE_BUTTON_LAST ) + { + _glfwInputMouseClick( button + GLFW_MOUSE_BUTTON_LEFT, + GLFW_RELEASE ); + } + return noErr; + } + break; + } + + case kEventMouseMoved: + case kEventMouseDragged: + { + HIPoint mouseLocation; + if( _glfwWin.mouseLock ) + { + if( GetEventParameter( event, + kEventParamMouseDelta, + typeHIPoint, + NULL, + sizeof( HIPoint ), + NULL, + &mouseLocation ) != noErr ) + { + break; + } + + _glfwInput.MousePosX += mouseLocation.x; + _glfwInput.MousePosY += mouseLocation.y; + } + else + { + if( GetEventParameter( event, + kEventParamMouseLocation, + typeHIPoint, + NULL, + sizeof( HIPoint ), + NULL, + &mouseLocation ) != noErr ) + { + break; + } + + _glfwInput.MousePosX = mouseLocation.x; + _glfwInput.MousePosY = mouseLocation.y; + + if( !_glfwWin.fullscreen ) + { + Rect content; + GetWindowBounds( _glfwWin.window, + kWindowContentRgn, + &content ); + + _glfwInput.MousePosX -= content.left; + _glfwInput.MousePosY -= content.top; + } + } + + if( _glfwWin.mousePosCallback ) + { + _glfwWin.mousePosCallback( _glfwInput.MousePosX, + _glfwInput.MousePosY ); + } + + break; + } + + case kEventMouseWheelMoved: + { + EventMouseWheelAxis axis; + if( GetEventParameter( event, + kEventParamMouseWheelAxis, + typeMouseWheelAxis, + NULL, + sizeof( EventMouseWheelAxis ), + NULL, + &axis) == noErr ) + { + long wheelDelta; + if( axis == kEventMouseWheelAxisY && + GetEventParameter( event, + kEventParamMouseWheelDelta, + typeLongInteger, + NULL, + sizeof( long ), + NULL, + &wheelDelta ) == noErr ) + { + _glfwInput.WheelPos += wheelDelta; + if( _glfwWin.mouseWheelCallback ) + { + _glfwWin.mouseWheelCallback( _glfwInput.WheelPos ); + } + return noErr; + } + } + break; + } + } + + return eventNotHandledErr; +} + +// The set of event class/kind combinations supported by commandHandler +// This is used by installEventHandlers below +static const EventTypeSpec GLFW_COMMAND_EVENT_TYPES[] = +{ + { kEventClassCommand, kEventCommandProcess } +}; + +static OSStatus commandHandler( EventHandlerCallRef handlerCallRef, + EventRef event, + void *userData ) +{ + if( _glfwWin.sysKeysDisabled ) + { + // TODO: Give adequate UI feedback that this is the case + return eventNotHandledErr; + } + + HICommand command; + if( GetEventParameter( event, + kEventParamDirectObject, + typeHICommand, + NULL, + sizeof( HICommand ), + NULL, + &command ) == noErr ) + { + switch( command.commandID ) + { + case kHICommandClose: + case kHICommandQuit: + { + // Check if the program wants us to close the window + if( _glfwWin.windowCloseCallback ) + { + if( _glfwWin.windowCloseCallback() ) + { + glfwCloseWindow(); + } + } + else + { + glfwCloseWindow(); + } + return noErr; + } + } + } + + return eventNotHandledErr; +} + +// The set of event class/kind combinations supported by windowEventHandler +// This is used by installEventHandlers below +static const EventTypeSpec GLFW_WINDOW_EVENT_TYPES[] = +{ + { kEventClassWindow, kEventWindowBoundsChanged }, + { kEventClassWindow, kEventWindowClose }, + { kEventClassWindow, kEventWindowDrawContent }, + { kEventClassWindow, kEventWindowActivated }, + { kEventClassWindow, kEventWindowDeactivated }, +}; + +static OSStatus windowEventHandler( EventHandlerCallRef handlerCallRef, + EventRef event, + void *userData ) +{ + switch( GetEventKind(event) ) + { + case kEventWindowBoundsChanged: + { + WindowRef window; + GetEventParameter( event, + kEventParamDirectObject, + typeWindowRef, + NULL, + sizeof(WindowRef), + NULL, + &window ); + + Rect rect; + GetWindowPortBounds( window, &rect ); + + if( _glfwWin.width != rect.right || + _glfwWin.height != rect.bottom ) + { + aglUpdateContext( _glfwWin.aglContext ); + + _glfwWin.width = rect.right; + _glfwWin.height = rect.bottom; + if( _glfwWin.windowSizeCallback ) + { + _glfwWin.windowSizeCallback( _glfwWin.width, _glfwWin.height ); + } + // Emulate (force) content invalidation + if( _glfwWin.windowRefreshCallback ) + { + _glfwWin.windowRefreshCallback(); + } + } + break; + } + + case kEventWindowClose: + { + // Check if the client wants us to close the window + if( _glfwWin.windowCloseCallback ) + { + if( _glfwWin.windowCloseCallback() ) + { + glfwCloseWindow(); + } + } + else + { + glfwCloseWindow(); + } + return noErr; + } + + case kEventWindowDrawContent: + { + if( _glfwWin.windowRefreshCallback ) + { + _glfwWin.windowRefreshCallback(); + } + break; + } + + case kEventWindowActivated: + { + _glfwWin.active = GL_TRUE; + break; + } + + case kEventWindowDeactivated: + { + _glfwWin.active = GL_FALSE; + _glfwInputDeactivation(); + break; + } + } + + return eventNotHandledErr; +} + +static int installEventHandlers( void ) +{ + OSStatus error; + + _glfwWin.mouseUPP = NewEventHandlerUPP( mouseEventHandler ); + + error = InstallEventHandler( GetApplicationEventTarget(), + _glfwWin.mouseUPP, + GetEventTypeCount( GLFW_MOUSE_EVENT_TYPES ), + GLFW_MOUSE_EVENT_TYPES, + NULL, + NULL ); + if( error != noErr ) + { + fprintf( stderr, "Failed to install Carbon application mouse event handler\n" ); + return GL_FALSE; + } + + _glfwWin.commandUPP = NewEventHandlerUPP( commandHandler ); + + error = InstallEventHandler( GetApplicationEventTarget(), + _glfwWin.commandUPP, + GetEventTypeCount( GLFW_COMMAND_EVENT_TYPES ), + GLFW_COMMAND_EVENT_TYPES, + NULL, + NULL ); + if( error != noErr ) + { + fprintf( stderr, "Failed to install Carbon application command event handler\n" ); + return GL_FALSE; + } + + _glfwWin.keyboardUPP = NewEventHandlerUPP( keyEventHandler ); + + error = InstallEventHandler( GetApplicationEventTarget(), + _glfwWin.keyboardUPP, + GetEventTypeCount( GLFW_KEY_EVENT_TYPES ), + GLFW_KEY_EVENT_TYPES, + NULL, + NULL ); + if( error != noErr ) + { + fprintf( stderr, "Failed to install Carbon application key event handler\n" ); + return GL_FALSE; + } + + return GL_TRUE; +} + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +#define _setAGLAttribute( aglAttributeName, AGLparameter ) \ +if ( AGLparameter != 0 ) \ +{ \ + AGLpixelFormatAttributes[numAGLAttrs++] = aglAttributeName; \ + AGLpixelFormatAttributes[numAGLAttrs++] = AGLparameter; \ +} + +#define _setCGLAttribute( cglAttributeName, CGLparameter ) \ +if ( CGLparameter != 0 ) \ +{ \ + CGLpixelFormatAttributes[ numCGLAttrs++ ] = cglAttributeName; \ + CGLpixelFormatAttributes[ numCGLAttrs++ ] = CGLparameter; \ +} + +//======================================================================== +// Here is where the window is created, and +// the OpenGL rendering context is created +//======================================================================== + +int _glfwPlatformOpenWindow( int width, int height, + const _GLFWwndconfig *wndconfig, + const _GLFWfbconfig *fbconfig ) +{ + OSStatus error; + unsigned int windowAttributes; + ProcessSerialNumber psn; + + // TODO: Break up this function! + + _glfwWin.windowUPP = NULL; + _glfwWin.mouseUPP = NULL; + _glfwWin.keyboardUPP = NULL; + _glfwWin.commandUPP = NULL; + _glfwWin.window = NULL; + _glfwWin.aglContext = NULL; + _glfwWin.aglPixelFormat = NULL; + _glfwWin.cglContext = NULL; + _glfwWin.cglPixelFormat = NULL; + + _glfwWin.refreshRate = wndconfig->refreshRate; + + // Fail if OpenGL 3.0 or above was requested + if( wndconfig->glMajor > 2 ) + { + fprintf( stderr, "OpenGL 3.0+ is not yet supported on Mac OS X\n" ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + if( _glfwLibrary.Unbundled ) + { + if( GetCurrentProcess( &psn ) != noErr ) + { + fprintf( stderr, "Failed to get the process serial number\n" ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + if( TransformProcessType( &psn, kProcessTransformToForegroundApplication ) != noErr ) + { + fprintf( stderr, "Failed to become a foreground application\n" ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + if( wndconfig->mode == GLFW_FULLSCREEN ) + { + if( SetFrontProcess( &psn ) != noErr ) + { + fprintf( stderr, "Failed to become the front process\n" ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + } + } + + if( !installEventHandlers() ) + { + fprintf( stderr, + "Failed to install Carbon application event handlers\n" ); + + _glfwPlatformTerminate(); + return GL_FALSE; + } + + // Windowed or fullscreen; AGL or CGL? Quite the mess... + // AGL appears to be the only choice for attaching OpenGL contexts to + // Carbon windows, but it leaves the user no control over fullscreen + // mode stretching. Solution: AGL for windowed, CGL for fullscreen. + if( wndconfig->mode == GLFW_WINDOW ) + { + // create AGL pixel format attribute list + GLint AGLpixelFormatAttributes[256]; + int numAGLAttrs = 0; + + AGLpixelFormatAttributes[numAGLAttrs++] = AGL_RGBA; + AGLpixelFormatAttributes[numAGLAttrs++] = AGL_DOUBLEBUFFER; + AGLpixelFormatAttributes[numAGLAttrs++] = AGL_CLOSEST_POLICY; + + if( fbconfig->stereo ) + { + AGLpixelFormatAttributes[numAGLAttrs++] = AGL_STEREO; + } + + _setAGLAttribute( AGL_AUX_BUFFERS, fbconfig->auxBuffers); + _setAGLAttribute( AGL_RED_SIZE, fbconfig->redBits ); + _setAGLAttribute( AGL_GREEN_SIZE, fbconfig->greenBits ); + _setAGLAttribute( AGL_BLUE_SIZE, fbconfig->blueBits ); + _setAGLAttribute( AGL_ALPHA_SIZE, fbconfig->alphaBits ); + _setAGLAttribute( AGL_DEPTH_SIZE, fbconfig->depthBits ); + _setAGLAttribute( AGL_STENCIL_SIZE, fbconfig->stencilBits ); + _setAGLAttribute( AGL_ACCUM_RED_SIZE, fbconfig->accumRedBits ); + _setAGLAttribute( AGL_ACCUM_GREEN_SIZE, fbconfig->accumGreenBits ); + _setAGLAttribute( AGL_ACCUM_BLUE_SIZE, fbconfig->accumBlueBits ); + _setAGLAttribute( AGL_ACCUM_ALPHA_SIZE, fbconfig->accumAlphaBits ); + + if( fbconfig->samples > 1 ) + { + _setAGLAttribute( AGL_SAMPLE_BUFFERS_ARB, 1 ); + _setAGLAttribute( AGL_SAMPLES_ARB, fbconfig->samples ); + AGLpixelFormatAttributes[numAGLAttrs++] = AGL_NO_RECOVERY; + } + + AGLpixelFormatAttributes[numAGLAttrs++] = AGL_NONE; + + // create pixel format descriptor + AGLDevice mainMonitor = GetMainDevice(); + _glfwWin.aglPixelFormat = aglChoosePixelFormat( &mainMonitor, + 1, + AGLpixelFormatAttributes ); + if( _glfwWin.aglPixelFormat == NULL ) + { + fprintf( stderr, + "Failed to choose AGL pixel format: %s\n", + aglErrorString( aglGetError() ) ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + // create AGL context + _glfwWin.aglContext = aglCreateContext( _glfwWin.aglPixelFormat, NULL ); + + if( _glfwWin.aglContext == NULL ) + { + fprintf( stderr, + "Failed to create AGL context: %s\n", + aglErrorString( aglGetError() ) ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + // create window + Rect windowContentBounds; + windowContentBounds.left = 0; + windowContentBounds.top = 0; + windowContentBounds.right = width; + windowContentBounds.bottom = height; + + windowAttributes = ( kWindowCloseBoxAttribute | + kWindowCollapseBoxAttribute | + kWindowStandardHandlerAttribute ); + + if( wndconfig->windowNoResize ) + { + windowAttributes |= kWindowLiveResizeAttribute; + } + else + { + windowAttributes |= ( kWindowFullZoomAttribute | + kWindowResizableAttribute ); + } + + error = CreateNewWindow( kDocumentWindowClass, + windowAttributes, + &windowContentBounds, + &( _glfwWin.window ) ); + if( ( error != noErr ) || ( _glfwWin.window == NULL ) ) + { + fprintf( stderr, "Failed to create Carbon window\n" ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + _glfwWin.windowUPP = NewEventHandlerUPP( windowEventHandler ); + + error = InstallWindowEventHandler( _glfwWin.window, + _glfwWin.windowUPP, + GetEventTypeCount( GLFW_WINDOW_EVENT_TYPES ), + GLFW_WINDOW_EVENT_TYPES, + NULL, + NULL ); + if( error != noErr ) + { + fprintf( stderr, "Failed to install Carbon window event handler\n" ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + // Don't care if we fail here + (void)SetWindowTitleWithCFString( _glfwWin.window, CFSTR( "GLFW Window" ) ); + (void)RepositionWindow( _glfwWin.window, + NULL, + kWindowCenterOnMainScreen ); + + if( !aglSetDrawable( _glfwWin.aglContext, + GetWindowPort( _glfwWin.window ) ) ) + { + fprintf( stderr, + "Failed to set the AGL context as the Carbon window drawable: %s\n", + aglErrorString( aglGetError() ) ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + // Make OpenGL context current + if( !aglSetCurrentContext( _glfwWin.aglContext ) ) + { + fprintf( stderr, + "Failed to make AGL context current: %s\n", + aglErrorString( aglGetError() ) ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + ShowWindow( _glfwWin.window ); + } + else + { + CGDisplayErr cgErr; + CGLError cglErr; + + CFDictionaryRef optimalMode; + + GLint numCGLvs = 0; + + CGLPixelFormatAttribute CGLpixelFormatAttributes[64]; + int numCGLAttrs = 0; + + // variables for enumerating color depths + GLint rgbColorDepth; + + // CGL pixel format attributes + _setCGLAttribute( kCGLPFADisplayMask, + CGDisplayIDToOpenGLDisplayMask( kCGDirectMainDisplay ) ); + + if( fbconfig->stereo ) + { + CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFAStereo; + } + + if( fbconfig->samples > 1 ) + { + _setCGLAttribute( kCGLPFASamples, (CGLPixelFormatAttribute)fbconfig->samples ); + _setCGLAttribute( kCGLPFASampleBuffers, (CGLPixelFormatAttribute)1 ); + CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFANoRecovery; + } + + CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFAFullScreen; + CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFADoubleBuffer; + CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFAAccelerated; + CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFANoRecovery; + CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFAMinimumPolicy; + + _setCGLAttribute( kCGLPFAAccumSize, + (CGLPixelFormatAttribute)( fbconfig->accumRedBits \ + + fbconfig->accumGreenBits \ + + fbconfig->accumBlueBits \ + + fbconfig->accumAlphaBits ) ); + + _setCGLAttribute( kCGLPFAAlphaSize, (CGLPixelFormatAttribute)fbconfig->alphaBits ); + _setCGLAttribute( kCGLPFADepthSize, (CGLPixelFormatAttribute)fbconfig->depthBits ); + _setCGLAttribute( kCGLPFAStencilSize, (CGLPixelFormatAttribute)fbconfig->stencilBits ); + _setCGLAttribute( kCGLPFAAuxBuffers, (CGLPixelFormatAttribute)fbconfig->auxBuffers ); + + CGLpixelFormatAttributes[ numCGLAttrs++ ] = (CGLPixelFormatAttribute)NULL; + + // create a suitable pixel format with above attributes.. + cglErr = CGLChoosePixelFormat( CGLpixelFormatAttributes, + &_glfwWin.cglPixelFormat, + &numCGLvs ); + if( cglErr != kCGLNoError ) + { + fprintf( stderr, + "Failed to choose CGL pixel format: %s\n", + CGLErrorString( cglErr ) ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + // ..and create a rendering context using that pixel format + cglErr = CGLCreateContext( _glfwWin.cglPixelFormat, NULL, &_glfwWin.cglContext ); + if( cglErr != kCGLNoError ) + { + fprintf( stderr, + "Failed to create CGL context: %s\n", + CGLErrorString( cglErr ) ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + // enumerate depth of RGB channels - unlike AGL, CGL works with + // a single parameter reflecting the full depth of the frame buffer + (void)CGLDescribePixelFormat( _glfwWin.cglPixelFormat, + 0, + kCGLPFAColorSize, + &rgbColorDepth ); + + // capture the display for our application + cgErr = CGCaptureAllDisplays(); + if( cgErr != kCGErrorSuccess ) + { + fprintf( stderr, + "Failed to capture Core Graphics displays\n"); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + // find closest matching NON-STRETCHED display mode.. + optimalMode = CGDisplayBestModeForParametersAndRefreshRateWithProperty( + kCGDirectMainDisplay, + rgbColorDepth, + width, + height, + wndconfig->refreshRate, + NULL, + NULL ); + if( optimalMode == NULL ) + { + fprintf( stderr, + "Failed to retrieve Core Graphics display mode\n"); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + // ..and switch to that mode + cgErr = CGDisplaySwitchToMode( kCGDirectMainDisplay, optimalMode ); + if( cgErr != kCGErrorSuccess ) + { + fprintf( stderr, + "Failed to switch to Core Graphics display mode\n"); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + // switch to our OpenGL context, and bring it up fullscreen + cglErr = CGLSetCurrentContext( _glfwWin.cglContext ); + if( cglErr != kCGLNoError ) + { + fprintf( stderr, + "Failed to make CGL context current: %s\n", + CGLErrorString( cglErr ) ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + cglErr = CGLSetFullScreen( _glfwWin.cglContext ); + if( cglErr != kCGLNoError ) + { + fprintf( stderr, + "Failed to set CGL fullscreen mode: %s\n", + CGLErrorString( cglErr ) ); + + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + } + + return GL_TRUE; +} + +//======================================================================== +// Properly kill the window/video display +//======================================================================== + +void _glfwPlatformCloseWindow( void ) +{ + if( _glfwWin.mouseUPP != NULL ) + { + DisposeEventHandlerUPP( _glfwWin.mouseUPP ); + _glfwWin.mouseUPP = NULL; + } + if( _glfwWin.commandUPP != NULL ) + { + DisposeEventHandlerUPP( _glfwWin.commandUPP ); + _glfwWin.commandUPP = NULL; + } + if( _glfwWin.keyboardUPP != NULL ) + { + DisposeEventHandlerUPP( _glfwWin.keyboardUPP ); + _glfwWin.keyboardUPP = NULL; + } + if( _glfwWin.windowUPP != NULL ) + { + DisposeEventHandlerUPP( _glfwWin.windowUPP ); + _glfwWin.windowUPP = NULL; + } + + if( _glfwWin.fullscreen ) + { + if( _glfwWin.cglContext != NULL ) + { + CGLSetCurrentContext( NULL ); + CGLClearDrawable( _glfwWin.cglContext ); + CGLDestroyContext( _glfwWin.cglContext ); + CGReleaseAllDisplays(); + _glfwWin.cglContext = NULL; + } + + if( _glfwWin.cglPixelFormat != NULL ) + { + CGLDestroyPixelFormat( _glfwWin.cglPixelFormat ); + _glfwWin.cglPixelFormat = NULL; + } + } + else + { + if( _glfwWin.aglContext != NULL ) + { + aglSetCurrentContext( NULL ); + aglSetDrawable( _glfwWin.aglContext, NULL ); + aglDestroyContext( _glfwWin.aglContext ); + _glfwWin.aglContext = NULL; + } + + if( _glfwWin.aglPixelFormat != NULL ) + { + aglDestroyPixelFormat( _glfwWin.aglPixelFormat ); + _glfwWin.aglPixelFormat = NULL; + } + } + + if( _glfwWin.window != NULL ) + { + ReleaseWindow( _glfwWin.window ); + _glfwWin.window = NULL; + } +} + +//======================================================================== +// Set the window title +//======================================================================== + +void _glfwPlatformSetWindowTitle( const char *title ) +{ + CFStringRef windowTitle; + + if( !_glfwWin.fullscreen ) + { + windowTitle = CFStringCreateWithCString( kCFAllocatorDefault, + title, + kCFStringEncodingISOLatin1 ); + + (void)SetWindowTitleWithCFString( _glfwWin.window, windowTitle ); + + CFRelease( windowTitle ); + } +} + +//======================================================================== +// Set the window size +//======================================================================== + +void _glfwPlatformSetWindowSize( int width, int height ) +{ + if( !_glfwWin.fullscreen ) + { + SizeWindow( _glfwWin.window, width, height, TRUE ); + } +} + +//======================================================================== +// Set the window position +//======================================================================== + +void _glfwPlatformSetWindowPos( int x, int y ) +{ + if( !_glfwWin.fullscreen ) + { + MoveWindow( _glfwWin.window, x, y, FALSE ); + } +} + +//======================================================================== +// Window iconification +//======================================================================== + +void _glfwPlatformIconifyWindow( void ) +{ + if( !_glfwWin.fullscreen ) + { + (void)CollapseWindow( _glfwWin.window, TRUE ); + } +} + +//======================================================================== +// Window un-iconification +//======================================================================== + +void _glfwPlatformRestoreWindow( void ) +{ + if( !_glfwWin.fullscreen ) + { + (void)CollapseWindow( _glfwWin.window, FALSE ); + } +} + +//======================================================================== +// Swap buffers (double-buffering) and poll any new events +//======================================================================== + +void _glfwPlatformSwapBuffers( void ) +{ + if( _glfwWin.fullscreen ) + { + CGLFlushDrawable( _glfwWin.cglContext ); + } + else + { + aglSwapBuffers( _glfwWin.aglContext ); + } +} + +//======================================================================== +// Set double buffering swap interval +//======================================================================== + +void _glfwPlatformSwapInterval( int interval ) +{ + GLint AGLparameter = interval; + + // CGL doesn't seem to like intervals other than 0 (vsync off) or 1 (vsync on) + long CGLparameter = ( interval ? 1 : 0 ); + + if( _glfwWin.fullscreen ) + { + // Don't care if we fail here.. + (void)CGLSetParameter( _glfwWin.cglContext, + kCGLCPSwapInterval, + (GLint*) &CGLparameter ); + } + else + { + // ..or here + (void)aglSetInteger( _glfwWin.aglContext, + AGL_SWAP_INTERVAL, + &AGLparameter ); + } +} + +//======================================================================== +// Read back framebuffer parameters from the context +//======================================================================== + +#define _getAGLAttribute( aglAttributeName, variableName ) \ +{ \ + GLint aglValue; \ + (void)aglDescribePixelFormat( _glfwWin.aglPixelFormat, aglAttributeName, &aglValue ); \ + variableName = aglValue; \ +} + +#define _getCGLAttribute( cglAttributeName, variableName ) \ +{ \ + GLint cglValue; \ + (void)CGLDescribePixelFormat( _glfwWin.cglPixelFormat, 0, cglAttributeName, &cglValue ); \ + variableName = cglValue; \ +} + +void _glfwPlatformRefreshWindowParams( void ) +{ + GLint rgbColorDepth; + GLint rgbaAccumDepth = 0; + GLint rgbChannelDepth = 0; + + if( _glfwWin.fullscreen ) + { + _getCGLAttribute( kCGLPFAAccelerated, _glfwWin.accelerated ); + _getCGLAttribute( kCGLPFAAlphaSize, _glfwWin.alphaBits ); + _getCGLAttribute( kCGLPFADepthSize, _glfwWin.depthBits ); + _getCGLAttribute( kCGLPFAStencilSize, _glfwWin.stencilBits ); + _getCGLAttribute( kCGLPFAAuxBuffers, _glfwWin.auxBuffers ); + _getCGLAttribute( kCGLPFAStereo, _glfwWin.stereo ); + _getCGLAttribute( kCGLPFASamples, _glfwWin.samples ); + + // Enumerate depth of RGB channels - unlike AGL, CGL works with + // a single parameter reflecting the full depth of the frame buffer + (void)CGLDescribePixelFormat( _glfwWin.cglPixelFormat, + 0, + kCGLPFAColorSize, + &rgbColorDepth ); + + if( rgbColorDepth == 24 || rgbColorDepth == 32 ) + { + rgbChannelDepth = 8; + } + if( rgbColorDepth == 16 ) + { + rgbChannelDepth = 5; + } + + _glfwWin.redBits = rgbChannelDepth; + _glfwWin.greenBits = rgbChannelDepth; + _glfwWin.blueBits = rgbChannelDepth; + + // Get pixel depth of accumulator - I haven't got the slightest idea + // how this number conforms to any other channel depth than 8 bits, + // so this might end up giving completely knackered results... + _getCGLAttribute( kCGLPFAColorSize, rgbaAccumDepth ); + if( rgbaAccumDepth == 32 ) + { + rgbaAccumDepth = 8; + } + + _glfwWin.accumRedBits = rgbaAccumDepth; + _glfwWin.accumGreenBits = rgbaAccumDepth; + _glfwWin.accumBlueBits = rgbaAccumDepth; + _glfwWin.accumAlphaBits = rgbaAccumDepth; + } + else + { + _getAGLAttribute( AGL_ACCELERATED, _glfwWin.accelerated ); + _getAGLAttribute( AGL_RED_SIZE, _glfwWin.redBits ); + _getAGLAttribute( AGL_GREEN_SIZE, _glfwWin.greenBits ); + _getAGLAttribute( AGL_BLUE_SIZE, _glfwWin.blueBits ); + _getAGLAttribute( AGL_ALPHA_SIZE, _glfwWin.alphaBits ); + _getAGLAttribute( AGL_DEPTH_SIZE, _glfwWin.depthBits ); + _getAGLAttribute( AGL_STENCIL_SIZE, _glfwWin.stencilBits ); + _getAGLAttribute( AGL_ACCUM_RED_SIZE, _glfwWin.accumRedBits ); + _getAGLAttribute( AGL_ACCUM_GREEN_SIZE, _glfwWin.accumGreenBits ); + _getAGLAttribute( AGL_ACCUM_BLUE_SIZE, _glfwWin.accumBlueBits ); + _getAGLAttribute( AGL_ACCUM_ALPHA_SIZE, _glfwWin.accumAlphaBits ); + _getAGLAttribute( AGL_AUX_BUFFERS, _glfwWin.auxBuffers ); + _getAGLAttribute( AGL_STEREO, _glfwWin.stereo ); + _getAGLAttribute( AGL_SAMPLES_ARB, _glfwWin.samples ); + } +} + +//======================================================================== +// Poll for new window and input events +//======================================================================== + +void _glfwPlatformPollEvents( void ) +{ + EventRef event; + EventTargetRef eventDispatcher = GetEventDispatcherTarget(); + + while ( ReceiveNextEvent( 0, NULL, 0.0, TRUE, &event ) == noErr ) + { + SendEventToEventTarget( event, eventDispatcher ); + ReleaseEvent( event ); + } +} + +//======================================================================== +// Wait for new window and input events +//======================================================================== + +void _glfwPlatformWaitEvents( void ) +{ + EventRef event; + + // Wait for new events + ReceiveNextEvent( 0, NULL, kEventDurationForever, FALSE, &event ); + + // Process the new events + _glfwPlatformPollEvents(); +} + +//======================================================================== +// Hide mouse cursor (lock it) +//======================================================================== + +void _glfwPlatformHideMouseCursor( void ) +{ + CGDisplayHideCursor( kCGDirectMainDisplay ); + CGAssociateMouseAndMouseCursorPosition( false ); +} + +//======================================================================== +// Show mouse cursor (unlock it) +//======================================================================== + +void _glfwPlatformShowMouseCursor( void ) +{ + CGDisplayShowCursor( kCGDirectMainDisplay ); + CGAssociateMouseAndMouseCursorPosition( true ); +} + +//======================================================================== +// Set physical mouse cursor position +//======================================================================== + +void _glfwPlatformSetMouseCursorPos( int x, int y ) +{ + Rect content; + + if( _glfwWin.fullscreen ) + { + CGDisplayMoveCursorToPoint( kCGDirectMainDisplay, + CGPointMake( x, y ) ); + } + else + { + GetWindowBounds(_glfwWin.window, kWindowContentRgn, &content); + + _glfwInput.MousePosX = x + content.left; + _glfwInput.MousePosY = y + content.top; + + CGDisplayMoveCursorToPoint( kCGDirectMainDisplay, + CGPointMake( _glfwInput.MousePosX, + _glfwInput.MousePosY ) ); + } +} + diff --git a/lib/carbon/libglfw.pc.cmake b/lib/carbon/libglfw.pc.cmake new file mode 100644 index 00000000..2700c59d --- /dev/null +++ b/lib/carbon/libglfw.pc.cmake @@ -0,0 +1,11 @@ +prefix=@PREFIX@ +exec_prefix=@PREFIX@ +libdir=@PREFIX@/lib +includedir=@PREFIX@/include + +Name: GLFW +Description: A portable framework for OpenGL development +Version: 2.7 +URL: http://glfw.sourceforge.net/ +Libs: -L${libdir} -lglfw -framework AGL -framework OpenGL -framework Carbon +Cflags: -I${includedir} diff --git a/lib/carbon/platform.h b/lib/carbon/platform.h new file mode 100644 index 00000000..f39751c7 --- /dev/null +++ b/lib/carbon/platform.h @@ -0,0 +1,268 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Carbon/AGL/CGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2003 Keith Bauer +// Copyright (c) 2003-2010 Camilla Berglund +// Copyright (c) 2006-2007 Robin Leffmann +// +// 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 _platform_h_ +#define _platform_h_ + + +// This is the Mac OS X version of GLFW +#define _GLFW_MAC_OS_X + +#include +#include +#include + +#include "../../include/GL/glfw.h" + +#if MACOSX_DEPLOYMENT_TARGET < MAC_OS_X_VERSION_10_3 + +#ifndef kCGLNoError +#define kCGLNoError 0 +#endif + +#endif + + +#ifndef GL_VERSION_3_0 + +typedef const GLubyte * (APIENTRY *PFNGLGETSTRINGIPROC) (GLenum, GLuint); + +#endif /*GL_VERSION_3_0*/ + + +//======================================================================== +// Defines +//======================================================================== + +#define _GLFW_MAX_PATH_LENGTH (8192) + +#define MAC_KEY_ENTER 0x24 +#define MAC_KEY_RETURN 0x34 +#define MAC_KEY_ESC 0x35 +#define MAC_KEY_F1 0x7A +#define MAC_KEY_F2 0x78 +#define MAC_KEY_F3 0x63 +#define MAC_KEY_F4 0x76 +#define MAC_KEY_F5 0x60 +#define MAC_KEY_F6 0x61 +#define MAC_KEY_F7 0x62 +#define MAC_KEY_F8 0x64 +#define MAC_KEY_F9 0x65 +#define MAC_KEY_F10 0x6D +#define MAC_KEY_F11 0x67 +#define MAC_KEY_F12 0x6F +#define MAC_KEY_F13 0x69 +#define MAC_KEY_F14 0x6B +#define MAC_KEY_F15 0x71 +#define MAC_KEY_UP 0x7E +#define MAC_KEY_DOWN 0x7D +#define MAC_KEY_LEFT 0x7B +#define MAC_KEY_RIGHT 0x7C +#define MAC_KEY_TAB 0x30 +#define MAC_KEY_BACKSPACE 0x33 +#define MAC_KEY_HELP 0x72 +#define MAC_KEY_DEL 0x75 +#define MAC_KEY_PAGEUP 0x74 +#define MAC_KEY_PAGEDOWN 0x79 +#define MAC_KEY_HOME 0x73 +#define MAC_KEY_END 0x77 +#define MAC_KEY_KP_0 0x52 +#define MAC_KEY_KP_1 0x53 +#define MAC_KEY_KP_2 0x54 +#define MAC_KEY_KP_3 0x55 +#define MAC_KEY_KP_4 0x56 +#define MAC_KEY_KP_5 0x57 +#define MAC_KEY_KP_6 0x58 +#define MAC_KEY_KP_7 0x59 +#define MAC_KEY_KP_8 0x5B +#define MAC_KEY_KP_9 0x5C +#define MAC_KEY_KP_DIVIDE 0x4B +#define MAC_KEY_KP_MULTIPLY 0x43 +#define MAC_KEY_KP_SUBTRACT 0x4E +#define MAC_KEY_KP_ADD 0x45 +#define MAC_KEY_KP_DECIMAL 0x41 +#define MAC_KEY_KP_EQUAL 0x51 +#define MAC_KEY_KP_ENTER 0x4C +#define MAC_KEY_NUMLOCK 0x47 + + +//======================================================================== +// GLFW platform specific types +//======================================================================== + +//------------------------------------------------------------------------ +// Pointer length integer +//------------------------------------------------------------------------ +typedef intptr_t GLFWintptr; + + +GLFWGLOBAL CFDictionaryRef _glfwDesktopVideoMode; + +//------------------------------------------------------------------------ +// Window structure +//------------------------------------------------------------------------ +typedef struct _GLFWwin_struct _GLFWwin; + +struct _GLFWwin_struct { + +// ========= PLATFORM INDEPENDENT MANDATORY PART ========================= + + // User callback functions + GLFWwindowsizefun windowSizeCallback; + GLFWwindowclosefun windowCloseCallback; + GLFWwindowrefreshfun windowRefreshCallback; + GLFWmousebuttonfun mouseButtonCallback; + GLFWmouseposfun mousePosCallback; + GLFWmousewheelfun mouseWheelCallback; + GLFWkeyfun keyCallback; + GLFWcharfun charCallback; + + // User selected window settings + int fullscreen; // Fullscreen flag + int mouseLock; // Mouse-lock flag + int autoPollEvents; // Auto polling flag + int sysKeysDisabled; // System keys disabled flag + int windowNoResize; // Resize- and maximize gadgets disabled flag + int refreshRate; // Vertical monitor refresh rate + + // Window status & parameters + int opened; // Flag telling if window is opened or not + int active; // Application active flag + int iconified; // Window iconified flag + int width, height; // Window width and heigth + int accelerated; // GL_TRUE if window is HW accelerated + + // Framebuffer attributes + int redBits; + int greenBits; + int blueBits; + int alphaBits; + int depthBits; + int stencilBits; + int accumRedBits; + int accumGreenBits; + int accumBlueBits; + int accumAlphaBits; + int auxBuffers; + int stereo; + int samples; + + // OpenGL extensions and context attributes + int has_GL_SGIS_generate_mipmap; + int has_GL_ARB_texture_non_power_of_two; + int glMajor, glMinor, glRevision; + int glForward, glDebug, glProfile; + + PFNGLGETSTRINGIPROC GetStringi; + +// ========= PLATFORM SPECIFIC PART ====================================== + + WindowRef window; + + AGLContext aglContext; + AGLPixelFormat aglPixelFormat; + + CGLContextObj cglContext; + CGLPixelFormatObj cglPixelFormat; + + EventHandlerUPP windowUPP; + EventHandlerUPP mouseUPP; + EventHandlerUPP commandUPP; + EventHandlerUPP keyboardUPP; +}; + +GLFWGLOBAL _GLFWwin _glfwWin; + + +//------------------------------------------------------------------------ +// User input status (some of this should go in _GLFWwin) +//------------------------------------------------------------------------ +GLFWGLOBAL struct { + +// ========= PLATFORM INDEPENDENT MANDATORY PART ========================= + + // Mouse status + int MousePosX, MousePosY; + int WheelPos; + char MouseButton[ GLFW_MOUSE_BUTTON_LAST + 1 ]; + + // Keyboard status + char Key[ GLFW_KEY_LAST + 1 ]; + int LastChar; + + // User selected settings + int StickyKeys; + int StickyMouseButtons; + int KeyRepeat; + +// ========= PLATFORM SPECIFIC PART ====================================== + + UInt32 Modifiers; + +} _glfwInput; + + + +//------------------------------------------------------------------------ +// Library global data +//------------------------------------------------------------------------ +GLFWGLOBAL struct { + +// ========= PLATFORM INDEPENDENT MANDATORY PART ========================= + + // Window opening hints + _GLFWhints hints; + +// ========= PLATFORM SPECIFIC PART ====================================== + + // Timer data + struct { + double t0; + } Timer; + + struct { + // Bundle for dynamically-loading extension function pointers + CFBundleRef OpenGLFramework; + } Libs; + + int Unbundled; + +} _glfwLibrary; + + +//======================================================================== +// Prototypes for platform specific internal functions +//======================================================================== + +void _glfwChangeToResourcesDirectory( void ); + +#endif // _platform_h_ diff --git a/lib/cocoa/CMakeLists.txt b/lib/cocoa/CMakeLists.txt new file mode 100644 index 00000000..115e9bf2 --- /dev/null +++ b/lib/cocoa/CMakeLists.txt @@ -0,0 +1,44 @@ + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/libglfw.pc.cmake + ${CMAKE_CURRENT_BINARY_DIR}/libglfw.pc @ONLY) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/lib + ${GLFW_INCLUDE_DIR}) + +set(cocoa_SOURCES + cocoa_enable.m + cocoa_fullscreen.m + cocoa_glext.m + cocoa_init.m + cocoa_joystick.m + cocoa_time.m + cocoa_window.m) + +# For some reason, CMake doesn't know about .m +set_source_files_properties(${cocoa_SOURCES} PROPERTIES LANGUAGE C) + +set(libglfw_SOURCES + ${common_SOURCES} + ${cocoa_SOURCES}) + +add_library(libglfwStatic STATIC ${libglfw_SOURCES}) +add_library(libglfwShared SHARED ${libglfw_SOURCES}) +target_link_libraries(libglfwShared ${GLFW_LIBRARIES}) +set_target_properties(libglfwStatic libglfwShared PROPERTIES + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME glfw +) + +# Append -fno-common to the compile flags to work around a bug in the Apple GCC +get_target_property(CFLAGS libglfwShared COMPILE_FLAGS) +if(NOT CFLAGS) + set(CFLAGS "") +endif(NOT CFLAGS) +set_target_properties(libglfwShared PROPERTIES COMPILE_FLAGS "${CFLAGS} -fno-common") + +install(TARGETS libglfwStatic libglfwShared DESTINATION lib) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libglfw.pc DESTINATION lib/pkgconfig) + diff --git a/lib/cocoa/cocoa_enable.m b/lib/cocoa/cocoa_enable.m new file mode 100644 index 00000000..7e194766 --- /dev/null +++ b/lib/cocoa/cocoa_enable.m @@ -0,0 +1,51 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Cocoa/NSOpenGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2009-2010 Camilla Berglund +// +// 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 "internal.h" + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Enable and disable system keys +//======================================================================== + +void _glfwPlatformEnableSystemKeys( void ) +{ + // This is checked in macosx_window.m; we take no action here +} + +void _glfwPlatformDisableSystemKeys( void ) +{ + // This is checked in macosx_window.m; we take no action here + // I don't think it's really possible to disable stuff like Exposé + // except in full-screen mode. +} + diff --git a/lib/cocoa/cocoa_fullscreen.m b/lib/cocoa/cocoa_fullscreen.m new file mode 100644 index 00000000..e716cee7 --- /dev/null +++ b/lib/cocoa/cocoa_fullscreen.m @@ -0,0 +1,102 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Cocoa/NSOpenGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2009-2010 Camilla Berglund +// +// 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 "internal.h" + +//======================================================================== +// Check whether the display mode should be included in enumeration +//======================================================================== + +static BOOL modeIsGood( NSDictionary *mode ) +{ + // This is a bit controversial, if you've got something other than an + // LCD computer monitor as an output device you might not want these + // checks. You might also want to reject modes which are interlaced, + // or TV out. There is no one-size-fits-all policy that can work here. + // This seems like a decent compromise, but certain applications may + // wish to patch this... + return [[mode objectForKey:(id)kCGDisplayBitsPerPixel] intValue] >= 15 && + [mode objectForKey:(id)kCGDisplayModeIsSafeForHardware] != nil && + [mode objectForKey:(id)kCGDisplayModeIsStretched] == nil; +} + +//======================================================================== +// Convert Core Graphics display mode to GLFW video mode +//======================================================================== + +static GLFWvidmode vidmodeFromCGDisplayMode( NSDictionary *mode ) +{ + unsigned int width = [[mode objectForKey:(id)kCGDisplayWidth] unsignedIntValue]; + unsigned int height = [[mode objectForKey:(id)kCGDisplayHeight] unsignedIntValue]; + unsigned int bps = [[mode objectForKey:(id)kCGDisplayBitsPerSample] unsignedIntValue]; + + GLFWvidmode result; + result.Width = width; + result.Height = height; + result.RedBits = bps; + result.GreenBits = bps; + result.BlueBits = bps; + return result; +} + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Get a list of available video modes +//======================================================================== + +int _glfwPlatformGetVideoModes( GLFWvidmode *list, int maxcount ) +{ + NSArray *modes = (NSArray *)CGDisplayAvailableModes( CGMainDisplayID() ); + + unsigned int i, j = 0, n = [modes count]; + for( i = 0; i < n && i < (unsigned)maxcount; i++ ) + { + NSDictionary *mode = [modes objectAtIndex:i]; + if( modeIsGood( mode ) ) + { + list[j++] = vidmodeFromCGDisplayMode( mode ); + } + } + + return j; +} + +//======================================================================== +// Get the desktop video mode +//======================================================================== + +void _glfwPlatformGetDesktopMode( GLFWvidmode *mode ) +{ + *mode = vidmodeFromCGDisplayMode( _glfwLibrary.DesktopMode ); +} + diff --git a/lib/cocoa/cocoa_glext.m b/lib/cocoa/cocoa_glext.m new file mode 100644 index 00000000..860b0041 --- /dev/null +++ b/lib/cocoa/cocoa_glext.m @@ -0,0 +1,63 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Cocoa/NSOpenGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2009-2010 Camilla Berglund +// +// 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 "internal.h" + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Check if an OpenGL extension is available at runtime +//======================================================================== + +int _glfwPlatformExtensionSupported( const char *extension ) +{ + // There are no AGL, CGL or NSGL extensions. + return GL_FALSE; +} + +//======================================================================== +// Get the function pointer to an OpenGL function +//======================================================================== + +void * _glfwPlatformGetProcAddress( const char *procname ) +{ + CFStringRef symbolName = CFStringCreateWithCString( kCFAllocatorDefault, + procname, + kCFStringEncodingASCII ); + + void *symbol = CFBundleGetFunctionPointerForName( _glfwLibrary.OpenGLFramework, + symbolName ); + + CFRelease( symbolName ); + + return symbol; +} + diff --git a/lib/cocoa/cocoa_init.m b/lib/cocoa/cocoa_init.m new file mode 100644 index 00000000..94cf1293 --- /dev/null +++ b/lib/cocoa/cocoa_init.m @@ -0,0 +1,250 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Cocoa/NSOpenGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2009-2010 Camilla Berglund +// +// 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. +// +//======================================================================== + +// Needed for _NSGetProgname +#include + +#include "internal.h" + +@interface GLFWApplication : NSApplication +@end + +@implementation GLFWApplication + +// From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost +// This works around an AppKit bug, where key up events while holding +// down the command key don't get sent to the key window. +- (void)sendEvent:(NSEvent *)event +{ + if( [event type] == NSKeyUp && ( [event modifierFlags] & NSCommandKeyMask ) ) + { + [[self keyWindow] sendEvent:event]; + } + else + { + [super sendEvent:event]; + } +} + +@end + +// Prior to Snow Leopard, we need to use this oddly-named semi-private API +// to get the application menu working properly. Need to be careful in +// case it goes away in a future OS update. +@interface NSApplication (NSAppleMenu) +- (void)setAppleMenu:(NSMenu *)m; +@end + +// Keys to search for as potential application names +NSString *GLFWNameKeys[] = +{ + @"CFBundleDisplayName", + @"CFBundleName", + @"CFBundleExecutable", +}; + +//======================================================================== +// Try to figure out what the calling application is called +//======================================================================== +static NSString *findAppName( void ) +{ + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; + + unsigned int i; + for( i = 0; i < sizeof(GLFWNameKeys) / sizeof(GLFWNameKeys[0]); i++ ) + { + id name = [infoDictionary objectForKey:GLFWNameKeys[i]]; + if (name && + [name isKindOfClass:[NSString class]] && + ![@"" isEqualToString:name]) + { + return name; + } + } + + // If we get here, we're unbundled + if( !_glfwLibrary.Unbundled ) + { + // Could do this only if we discover we're unbundled, but it should + // do no harm... + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType( &psn, kProcessTransformToForegroundApplication ); + + // Having the app in front of the terminal window is also generally + // handy. There is an NSApplication API to do this, but... + SetFrontProcess( &psn ); + + _glfwLibrary.Unbundled = GL_TRUE; + } + + char **progname = _NSGetProgname(); + if( progname && *progname ) + { + // TODO: UTF8? + return [NSString stringWithUTF8String:*progname]; + } + + // Really shouldn't get here + return @"GLFW Application"; +} + +//======================================================================== +// Set up the menu bar (manually) +// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that +// could go away at any moment, lots of stuff that really should be +// localize(d|able), etc. Loading a nib would save us this horror, but that +// doesn't seem like a good thing to require of GLFW's clients. +//======================================================================== +static void setUpMenuBar( void ) +{ + NSString *appName = findAppName(); + + NSMenu *bar = [[NSMenu alloc] init]; + [NSApp setMainMenu:bar]; + + NSMenuItem *appMenuItem = + [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; + NSMenu *appMenu = [[NSMenu alloc] init]; + [appMenuItem setSubmenu:appMenu]; + + [appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName] + action:@selector(orderFrontStandardAboutPanel:) + keyEquivalent:@""]; + [appMenu addItem:[NSMenuItem separatorItem]]; + NSMenu *servicesMenu = [[NSMenu alloc] init]; + [NSApp setServicesMenu:servicesMenu]; + [[appMenu addItemWithTitle:@"Services" + action:NULL + keyEquivalent:@""] setSubmenu:servicesMenu]; + [appMenu addItem:[NSMenuItem separatorItem]]; + [appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName] + action:@selector(hide:) + keyEquivalent:@"h"]; + [[appMenu addItemWithTitle:@"Hide Others" + action:@selector(hideOtherApplications:) + keyEquivalent:@"h"] + setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask]; + [appMenu addItemWithTitle:@"Show All" + action:@selector(unhideAllApplications:) + keyEquivalent:@""]; + [appMenu addItem:[NSMenuItem separatorItem]]; + [appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName] + action:@selector(terminate:) + keyEquivalent:@"q"]; + + NSMenuItem *windowMenuItem = + [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; + NSMenu *windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + [NSApp setWindowsMenu:windowMenu]; + [windowMenuItem setSubmenu:windowMenu]; + + [windowMenu addItemWithTitle:@"Miniaturize" + action:@selector(performMiniaturize:) + keyEquivalent:@"m"]; + [windowMenu addItemWithTitle:@"Zoom" + action:@selector(performZoom:) + keyEquivalent:@""]; + [windowMenu addItem:[NSMenuItem separatorItem]]; + [windowMenu addItemWithTitle:@"Bring All to Front" + action:@selector(arrangeInFront:) + keyEquivalent:@""]; + + // At least guard the call to private API to avoid an exception if it + // goes away. Hopefully that means the worst we'll break in future is to + // look ugly... + if( [NSApp respondsToSelector:@selector(setAppleMenu:)] ) + { + [NSApp setAppleMenu:appMenu]; + } +} + +//======================================================================== +// Terminate GLFW when exiting application +//======================================================================== + +static void glfw_atexit( void ) +{ + glfwTerminate(); +} + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Initialize the GLFW library +//======================================================================== + +int _glfwPlatformInit( void ) +{ + _glfwLibrary.AutoreleasePool = [[NSAutoreleasePool alloc] init]; + + // Implicitly create shared NSApplication instance + [GLFWApplication sharedApplication]; + + NSString* resourcePath = [[NSBundle mainBundle] resourcePath]; + + if( access( [resourcePath cStringUsingEncoding:NSUTF8StringEncoding], R_OK ) == 0 ) + { + chdir( [resourcePath cStringUsingEncoding:NSUTF8StringEncoding] ); + } + + // Setting up menu bar must go exactly here else weirdness ensues + setUpMenuBar(); + + [NSApp finishLaunching]; + + // Install atexit routine + atexit( glfw_atexit ); + + _glfwPlatformSetTime( 0.0 ); + + _glfwLibrary.DesktopMode = + (NSDictionary *)CGDisplayCurrentMode( CGMainDisplayID() ); + + return GL_TRUE; +} + +//======================================================================== +// Close window, if open, and shut down GLFW +//======================================================================== + +int _glfwPlatformTerminate( void ) +{ + glfwCloseWindow(); + + // TODO: Probably other cleanup + + [_glfwLibrary.AutoreleasePool release]; + _glfwLibrary.AutoreleasePool = nil; + + return GL_TRUE; +} + diff --git a/lib/cocoa/cocoa_joystick.m b/lib/cocoa/cocoa_joystick.m new file mode 100644 index 00000000..bd3ea640 --- /dev/null +++ b/lib/cocoa/cocoa_joystick.m @@ -0,0 +1,65 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Cocoa/NSOpenGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2009-2010 Camilla Berglund +// +// 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 "internal.h" + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Determine joystick capabilities +//======================================================================== + +int _glfwPlatformGetJoystickParam( int joy, int param ) +{ + // TODO: Implement this. + return 0; +} + +//======================================================================== +// Get joystick axis positions +//======================================================================== + +int _glfwPlatformGetJoystickPos( int joy, float *pos, int numaxes ) +{ + // TODO: Implement this. + return 0; +} + +//======================================================================== +// Get joystick button states +//======================================================================== + +int _glfwPlatformGetJoystickButtons( int joy, unsigned char *buttons, int numbuttons ) +{ + // TODO: Implement this. + return 0; +} + diff --git a/lib/cocoa/cocoa_time.m b/lib/cocoa/cocoa_time.m new file mode 100644 index 00000000..df68b24b --- /dev/null +++ b/lib/cocoa/cocoa_time.m @@ -0,0 +1,53 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Cocoa/NSOpenGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2009-2010 Camilla Berglund +// +// 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 "internal.h" + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Return timer value in seconds +//======================================================================== + +double _glfwPlatformGetTime( void ) +{ + return [NSDate timeIntervalSinceReferenceDate] - _glfwLibrary.Timer.t0; +} + +//======================================================================== +// Set timer value in seconds +//======================================================================== + +void _glfwPlatformSetTime( double time ) +{ + _glfwLibrary.Timer.t0 = [NSDate timeIntervalSinceReferenceDate] - time; +} + diff --git a/lib/cocoa/cocoa_window.m b/lib/cocoa/cocoa_window.m new file mode 100644 index 00000000..7bc0b996 --- /dev/null +++ b/lib/cocoa/cocoa_window.m @@ -0,0 +1,886 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Cocoa/NSOpenGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2009-2010 Camilla Berglund +// +// 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 "internal.h" + +//======================================================================== +// Delegate for window related notifications +// (but also used as an application delegate) +//======================================================================== + +@interface GLFWWindowDelegate : NSObject +@end + +@implementation GLFWWindowDelegate + +- (BOOL)windowShouldClose:(id)window +{ + if( _glfwWin.windowCloseCallback ) + { + if( !_glfwWin.windowCloseCallback() ) + { + return NO; + } + } + + // This is horribly ugly, but it works + glfwCloseWindow(); + return NO; +} + +- (void)windowDidResize:(NSNotification *)notification +{ + [_glfwWin.context update]; + + NSRect contentRect = + [_glfwWin.window contentRectForFrameRect:[_glfwWin.window frame]]; + _glfwWin.width = contentRect.size.width; + _glfwWin.height = contentRect.size.height; + + if( _glfwWin.windowSizeCallback ) + { + _glfwWin.windowSizeCallback( _glfwWin.width, _glfwWin.height ); + } +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + if( _glfwWin.windowCloseCallback ) + { + if( !_glfwWin.windowCloseCallback() ) + { + return NSTerminateCancel; + } + } + + // This is horribly ugly, but it works + glfwCloseWindow(); + return NSTerminateCancel; +} + +@end + +// TODO: Need to find mappings for F13-F15, volume down/up/mute, and eject. +static const unsigned int MAC_TO_GLFW_KEYCODE_MAPPING[128] = +{ + /* 00 */ 'A', + /* 01 */ 'S', + /* 02 */ 'D', + /* 03 */ 'F', + /* 04 */ 'H', + /* 05 */ 'G', + /* 06 */ 'Z', + /* 07 */ 'X', + /* 08 */ 'C', + /* 09 */ 'V', + /* 0a */ -1, + /* 0b */ 'B', + /* 0c */ 'Q', + /* 0d */ 'W', + /* 0e */ 'E', + /* 0f */ 'R', + /* 10 */ 'Y', + /* 11 */ 'T', + /* 12 */ '1', + /* 13 */ '2', + /* 14 */ '3', + /* 15 */ '4', + /* 16 */ '6', + /* 17 */ '5', + /* 18 */ '=', + /* 19 */ '9', + /* 1a */ '7', + /* 1b */ '-', + /* 1c */ '8', + /* 1d */ '0', + /* 1e */ ']', + /* 1f */ 'O', + /* 20 */ 'U', + /* 21 */ '[', + /* 22 */ 'I', + /* 23 */ 'P', + /* 24 */ GLFW_KEY_ENTER, + /* 25 */ 'L', + /* 26 */ 'J', + /* 27 */ '\'', + /* 28 */ 'K', + /* 29 */ ';', + /* 2a */ '\\', + /* 2b */ ',', + /* 2c */ '/', + /* 2d */ 'N', + /* 2e */ 'M', + /* 2f */ '.', + /* 30 */ GLFW_KEY_TAB, + /* 31 */ GLFW_KEY_SPACE, + /* 32 */ '`', + /* 33 */ GLFW_KEY_BACKSPACE, + /* 34 */ -1, + /* 35 */ GLFW_KEY_ESC, + /* 36 */ GLFW_KEY_RSUPER, + /* 37 */ GLFW_KEY_LSUPER, + /* 38 */ GLFW_KEY_LSHIFT, + /* 39 */ GLFW_KEY_CAPS_LOCK, + /* 3a */ GLFW_KEY_LALT, + /* 3b */ GLFW_KEY_LCTRL, + /* 3c */ GLFW_KEY_RSHIFT, + /* 3d */ GLFW_KEY_RALT, + /* 3e */ GLFW_KEY_RCTRL, + /* 3f */ -1, /*Function*/ + /* 40 */ GLFW_KEY_F17, + /* 41 */ GLFW_KEY_KP_DECIMAL, + /* 42 */ -1, + /* 43 */ GLFW_KEY_KP_MULTIPLY, + /* 44 */ -1, + /* 45 */ GLFW_KEY_KP_ADD, + /* 46 */ -1, + /* 47 */ -1, /*KeypadClear*/ + /* 48 */ -1, /*VolumeUp*/ + /* 49 */ -1, /*VolumeDown*/ + /* 4a */ -1, /*Mute*/ + /* 4b */ GLFW_KEY_KP_DIVIDE, + /* 4c */ GLFW_KEY_KP_ENTER, + /* 4d */ -1, + /* 4e */ GLFW_KEY_KP_SUBTRACT, + /* 4f */ GLFW_KEY_F18, + /* 50 */ GLFW_KEY_F19, + /* 51 */ GLFW_KEY_KP_EQUAL, + /* 52 */ GLFW_KEY_KP_0, + /* 53 */ GLFW_KEY_KP_1, + /* 54 */ GLFW_KEY_KP_2, + /* 55 */ GLFW_KEY_KP_3, + /* 56 */ GLFW_KEY_KP_4, + /* 57 */ GLFW_KEY_KP_5, + /* 58 */ GLFW_KEY_KP_6, + /* 59 */ GLFW_KEY_KP_7, + /* 5a */ GLFW_KEY_F20, + /* 5b */ GLFW_KEY_KP_8, + /* 5c */ GLFW_KEY_KP_9, + /* 5d */ -1, + /* 5e */ -1, + /* 5f */ -1, + /* 60 */ GLFW_KEY_F5, + /* 61 */ GLFW_KEY_F6, + /* 62 */ GLFW_KEY_F7, + /* 63 */ GLFW_KEY_F3, + /* 64 */ GLFW_KEY_F8, + /* 65 */ GLFW_KEY_F9, + /* 66 */ -1, + /* 67 */ GLFW_KEY_F11, + /* 68 */ -1, + /* 69 */ GLFW_KEY_F13, + /* 6a */ GLFW_KEY_F16, + /* 6b */ GLFW_KEY_F14, + /* 6c */ -1, + /* 6d */ GLFW_KEY_F10, + /* 6e */ -1, + /* 6f */ GLFW_KEY_F12, + /* 70 */ -1, + /* 71 */ GLFW_KEY_F15, + /* 72 */ GLFW_KEY_INSERT, /*Help*/ + /* 73 */ GLFW_KEY_HOME, + /* 74 */ GLFW_KEY_PAGEUP, + /* 75 */ GLFW_KEY_DEL, + /* 76 */ GLFW_KEY_F4, + /* 77 */ GLFW_KEY_END, + /* 78 */ GLFW_KEY_F2, + /* 79 */ GLFW_KEY_PAGEDOWN, + /* 7a */ GLFW_KEY_F1, + /* 7b */ GLFW_KEY_LEFT, + /* 7c */ GLFW_KEY_RIGHT, + /* 7d */ GLFW_KEY_DOWN, + /* 7e */ GLFW_KEY_UP, + /* 7f */ -1, +}; + +//======================================================================== +// Converts a Mac OS X keycode to a GLFW keycode +//======================================================================== + +static int convertMacKeyCode( unsigned int macKeyCode ) +{ + if( macKeyCode >= 128 ) + { + return -1; + } + + // This treats keycodes as *positional*; that is, we'll return 'a' + // for the key left of 's', even on an AZERTY keyboard. The charInput + // function should still get 'q' though. + return MAC_TO_GLFW_KEYCODE_MAPPING[macKeyCode]; +} + +//======================================================================== +// Content view class for the GLFW window +//======================================================================== + +@interface GLFWContentView : NSView +@end + +@implementation GLFWContentView + +- (BOOL)isOpaque +{ + return YES; +} + +- (BOOL)canBecomeKeyView +{ + return YES; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)mouseDown:(NSEvent *)event +{ + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS ); +} + +- (void)mouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)mouseUp:(NSEvent *)event +{ + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE ); +} + +- (void)mouseMoved:(NSEvent *)event +{ + if( _glfwWin.mouseLock ) + { + _glfwInput.MousePosX += [event deltaX]; + _glfwInput.MousePosY += [event deltaY]; + } + else + { + NSPoint p = [event locationInWindow]; + + // Cocoa coordinate system has origin at lower left + _glfwInput.MousePosX = p.x; + _glfwInput.MousePosY = [[_glfwWin.window contentView] bounds].size.height - p.y; + } + + if( _glfwWin.mousePosCallback ) + { + _glfwWin.mousePosCallback( _glfwInput.MousePosX, _glfwInput.MousePosY ); + } +} + +- (void)rightMouseDown:(NSEvent *)event +{ + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS ); +} + +- (void)rightMouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)rightMouseUp:(NSEvent *)event +{ + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_RIGHT, GLFW_RELEASE ); +} + +- (void)otherMouseDown:(NSEvent *)event +{ + _glfwInputMouseClick( [event buttonNumber], GLFW_PRESS ); +} + +- (void)otherMouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)otherMouseUp:(NSEvent *)event +{ + _glfwInputMouseClick( [event buttonNumber], GLFW_RELEASE ); +} + +- (void)keyDown:(NSEvent *)event +{ + NSUInteger length; + NSString* characters; + int i, code = convertMacKeyCode( [event keyCode] ); + + if( code != -1 ) + { + _glfwInputKey( code, GLFW_PRESS ); + + if( [event modifierFlags] & NSCommandKeyMask ) + { + if( !_glfwWin.sysKeysDisabled ) + { + [super keyDown:event]; + } + } + else + { + characters = [event characters]; + length = [characters length]; + + for( i = 0; i < length; i++ ) + { + _glfwInputChar( [characters characterAtIndex:i], GLFW_PRESS ); + } + } + } +} + +- (void)flagsChanged:(NSEvent *)event +{ + unsigned int newModifierFlags = [event modifierFlags] | NSDeviceIndependentModifierFlagsMask; + int mode; + + if( newModifierFlags > _glfwWin.modifierFlags ) + { + mode = GLFW_PRESS; + } + else + { + mode = GLFW_RELEASE; + } + + _glfwWin.modifierFlags = newModifierFlags; + _glfwInputKey( MAC_TO_GLFW_KEYCODE_MAPPING[[event keyCode]], mode ); +} + +- (void)keyUp:(NSEvent *)event +{ + NSUInteger length; + NSString* characters; + int i, code = convertMacKeyCode( [event keyCode] ); + + if( code != -1 ) + { + _glfwInputKey( code, GLFW_RELEASE ); + + characters = [event characters]; + length = [characters length]; + + for( i = 0; i < length; i++ ) + { + _glfwInputChar( [characters characterAtIndex:i], GLFW_RELEASE ); + } + } +} + +- (void)scrollWheel:(NSEvent *)event +{ + _glfwInput.WheelPosFloating += [event deltaY]; + _glfwInput.WheelPos = lrint(_glfwInput.WheelPosFloating); + + if( _glfwWin.mouseWheelCallback ) + { + _glfwWin.mouseWheelCallback( _glfwInput.WheelPos ); + } +} + +@end + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Here is where the window is created, and the OpenGL rendering context is +// created +//======================================================================== + +int _glfwPlatformOpenWindow( int width, int height, + const _GLFWwndconfig *wndconfig, + const _GLFWfbconfig *fbconfig ) +{ + int colorBits; + + _glfwWin.pixelFormat = nil; + _glfwWin.window = nil; + _glfwWin.context = nil; + _glfwWin.delegate = nil; + + // Fail if OpenGL 3.0 or above was requested + if( wndconfig->glMajor > 2 ) + { + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + _glfwWin.delegate = [[GLFWWindowDelegate alloc] init]; + if( _glfwWin.delegate == nil ) + { + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + [NSApp setDelegate:_glfwWin.delegate]; + + // Mac OS X needs non-zero color size, so set resonable values + colorBits = fbconfig->redBits + fbconfig->greenBits + fbconfig->blueBits; + if( colorBits == 0 ) + { + colorBits = 24; + } + else if( colorBits < 15 ) + { + colorBits = 15; + } + + // Ignored hints: + // OpenGLMajor, OpenGLMinor, OpenGLForward: + // pending Mac OS X support for OpenGL 3.x + // OpenGLDebug + // pending it meaning anything on Mac OS X + + // Don't use accumulation buffer support; it's not accelerated + // Aux buffers probably aren't accelerated either + + CFDictionaryRef fullscreenMode = NULL; + if( wndconfig->mode == GLFW_FULLSCREEN ) + { + fullscreenMode = + // I think it's safe to pass 0 to the refresh rate for this function + // rather than conditionalizing the code to call the version which + // doesn't specify refresh... + CGDisplayBestModeForParametersAndRefreshRateWithProperty( + CGMainDisplayID(), + colorBits + fbconfig->alphaBits, + width, + height, + wndconfig->refreshRate, + // Controversial, see macosx_fullscreen.m for discussion + kCGDisplayModeIsSafeForHardware, + NULL); + + width = [[(id)fullscreenMode objectForKey:(id)kCGDisplayWidth] intValue]; + height = [[(id)fullscreenMode objectForKey:(id)kCGDisplayHeight] intValue]; + } + + unsigned int styleMask = 0; + if( wndconfig->mode == GLFW_WINDOW ) + { + styleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; + + if( !wndconfig->windowNoResize ) + { + styleMask |= NSResizableWindowMask; + } + } + else + { + styleMask = NSBorderlessWindowMask; + } + + _glfwWin.window = [[NSWindow alloc] + initWithContentRect:NSMakeRect(0, 0, width, height) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:NO]; + [_glfwWin.window setContentView:[[GLFWContentView alloc] init]]; + [_glfwWin.window setDelegate:_glfwWin.delegate]; + [_glfwWin.window setAcceptsMouseMovedEvents:YES]; + [_glfwWin.window center]; + + if( wndconfig->mode == GLFW_FULLSCREEN ) + { + CGCaptureAllDisplays(); + CGDisplaySwitchToMode( CGMainDisplayID(), fullscreenMode ); + } + + unsigned int attribute_count = 0; +#define ADD_ATTR(x) attributes[attribute_count++] = x +#define ADD_ATTR2(x, y) (void)({ ADD_ATTR(x); ADD_ATTR(y); }) +#define MAX_ATTRS 24 // urgh + NSOpenGLPixelFormatAttribute attributes[MAX_ATTRS]; + + ADD_ATTR( NSOpenGLPFADoubleBuffer ); + + if( wndconfig->mode == GLFW_FULLSCREEN ) + { + ADD_ATTR( NSOpenGLPFAFullScreen ); + ADD_ATTR( NSOpenGLPFANoRecovery ); + ADD_ATTR2( NSOpenGLPFAScreenMask, + CGDisplayIDToOpenGLDisplayMask( CGMainDisplayID() ) ); + } + + ADD_ATTR2( NSOpenGLPFAColorSize, colorBits ); + + if( fbconfig->alphaBits > 0) + { + ADD_ATTR2( NSOpenGLPFAAlphaSize, fbconfig->alphaBits ); + } + + if( fbconfig->depthBits > 0) + { + ADD_ATTR2( NSOpenGLPFADepthSize, fbconfig->depthBits ); + } + + if( fbconfig->stencilBits > 0) + { + ADD_ATTR2( NSOpenGLPFAStencilSize, fbconfig->stencilBits ); + } + + int accumBits = fbconfig->accumRedBits + fbconfig->accumGreenBits + + fbconfig->accumBlueBits + fbconfig->accumAlphaBits; + + if( accumBits > 0) + { + ADD_ATTR2( NSOpenGLPFAAccumSize, accumBits ); + } + + if( fbconfig->auxBuffers > 0) + { + ADD_ATTR2( NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers ); + } + + if( fbconfig->stereo) + { + ADD_ATTR( NSOpenGLPFAStereo ); + } + + if( fbconfig->samples > 0) + { + ADD_ATTR2( NSOpenGLPFASampleBuffers, 1 ); + ADD_ATTR2( NSOpenGLPFASamples, fbconfig->samples ); + } + + ADD_ATTR(0); + + _glfwWin.pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + if( _glfwWin.pixelFormat == nil ) + { + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + _glfwWin.context = [[NSOpenGLContext alloc] initWithFormat:_glfwWin.pixelFormat + shareContext:nil]; + if( _glfwWin.context == nil ) + { + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + [_glfwWin.window makeKeyAndOrderFront:nil]; + [_glfwWin.context setView:[_glfwWin.window contentView]]; + + if( wndconfig->mode == GLFW_FULLSCREEN ) + { + // TODO: Make this work on pre-Leopard systems + [[_glfwWin.window contentView] enterFullScreenMode:[NSScreen mainScreen] + withOptions:nil]; + } + + [_glfwWin.context makeCurrentContext]; + + NSPoint point = [[NSCursor currentCursor] hotSpot]; + _glfwInput.MousePosX = point.x; + _glfwInput.MousePosY = point.y; + + return GL_TRUE; +} + +//======================================================================== +// Properly kill the window / video display +//======================================================================== + +void _glfwPlatformCloseWindow( void ) +{ + [_glfwWin.window orderOut:nil]; + + if( _glfwWin.fullscreen ) + { + [[_glfwWin.window contentView] exitFullScreenModeWithOptions:nil]; + CGDisplaySwitchToMode( CGMainDisplayID(), + (CFDictionaryRef)_glfwLibrary.DesktopMode ); + CGReleaseAllDisplays(); + } + + [_glfwWin.pixelFormat release]; + _glfwWin.pixelFormat = nil; + + [NSOpenGLContext clearCurrentContext]; + [_glfwWin.context release]; + _glfwWin.context = nil; + + [_glfwWin.window setDelegate:nil]; + [NSApp setDelegate:nil]; + [_glfwWin.delegate release]; + _glfwWin.delegate = nil; + + [_glfwWin.window close]; + _glfwWin.window = nil; + + // TODO: Probably more cleanup +} + +//======================================================================== +// Set the window title +//======================================================================== + +void _glfwPlatformSetWindowTitle( const char *title ) +{ + [_glfwWin.window setTitle:[NSString stringWithCString:title + encoding:NSISOLatin1StringEncoding]]; +} + +//======================================================================== +// Set the window size +//======================================================================== + +void _glfwPlatformSetWindowSize( int width, int height ) +{ + [_glfwWin.window setContentSize:NSMakeSize(width, height)]; +} + +//======================================================================== +// Set the window position +//======================================================================== + +void _glfwPlatformSetWindowPos( int x, int y ) +{ + NSRect contentRect = [_glfwWin.window contentRectForFrameRect:[_glfwWin.window frame]]; + + // We assume here that the client code wants to position the window within the + // screen the window currently occupies + NSRect screenRect = [[_glfwWin.window screen] visibleFrame]; + contentRect.origin = NSMakePoint(screenRect.origin.x + x, + screenRect.origin.y + screenRect.size.height - + y - contentRect.size.height); + + [_glfwWin.window setFrame:[_glfwWin.window frameRectForContentRect:contentRect] + display:YES]; +} + +//======================================================================== +// Iconify the window +//======================================================================== + +void _glfwPlatformIconifyWindow( void ) +{ + [_glfwWin.window miniaturize:nil]; +} + +//======================================================================== +// Restore (un-iconify) the window +//======================================================================== + +void _glfwPlatformRestoreWindow( void ) +{ + [_glfwWin.window deminiaturize:nil]; +} + +//======================================================================== +// Swap buffers +//======================================================================== + +void _glfwPlatformSwapBuffers( void ) +{ + // ARP appears to be unnecessary, but this is future-proof + [_glfwWin.context flushBuffer]; +} + +//======================================================================== +// Set double buffering swap interval +//======================================================================== + +void _glfwPlatformSwapInterval( int interval ) +{ + GLint sync = interval; + [_glfwWin.context setValues:&sync forParameter:NSOpenGLCPSwapInterval]; +} + +//======================================================================== +// Write back window parameters into GLFW window structure +//======================================================================== + +void _glfwPlatformRefreshWindowParams( void ) +{ + GLint value; + + // Since GLFW 2.x doesn't understand screens, we use virtual screen zero + + [_glfwWin.pixelFormat getValues:&value + forAttribute:NSOpenGLPFAAccelerated + forVirtualScreen:0]; + _glfwWin.accelerated = value; + + [_glfwWin.pixelFormat getValues:&value + forAttribute:NSOpenGLPFAAlphaSize + forVirtualScreen:0]; + _glfwWin.alphaBits = value; + + // It seems that the color size includes the size of the alpha channel + [_glfwWin.pixelFormat getValues:&value + forAttribute:NSOpenGLPFAColorSize + forVirtualScreen:0]; + value -= _glfwWin.alphaBits; + _glfwWin.redBits = value / 3; + _glfwWin.greenBits = value / 3; + _glfwWin.blueBits = value / 3; + + [_glfwWin.pixelFormat getValues:&value + forAttribute:NSOpenGLPFADepthSize + forVirtualScreen:0]; + _glfwWin.depthBits = value; + + [_glfwWin.pixelFormat getValues:&value + forAttribute:NSOpenGLPFAStencilSize + forVirtualScreen:0]; + _glfwWin.stencilBits = value; + + [_glfwWin.pixelFormat getValues:&value + forAttribute:NSOpenGLPFAAccumSize + forVirtualScreen:0]; + _glfwWin.accumRedBits = value / 3; + _glfwWin.accumGreenBits = value / 3; + _glfwWin.accumBlueBits = value / 3; + + // TODO: Figure out what to set this value to + _glfwWin.accumAlphaBits = 0; + + [_glfwWin.pixelFormat getValues:&value + forAttribute:NSOpenGLPFAAuxBuffers + forVirtualScreen:0]; + _glfwWin.auxBuffers = value; + + [_glfwWin.pixelFormat getValues:&value + forAttribute:NSOpenGLPFAStereo + forVirtualScreen:0]; + _glfwWin.stereo = value; + + [_glfwWin.pixelFormat getValues:&value + forAttribute:NSOpenGLPFASamples + forVirtualScreen:0]; + _glfwWin.samples = value; + + // These are forced to false as long as Mac OS X lacks support for OpenGL 3.0+ + _glfwWin.glForward = GL_FALSE; + _glfwWin.glDebug = GL_FALSE; + _glfwWin.glProfile = 0; +} + +//======================================================================== +// Poll for new window and input events +//======================================================================== + +void _glfwPlatformPollEvents( void ) +{ + NSEvent *event; + + do + { + event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + + if (event) + { + [NSApp sendEvent:event]; + } + } + while (event); + + [_glfwLibrary.AutoreleasePool drain]; + _glfwLibrary.AutoreleasePool = [[NSAutoreleasePool alloc] init]; +} + +//======================================================================== +// Wait for new window and input events +//======================================================================== + +void _glfwPlatformWaitEvents( void ) +{ + // I wanted to pass NO to dequeue:, and rely on PollEvents to + // dequeue and send. For reasons not at all clear to me, passing + // NO to dequeue: causes this method never to return. + NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantFuture] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + [NSApp sendEvent:event]; + + _glfwPlatformPollEvents(); +} + +//======================================================================== +// Hide mouse cursor (lock it) +//======================================================================== + +void _glfwPlatformHideMouseCursor( void ) +{ + [NSCursor hide]; + CGAssociateMouseAndMouseCursorPosition( false ); +} + +//======================================================================== +// Show mouse cursor (unlock it) +//======================================================================== + +void _glfwPlatformShowMouseCursor( void ) +{ + [NSCursor unhide]; + CGAssociateMouseAndMouseCursorPosition( true ); +} + +//======================================================================== +// Set physical mouse cursor position +//======================================================================== + +void _glfwPlatformSetMouseCursorPos( int x, int y ) +{ + // The library seems to assume that after calling this the mouse won't move, + // but obviously it will, and escape the app's window, and activate other apps, + // and other badness in pain. I think the API's just silly, but maybe I'm + // misunderstanding it... + + // Also, (x, y) are window coords... + + // Also, it doesn't seem possible to write this robustly without + // calculating the maximum y coordinate of all screens, since Cocoa's + // "global coordinates" are upside down from CG's... + + // Without this (once per app run, but it's convenient to do it here) + // events will be suppressed for a default of 0.25 seconds after we + // move the cursor. + CGSetLocalEventsSuppressionInterval( 0.0 ); + + NSPoint localPoint = NSMakePoint( x, y ); + NSPoint globalPoint = [_glfwWin.window convertBaseToScreen:localPoint]; + CGPoint mainScreenOrigin = CGDisplayBounds( CGMainDisplayID() ).origin; + double mainScreenHeight = CGDisplayBounds( CGMainDisplayID() ).size.height; + CGPoint targetPoint = CGPointMake( globalPoint.x - mainScreenOrigin.x, + mainScreenHeight - globalPoint.y - mainScreenOrigin.y ); + CGDisplayMoveCursorToPoint( CGMainDisplayID(), targetPoint ); +} + diff --git a/lib/cocoa/libglfw.pc.cmake b/lib/cocoa/libglfw.pc.cmake new file mode 100644 index 00000000..2700c59d --- /dev/null +++ b/lib/cocoa/libglfw.pc.cmake @@ -0,0 +1,11 @@ +prefix=@PREFIX@ +exec_prefix=@PREFIX@ +libdir=@PREFIX@/lib +includedir=@PREFIX@/include + +Name: GLFW +Description: A portable framework for OpenGL development +Version: 2.7 +URL: http://glfw.sourceforge.net/ +Libs: -L${libdir} -lglfw -framework AGL -framework OpenGL -framework Carbon +Cflags: -I${includedir} diff --git a/lib/cocoa/platform.h b/lib/cocoa/platform.h new file mode 100644 index 00000000..844f0c4f --- /dev/null +++ b/lib/cocoa/platform.h @@ -0,0 +1,189 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Cocoa/NSOpenGL +// API Version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2009-2010 Camilla Berglund +// +// 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 _platform_h_ +#define _platform_h_ + + +// This is the Mac OS X version of GLFW +#define _GLFW_MAC_OS_X + +#if defined(__OBJC__) +#import +#else +typedef void *id; +#endif + +#include "../../include/GL/glfw.h" + + +#ifndef GL_VERSION_3_0 + +typedef const GLubyte * (APIENTRY *PFNGLGETSTRINGIPROC) (GLenum, GLuint); + +#endif /*GL_VERSION_3_0*/ + + +//======================================================================== +// GLFW platform specific types +//======================================================================== + +//------------------------------------------------------------------------ +// Pointer length integer +//------------------------------------------------------------------------ +typedef intptr_t GLFWintptr; + +//------------------------------------------------------------------------ +// Window structure +//------------------------------------------------------------------------ +typedef struct _GLFWwin_struct _GLFWwin; + +struct _GLFWwin_struct { + +// ========= PLATFORM INDEPENDENT MANDATORY PART ========================= + + // User callback functions + GLFWwindowsizefun windowSizeCallback; + GLFWwindowclosefun windowCloseCallback; + GLFWwindowrefreshfun windowRefreshCallback; + GLFWmousebuttonfun mouseButtonCallback; + GLFWmouseposfun mousePosCallback; + GLFWmousewheelfun mouseWheelCallback; + GLFWkeyfun keyCallback; + GLFWcharfun charCallback; + + // User selected window settings + int fullscreen; // Fullscreen flag + int mouseLock; // Mouse-lock flag + int autoPollEvents; // Auto polling flag + int sysKeysDisabled; // System keys disabled flag + int windowNoResize; // Resize- and maximize gadgets disabled flag + int refreshRate; // Vertical monitor refresh rate + + // Window status & parameters + int opened; // Flag telling if window is opened or not + int active; // Application active flag + int iconified; // Window iconified flag + int width, height; // Window width and heigth + int accelerated; // GL_TRUE if window is HW accelerated + + // Framebuffer attributes + int redBits; + int greenBits; + int blueBits; + int alphaBits; + int depthBits; + int stencilBits; + int accumRedBits; + int accumGreenBits; + int accumBlueBits; + int accumAlphaBits; + int auxBuffers; + int stereo; + int samples; + + // OpenGL extensions and context attributes + int has_GL_SGIS_generate_mipmap; + int has_GL_ARB_texture_non_power_of_two; + int glMajor, glMinor, glRevision; + int glForward, glDebug, glProfile; + + PFNGLGETSTRINGIPROC GetStringi; + +// ========= PLATFORM SPECIFIC PART ====================================== + + id window; + id pixelFormat; + id context; + id delegate; + unsigned int modifierFlags; +}; + +GLFWGLOBAL _GLFWwin _glfwWin; + + +//------------------------------------------------------------------------ +// Library global data +//------------------------------------------------------------------------ +GLFWGLOBAL struct { + +// ========= PLATFORM INDEPENDENT MANDATORY PART ========================= + + // Window opening hints + _GLFWhints hints; + +// ========= PLATFORM SPECIFIC PART ====================================== + + // Timer data + struct { + double t0; + } Timer; + + // dlopen handle for dynamically-loading extension function pointers + void *OpenGLFramework; + + int Unbundled; + + id DesktopMode; + + id AutoreleasePool; + +} _glfwLibrary; + + +//------------------------------------------------------------------------ +// User input status (some of this should go in _GLFWwin) +//------------------------------------------------------------------------ +GLFWGLOBAL struct { + +// ========= PLATFORM INDEPENDENT MANDATORY PART ========================= + + // Mouse status + int MousePosX, MousePosY; + int WheelPos; + char MouseButton[ GLFW_MOUSE_BUTTON_LAST+1 ]; + + // Keyboard status + char Key[ GLFW_KEY_LAST+1 ]; + int LastChar; + + // User selected settings + int StickyKeys; + int StickyMouseButtons; + int KeyRepeat; + + +// ========= PLATFORM SPECIFIC PART ====================================== + + double WheelPosFloating; + +} _glfwInput; + + +#endif // _platform_h_ diff --git a/lib/enable.c b/lib/enable.c new file mode 100644 index 00000000..afafd89f --- /dev/null +++ b/lib/enable.c @@ -0,0 +1,310 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Any +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +//======================================================================== +// Enable (show) mouse cursor +//======================================================================== + +static void enableMouseCursor( void ) +{ + int centerPosX, centerPosY; + + if( !_glfwWin.opened || !_glfwWin.mouseLock ) + { + return; + } + + // Show mouse cursor + _glfwPlatformShowMouseCursor(); + + centerPosX = _glfwWin.width / 2; + centerPosY = _glfwWin.height / 2; + + if( centerPosX != _glfwInput.MousePosX || centerPosY != _glfwInput.MousePosY ) + { + _glfwPlatformSetMouseCursorPos( centerPosX, centerPosY ); + + _glfwInput.MousePosX = centerPosX; + _glfwInput.MousePosY = centerPosY; + + if( _glfwWin.mousePosCallback ) + { + _glfwWin.mousePosCallback( _glfwInput.MousePosX, + _glfwInput.MousePosY ); + } + } + + // From now on the mouse is unlocked + _glfwWin.mouseLock = GL_FALSE; +} + +//======================================================================== +// Disable (hide) mouse cursor +//======================================================================== + +static void disableMouseCursor( void ) +{ + if( !_glfwWin.opened || _glfwWin.mouseLock ) + { + return; + } + + // Hide mouse cursor + _glfwPlatformHideMouseCursor(); + + // Move cursor to the middle of the window + _glfwPlatformSetMouseCursorPos( _glfwWin.width >> 1, + _glfwWin.height >> 1 ); + + // From now on the mouse is locked + _glfwWin.mouseLock = GL_TRUE; +} + + +//======================================================================== +// Enable sticky keys +//======================================================================== + +static void enableStickyKeys( void ) +{ + _glfwInput.StickyKeys = 1; +} + +//======================================================================== +// Disable sticky keys +//======================================================================== + +static void disableStickyKeys( void ) +{ + int i; + + _glfwInput.StickyKeys = 0; + + // Release all sticky keys + for( i = 0; i <= GLFW_KEY_LAST; i++ ) + { + if( _glfwInput.Key[ i ] == 2 ) + { + _glfwInput.Key[ i ] = 0; + } + } +} + + +//======================================================================== +// Enable sticky mouse buttons +//======================================================================== + +static void enableStickyMouseButtons( void ) +{ + _glfwInput.StickyMouseButtons = 1; +} + +//======================================================================== +// Disable sticky mouse buttons +//======================================================================== + +static void disableStickyMouseButtons( void ) +{ + int i; + + _glfwInput.StickyMouseButtons = 0; + + // Release all sticky mouse buttons + for( i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++ ) + { + if( _glfwInput.MouseButton[ i ] == 2 ) + { + _glfwInput.MouseButton[ i ] = 0; + } + } +} + + +//======================================================================== +// Enable system keys +//======================================================================== + +static void enableSystemKeys( void ) +{ + if( !_glfwWin.sysKeysDisabled ) + { + return; + } + + _glfwPlatformEnableSystemKeys(); + + // Indicate that system keys are no longer disabled + _glfwWin.sysKeysDisabled = GL_FALSE; +} + +//======================================================================== +// Disable system keys +//======================================================================== + +static void disableSystemKeys( void ) +{ + if( _glfwWin.sysKeysDisabled ) + { + return; + } + + _glfwPlatformDisableSystemKeys(); + + // Indicate that system keys are now disabled + _glfwWin.sysKeysDisabled = GL_TRUE; +} + + +//======================================================================== +// Enable key repeat +//======================================================================== + +static void enableKeyRepeat( void ) +{ + _glfwInput.KeyRepeat = 1; +} + +//======================================================================== +// Disable key repeat +//======================================================================== + +static void disableKeyRepeat( void ) +{ + _glfwInput.KeyRepeat = 0; +} + + +//======================================================================== +// Enable automatic event polling +//======================================================================== + +static void enableAutoPollEvents( void ) +{ + _glfwWin.autoPollEvents = 1; +} + +//======================================================================== +// Disable automatic event polling +//======================================================================== + +static void disableAutoPollEvents( void ) +{ + _glfwWin.autoPollEvents = 0; +} + + + +//************************************************************************ +//**** GLFW user functions **** +//************************************************************************ + +//======================================================================== +// Enable certain GLFW/window/system functions. +//======================================================================== + +GLFWAPI void glfwEnable( int token ) +{ + // Is GLFW initialized? + if( !_glfwInitialized ) + { + return; + } + + switch( token ) + { + case GLFW_MOUSE_CURSOR: + enableMouseCursor(); + break; + case GLFW_STICKY_KEYS: + enableStickyKeys(); + break; + case GLFW_STICKY_MOUSE_BUTTONS: + enableStickyMouseButtons(); + break; + case GLFW_SYSTEM_KEYS: + enableSystemKeys(); + break; + case GLFW_KEY_REPEAT: + enableKeyRepeat(); + break; + case GLFW_AUTO_POLL_EVENTS: + enableAutoPollEvents(); + break; + default: + break; + } +} + + +//======================================================================== +// Disable certain GLFW/window/system functions. +//======================================================================== + +GLFWAPI void glfwDisable( int token ) +{ + // Is GLFW initialized? + if( !_glfwInitialized ) + { + return; + } + + switch( token ) + { + case GLFW_MOUSE_CURSOR: + disableMouseCursor(); + break; + case GLFW_STICKY_KEYS: + disableStickyKeys(); + break; + case GLFW_STICKY_MOUSE_BUTTONS: + disableStickyMouseButtons(); + break; + case GLFW_SYSTEM_KEYS: + disableSystemKeys(); + break; + case GLFW_KEY_REPEAT: + disableKeyRepeat(); + break; + case GLFW_AUTO_POLL_EVENTS: + disableAutoPollEvents(); + break; + default: + break; + } +} + diff --git a/lib/fullscreen.c b/lib/fullscreen.c new file mode 100644 index 00000000..0e363cc6 --- /dev/null +++ b/lib/fullscreen.c @@ -0,0 +1,94 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Any +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//************************************************************************ +//**** GLFW user functions **** +//************************************************************************ + +//======================================================================== +// glfwGetVideoModes() - Get a list of available video modes +//======================================================================== + +GLFWAPI int glfwGetVideoModes( GLFWvidmode *list, int maxcount ) +{ + int count, i, swap, res1, res2, depth1, depth2; + GLFWvidmode vm; + + if( !_glfwInitialized || maxcount <= 0 || list == (GLFWvidmode*) 0 ) + { + return 0; + } + + // Get list of video modes + count = _glfwPlatformGetVideoModes( list, maxcount ); + + // Sort list (bubble sort) + do + { + swap = 0; + for( i = 0; i < count-1; ++ i ) + { + res1 = list[i].Width*list[i].Height; + depth1 = list[i].RedBits+list[i].GreenBits+list[i].BlueBits; + res2 = list[i+1].Width*list[i+1].Height; + depth2 = list[i+1].RedBits+list[i+1].GreenBits+ + list[i+1].BlueBits; + if( (depth2 +// +// 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 "internal.h" + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +#ifndef GL_VERSION_3_0 +#define GL_NUM_EXTENSIONS 0x821D +#endif + +//======================================================================== +// Parses the OpenGL version string and extracts the version number +//======================================================================== + +void _glfwParseGLVersion( int *major, int *minor, int *rev ) +{ + GLuint _major, _minor = 0, _rev = 0; + const GLubyte *version; + const GLubyte *ptr; + + // Get OpenGL version string + version = glGetString( GL_VERSION ); + if( !version ) + { + return; + } + + // Parse string + ptr = version; + for( _major = 0; *ptr >= '0' && *ptr <= '9'; ptr ++ ) + { + _major = 10*_major + (*ptr - '0'); + } + if( *ptr == '.' ) + { + ptr ++; + for( _minor = 0; *ptr >= '0' && *ptr <= '9'; ptr ++ ) + { + _minor = 10*_minor + (*ptr - '0'); + } + if( *ptr == '.' ) + { + ptr ++; + for( _rev = 0; *ptr >= '0' && *ptr <= '9'; ptr ++ ) + { + _rev = 10*_rev + (*ptr - '0'); + } + } + } + + // Return parsed values + *major = _major; + *minor = _minor; + *rev = _rev; +} + +//======================================================================== +// _glfwStringInExtensionString() - Check if a string can be found in an +// OpenGL extension string +//======================================================================== + +int _glfwStringInExtensionString( const char *string, + const GLubyte *extensions ) +{ + const GLubyte *start; + GLubyte *where, *terminator; + + // It takes a bit of care to be fool-proof about parsing the + // OpenGL extensions string. Don't be fooled by sub-strings, + // etc. + start = extensions; + while( 1 ) + { + where = (GLubyte *) strstr( (const char *) start, string ); + if( !where ) + { + return GL_FALSE; + } + terminator = where + strlen( string ); + if( where == start || *(where - 1) == ' ' ) + { + if( *terminator == ' ' || *terminator == '\0' ) + { + break; + } + } + start = terminator; + } + + return GL_TRUE; +} + + + +//************************************************************************ +//**** GLFW user functions **** +//************************************************************************ + +//======================================================================== +// Check if an OpenGL extension is available at runtime +//======================================================================== + +GLFWAPI int glfwExtensionSupported( const char *extension ) +{ + const GLubyte *extensions; + GLubyte *where; + GLint count; + int i; + + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return GL_FALSE; + } + + // Extension names should not have spaces + where = (GLubyte *) strchr( extension, ' ' ); + if( where || *extension == '\0' ) + { + return GL_FALSE; + } + + if( _glfwWin.glMajor < 3 ) + { + // Check if extension is in the old style OpenGL extensions string + + extensions = glGetString( GL_EXTENSIONS ); + if( extensions != NULL ) + { + if( _glfwStringInExtensionString( extension, extensions ) ) + { + return GL_TRUE; + } + } + } + else + { + // Check if extension is in the modern OpenGL extensions string list + + glGetIntegerv( GL_NUM_EXTENSIONS, &count ); + + for( i = 0; i < count; i++ ) + { + if( strcmp( (const char*) _glfwWin.GetStringi( GL_EXTENSIONS, i ), + extension ) == 0 ) + { + return GL_TRUE; + } + } + } + + // Additional platform specific extension checking (e.g. WGL) + if( _glfwPlatformExtensionSupported( extension ) ) + { + return GL_TRUE; + } + + return GL_FALSE; +} + + +//======================================================================== +// glfwGetProcAddress() - Get the function pointer to an OpenGL function. +// This function can be used to get access to extended OpenGL functions. +//======================================================================== + +GLFWAPI void * glfwGetProcAddress( const char *procname ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return NULL; + } + + return _glfwPlatformGetProcAddress( procname ); +} + + +//======================================================================== +// Returns the OpenGL version +//======================================================================== + +GLFWAPI void glfwGetGLVersion( int *major, int *minor, int *rev ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + if( major != NULL ) + { + *major = _glfwWin.glMajor; + } + if( minor != NULL ) + { + *minor = _glfwWin.glMinor; + } + if( rev != NULL ) + { + *rev = _glfwWin.glRevision; + } +} + diff --git a/lib/init.c b/lib/init.c new file mode 100644 index 00000000..f3d3dee7 --- /dev/null +++ b/lib/init.c @@ -0,0 +1,110 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Any +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 _init_c_ +#include "internal.h" + + +//************************************************************************ +//**** GLFW user functions **** +//************************************************************************ + +//======================================================================== +// glfwInit() - Initialize various GLFW state +//======================================================================== + +GLFWAPI int glfwInit( void ) +{ + // Is GLFW already initialized? + if( _glfwInitialized ) + { + return GL_TRUE; + } + + memset( &_glfwLibrary, 0, sizeof( _glfwLibrary ) ); + memset( &_glfwWin, 0, sizeof( _glfwWin ) ); + + // Window is not yet opened + _glfwWin.opened = GL_FALSE; + + // Default enable/disable settings + _glfwWin.sysKeysDisabled = GL_FALSE; + + // Clear window hints + _glfwClearWindowHints(); + + // Platform specific initialization + if( !_glfwPlatformInit() ) + { + return GL_FALSE; + } + + // Form now on, GLFW state is valid + _glfwInitialized = GL_TRUE; + + return GL_TRUE; +} + + + +//======================================================================== +// Close window and shut down library +//======================================================================== + +GLFWAPI void glfwTerminate( void ) +{ + // Is GLFW initialized? + if( !_glfwInitialized ) + { + return; + } + + // Platform specific termination + if( !_glfwPlatformTerminate() ) + { + return; + } + + // GLFW is no longer initialized + _glfwInitialized = GL_FALSE; +} + + +//======================================================================== +// glfwGetVersion() - Get GLFW version +//======================================================================== + +GLFWAPI void glfwGetVersion( int *major, int *minor, int *rev ) +{ + if( major != NULL ) *major = GLFW_VERSION_MAJOR; + if( minor != NULL ) *minor = GLFW_VERSION_MINOR; + if( rev != NULL ) *rev = GLFW_VERSION_REVISION; +} + diff --git a/lib/input.c b/lib/input.c new file mode 100644 index 00000000..faf47cc2 --- /dev/null +++ b/lib/input.c @@ -0,0 +1,280 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Any +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//======================================================================== +// glfwGetKey() +//======================================================================== + +GLFWAPI int glfwGetKey( int key ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return GLFW_RELEASE; + } + + // Is it a valid key? + if( key < 0 || key > GLFW_KEY_LAST ) + { + return GLFW_RELEASE; + } + + if( _glfwInput.Key[ key ] == GLFW_STICK ) + { + // Sticky mode: release key now + _glfwInput.Key[ key ] = GLFW_RELEASE; + return GLFW_PRESS; + } + + return (int) _glfwInput.Key[ key ]; +} + + +//======================================================================== +// glfwGetMouseButton() +//======================================================================== + +GLFWAPI int glfwGetMouseButton( int button ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return GLFW_RELEASE; + } + + // Is it a valid mouse button? + if( button < 0 || button > GLFW_MOUSE_BUTTON_LAST ) + { + return GLFW_RELEASE; + } + + if( _glfwInput.MouseButton[ button ] == GLFW_STICK ) + { + // Sticky mode: release mouse button now + _glfwInput.MouseButton[ button ] = GLFW_RELEASE; + return GLFW_PRESS; + } + + return (int) _glfwInput.MouseButton[ button ]; +} + + +//======================================================================== +// glfwGetMousePos() +//======================================================================== + +GLFWAPI void glfwGetMousePos( int *xpos, int *ypos ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Return mouse position + if( xpos != NULL ) + { + *xpos = _glfwInput.MousePosX; + } + if( ypos != NULL ) + { + *ypos = _glfwInput.MousePosY; + } +} + + +//======================================================================== +// glfwSetMousePos() +//======================================================================== + +GLFWAPI void glfwSetMousePos( int xpos, int ypos ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Don't do anything if the mouse position did not change + if( xpos == _glfwInput.MousePosX && ypos == _glfwInput.MousePosY ) + { + return; + } + + // Set GLFW mouse position + _glfwInput.MousePosX = xpos; + _glfwInput.MousePosY = ypos; + + // If we have a locked mouse, do not change cursor position + if( _glfwWin.mouseLock ) + { + return; + } + + // Update physical cursor position + _glfwPlatformSetMouseCursorPos( xpos, ypos ); +} + + +//======================================================================== +// glfwGetMouseWheel() +//======================================================================== + +GLFWAPI int glfwGetMouseWheel( void ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return 0; + } + + // Return mouse wheel position + return _glfwInput.WheelPos; +} + + +//======================================================================== +// glfwSetMouseWheel() +//======================================================================== + +GLFWAPI void glfwSetMouseWheel( int pos ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Set mouse wheel position + _glfwInput.WheelPos = pos; +} + + +//======================================================================== +// glfwSetKeyCallback() - Set callback function for keyboard input +//======================================================================== + +GLFWAPI void glfwSetKeyCallback( GLFWkeyfun cbfun ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Set callback function + _glfwWin.keyCallback = cbfun; +} + + +//======================================================================== +// glfwSetCharCallback() - Set callback function for character input +//======================================================================== + +GLFWAPI void glfwSetCharCallback( GLFWcharfun cbfun ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Set callback function + _glfwWin.charCallback = cbfun; +} + + +//======================================================================== +// glfwSetMouseButtonCallback() - Set callback function for mouse clicks +//======================================================================== + +GLFWAPI void glfwSetMouseButtonCallback( GLFWmousebuttonfun cbfun ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Set callback function + _glfwWin.mouseButtonCallback = cbfun; +} + + +//======================================================================== +// glfwSetMousePosCallback() - Set callback function for mouse moves +//======================================================================== + +GLFWAPI void glfwSetMousePosCallback( GLFWmouseposfun cbfun ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Set callback function + _glfwWin.mousePosCallback = cbfun; + + // Call the callback function to let the application know the current + // mouse position + if( cbfun ) + { + cbfun( _glfwInput.MousePosX, _glfwInput.MousePosY ); + } +} + + +//======================================================================== +// glfwSetMouseWheelCallback() - Set callback function for mouse wheel +//======================================================================== + +GLFWAPI void glfwSetMouseWheelCallback( GLFWmousewheelfun cbfun ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Set callback function + _glfwWin.mouseWheelCallback = cbfun; + + // Call the callback function to let the application know the current + // mouse wheel position + if( cbfun ) + { + cbfun( _glfwInput.WheelPos ); + } +} + diff --git a/lib/internal.h b/lib/internal.h new file mode 100644 index 00000000..3aeb081a --- /dev/null +++ b/lib/internal.h @@ -0,0 +1,221 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Any +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 _internal_h_ +#define _internal_h_ + +//======================================================================== +// GLFWGLOBAL is a macro that places all global variables in the init.c +// module (all other modules reference global variables as 'extern') +//======================================================================== + +#if defined( _init_c_ ) +#define GLFWGLOBAL +#else +#define GLFWGLOBAL extern +#endif + + +//======================================================================== +// Input handling definitions +//======================================================================== + +// Internal key and button state/action definitions +#define GLFW_STICK 2 + + +//======================================================================== +// System independent include files +//======================================================================== + +#include +#include +#include + + +//------------------------------------------------------------------------ +// Window opening hints (set by glfwOpenWindowHint) +// A bucket of semi-random stuff bunched together for historical reasons +// This is used only by the platform independent code and only to store +// parameters passed to us by glfwOpenWindowHint +//------------------------------------------------------------------------ +typedef struct { + int refreshRate; + int accumRedBits; + int accumGreenBits; + int accumBlueBits; + int accumAlphaBits; + int auxBuffers; + int stereo; + int windowNoResize; + int samples; + int glMajor; + int glMinor; + int glForward; + int glDebug; + int glProfile; +} _GLFWhints; + + +//------------------------------------------------------------------------ +// Platform specific definitions goes in platform.h (which also includes +// glfw.h) +//------------------------------------------------------------------------ + +#include "platform.h" + + +//------------------------------------------------------------------------ +// Parameters relating to the creation of the context and window but not +// directly related to the properties of the framebuffer +// This is used to pass window and context creation parameters from the +// platform independent code to the platform specific code +//------------------------------------------------------------------------ +typedef struct { + int mode; + int refreshRate; + int windowNoResize; + int glMajor; + int glMinor; + int glForward; + int glDebug; + int glProfile; +} _GLFWwndconfig; + + +//------------------------------------------------------------------------ +// Framebuffer configuration descriptor, i.e. buffers and their sizes +// Also a platform specific ID used to map back to the actual backend APIs +// This is used to pass framebuffer parameters from the platform independent +// code to the platform specific code, and also to enumerate and select +// available framebuffer configurations +//------------------------------------------------------------------------ +typedef struct { + int redBits; + int greenBits; + int blueBits; + int alphaBits; + int depthBits; + int stencilBits; + int accumRedBits; + int accumGreenBits; + int accumBlueBits; + int accumAlphaBits; + int auxBuffers; + int stereo; + int samples; + GLFWintptr platformID; +} _GLFWfbconfig; + + +//======================================================================== +// System independent global variables (GLFW internals) +//======================================================================== + +// Flag indicating if GLFW has been initialized +#if defined( _init_c_ ) +int _glfwInitialized = 0; +#else +GLFWGLOBAL int _glfwInitialized; +#endif + + +//======================================================================== +// Prototypes for platform specific implementation functions +//======================================================================== + +// Init/terminate +int _glfwPlatformInit( void ); +int _glfwPlatformTerminate( void ); + +// Enable/Disable +void _glfwPlatformEnableSystemKeys( void ); +void _glfwPlatformDisableSystemKeys( void ); + +// Fullscreen +int _glfwPlatformGetVideoModes( GLFWvidmode *list, int maxcount ); +void _glfwPlatformGetDesktopMode( GLFWvidmode *mode ); + +// OpenGL extensions +int _glfwPlatformExtensionSupported( const char *extension ); +void * _glfwPlatformGetProcAddress( const char *procname ); + +// Joystick +int _glfwPlatformGetJoystickParam( int joy, int param ); +int _glfwPlatformGetJoystickPos( int joy, float *pos, int numaxes ); +int _glfwPlatformGetJoystickButtons( int joy, unsigned char *buttons, int numbuttons ); + +// Time +double _glfwPlatformGetTime( void ); +void _glfwPlatformSetTime( double time ); + +// Window management +int _glfwPlatformOpenWindow( int width, int height, const _GLFWwndconfig *wndconfig, const _GLFWfbconfig *fbconfig ); +void _glfwPlatformCloseWindow( void ); +void _glfwPlatformSetWindowTitle( const char *title ); +void _glfwPlatformSetWindowSize( int width, int height ); +void _glfwPlatformSetWindowPos( int x, int y ); +void _glfwPlatformIconifyWindow( void ); +void _glfwPlatformRestoreWindow( void ); +void _glfwPlatformSwapBuffers( void ); +void _glfwPlatformSwapInterval( int interval ); +void _glfwPlatformRefreshWindowParams( void ); +void _glfwPlatformPollEvents( void ); +void _glfwPlatformWaitEvents( void ); +void _glfwPlatformHideMouseCursor( void ); +void _glfwPlatformShowMouseCursor( void ); +void _glfwPlatformSetMouseCursorPos( int x, int y ); + + +//======================================================================== +// Prototypes for platform independent internal functions +//======================================================================== + +// Window management (window.c) +void _glfwClearWindowHints( void ); + +// Input handling (window.c) +void _glfwClearInput( void ); +void _glfwInputDeactivation( void ); +void _glfwInputKey( int key, int action ); +void _glfwInputChar( int character, int action ); +void _glfwInputMouseClick( int button, int action ); + +// OpenGL extensions (glext.c) +void _glfwParseGLVersion( int *major, int *minor, int *rev ); +int _glfwStringInExtensionString( const char *string, const GLubyte *extensions ); + +// Framebuffer configs +const _GLFWfbconfig *_glfwChooseFBConfig( const _GLFWfbconfig *desired, + const _GLFWfbconfig *alternatives, + unsigned int count ); + + +#endif // _internal_h_ diff --git a/lib/joystick.c b/lib/joystick.c new file mode 100644 index 00000000..32841a75 --- /dev/null +++ b/lib/joystick.c @@ -0,0 +1,102 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Any +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//************************************************************************ +//**** GLFW user functions **** +//************************************************************************ + +//======================================================================== +// glfwGetJoystickParam() - Determine joystick capabilities +//======================================================================== + +GLFWAPI int glfwGetJoystickParam( int joy, int param ) +{ + // Is GLFW initialized? + if( !_glfwInitialized ) + { + return 0; + } + + return _glfwPlatformGetJoystickParam( joy, param ); +} + + +//======================================================================== +// glfwGetJoystickPos() - Get joystick axis positions +//======================================================================== + +GLFWAPI int glfwGetJoystickPos( int joy, float *pos, int numaxes ) +{ + int i; + + // Is GLFW initialized? + if( !_glfwInitialized ) + { + return 0; + } + + // Clear positions + for( i = 0; i < numaxes; i++ ) + { + pos[ i ] = 0.0f; + } + + return _glfwPlatformGetJoystickPos( joy, pos, numaxes ); +} + + +//======================================================================== +// glfwGetJoystickButtons() - Get joystick button states +//======================================================================== + +GLFWAPI int glfwGetJoystickButtons( int joy, + unsigned char *buttons, + int numbuttons ) +{ + int i; + + // Is GLFW initialized? + if( !_glfwInitialized ) + { + return 0; + } + + // Clear button states + for( i = 0; i < numbuttons; i++ ) + { + buttons[ i ] = GLFW_RELEASE; + } + + return _glfwPlatformGetJoystickButtons( joy, buttons, numbuttons ); +} + diff --git a/lib/time.c b/lib/time.c new file mode 100644 index 00000000..f7ea31c5 --- /dev/null +++ b/lib/time.c @@ -0,0 +1,68 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Any +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//************************************************************************ +//**** GLFW user functions **** +//************************************************************************ + +//======================================================================== +// glfwGetTime() - Return timer value in seconds +//======================================================================== + +GLFWAPI double glfwGetTime( void ) +{ + // Is GLFW initialized? + if( !_glfwInitialized ) + { + return 0.0; + } + + return _glfwPlatformGetTime(); +} + + +//======================================================================== +// glfwSetTime() - Set timer value in seconds +//======================================================================== + +GLFWAPI void glfwSetTime( double time ) +{ + // Is GLFW initialized? + if( !_glfwInitialized ) + { + return; + } + + _glfwPlatformSetTime( time ); +} + diff --git a/lib/win32/CMakeLists.txt b/lib/win32/CMakeLists.txt new file mode 100644 index 00000000..d5230c68 --- /dev/null +++ b/lib/win32/CMakeLists.txt @@ -0,0 +1,55 @@ + +if(CYGWIN) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/libglfw.pc.cmake + ${CMAKE_CURRENT_BINARY_DIR}/libglfw.pc @ONLY) + +# These lines are intended to remove the --export-all-symbols +# flag added in the Modules/Platform/CYGWIN.cmake file of the +# CMake distribution. +# This is a HACK. If you have trouble _linking_ the GLFW +# _shared_ library on Cygwin, try disabling this. + set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared") + set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS}) + +endif(CYGWIN) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/lib) + +set(libglfw_SOURCES + ${common_SOURCES} + win32_enable.c + win32_fullscreen.c + win32_glext.c + win32_init.c + win32_joystick.c + win32_time.c + win32_window.c + win32_dllmain.c) + +add_library(libglfwStatic STATIC ${libglfw_SOURCES}) +add_library(libglfwShared SHARED glfwdll.def ${libglfw_SOURCES}) + +target_link_libraries(libglfwShared ${OPENGL_gl_LIBRARY}) +set_target_properties(libglfwShared PROPERTIES + DEFINE_SYMBOL GLFW_BUILD_DLL + PREFIX "" + IMPORT_PREFIX "" + IMPORT_SUFFIX "dll.lib") + +set_target_properties(libglfwStatic libglfwShared PROPERTIES + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME glfw) + +if(CYGWIN) + # Build for the regular Win32 environment (not Cygwin) + set_target_properties(libglfwStatic libglfwShared PROPERTIES COMPILE_FLAGS "-mwin32 -mno-cygwin") + set_target_properties(libglfwStatic libglfwShared PROPERTIES LINK_FLAGS "-mwin32 -mno-cygwin") +endif(CYGWIN) + +install(TARGETS libglfwStatic libglfwShared DESTINATION lib) + +if(CYGWIN) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libglfw.pc DESTINATION lib/pkgconfig) +endif(CYGWIN) + diff --git a/lib/win32/glfwdll.def b/lib/win32/glfwdll.def new file mode 100644 index 00000000..4f4ce470 --- /dev/null +++ b/lib/win32/glfwdll.def @@ -0,0 +1,46 @@ +LIBRARY GLFW.DLL + +EXPORTS +glfwCloseWindow +glfwDisable +glfwEnable +glfwExtensionSupported +glfwGetDesktopMode +glfwGetGLVersion +glfwGetJoystickButtons +glfwGetJoystickParam +glfwGetJoystickPos +glfwGetKey +glfwGetMouseButton +glfwGetMousePos +glfwGetMouseWheel +glfwGetProcAddress +glfwGetTime +glfwGetVersion +glfwGetVideoModes +glfwGetWindowParam +glfwGetWindowSize +glfwIconifyWindow +glfwInit +glfwOpenWindow +glfwOpenWindowHint +glfwPollEvents +glfwRestoreWindow +glfwSetCharCallback +glfwSetKeyCallback +glfwSetMouseButtonCallback +glfwSetMousePos +glfwSetMousePosCallback +glfwSetMouseWheel +glfwSetMouseWheelCallback +glfwSetTime +glfwSetWindowCloseCallback +glfwSetWindowRefreshCallback +glfwSetWindowPos +glfwSetWindowSize +glfwSetWindowSizeCallback +glfwSetWindowTitle +glfwSwapBuffers +glfwSwapInterval +glfwTerminate +glfwWaitEvents diff --git a/lib/win32/glfwdll_mgw1.def b/lib/win32/glfwdll_mgw1.def new file mode 100644 index 00000000..bdf475f2 --- /dev/null +++ b/lib/win32/glfwdll_mgw1.def @@ -0,0 +1,46 @@ +LIBRARY GLFW.DLL + +EXPORTS +glfwCloseWindow = glfwCloseWindow@0 +glfwDisable = glfwDisable@4 +glfwEnable = glfwEnable@4 +glfwExtensionSupported = glfwExtensionSupported@4 +glfwGetDesktopMode = glfwGetDesktopMode@4 +glfwGetGLVersion = glfwGetGLVersion@12 +glfwGetJoystickButtons = glfwGetJoystickButtons@12 +glfwGetJoystickParam = glfwGetJoystickParam@8 +glfwGetJoystickPos = glfwGetJoystickPos@12 +glfwGetKey = glfwGetKey@4 +glfwGetMouseButton = glfwGetMouseButton@4 +glfwGetMousePos = glfwGetMousePos@8 +glfwGetMouseWheel = glfwGetMouseWheel@0 +glfwGetProcAddress = glfwGetProcAddress@4 +glfwGetTime = glfwGetTime@0 +glfwGetVersion = glfwGetVersion@12 +glfwGetVideoModes = glfwGetVideoModes@8 +glfwGetWindowParam = glfwGetWindowParam@4 +glfwGetWindowSize = glfwGetWindowSize@8 +glfwIconifyWindow = glfwIconifyWindow@0 +glfwInit = glfwInit@0 +glfwOpenWindow = glfwOpenWindow@36 +glfwOpenWindowHint = glfwOpenWindowHint@8 +glfwPollEvents = glfwPollEvents@0 +glfwRestoreWindow = glfwRestoreWindow@0 +glfwSetCharCallback = glfwSetCharCallback@4 +glfwSetKeyCallback = glfwSetKeyCallback@4 +glfwSetMouseButtonCallback = glfwSetMouseButtonCallback@4 +glfwSetMousePos = glfwSetMousePos@8 +glfwSetMousePosCallback = glfwSetMousePosCallback@4 +glfwSetMouseWheel = glfwSetMouseWheel@4 +glfwSetMouseWheelCallback = glfwSetMouseWheelCallback@4 +glfwSetTime = glfwSetTime@8 +glfwSetWindowCloseCallback = glfwSetWindowCloseCallback@4 +glfwSetWindowRefreshCallback = glfwSetWindowRefreshCallback@4 +glfwSetWindowPos = glfwSetWindowPos@8 +glfwSetWindowSize = glfwSetWindowSize@8 +glfwSetWindowSizeCallback = glfwSetWindowSizeCallback@4 +glfwSetWindowTitle = glfwSetWindowTitle@4 +glfwSwapBuffers = glfwSwapBuffers@0 +glfwSwapInterval = glfwSwapInterval@4 +glfwTerminate = glfwTerminate@0 +glfwWaitEvents = glfwWaitEvents@0 diff --git a/lib/win32/glfwdll_mgw2.def b/lib/win32/glfwdll_mgw2.def new file mode 100644 index 00000000..2a2b6ce1 --- /dev/null +++ b/lib/win32/glfwdll_mgw2.def @@ -0,0 +1,46 @@ +LIBRARY GLFW.DLL + +EXPORTS +glfwCloseWindow@0 +glfwDisable@4 +glfwEnable@4 +glfwExtensionSupported@4 +glfwGetDesktopMode@4 +glfwGetGLVersion@12 +glfwGetJoystickButtons@12 +glfwGetJoystickParam@8 +glfwGetJoystickPos@12 +glfwGetKey@4 +glfwGetMouseButton@4 +glfwGetMousePos@8 +glfwGetMouseWheel@0 +glfwGetProcAddress@4 +glfwGetTime@0 +glfwGetVersion@12 +glfwGetVideoModes@8 +glfwGetWindowParam@4 +glfwGetWindowSize@8 +glfwIconifyWindow@0 +glfwInit@0 +glfwOpenWindow@36 +glfwOpenWindowHint@8 +glfwPollEvents@0 +glfwRestoreWindow@0 +glfwSetCharCallback@4 +glfwSetKeyCallback@4 +glfwSetMouseButtonCallback@4 +glfwSetMousePos@8 +glfwSetMousePosCallback@4 +glfwSetMouseWheel@4 +glfwSetMouseWheelCallback@4 +glfwSetTime@8 +glfwSetWindowCloseCallback@4 +glfwSetWindowRefreshCallback@4 +glfwSetWindowPos@8 +glfwSetWindowSize@8 +glfwSetWindowSizeCallback@4 +glfwSetWindowTitle@4 +glfwSwapBuffers@0 +glfwSwapInterval@4 +glfwTerminate@0 +glfwWaitEvents@0 diff --git a/lib/win32/libglfw.pc.cmake b/lib/win32/libglfw.pc.cmake new file mode 100644 index 00000000..684b4ba2 --- /dev/null +++ b/lib/win32/libglfw.pc.cmake @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: GLFW +Description: A portable framework for OpenGL development +Version: 2.7 +URL: http://glfw.sourceforge.net/ +Libs: -L${libdir} -lglfw @GLFW_LIBRARIES@ +Cflags: -I${includedir} -mwin32 diff --git a/lib/win32/platform.h b/lib/win32/platform.h new file mode 100644 index 00000000..46cef336 --- /dev/null +++ b/lib/win32/platform.h @@ -0,0 +1,480 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Win32/WGL +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 _platform_h_ +#define _platform_h_ + + +// This is the Windows version of GLFW +#define _GLFW_WIN32 + +// We don't need all the fancy stuff +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#define VC_EXTRALEAN + +// Include files +#include +#include +#include "../../include/GL/glfw.h" + + +//======================================================================== +// Hack: Define things that some 's do not define +//======================================================================== + +// Some old versions of w32api (used by MinGW and Cygwin) define +// WH_KEYBOARD_LL without typedef:ing KBDLLHOOKSTRUCT (!) +#if defined(__MINGW32__) || defined(__CYGWIN__) +#include +#if defined(WH_KEYBOARD_LL) && (__W32API_MAJOR_VERSION == 1) && (__W32API_MINOR_VERSION <= 2) +#undef WH_KEYBOARD_LL +#endif +#endif + +//------------------------------------------------------------------------ +// ** NOTE ** If this gives you compiler errors and you are using MinGW +// (or Dev-C++), update to w32api version 1.3 or later: +// http://sourceforge.net/project/showfiles.php?group_id=2435 +//------------------------------------------------------------------------ +#ifndef WH_KEYBOARD_LL +#define WH_KEYBOARD_LL 13 +typedef struct tagKBDLLHOOKSTRUCT { + DWORD vkCode; + DWORD scanCode; + DWORD flags; + DWORD time; + DWORD dwExtraInfo; +} KBDLLHOOKSTRUCT, FAR *LPKBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT; +#endif // WH_KEYBOARD_LL + +#ifndef LLKHF_ALTDOWN +#define LLKHF_ALTDOWN 0x00000020 +#endif + +#ifndef SPI_SETSCREENSAVERRUNNING +#define SPI_SETSCREENSAVERRUNNING 97 +#endif +#ifndef SPI_GETANIMATION +#define SPI_GETANIMATION 72 +#endif +#ifndef SPI_SETANIMATION +#define SPI_SETANIMATION 73 +#endif +#ifndef SPI_GETFOREGROUNDLOCKTIMEOUT +#define SPI_GETFOREGROUNDLOCKTIMEOUT 0x2000 +#endif +#ifndef SPI_SETFOREGROUNDLOCKTIMEOUT +#define SPI_SETFOREGROUNDLOCKTIMEOUT 0x2001 +#endif + +#ifndef CDS_FULLSCREEN +#define CDS_FULLSCREEN 4 +#endif + +#ifndef PFD_GENERIC_ACCELERATED +#define PFD_GENERIC_ACCELERATED 0x00001000 +#endif +#ifndef PFD_DEPTH_DONTCARE +#define PFD_DEPTH_DONTCARE 0x20000000 +#endif + +#ifndef ENUM_CURRENT_SETTINGS +#define ENUM_CURRENT_SETTINGS -1 +#endif +#ifndef ENUM_REGISTRY_SETTINGS +#define ENUM_REGISTRY_SETTINGS -2 +#endif + +#ifndef WM_MOUSEWHEEL +#define WM_MOUSEWHEEL 0x020A +#endif +#ifndef WHEEL_DELTA +#define WHEEL_DELTA 120 +#endif + +#ifndef WM_XBUTTONDOWN +#define WM_XBUTTONDOWN 0x020B +#endif +#ifndef WM_XBUTTONUP +#define WM_XBUTTONUP 0x020C +#endif +#ifndef XBUTTON1 +#define XBUTTON1 1 +#endif +#ifndef XBUTTON2 +#define XBUTTON2 2 +#endif + +#ifndef WGL_ARB_pixel_format + +// wglSwapIntervalEXT typedef (Win32 buffer-swap interval control) +typedef int (APIENTRY * WGLSWAPINTERVALEXT_T) (int); +// wglGetPixelFormatAttribivARB typedef +typedef BOOL (WINAPI * WGLGETPIXELFORMATATTRIBIVARB_T) (HDC, int, int, UINT, const int *, int *); +// wglGetExtensionStringEXT typedef +typedef const char *(APIENTRY * WGLGETEXTENSIONSSTRINGEXT_T)( void ); +// wglGetExtensionStringARB typedef +typedef const char *(APIENTRY * WGLGETEXTENSIONSSTRINGARB_T)( HDC ); + +/* Constants for wglGetPixelFormatAttribivARB */ +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_ACCUM_BITS_ARB 0x201D +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 + +/* Constants for WGL_ACCELERATION_ARB */ +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_GENERIC_ACCELERATION_ARB 0x2026 +#define WGL_FULL_ACCELERATION_ARB 0x2027 + +/* Constants for WGL_PIXEL_TYPE_ARB */ +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_TYPE_COLORINDEX_ARB 0x202C + +#endif /*WGL_ARB_pixel_format*/ + + +#ifndef WGL_ARB_create_context + +/* wglCreateContextAttribsARB */ +typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC, HGLRC, const int *); + +/* Tokens for wglCreateContextAttribsARB attributes */ +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 + +/* Bits for WGL_CONTEXT_FLAGS_ARB */ +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 + +/* Bits for WGL_CONTEXT_PROFILE_MASK_ARB */ +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 + +#endif /*WGL_ARB_create_context*/ + + +#ifndef GL_VERSION_3_0 + +typedef const GLubyte * (APIENTRY *PFNGLGETSTRINGIPROC) (GLenum, GLuint); + +#endif /*GL_VERSION_3_0*/ + + +//======================================================================== +// DLLs that are loaded at glfwInit() +//======================================================================== + +// gdi32.dll function pointer typedefs +#ifndef _GLFW_NO_DLOAD_GDI32 +typedef int (WINAPI * CHOOSEPIXELFORMAT_T) (HDC,CONST PIXELFORMATDESCRIPTOR*); +typedef int (WINAPI * DESCRIBEPIXELFORMAT_T) (HDC,int,UINT,LPPIXELFORMATDESCRIPTOR); +typedef int (WINAPI * GETPIXELFORMAT_T) (HDC); +typedef BOOL (WINAPI * SETPIXELFORMAT_T) (HDC,int,const PIXELFORMATDESCRIPTOR*); +typedef BOOL (WINAPI * SWAPBUFFERS_T) (HDC); +#endif // _GLFW_NO_DLOAD_GDI32 + +// winmm.dll function pointer typedefs +#ifndef _GLFW_NO_DLOAD_WINMM +typedef MMRESULT (WINAPI * JOYGETDEVCAPSA_T) (UINT,LPJOYCAPSA,UINT); +typedef MMRESULT (WINAPI * JOYGETPOS_T) (UINT,LPJOYINFO); +typedef MMRESULT (WINAPI * JOYGETPOSEX_T) (UINT,LPJOYINFOEX); +typedef DWORD (WINAPI * TIMEGETTIME_T) (void); +#endif // _GLFW_NO_DLOAD_WINMM + + +// gdi32.dll shortcuts +#ifndef _GLFW_NO_DLOAD_GDI32 +#define _glfw_ChoosePixelFormat _glfwLibrary.Libs.ChoosePixelFormat +#define _glfw_DescribePixelFormat _glfwLibrary.Libs.DescribePixelFormat +#define _glfw_GetPixelFormat _glfwLibrary.Libs.GetPixelFormat +#define _glfw_SetPixelFormat _glfwLibrary.Libs.SetPixelFormat +#define _glfw_SwapBuffers _glfwLibrary.Libs.SwapBuffers +#else +#define _glfw_ChoosePixelFormat ChoosePixelFormat +#define _glfw_DescribePixelFormat DescribePixelFormat +#define _glfw_GetPixelFormat GetPixelFormat +#define _glfw_SetPixelFormat SetPixelFormat +#define _glfw_SwapBuffers SwapBuffers +#endif // _GLFW_NO_DLOAD_GDI32 + +// winmm.dll shortcuts +#ifndef _GLFW_NO_DLOAD_WINMM +#define _glfw_joyGetDevCaps _glfwLibrary.Libs.joyGetDevCapsA +#define _glfw_joyGetPos _glfwLibrary.Libs.joyGetPos +#define _glfw_joyGetPosEx _glfwLibrary.Libs.joyGetPosEx +#define _glfw_timeGetTime _glfwLibrary.Libs.timeGetTime +#else +#define _glfw_joyGetDevCaps joyGetDevCapsA +#define _glfw_joyGetPos joyGetPos +#define _glfw_joyGetPosEx joyGetPosEx +#define _glfw_timeGetTime timeGetTime +#endif // _GLFW_NO_DLOAD_WINMM + + +//======================================================================== +// GLFW platform specific types +//======================================================================== + +//------------------------------------------------------------------------ +// Pointer length integer +//------------------------------------------------------------------------ +typedef INT_PTR GLFWintptr; + + +//------------------------------------------------------------------------ +// Window structure +//------------------------------------------------------------------------ +typedef struct _GLFWwin_struct _GLFWwin; + +struct _GLFWwin_struct { + +// ========= PLATFORM INDEPENDENT MANDATORY PART ========================= + + // User callback functions + GLFWwindowsizefun windowSizeCallback; + GLFWwindowclosefun windowCloseCallback; + GLFWwindowrefreshfun windowRefreshCallback; + GLFWmousebuttonfun mouseButtonCallback; + GLFWmouseposfun mousePosCallback; + GLFWmousewheelfun mouseWheelCallback; + GLFWkeyfun keyCallback; + GLFWcharfun charCallback; + + // User selected window settings + int fullscreen; // Fullscreen flag + int mouseLock; // Mouse-lock flag + int autoPollEvents; // Auto polling flag + int sysKeysDisabled; // System keys disabled flag + int windowNoResize; // Resize- and maximize gadgets disabled flag + int refreshRate; // Vertical monitor refresh rate + + // Window status & parameters + int opened; // Flag telling if window is opened or not + int active; // Application active flag + int iconified; // Window iconified flag + int width, height; // Window width and heigth + int accelerated; // GL_TRUE if window is HW accelerated + + // Framebuffer attributes + int redBits; + int greenBits; + int blueBits; + int alphaBits; + int depthBits; + int stencilBits; + int accumRedBits; + int accumGreenBits; + int accumBlueBits; + int accumAlphaBits; + int auxBuffers; + int stereo; + int samples; + + // OpenGL extensions and context attributes + int has_GL_SGIS_generate_mipmap; + int has_GL_ARB_texture_non_power_of_two; + int glMajor, glMinor, glRevision; + int glForward, glDebug, glProfile; + + PFNGLGETSTRINGIPROC GetStringi; + + +// ========= PLATFORM SPECIFIC PART ====================================== + + // Platform specific window resources + HDC DC; // Private GDI device context + HGLRC context; // Permanent rendering context + HWND window; // Window handle + ATOM classAtom; // Window class atom + int modeID; // Mode ID for fullscreen mode + HHOOK keyboardHook; // Keyboard hook handle + DWORD dwStyle; // Window styles used for window creation + DWORD dwExStyle; // --"-- + + // Platform specific extensions (context specific) + WGLSWAPINTERVALEXT_T SwapIntervalEXT; + WGLGETPIXELFORMATATTRIBIVARB_T GetPixelFormatAttribivARB; + WGLGETEXTENSIONSSTRINGEXT_T GetExtensionsStringEXT; + WGLGETEXTENSIONSSTRINGARB_T GetExtensionsStringARB; + PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + GLboolean has_WGL_EXT_swap_control; + GLboolean has_WGL_ARB_multisample; + GLboolean has_WGL_ARB_pixel_format; + GLboolean has_WGL_ARB_create_context; + + // Various platform specific internal variables + int oldMouseLock; // Old mouse-lock flag (used for remembering + // mouse-lock state when iconifying) + int oldMouseLockValid; + int desiredRefreshRate; // Desired vertical monitor refresh rate + +}; + +GLFWGLOBAL _GLFWwin _glfwWin; + + +//------------------------------------------------------------------------ +// User input status (most of this should go in _GLFWwin) +//------------------------------------------------------------------------ +GLFWGLOBAL struct { + +// ========= PLATFORM INDEPENDENT MANDATORY PART ========================= + + // Mouse status + int MousePosX, MousePosY; + int WheelPos; + char MouseButton[ GLFW_MOUSE_BUTTON_LAST+1 ]; + + // Keyboard status + char Key[ GLFW_KEY_LAST+1 ]; + int LastChar; + + // User selected settings + int StickyKeys; + int StickyMouseButtons; + int KeyRepeat; + + +// ========= PLATFORM SPECIFIC PART ====================================== + + // Platform specific internal variables + int MouseMoved, OldMouseX, OldMouseY; + +} _glfwInput; + + +//------------------------------------------------------------------------ +// Library global data +//------------------------------------------------------------------------ +GLFWGLOBAL struct { + + // Window opening hints + _GLFWhints hints; + +// ========= PLATFORM SPECIFIC PART ====================================== + + HINSTANCE instance; // Instance of the application + + // Timer data + struct { + int HasPerformanceCounter; + double Resolution; + unsigned int t0_32; + __int64 t0_64; + } Timer; + + // System information + struct { + int winVer; + int hasUnicode; + DWORD foregroundLockTimeout; + } Sys; + +#if !defined(_GLFW_NO_DLOAD_WINMM) || !defined(_GLFW_NO_DLOAD_GDI32) + // Library handles and function pointers + struct { +#ifndef _GLFW_NO_DLOAD_GDI32 + // gdi32.dll + HINSTANCE gdi32; + CHOOSEPIXELFORMAT_T ChoosePixelFormat; + DESCRIBEPIXELFORMAT_T DescribePixelFormat; + GETPIXELFORMAT_T GetPixelFormat; + SETPIXELFORMAT_T SetPixelFormat; + SWAPBUFFERS_T SwapBuffers; +#endif // _GLFW_NO_DLOAD_GDI32 + + // winmm.dll +#ifndef _GLFW_NO_DLOAD_WINMM + HINSTANCE winmm; + JOYGETDEVCAPSA_T joyGetDevCapsA; + JOYGETPOS_T joyGetPos; + JOYGETPOSEX_T joyGetPosEx; + TIMEGETTIME_T timeGetTime; +#endif // _GLFW_NO_DLOAD_WINMM + } Libs; +#endif + +} _glfwLibrary; + + +//======================================================================== +// Various Windows version constants +//======================================================================== + +#define _GLFW_WIN_UNKNOWN 0x0000 // Earlier than 95 or NT4 +#define _GLFW_WIN_95 0x0001 +#define _GLFW_WIN_98 0x0002 +#define _GLFW_WIN_ME 0x0003 +#define _GLFW_WIN_UNKNOWN_9x 0x0004 // Later than ME +#define _GLFW_WIN_NT4 0x0101 +#define _GLFW_WIN_2K 0x0102 +#define _GLFW_WIN_XP 0x0103 +#define _GLFW_WIN_NET_SERVER 0x0104 +#define _GLFW_WIN_UNKNOWN_NT 0x0105 // Later than .NET Server + + +//======================================================================== +// Prototypes for platform specific internal functions +//======================================================================== + +// Time +void _glfwInitTimer( void ); + +// Fullscreen support +int _glfwGetClosestVideoModeBPP( int *w, int *h, int *bpp, int *refresh ); +int _glfwGetClosestVideoMode( int *w, int *h, int *r, int *g, int *b, int *refresh ); +void _glfwSetVideoModeMODE( int mode ); +void _glfwSetVideoMode( int *w, int *h, int r, int g, int b, int refresh ); + + +#endif // _platform_h_ diff --git a/lib/win32/win32_dllmain.c b/lib/win32/win32_dllmain.c new file mode 100644 index 00000000..d7a9c1a9 --- /dev/null +++ b/lib/win32/win32_dllmain.c @@ -0,0 +1,49 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Win32/WGL +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +#if defined(GLFW_BUILD_DLL) + +//======================================================================== +// GLFW DLL entry point +//======================================================================== + +BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, LPVOID reserved ) +{ + // NOTE: Some compilers complains about instance and x never being used - + // never mind that (we don't want to use them)! + + return TRUE; +} + +#endif // GLFW_BUILD_DLL + diff --git a/lib/win32/win32_enable.c b/lib/win32/win32_enable.c new file mode 100644 index 00000000..3e72b787 --- /dev/null +++ b/lib/win32/win32_enable.c @@ -0,0 +1,155 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Win32/WGL +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +//======================================================================== +// Low level keyboard hook (system callback) function +// Used to disable system keys under Windows NT +//======================================================================== + +static LRESULT CALLBACK keyboardHook( int nCode, WPARAM wParam, LPARAM lParam ) +{ + BOOL syskeys = FALSE; + PKBDLLHOOKSTRUCT p; + + // We are only looking for keyboard events - interpret lParam as a + // pointer to a KBDLLHOOKSTRUCT + p = (PKBDLLHOOKSTRUCT) lParam; + + if( nCode == HC_ACTION ) + { + // We have a keyboard event + + switch( wParam ) + { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + // Detect: ALT+TAB, ALT+ESC, ALT+F4, CTRL+ESC, + // LWIN, RWIN, APPS (mysterious menu key) + syskeys = ( p->vkCode == VK_TAB && + p->flags & LLKHF_ALTDOWN ) || + ( p->vkCode == VK_ESCAPE && + p->flags & LLKHF_ALTDOWN ) || + ( p->vkCode == VK_F4 && + p->flags & LLKHF_ALTDOWN ) || + ( p->vkCode == VK_ESCAPE && + (GetKeyState(VK_CONTROL) & 0x8000)) || + p->vkCode == VK_LWIN || + p->vkCode == VK_RWIN || + p->vkCode == VK_APPS; + break; + + default: + break; + } + } + + // Was it a system key combination (e.g. ALT+TAB)? + if( syskeys ) + { + // Pass the key event to our window message loop + if( _glfwWin.opened ) + { + PostMessage( _glfwWin.window, (UINT) wParam, p->vkCode, 0 ); + } + + // We've taken care of it - don't let the system know about this + // key event + return 1; + } + else + { + // It's a harmless key press, let the system deal with it + return CallNextHookEx( _glfwWin.keyboardHook, nCode, wParam, lParam ); + } +} + + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Enable system keys +//======================================================================== + +void _glfwPlatformEnableSystemKeys( void ) +{ + BOOL dummy; + + // Use different methods depending on operating system version + if( _glfwLibrary.Sys.winVer >= _GLFW_WIN_NT4 ) + { + if( _glfwWin.keyboardHook != NULL ) + { + UnhookWindowsHookEx( _glfwWin.keyboardHook ); + _glfwWin.keyboardHook = NULL; + } + } + else + { + (void) SystemParametersInfo( SPI_SETSCREENSAVERRUNNING, FALSE, &dummy, 0 ); + } +} + +//======================================================================== +// Disable system keys +//======================================================================== + +void _glfwPlatformDisableSystemKeys( void ) +{ + BOOL dummy; + + // Use different methods depending on operating system version + if( _glfwLibrary.Sys.winVer >= _GLFW_WIN_NT4 ) + { + // Under Windows NT, install a low level keyboard hook + _glfwWin.keyboardHook = SetWindowsHookEx( WH_KEYBOARD_LL, + keyboardHook, + _glfwLibrary.instance, + 0 ); + } + else + { + // Under Windows 95/98/ME, fool Windows that a screensaver + // is running => prevents ALT+TAB, CTRL+ESC and CTRL+ALT+DEL + (void) SystemParametersInfo( SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0 ); + } +} + diff --git a/lib/win32/win32_fullscreen.c b/lib/win32/win32_fullscreen.c new file mode 100644 index 00000000..5784d797 --- /dev/null +++ b/lib/win32/win32_fullscreen.c @@ -0,0 +1,320 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Win32/WGL +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +//======================================================================== +// Convert BPP to RGB bits based on "best guess" +//======================================================================== + +static void bpp2rgb( int bpp, int *r, int *g, int *b ) +{ + int delta; + + // We assume that by 32 they really meant 24 + if( bpp == 32 ) + { + bpp = 24; + } + + // Convert "bits per pixel" to red, green & blue sizes + + *r = *g = *b = bpp / 3; + delta = bpp - (*r * 3); + if( delta >= 1 ) + { + *g = *g + 1; + } + if( delta == 2 ) + { + *r = *r + 1; + } +} + + +//======================================================================== +// Return closest video mode by dimensions, refresh rate and bits per pixel +//======================================================================== + +int _glfwGetClosestVideoModeBPP( int *w, int *h, int *bpp, int *refresh ) +{ + int mode, bestmode, match, bestmatch, rr, bestrr, success; + DEVMODE dm; + + // Find best match + bestmatch = 0x7fffffff; + bestrr = 0x7fffffff; + mode = bestmode = 0; + do + { + dm.dmSize = sizeof( DEVMODE ); + success = EnumDisplaySettings( NULL, mode, &dm ); + if( success ) + { + match = dm.dmBitsPerPel - *bpp; + if( match < 0 ) match = -match; + match = ( match << 25 ) | + ( (dm.dmPelsWidth - *w) * + (dm.dmPelsWidth - *w) + + (dm.dmPelsHeight - *h) * + (dm.dmPelsHeight - *h) ); + if( match < bestmatch ) + { + bestmatch = match; + bestmode = mode; + bestrr = (dm.dmDisplayFrequency - *refresh) * + (dm.dmDisplayFrequency - *refresh); + } + else if( match == bestmatch && *refresh > 0 ) + { + rr = (dm.dmDisplayFrequency - *refresh) * + (dm.dmDisplayFrequency - *refresh); + if( rr < bestrr ) + { + bestmatch = match; + bestmode = mode; + bestrr = rr; + } + } + } + mode ++; + } + while( success ); + + // Get the parameters for the best matching display mode + dm.dmSize = sizeof( DEVMODE ); + (void) EnumDisplaySettings( NULL, bestmode, &dm ); + + // Fill out actual width and height + *w = dm.dmPelsWidth; + *h = dm.dmPelsHeight; + + // Return bits per pixel + *bpp = dm.dmBitsPerPel; + + // Return vertical refresh rate + *refresh = dm.dmDisplayFrequency; + + return bestmode; +} + + +//======================================================================== +// Return closest video mode by dimensions, refresh rate and channel sizes +//======================================================================== + +static int getClosestVideoMode( int *w, int *h, + int *r, int *g, int *b, + int *refresh ) +{ + int bpp, bestmode; + + // Colorbits = sum of red/green/blue bits + bpp = *r + *g + *b; + + // If colorbits < 15 (e.g. 0) or >= 24, default to 32 bpp + if( bpp < 15 || bpp >= 24 ) + { + bpp = 32; + } + + // Find best match + bestmode = _glfwGetClosestVideoModeBPP( w, h, &bpp, refresh ); + + // Convert "bits per pixel" to red, green & blue sizes + bpp2rgb( bpp, r, g, b ); + + return bestmode; +} + + +//======================================================================== +// Change the current video mode +//======================================================================== + +void _glfwSetVideoModeMODE( int mode ) +{ + DEVMODE dm; + int success; + + // Get the parameters for the best matching display mode + dm.dmSize = sizeof( DEVMODE ); + (void) EnumDisplaySettings( NULL, mode, &dm ); + + // Set which fields we want to specify + dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL; + + // Do we have a prefered refresh rate? + if( _glfwWin.desiredRefreshRate > 0 ) + { + dm.dmFields = dm.dmFields | DM_DISPLAYFREQUENCY; + dm.dmDisplayFrequency = _glfwWin.desiredRefreshRate; + } + + // Change display setting + dm.dmSize = sizeof( DEVMODE ); + success = ChangeDisplaySettings( &dm, CDS_FULLSCREEN ); + + // If the mode change was not possible, query the current display + // settings (we'll use the desktop resolution for fullscreen mode) + if( success == DISP_CHANGE_SUCCESSFUL ) + { + _glfwWin.modeID = mode; + } + else + { + _glfwWin.modeID = ENUM_REGISTRY_SETTINGS; + EnumDisplaySettings( NULL, ENUM_REGISTRY_SETTINGS, &dm ); + } + + // Set the window size to that of the display mode + _glfwWin.width = dm.dmPelsWidth; + _glfwWin.height = dm.dmPelsHeight; +} + + +//======================================================================== +// _glfwSetVideoMode() - Change the current video mode +//======================================================================== + +void _glfwSetVideoMode( int *w, int *h, int r, int g, int b, int refresh ) +{ + int bestmode; + + // Find a best match mode + bestmode = getClosestVideoMode( w, h, &r, &g, &b, &refresh ); + + // Change mode + _glfwSetVideoModeMODE( bestmode ); +} + + +//************************************************************************ +//**** GLFW user functions **** +//************************************************************************ + +//======================================================================== +// _glfwPlatformGetVideoModes() - Get a list of available video modes +//======================================================================== + +int _glfwPlatformGetVideoModes( GLFWvidmode *list, int maxcount ) +{ + int count, success, mode, i, j; + int m1, m2, bpp, r, g, b; + DEVMODE dm; + + // Loop through all video modes and extract all the UNIQUE modes + count = 0; + mode = 0; + do + { + // Get video mode properties + dm.dmSize = sizeof( DEVMODE ); + success = EnumDisplaySettings( NULL, mode, &dm ); + + // Is it a valid mode? (only list depths >= 15 bpp) + if( success && dm.dmBitsPerPel >= 15 ) + { + // Convert to RGB, and back to bpp ("mask out" alpha bits etc) + bpp2rgb( dm.dmBitsPerPel, &r, &g, &b ); + bpp = r + g + b; + + // Mode "code" for this mode + m1 = (bpp << 25) | (dm.dmPelsWidth * dm.dmPelsHeight); + + // Insert mode in list (sorted), and avoid duplicates + for( i = 0; i < count; i ++ ) + { + // Mode "code" for already listed mode + bpp = list[i].RedBits + list[i].GreenBits + + list[i].BlueBits; + m2 = (bpp << 25) | (list[i].Width * list[i].Height); + if( m1 <= m2 ) + { + break; + } + } + + // New entry at the end of the list? + if( i >= count ) + { + list[count].Width = dm.dmPelsWidth; + list[count].Height = dm.dmPelsHeight; + list[count].RedBits = r; + list[count].GreenBits = g; + list[count].BlueBits = b; + count ++; + } + // Insert new entry in the list? + else if( m1 < m2 ) + { + for( j = count; j > i; j -- ) + { + list[j] = list[j-1]; + } + list[i].Width = dm.dmPelsWidth; + list[i].Height = dm.dmPelsHeight; + list[i].RedBits = r; + list[i].GreenBits = g; + list[i].BlueBits = b; + count ++; + } + } + mode ++; + } + while( success && (count < maxcount) ); + + return count; +} + + +//======================================================================== +// Get the desktop video mode +//======================================================================== + +void _glfwPlatformGetDesktopMode( GLFWvidmode *mode ) +{ + DEVMODE dm; + + // Get desktop display mode + dm.dmSize = sizeof( DEVMODE ); + (void) EnumDisplaySettings( NULL, ENUM_REGISTRY_SETTINGS, &dm ); + + // Return desktop mode parameters + mode->Width = dm.dmPelsWidth; + mode->Height = dm.dmPelsHeight; + bpp2rgb( dm.dmBitsPerPel, &mode->RedBits, &mode->GreenBits, &mode->BlueBits ); +} + diff --git a/lib/win32/win32_glext.c b/lib/win32/win32_glext.c new file mode 100644 index 00000000..820adc50 --- /dev/null +++ b/lib/win32/win32_glext.c @@ -0,0 +1,82 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Win32/WGL +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Check if the current context supports the specified WGL extension +//======================================================================== + +int _glfwPlatformExtensionSupported( const char *extension ) +{ + const GLubyte *extensions; + + if( _glfwWin.GetExtensionsStringEXT != NULL ) + { + extensions = (GLubyte *) _glfwWin.GetExtensionsStringEXT(); + if( extensions != NULL ) + { + if( _glfwStringInExtensionString( extension, extensions ) ) + { + return GL_TRUE; + } + } + } + + if( _glfwWin.GetExtensionsStringARB != NULL ) + { + extensions = (GLubyte *) _glfwWin.GetExtensionsStringARB( _glfwWin.DC ); + if( extensions != NULL ) + { + if( _glfwStringInExtensionString( extension, extensions ) ) + { + return GL_TRUE; + } + } + } + + return GL_FALSE; +} + + +//======================================================================== +// Get the function pointer to an OpenGL function +//======================================================================== + +void *_glfwPlatformGetProcAddress( const char *procname ) +{ + return (void *) wglGetProcAddress( procname ); +} + diff --git a/lib/win32/win32_init.c b/lib/win32/win32_init.c new file mode 100644 index 00000000..8ad41af1 --- /dev/null +++ b/lib/win32/win32_init.c @@ -0,0 +1,281 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Win32/WGL +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + +// With the Borland C++ compiler, we want to disable FPU exceptions +#ifdef __BORLANDC__ +#include +#endif // __BORLANDC__ + + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +//======================================================================== +// _glfwInitLibraries() - Load necessary libraries (DLLs) +//======================================================================== + +static int _glfwInitLibraries( void ) +{ + // gdi32.dll (OpenGL pixel format functions & SwapBuffers) +#ifndef _GLFW_NO_DLOAD_GDI32 + _glfwLibrary.Libs.gdi32 = LoadLibrary( "gdi32.dll" ); + if( _glfwLibrary.Libs.gdi32 != NULL ) + { + _glfwLibrary.Libs.ChoosePixelFormat = (CHOOSEPIXELFORMAT_T) + GetProcAddress( _glfwLibrary.Libs.gdi32, "ChoosePixelFormat" ); + _glfwLibrary.Libs.DescribePixelFormat = (DESCRIBEPIXELFORMAT_T) + GetProcAddress( _glfwLibrary.Libs.gdi32, "DescribePixelFormat" ); + _glfwLibrary.Libs.GetPixelFormat = (GETPIXELFORMAT_T) + GetProcAddress( _glfwLibrary.Libs.gdi32, "GetPixelFormat" ); + _glfwLibrary.Libs.SetPixelFormat = (SETPIXELFORMAT_T) + GetProcAddress( _glfwLibrary.Libs.gdi32, "SetPixelFormat" ); + _glfwLibrary.Libs.SwapBuffers = (SWAPBUFFERS_T) + GetProcAddress( _glfwLibrary.Libs.gdi32, "SwapBuffers" ); + if( _glfwLibrary.Libs.ChoosePixelFormat == NULL || + _glfwLibrary.Libs.DescribePixelFormat == NULL || + _glfwLibrary.Libs.GetPixelFormat == NULL || + _glfwLibrary.Libs.SetPixelFormat == NULL || + _glfwLibrary.Libs.SwapBuffers == NULL ) + { + FreeLibrary( _glfwLibrary.Libs.gdi32 ); + _glfwLibrary.Libs.gdi32 = NULL; + return GL_FALSE; + } + } + else + { + return GL_FALSE; + } +#endif // _GLFW_NO_DLOAD_GDI32 + + // winmm.dll (for joystick and timer support) +#ifndef _GLFW_NO_DLOAD_WINMM + _glfwLibrary.Libs.winmm = LoadLibrary( "winmm.dll" ); + if( _glfwLibrary.Libs.winmm != NULL ) + { + _glfwLibrary.Libs.joyGetDevCapsA = (JOYGETDEVCAPSA_T) + GetProcAddress( _glfwLibrary.Libs.winmm, "joyGetDevCapsA" ); + _glfwLibrary.Libs.joyGetPos = (JOYGETPOS_T) + GetProcAddress( _glfwLibrary.Libs.winmm, "joyGetPos" ); + _glfwLibrary.Libs.joyGetPosEx = (JOYGETPOSEX_T) + GetProcAddress( _glfwLibrary.Libs.winmm, "joyGetPosEx" ); + _glfwLibrary.Libs.timeGetTime = (TIMEGETTIME_T) + GetProcAddress( _glfwLibrary.Libs.winmm, "timeGetTime" ); + if( _glfwLibrary.Libs.joyGetDevCapsA == NULL || + _glfwLibrary.Libs.joyGetPos == NULL || + _glfwLibrary.Libs.joyGetPosEx == NULL || + _glfwLibrary.Libs.timeGetTime == NULL ) + { + FreeLibrary( _glfwLibrary.Libs.winmm ); + _glfwLibrary.Libs.winmm = NULL; + return GL_FALSE; + } + } + else + { + return GL_FALSE; + } +#endif // _GLFW_NO_DLOAD_WINMM + + return GL_TRUE; +} + + +//======================================================================== +// _glfwFreeLibraries() - Unload used libraries (DLLs) +//======================================================================== + +static void _glfwFreeLibraries( void ) +{ + // gdi32.dll +#ifndef _GLFW_NO_DLOAD_GDI32 + if( _glfwLibrary.Libs.gdi32 != NULL ) + { + FreeLibrary( _glfwLibrary.Libs.gdi32 ); + _glfwLibrary.Libs.gdi32 = NULL; + } +#endif // _GLFW_NO_DLOAD_GDI32 + + // winmm.dll +#ifndef _GLFW_NO_DLOAD_WINMM + if( _glfwLibrary.Libs.winmm != NULL ) + { + FreeLibrary( _glfwLibrary.Libs.winmm ); + _glfwLibrary.Libs.winmm = NULL; + } +#endif // _GLFW_NO_DLOAD_WINMM +} + + +//======================================================================== +// _glfwTerminate_atexit() - Terminate GLFW when exiting application +//======================================================================== + +void _glfwTerminate_atexit( void ) +{ + glfwTerminate(); +} + + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// _glfwPlatformInit() - Initialize various GLFW state +//======================================================================== + +int _glfwPlatformInit( void ) +{ + OSVERSIONINFO osi; + + // To make SetForegroundWindow() work as we want, we need to fiddle + // with the FOREGROUNDLOCKTIMEOUT system setting (we do this as early + // as possible in the hope of still being the foreground process) + SystemParametersInfo( SPI_GETFOREGROUNDLOCKTIMEOUT, 0, + &_glfwLibrary.Sys.foregroundLockTimeout, 0 ); + SystemParametersInfo( SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (LPVOID)0, + SPIF_SENDCHANGE ); + + // Check which OS version we are running + osi.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); + GetVersionEx( &osi ); + _glfwLibrary.Sys.winVer = _GLFW_WIN_UNKNOWN; + if( osi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) + { + if( osi.dwMajorVersion == 4 && osi.dwMinorVersion < 10 ) + { + _glfwLibrary.Sys.winVer = _GLFW_WIN_95; + } + else if( osi.dwMajorVersion == 4 && osi.dwMinorVersion < 90 ) + { + _glfwLibrary.Sys.winVer = _GLFW_WIN_98; + } + else if( osi.dwMajorVersion == 4 && osi.dwMinorVersion == 90 ) + { + _glfwLibrary.Sys.winVer = _GLFW_WIN_ME; + } + else if( osi.dwMajorVersion >= 4 ) + { + _glfwLibrary.Sys.winVer = _GLFW_WIN_UNKNOWN_9x; + } + } + else if( osi.dwPlatformId == VER_PLATFORM_WIN32_NT ) + { + if( osi.dwMajorVersion == 4 && osi.dwMinorVersion == 0 ) + { + _glfwLibrary.Sys.winVer = _GLFW_WIN_NT4; + } + else if( osi.dwMajorVersion == 5 && osi.dwMinorVersion == 0 ) + { + _glfwLibrary.Sys.winVer = _GLFW_WIN_2K; + } + else if( osi.dwMajorVersion == 5 && osi.dwMinorVersion == 1 ) + { + _glfwLibrary.Sys.winVer = _GLFW_WIN_XP; + } + else if( osi.dwMajorVersion == 5 && osi.dwMinorVersion == 2 ) + { + _glfwLibrary.Sys.winVer = _GLFW_WIN_NET_SERVER; + } + else if( osi.dwMajorVersion >= 5 ) + { + _glfwLibrary.Sys.winVer = _GLFW_WIN_UNKNOWN_NT; + } + } + + // Do we have Unicode support? + if( _glfwLibrary.Sys.winVer >= _GLFW_WIN_NT4 ) + { + // Windows NT/2000/XP/.NET has Unicode support + _glfwLibrary.Sys.hasUnicode = GL_TRUE; + } + else + { + // Windows 9x/ME does not have Unicode support + _glfwLibrary.Sys.hasUnicode = GL_FALSE; + } + + // Load libraries (DLLs) + if( !_glfwInitLibraries() ) + { + return GL_FALSE; + } + + // With the Borland C++ compiler, we want to disable FPU exceptions + // (this is recommended for OpenGL applications under Windows) +#ifdef __BORLANDC__ + _control87( MCW_EM, MCW_EM ); +#endif + + // Retrieve GLFW instance handle + _glfwLibrary.instance = GetModuleHandle( NULL ); + + // System keys are not disabled + _glfwWin.keyboardHook = NULL; + + // Install atexit() routine + atexit( _glfwTerminate_atexit ); + + // Start the timer + _glfwInitTimer(); + + return GL_TRUE; +} + + +//======================================================================== +// Close window and shut down library +//======================================================================== + +int _glfwPlatformTerminate( void ) +{ + // Close OpenGL window + glfwCloseWindow(); + + // Enable system keys again (if they were disabled) + glfwEnable( GLFW_SYSTEM_KEYS ); + + // Unload libraries (DLLs) + _glfwFreeLibraries(); + + // Restore FOREGROUNDLOCKTIMEOUT system setting + SystemParametersInfo( SPI_SETFOREGROUNDLOCKTIMEOUT, 0, + (LPVOID)_glfwLibrary.Sys.foregroundLockTimeout, + SPIF_SENDCHANGE ); + + return GL_TRUE; +} + diff --git a/lib/win32/win32_joystick.c b/lib/win32/win32_joystick.c new file mode 100644 index 00000000..64b55899 --- /dev/null +++ b/lib/win32/win32_joystick.c @@ -0,0 +1,234 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Win32/WGL +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +//======================================================================== +// _glfwJoystickPresent() - Return GL_TRUE if joystick is present, +// else return GL_FALSE. +//======================================================================== + +static int _glfwJoystickPresent( int joy ) +{ + JOYINFO ji; + + // Windows NT 4.0 MMSYSTEM only supports 2 sticks (other Windows + // versions support 16 sticks) + if( _glfwLibrary.Sys.winVer == _GLFW_WIN_NT4 && joy > GLFW_JOYSTICK_2 ) + { + return GL_FALSE; + } + + // Is it a valid stick ID (Windows don't support more than 16 sticks)? + if( joy < GLFW_JOYSTICK_1 || joy > GLFW_JOYSTICK_16 ) + { + return GL_FALSE; + } + + // Is the joystick present? + if( _glfw_joyGetPos( joy - GLFW_JOYSTICK_1, &ji ) != JOYERR_NOERROR ) + { + return GL_FALSE; + } + + return GL_TRUE; +} + + +//======================================================================== +// _glfwCalcJoystickPos() - Calculate joystick position +//======================================================================== + +static float _glfwCalcJoystickPos( DWORD pos, DWORD min, DWORD max ) +{ + float fpos = (float) pos; + float fmin = (float) min; + float fmax = (float) max; + return (2.0f*(fpos - fmin) / (fmax - fmin)) - 1.0f; +} + + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// _glfwPlatformGetJoystickParam() - Determine joystick capabilities +//======================================================================== + +int _glfwPlatformGetJoystickParam( int joy, int param ) +{ + JOYCAPS jc; + +// return 0; + + // Is joystick present? + if( !_glfwJoystickPresent( joy ) ) + { + return 0; + } + + // We got this far, the joystick is present + if( param == GLFW_PRESENT ) + { + return GL_TRUE; + } + + // Get joystick capabilities + _glfw_joyGetDevCaps( joy - GLFW_JOYSTICK_1, &jc, sizeof(JOYCAPS) ); + + switch( param ) + { + case GLFW_AXES: + // Return number of joystick axes + return jc.wNumAxes; + + case GLFW_BUTTONS: + // Return number of joystick axes + return jc.wNumButtons; + + default: + break; + } + + return 0; +} + + +//======================================================================== +// _glfwPlatformGetJoystickPos() - Get joystick axis positions +//======================================================================== + +int _glfwPlatformGetJoystickPos( int joy, float *pos, int numaxes ) +{ + JOYCAPS jc; + JOYINFOEX ji; + int axis; + +// return 0; + + // Is joystick present? + if( !_glfwJoystickPresent( joy ) ) + { + return 0; + } + + // Get joystick capabilities + _glfw_joyGetDevCaps( joy - GLFW_JOYSTICK_1, &jc, sizeof(JOYCAPS) ); + + // Get joystick state + ji.dwSize = sizeof( JOYINFOEX ); + ji.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | + JOY_RETURNR | JOY_RETURNU | JOY_RETURNV; + _glfw_joyGetPosEx( joy - GLFW_JOYSTICK_1, &ji ); + + // Get position values for all axes + axis = 0; + if( axis < numaxes ) + { + pos[ axis++ ] = _glfwCalcJoystickPos( ji.dwXpos, jc.wXmin, + jc.wXmax ); + } + if( axis < numaxes ) + { + pos[ axis++ ] = -_glfwCalcJoystickPos( ji.dwYpos, jc.wYmin, + jc.wYmax ); + } + if( axis < numaxes && jc.wCaps & JOYCAPS_HASZ ) + { + pos[ axis++ ] = _glfwCalcJoystickPos( ji.dwZpos, jc.wZmin, + jc.wZmax ); + } + if( axis < numaxes && jc.wCaps & JOYCAPS_HASR ) + { + pos[ axis++ ] = _glfwCalcJoystickPos( ji.dwRpos, jc.wRmin, + jc.wRmax ); + } + if( axis < numaxes && jc.wCaps & JOYCAPS_HASU ) + { + pos[ axis++ ] = _glfwCalcJoystickPos( ji.dwUpos, jc.wUmin, + jc.wUmax ); + } + if( axis < numaxes && jc.wCaps & JOYCAPS_HASV ) + { + pos[ axis++ ] = -_glfwCalcJoystickPos( ji.dwVpos, jc.wVmin, + jc.wVmax ); + } + + // Return number of returned axes + return axis; +} + + +//======================================================================== +// _glfwPlatformGetJoystickButtons() - Get joystick button states +//======================================================================== + +int _glfwPlatformGetJoystickButtons( int joy, unsigned char *buttons, + int numbuttons ) +{ + JOYCAPS jc; + JOYINFOEX ji; + int button; + +// return 0; + + // Is joystick present? + if( !_glfwJoystickPresent( joy ) ) + { + return 0; + } + + // Get joystick capabilities + _glfw_joyGetDevCaps( joy - GLFW_JOYSTICK_1, &jc, sizeof(JOYCAPS) ); + + // Get joystick state + ji.dwSize = sizeof( JOYINFOEX ); + ji.dwFlags = JOY_RETURNBUTTONS; + _glfw_joyGetPosEx( joy - GLFW_JOYSTICK_1, &ji ); + + // Get states of all requested buttons + button = 0; + while( button < numbuttons && button < (int) jc.wNumButtons ) + { + buttons[ button ] = (unsigned char) + (ji.dwButtons & (1UL << button) ? GLFW_PRESS : GLFW_RELEASE); + button ++; + } + + return button; +} + diff --git a/lib/win32/win32_time.c b/lib/win32/win32_time.c new file mode 100644 index 00000000..6bd112c3 --- /dev/null +++ b/lib/win32/win32_time.c @@ -0,0 +1,118 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Win32/WGL +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +//======================================================================== +// _glfwInitTimer() - Initialise timer +//======================================================================== + +void _glfwInitTimer( void ) +{ + __int64 freq; + + // Check if we have a performance counter + if( QueryPerformanceFrequency( (LARGE_INTEGER *)&freq ) ) + { + // Performance counter is available => use it! + _glfwLibrary.Timer.HasPerformanceCounter = GL_TRUE; + + // Counter resolution is 1 / counter frequency + _glfwLibrary.Timer.Resolution = 1.0 / (double)freq; + + // Set start time for timer + QueryPerformanceCounter( (LARGE_INTEGER *)&_glfwLibrary.Timer.t0_64 ); + } + else + { + // No performace counter available => use the tick counter + _glfwLibrary.Timer.HasPerformanceCounter = GL_FALSE; + + // Counter resolution is 1 ms + _glfwLibrary.Timer.Resolution = 0.001; + + // Set start time for timer + _glfwLibrary.Timer.t0_32 = _glfw_timeGetTime(); + } +} + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Return timer value in seconds +//======================================================================== + +double _glfwPlatformGetTime( void ) +{ + double t; + __int64 t_64; + + if( _glfwLibrary.Timer.HasPerformanceCounter ) + { + QueryPerformanceCounter( (LARGE_INTEGER *)&t_64 ); + t = (double)(t_64 - _glfwLibrary.Timer.t0_64); + } + else + { + t = (double)(_glfw_timeGetTime() - _glfwLibrary.Timer.t0_32); + } + + // Calculate the current time in seconds + return t * _glfwLibrary.Timer.Resolution; +} + + +//======================================================================== +// Set timer value in seconds +//======================================================================== + +void _glfwPlatformSetTime( double t ) +{ + __int64 t_64; + + if( _glfwLibrary.Timer.HasPerformanceCounter ) + { + QueryPerformanceCounter( (LARGE_INTEGER *)&t_64 ); + _glfwLibrary.Timer.t0_64 = t_64 - (__int64)(t/_glfwLibrary.Timer.Resolution); + } + else + { + _glfwLibrary.Timer.t0_32 = _glfw_timeGetTime() - (int)(t*1000.0); + } +} + diff --git a/lib/win32/win32_window.c b/lib/win32/win32_window.c new file mode 100644 index 00000000..15071c36 --- /dev/null +++ b/lib/win32/win32_window.c @@ -0,0 +1,1817 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Win32/WGL +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +// We use versioned window class names in order not to cause conflicts +// between applications using different versions of GLFW +#define _GLFW_WNDCLASSNAME "GLFW27" + + +//======================================================================== +// Enable/disable minimize/restore animations +//======================================================================== + +static int setMinMaxAnimations( int enable ) +{ + ANIMATIONINFO AI; + int old_enable; + + // Get old animation setting + AI.cbSize = sizeof( ANIMATIONINFO ); + SystemParametersInfo( SPI_GETANIMATION, AI.cbSize, &AI, 0 ); + old_enable = AI.iMinAnimate; + + // If requested, change setting + if( old_enable != enable ) + { + AI.iMinAnimate = enable; + SystemParametersInfo( SPI_SETANIMATION, AI.cbSize, &AI, + SPIF_SENDCHANGE ); + } + + return old_enable; +} + + +//======================================================================== +// Focus the window and bring it to the top of the stack +// Due to some nastiness with how Win98/ME/2k/XP handles SetForegroundWindow, +// we have to go through some really bizarre measures to achieve this +//======================================================================== + +static void setForegroundWindow( HWND hWnd ) +{ + int try_count = 0; + int old_animate; + + // Try the standard approach first... + BringWindowToTop( hWnd ); + SetForegroundWindow( hWnd ); + + // If it worked, return now + if( hWnd == GetForegroundWindow() ) + { + // Try to modify the system settings (since this is the foreground + // process, we are allowed to do this) + SystemParametersInfo( SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (LPVOID)0, + SPIF_SENDCHANGE ); + return; + } + + // For other Windows versions than 95 & NT4.0, the standard approach + // may not work, so if we failed we have to "trick" Windows into + // making our window the foureground window: Iconify and restore + // again. It is ugly, but it seems to work (we turn off those annoying + // zoom animations to make it look a bit better at least). + + // Turn off minimize/restore animations + old_animate = setMinMaxAnimations( 0 ); + + // We try this a few times, just to be on the safe side of things... + do + { + // Iconify & restore + ShowWindow( hWnd, SW_HIDE ); + ShowWindow( hWnd, SW_SHOWMINIMIZED ); + ShowWindow( hWnd, SW_SHOWNORMAL ); + + // Try to get focus + BringWindowToTop( hWnd ); + SetForegroundWindow( hWnd ); + + // We do not want to keep going on forever, so we keep track of + // how many times we tried + try_count ++; + } + while( hWnd != GetForegroundWindow() && try_count <= 3 ); + + // Restore the system minimize/restore animation setting + (void) setMinMaxAnimations( old_animate ); + + // Try to modify the system settings (since this is now hopefully the + // foreground process, we are probably allowed to do this) + SystemParametersInfo( SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (LPVOID)0, + SPIF_SENDCHANGE ); +} + + +//======================================================================== +// Returns the specified attribute of the specified pixel format +// NOTE: Do not call this unless we have found WGL_ARB_pixel_format +//======================================================================== + +static int getPixelFormatAttrib(int pixelFormat, int attrib) +{ + int value = 0; + + if( !_glfwWin.GetPixelFormatAttribivARB( _glfwWin.DC, pixelFormat, 0, 1, &attrib, &value) ) + { + // NOTE: We should probably handle this error somehow + return 0; + } + + return value; +} + + +//======================================================================== +// Return a list of available and usable framebuffer configs +//======================================================================== + +static _GLFWfbconfig *getFBConfigs( unsigned int *found ) +{ + _GLFWfbconfig *result; + PIXELFORMATDESCRIPTOR pfd; + int i, count; + + *found = 0; + + if( _glfwWin.has_WGL_ARB_pixel_format ) + { + count = getPixelFormatAttrib( 1, WGL_NUMBER_PIXEL_FORMATS_ARB ); + } + else + { + count = _glfw_DescribePixelFormat( _glfwWin.DC, 1, sizeof( PIXELFORMATDESCRIPTOR ), NULL ); + } + + if( !count ) + { + fprintf( stderr, "No Win32 pixel formats available\n" ); + return NULL; + } + + result = (_GLFWfbconfig*) malloc( sizeof( _GLFWfbconfig ) * count ); + if( !result ) + { + fprintf(stderr, "Out of memory"); + return NULL; + } + + for( i = 1; i <= count; i++ ) + { + if( _glfwWin.has_WGL_ARB_pixel_format ) + { + // Get pixel format attributes through WGL_ARB_pixel_format + + if( !getPixelFormatAttrib( i, WGL_SUPPORT_OPENGL_ARB ) || + !getPixelFormatAttrib( i, WGL_DRAW_TO_WINDOW_ARB ) || + !getPixelFormatAttrib( i, WGL_DOUBLE_BUFFER_ARB ) ) + { + // Only consider doublebuffered OpenGL pixel formats for windows + continue; + } + + if( getPixelFormatAttrib( i, WGL_PIXEL_TYPE_ARB ) != WGL_TYPE_RGBA_ARB ) + { + // Only consider RGBA pixel formats + continue; + } + + result[*found].redBits = getPixelFormatAttrib( i, WGL_RED_BITS_ARB ); + result[*found].greenBits = getPixelFormatAttrib( i, WGL_GREEN_BITS_ARB ); + result[*found].blueBits = getPixelFormatAttrib( i, WGL_BLUE_BITS_ARB ); + result[*found].alphaBits = getPixelFormatAttrib( i, WGL_ALPHA_BITS_ARB ); + + result[*found].depthBits = getPixelFormatAttrib( i, WGL_DEPTH_BITS_ARB ); + result[*found].stencilBits = getPixelFormatAttrib( i, WGL_STENCIL_BITS_ARB ); + + result[*found].accumRedBits = getPixelFormatAttrib( i, WGL_ACCUM_RED_BITS_ARB ); + result[*found].accumGreenBits = getPixelFormatAttrib( i, WGL_ACCUM_GREEN_BITS_ARB ); + result[*found].accumBlueBits = getPixelFormatAttrib( i, WGL_ACCUM_BLUE_BITS_ARB ); + result[*found].accumAlphaBits = getPixelFormatAttrib( i, WGL_ACCUM_ALPHA_BITS_ARB ); + + result[*found].auxBuffers = getPixelFormatAttrib( i, WGL_AUX_BUFFERS_ARB ); + result[*found].stereo = getPixelFormatAttrib( i, WGL_STEREO_ARB ); + + if( _glfwWin.has_WGL_ARB_multisample ) + { + result[*found].samples = getPixelFormatAttrib( i, WGL_SAMPLES_ARB ); + } + else + { + result[*found].samples = 0; + } + } + else + { + // Get pixel format attributes through old-fashioned PFDs + + if( !_glfw_DescribePixelFormat( _glfwWin.DC, i, sizeof( PIXELFORMATDESCRIPTOR ), &pfd ) ) + { + continue; + } + + if( !( pfd.dwFlags & PFD_DRAW_TO_WINDOW ) || + !( pfd.dwFlags & PFD_SUPPORT_OPENGL ) || + !( pfd.dwFlags & PFD_DOUBLEBUFFER ) ) + { + // Only consider doublebuffered OpenGL pixel formats for windows + continue; + } + + if( !( pfd.dwFlags & PFD_GENERIC_ACCELERATED ) && + ( pfd.dwFlags & PFD_GENERIC_FORMAT ) ) + { + continue; + } + + if( pfd.iPixelType != PFD_TYPE_RGBA ) + { + // Only RGBA pixel formats considered + continue; + } + + result[*found].redBits = pfd.cRedBits; + result[*found].greenBits = pfd.cGreenBits; + result[*found].blueBits = pfd.cBlueBits; + result[*found].alphaBits = pfd.cAlphaBits; + + result[*found].depthBits = pfd.cDepthBits; + result[*found].stencilBits = pfd.cStencilBits; + + result[*found].accumRedBits = pfd.cAccumRedBits; + result[*found].accumGreenBits = pfd.cAccumGreenBits; + result[*found].accumBlueBits = pfd.cAccumBlueBits; + result[*found].accumAlphaBits = pfd.cAccumAlphaBits; + + result[*found].auxBuffers = pfd.cAuxBuffers; + result[*found].stereo = ( pfd.dwFlags & PFD_STEREO ) ? GL_TRUE : GL_FALSE; + + // PFD pixel formats do not support FSAA + result[*found].samples = 0; + } + + result[*found].platformID = i; + + (*found)++; + } + + return result; +} + + +//======================================================================== +// Creates an OpenGL context on the specified device context +//======================================================================== + +static HGLRC createContext( HDC dc, const _GLFWwndconfig* wndconfig, int pixelFormat ) +{ + PIXELFORMATDESCRIPTOR pfd; + int flags, i = 0, attribs[7]; + + if( !_glfw_DescribePixelFormat( dc, pixelFormat, sizeof(pfd), &pfd ) ) + { + return NULL; + } + + if( !_glfw_SetPixelFormat( dc, pixelFormat, &pfd ) ) + { + return NULL; + } + + if( _glfwWin.has_WGL_ARB_create_context ) + { + // Use the newer wglCreateContextAttribsARB + + if( wndconfig->glMajor != 1 || wndconfig->glMinor != 0 ) + { + // Request an explicitly versioned context + + attribs[i++] = WGL_CONTEXT_MAJOR_VERSION_ARB; + attribs[i++] = wndconfig->glMajor; + attribs[i++] = WGL_CONTEXT_MINOR_VERSION_ARB; + attribs[i++] = wndconfig->glMinor; + } + + if( wndconfig->glForward || wndconfig->glDebug ) + { + flags = 0; + + if( wndconfig->glForward ) + { + flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + } + + if( wndconfig->glDebug ) + { + flags |= WGL_CONTEXT_DEBUG_BIT_ARB; + } + + attribs[i++] = WGL_CONTEXT_FLAGS_ARB; + attribs[i++] = flags; + } + + if( wndconfig->glProfile ) + { + if( wndconfig->glProfile == GLFW_OPENGL_CORE_PROFILE ) + { + flags = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + } + else + { + flags = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + } + + attribs[i++] = WGL_CONTEXT_PROFILE_MASK_ARB; + attribs[i++] = flags; + } + + attribs[i++] = 0; + + return _glfwWin.CreateContextAttribsARB( dc, NULL, attribs ); + } + + return wglCreateContext( dc ); +} + + +//======================================================================== +// Translates a Windows key to the corresponding GLFW key +//======================================================================== + +static int translateKey( WPARAM wParam, LPARAM lParam ) +{ + MSG next_msg; + DWORD msg_time; + DWORD scan_code; + + // Check for numeric keypad keys + // Note: This way we always force "NumLock = ON", which at least + // enables GLFW users to detect numeric keypad keys + int hiFlags = HIWORD( lParam ); + + if ( !( hiFlags & 0x100 ) ) + { + switch( MapVirtualKey( hiFlags & 0xFF, 1 ) ) + { + case VK_INSERT: return GLFW_KEY_KP_0; + case VK_END: return GLFW_KEY_KP_1; + case VK_DOWN: return GLFW_KEY_KP_2; + case VK_NEXT: return GLFW_KEY_KP_3; + case VK_LEFT: return GLFW_KEY_KP_4; + case VK_CLEAR: return GLFW_KEY_KP_5; + case VK_RIGHT: return GLFW_KEY_KP_6; + case VK_HOME: return GLFW_KEY_KP_7; + case VK_UP: return GLFW_KEY_KP_8; + case VK_PRIOR: return GLFW_KEY_KP_9; + case VK_DIVIDE: return GLFW_KEY_KP_DIVIDE; + case VK_MULTIPLY: return GLFW_KEY_KP_MULTIPLY; + case VK_SUBTRACT: return GLFW_KEY_KP_SUBTRACT; + case VK_ADD: return GLFW_KEY_KP_ADD; + case VK_DELETE: return GLFW_KEY_KP_DECIMAL; + } + } + + // Check which key was pressed or released + switch( wParam ) + { + // The SHIFT keys require special handling + case VK_SHIFT: + { + // Compare scan code for this key with that of VK_RSHIFT in + // order to determine which shift key was pressed (left or + // right) + scan_code = MapVirtualKey( VK_RSHIFT, 0 ); + if( ((lParam & 0x01ff0000) >> 16) == scan_code ) + { + return GLFW_KEY_RSHIFT; + } + + return GLFW_KEY_LSHIFT; + } + + // The CTRL keys require special handling + case VK_CONTROL: + { + // Is this an extended key (i.e. right key)? + if( lParam & 0x01000000 ) + { + return GLFW_KEY_RCTRL; + } + + // Here is a trick: "Alt Gr" sends LCTRL, then RALT. We only + // want the RALT message, so we try to see if the next message + // is a RALT message. In that case, this is a false LCTRL! + msg_time = GetMessageTime(); + if( PeekMessage( &next_msg, NULL, 0, 0, PM_NOREMOVE ) ) + { + if( next_msg.message == WM_KEYDOWN || + next_msg.message == WM_SYSKEYDOWN ) + { + if( next_msg.wParam == VK_MENU && + (next_msg.lParam & 0x01000000) && + next_msg.time == msg_time ) + { + // Next message is a RALT down message, which + // means that this is NOT a proper LCTRL message! + return GLFW_KEY_UNKNOWN; + } + } + } + + return GLFW_KEY_LCTRL; + } + + // The ALT keys require special handling + case VK_MENU: + { + // Is this an extended key (i.e. right key)? + if( lParam & 0x01000000 ) + { + return GLFW_KEY_RALT; + } + + return GLFW_KEY_LALT; + } + + // The ENTER keys require special handling + case VK_RETURN: + { + // Is this an extended key (i.e. right key)? + if( lParam & 0x01000000 ) + { + return GLFW_KEY_KP_ENTER; + } + + return GLFW_KEY_ENTER; + } + + // Special keys (non character keys) + case VK_ESCAPE: return GLFW_KEY_ESC; + case VK_TAB: return GLFW_KEY_TAB; + case VK_BACK: return GLFW_KEY_BACKSPACE; + case VK_HOME: return GLFW_KEY_HOME; + case VK_END: return GLFW_KEY_END; + case VK_PRIOR: return GLFW_KEY_PAGEUP; + case VK_NEXT: return GLFW_KEY_PAGEDOWN; + case VK_INSERT: return GLFW_KEY_INSERT; + case VK_DELETE: return GLFW_KEY_DEL; + case VK_LEFT: return GLFW_KEY_LEFT; + case VK_UP: return GLFW_KEY_UP; + case VK_RIGHT: return GLFW_KEY_RIGHT; + case VK_DOWN: return GLFW_KEY_DOWN; + case VK_F1: return GLFW_KEY_F1; + case VK_F2: return GLFW_KEY_F2; + case VK_F3: return GLFW_KEY_F3; + case VK_F4: return GLFW_KEY_F4; + case VK_F5: return GLFW_KEY_F5; + case VK_F6: return GLFW_KEY_F6; + case VK_F7: return GLFW_KEY_F7; + case VK_F8: return GLFW_KEY_F8; + case VK_F9: return GLFW_KEY_F9; + case VK_F10: return GLFW_KEY_F10; + case VK_F11: return GLFW_KEY_F11; + case VK_F12: return GLFW_KEY_F12; + case VK_F13: return GLFW_KEY_F13; + case VK_F14: return GLFW_KEY_F14; + case VK_F15: return GLFW_KEY_F15; + case VK_F16: return GLFW_KEY_F16; + case VK_F17: return GLFW_KEY_F17; + case VK_F18: return GLFW_KEY_F18; + case VK_F19: return GLFW_KEY_F19; + case VK_F20: return GLFW_KEY_F20; + case VK_F21: return GLFW_KEY_F21; + case VK_F22: return GLFW_KEY_F22; + case VK_F23: return GLFW_KEY_F23; + case VK_F24: return GLFW_KEY_F24; + case VK_SPACE: return GLFW_KEY_SPACE; + + // Numeric keypad + case VK_NUMPAD0: return GLFW_KEY_KP_0; + case VK_NUMPAD1: return GLFW_KEY_KP_1; + case VK_NUMPAD2: return GLFW_KEY_KP_2; + case VK_NUMPAD3: return GLFW_KEY_KP_3; + case VK_NUMPAD4: return GLFW_KEY_KP_4; + case VK_NUMPAD5: return GLFW_KEY_KP_5; + case VK_NUMPAD6: return GLFW_KEY_KP_6; + case VK_NUMPAD7: return GLFW_KEY_KP_7; + case VK_NUMPAD8: return GLFW_KEY_KP_8; + case VK_NUMPAD9: return GLFW_KEY_KP_9; + case VK_DIVIDE: return GLFW_KEY_KP_DIVIDE; + case VK_MULTIPLY: return GLFW_KEY_KP_MULTIPLY; + case VK_SUBTRACT: return GLFW_KEY_KP_SUBTRACT; + case VK_ADD: return GLFW_KEY_KP_ADD; + case VK_DECIMAL: return GLFW_KEY_KP_DECIMAL; + case VK_NUMLOCK: return GLFW_KEY_KP_NUM_LOCK; + + case VK_CAPITAL: return GLFW_KEY_CAPS_LOCK; + case VK_SCROLL: return GLFW_KEY_SCROLL_LOCK; + case VK_PAUSE: return GLFW_KEY_PAUSE; + + case VK_LWIN: return GLFW_KEY_LSUPER; + case VK_RWIN: return GLFW_KEY_RSUPER; + case VK_APPS: return GLFW_KEY_MENU; + + // The rest (should be printable keys) + default: + { + // Convert to printable character (ISO-8859-1 or Unicode) + wParam = MapVirtualKey( (UINT) wParam, 2 ) & 0x0000FFFF; + + // Make sure that the character is uppercase + if( _glfwLibrary.Sys.hasUnicode ) + { + wParam = (WPARAM) CharUpperW( (LPWSTR) wParam ); + } + else + { + wParam = (WPARAM) CharUpperA( (LPSTR) wParam ); + } + + // Valid ISO-8859-1 character? + if( (wParam >= 32 && wParam <= 126) || + (wParam >= 160 && wParam <= 255) ) + { + return (int) wParam; + } + + return GLFW_KEY_UNKNOWN; + } + } +} + + +//======================================================================== +// Translates a Windows key to Unicode +//======================================================================== + +static void translateChar( DWORD wParam, DWORD lParam, int action ) +{ + BYTE keyboard_state[ 256 ]; + UCHAR char_buf[ 10 ]; + WCHAR unicode_buf[ 10 ]; + UINT scan_code; + int i, num_chars, unicode; + + GetKeyboardState( keyboard_state ); + + // Derive scan code from lParam and action + scan_code = (lParam & 0x01ff0000) >> 16; + if( action == GLFW_RELEASE ) + { + scan_code |= 0x8000000; + } + + if( _glfwLibrary.Sys.hasUnicode ) + { + num_chars = ToUnicode( + wParam, // virtual-key code + scan_code, // scan code + keyboard_state, // key-state array + unicode_buf, // buffer for translated key + 10, // size of translated key buffer + 0 // active-menu flag + ); + unicode = 1; + } + else + { + // Convert to ISO-8859-1 + num_chars = ToAscii( + wParam, // virtual-key code + scan_code, // scan code + keyboard_state, // key-state array + (LPWORD) char_buf, // buffer for translated key + 0 // active-menu flag + ); + unicode = 0; + } + + // Report characters + for( i = 0; i < num_chars; i++ ) + { + // Get next character from buffer + if( unicode ) + { + _glfwInputChar( (int) unicode_buf[ i ], action ); + } + else + { + _glfwInputChar( (int) char_buf[ i ], action ); + } + } +} + + +//======================================================================== +// Window callback function (handles window events) +//======================================================================== + +static LRESULT CALLBACK windowProc( HWND hWnd, UINT uMsg, + WPARAM wParam, LPARAM lParam ) +{ + int wheelDelta, iconified; + + switch( uMsg ) + { + // Window activate message? (iconification?) + case WM_ACTIVATE: + { + _glfwWin.active = LOWORD(wParam) != WA_INACTIVE ? GL_TRUE : GL_FALSE; + + iconified = HIWORD(wParam) ? GL_TRUE : GL_FALSE; + + // Were we deactivated/iconified? + if( (!_glfwWin.active || iconified) && !_glfwWin.iconified ) + { + _glfwInputDeactivation(); + + // If we are in fullscreen mode we need to iconify + if( _glfwWin.opened && _glfwWin.fullscreen ) + { + // Do we need to manually iconify? + if( !iconified ) + { + // Minimize window + CloseWindow( _glfwWin.window ); + iconified = GL_TRUE; + } + + // Restore the original desktop resolution + ChangeDisplaySettings( NULL, CDS_FULLSCREEN ); + } + + // Unlock mouse if locked + if( !_glfwWin.oldMouseLockValid ) + { + _glfwWin.oldMouseLock = _glfwWin.mouseLock; + _glfwWin.oldMouseLockValid = GL_TRUE; + glfwEnable( GLFW_MOUSE_CURSOR ); + } + } + else if( _glfwWin.active || !iconified ) + { + // If we are in fullscreen mode we need to maximize + if( _glfwWin.opened && _glfwWin.fullscreen && _glfwWin.iconified ) + { + // Change display settings to the user selected mode + _glfwSetVideoModeMODE( _glfwWin.modeID ); + + // Do we need to manually restore window? + if( iconified ) + { + // Restore window + OpenIcon( _glfwWin.window ); + iconified = GL_FALSE; + + // Activate window + ShowWindow( hWnd, SW_SHOW ); + setForegroundWindow( _glfwWin.window ); + SetFocus( _glfwWin.window ); + } + } + + // Lock mouse, if necessary + if( _glfwWin.oldMouseLockValid && _glfwWin.oldMouseLock ) + { + glfwDisable( GLFW_MOUSE_CURSOR ); + } + _glfwWin.oldMouseLockValid = GL_FALSE; + } + + _glfwWin.iconified = iconified; + return 0; + } + + case WM_SYSCOMMAND: + { + switch( wParam & 0xfff0 ) + { + case SC_SCREENSAVE: + case SC_MONITORPOWER: + { + if( _glfwWin.fullscreen ) + { + // Disallow screen saver and screen blanking if we are + // running in fullscreen mode + return 0; + } + else + { + break; + } + } + + // User trying to access application menu using ALT? + case SC_KEYMENU: + return 0; + } + break; + } + + case WM_CLOSE: + { + // Translate this to WM_QUIT so that we can handle all cases in the + // same place + PostQuitMessage( 0 ); + return 0; + } + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + _glfwInputKey( translateKey( wParam, lParam ), GLFW_PRESS ); + + if( _glfwWin.charCallback ) + { + translateChar( (DWORD) wParam, (DWORD) lParam, GLFW_PRESS ); + } + return 0; + } + + case WM_KEYUP: + case WM_SYSKEYUP: + { + // Special trick: release both shift keys on SHIFT up event + if( wParam == VK_SHIFT ) + { + _glfwInputKey( GLFW_KEY_LSHIFT, GLFW_RELEASE ); + _glfwInputKey( GLFW_KEY_RSHIFT, GLFW_RELEASE ); + } + else + { + _glfwInputKey( translateKey( wParam, lParam ), GLFW_RELEASE ); + } + + if( _glfwWin.charCallback ) + { + translateChar( (DWORD) wParam, (DWORD) lParam, GLFW_RELEASE ); + } + + return 0; + } + + case WM_LBUTTONDOWN: + SetCapture(hWnd); + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS ); + return 0; + case WM_RBUTTONDOWN: + SetCapture(hWnd); + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS ); + return 0; + case WM_MBUTTONDOWN: + SetCapture(hWnd); + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS ); + return 0; + case WM_XBUTTONDOWN: + { + if( HIWORD(wParam) == XBUTTON1 ) + { + SetCapture(hWnd); + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_4, GLFW_PRESS ); + } + else if( HIWORD(wParam) == XBUTTON2 ) + { + SetCapture(hWnd); + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_5, GLFW_PRESS ); + } + return 1; + } + + case WM_LBUTTONUP: + ReleaseCapture(); + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE ); + return 0; + case WM_RBUTTONUP: + ReleaseCapture(); + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_RIGHT, GLFW_RELEASE ); + return 0; + case WM_MBUTTONUP: + ReleaseCapture(); + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_MIDDLE, GLFW_RELEASE ); + return 0; + case WM_XBUTTONUP: + { + if( HIWORD(wParam) == XBUTTON1 ) + { + ReleaseCapture(); + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_4, GLFW_RELEASE ); + } + else if( HIWORD(wParam) == XBUTTON2 ) + { + ReleaseCapture(); + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_5, GLFW_RELEASE ); + } + return 1; + } + + case WM_MOUSEMOVE: + { + int NewMouseX, NewMouseY; + + // Get signed (!) mouse position + NewMouseX = (int)((short)LOWORD(lParam)); + NewMouseY = (int)((short)HIWORD(lParam)); + + if( NewMouseX != _glfwInput.OldMouseX || + NewMouseY != _glfwInput.OldMouseY ) + { + if( _glfwWin.mouseLock ) + { + _glfwInput.MousePosX += NewMouseX - + _glfwInput.OldMouseX; + _glfwInput.MousePosY += NewMouseY - + _glfwInput.OldMouseY; + } + else + { + _glfwInput.MousePosX = NewMouseX; + _glfwInput.MousePosY = NewMouseY; + } + _glfwInput.OldMouseX = NewMouseX; + _glfwInput.OldMouseY = NewMouseY; + _glfwInput.MouseMoved = GL_TRUE; + + if( _glfwWin.mousePosCallback ) + { + _glfwWin.mousePosCallback( _glfwInput.MousePosX, + _glfwInput.MousePosY ); + } + } + return 0; + } + + case WM_MOUSEWHEEL: + { + // WM_MOUSEWHEEL is not supported under Windows 95 + if( _glfwLibrary.Sys.winVer != _GLFW_WIN_95 ) + { + wheelDelta = (((int)wParam) >> 16) / WHEEL_DELTA; + _glfwInput.WheelPos += wheelDelta; + if( _glfwWin.mouseWheelCallback ) + { + _glfwWin.mouseWheelCallback( _glfwInput.WheelPos ); + } + return 0; + } + break; + } + + case WM_SIZE: + { + _glfwWin.width = LOWORD(lParam); + _glfwWin.height = HIWORD(lParam); + + // If the mouse is locked, update the clipping rect + if( _glfwWin.mouseLock ) + { + RECT ClipWindowRect; + if( GetWindowRect( _glfwWin.window, &ClipWindowRect ) ) + { + ClipCursor( &ClipWindowRect ); + } + } + + if( _glfwWin.windowSizeCallback ) + { + _glfwWin.windowSizeCallback( LOWORD(lParam), HIWORD(lParam) ); + } + return 0; + } + + case WM_MOVE: + { + // If the mouse is locked, update the clipping rect + if( _glfwWin.mouseLock ) + { + RECT ClipWindowRect; + if( GetWindowRect( _glfwWin.window, &ClipWindowRect ) ) + { + ClipCursor( &ClipWindowRect ); + } + } + return 0; + } + + // Was the window contents damaged? + case WM_PAINT: + { + if( _glfwWin.windowRefreshCallback ) + { + _glfwWin.windowRefreshCallback(); + } + break; + } + + case WM_DISPLAYCHANGE: + { + // TODO: Do stuff here. + + break; + } + } + + // Pass all unhandled messages to DefWindowProc + return DefWindowProc( hWnd, uMsg, wParam, lParam ); +} + + +//======================================================================== +// Translate client window size to full window size (including window borders) +//======================================================================== + +static void getFullWindowSize( int clientWidth, int clientHeight, + int *fullWidth, int *fullHeight ) +{ + RECT rect; + + // Create a window rectangle + rect.left = (long)0; + rect.right = (long)clientWidth - 1; + rect.top = (long)0; + rect.bottom = (long)clientHeight - 1; + + // Adjust according to window styles + AdjustWindowRectEx( &rect, _glfwWin.dwStyle, FALSE, _glfwWin.dwExStyle ); + + // Calculate width and height of full window + *fullWidth = rect.right - rect.left + 1; + *fullHeight = rect.bottom - rect.top + 1; +} + + +//======================================================================== +// Initialize WGL-specific extensions +// This function is called once before initial context creation, i.e. before +// any WGL extensions could be present. This is done in order to have both +// extension variable clearing and loading in the same place, hopefully +// decreasing the possibility of forgetting to add one without the other. +//======================================================================== + +static void initWGLExtensions( void ) +{ + // This needs to include every function pointer loaded below + _glfwWin.SwapIntervalEXT = NULL; + _glfwWin.GetPixelFormatAttribivARB = NULL; + _glfwWin.GetExtensionsStringARB = NULL; + _glfwWin.GetExtensionsStringEXT = NULL; + _glfwWin.CreateContextAttribsARB = NULL; + + // This needs to include every extension used below except for + // WGL_ARB_extensions_string and WGL_EXT_extensions_string + _glfwWin.has_WGL_EXT_swap_control = GL_FALSE; + _glfwWin.has_WGL_ARB_pixel_format = GL_FALSE; + _glfwWin.has_WGL_ARB_multisample = GL_FALSE; + _glfwWin.has_WGL_ARB_create_context = GL_FALSE; + + _glfwWin.GetExtensionsStringEXT = (WGLGETEXTENSIONSSTRINGEXT_T) + wglGetProcAddress( "wglGetExtensionsStringEXT" ); + if( !_glfwWin.GetExtensionsStringEXT ) + { + _glfwWin.GetExtensionsStringARB = (WGLGETEXTENSIONSSTRINGARB_T) + wglGetProcAddress( "wglGetExtensionsStringARB" ); + if( !_glfwWin.GetExtensionsStringARB ) + { + return; + } + } + + if( _glfwPlatformExtensionSupported( "WGL_ARB_multisample" ) ) + { + _glfwWin.has_WGL_ARB_multisample = GL_TRUE; + } + + if( _glfwPlatformExtensionSupported( "WGL_ARB_create_context" ) ) + { + _glfwWin.has_WGL_ARB_create_context = GL_TRUE; + _glfwWin.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) + wglGetProcAddress( "wglCreateContextAttribsARB" ); + } + + if( _glfwPlatformExtensionSupported( "WGL_EXT_swap_control" ) ) + { + _glfwWin.has_WGL_EXT_swap_control = GL_TRUE; + _glfwWin.SwapIntervalEXT = (WGLSWAPINTERVALEXT_T) + wglGetProcAddress( "wglSwapIntervalEXT" ); + } + + if( _glfwPlatformExtensionSupported( "WGL_ARB_pixel_format" ) ) + { + _glfwWin.has_WGL_ARB_pixel_format = GL_TRUE; + _glfwWin.GetPixelFormatAttribivARB = (WGLGETPIXELFORMATATTRIBIVARB_T) + wglGetProcAddress( "wglGetPixelFormatAttribivARB" ); + } +} + + +//======================================================================== +// Registers the GLFW window class +//======================================================================== + +static ATOM registerWindowClass( void ) +{ + WNDCLASS wc; + + // Set window class parameters + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraw on... + wc.lpfnWndProc = (WNDPROC)windowProc; // Message handler + wc.cbClsExtra = 0; // No extra class data + wc.cbWndExtra = 0; // No extra window data + wc.hInstance = _glfwLibrary.instance; // Set instance + wc.hCursor = LoadCursor( NULL, IDC_ARROW ); // Load arrow pointer + wc.hbrBackground = NULL; // No background + wc.lpszMenuName = NULL; // No menu + wc.lpszClassName = _GLFW_WNDCLASSNAME; // Set class name + + // Load user-provided icon if available + wc.hIcon = LoadIcon( _glfwLibrary.instance, "GLFW_ICON" ); + if( !wc.hIcon ) + { + // Load default icon + wc.hIcon = LoadIcon( NULL, IDI_WINLOGO ); + } + + return RegisterClass( &wc ); +} + + +//======================================================================== +// Returns the closest matching pixel format, or zero on error +//======================================================================== + +static int choosePixelFormat( const _GLFWfbconfig *fbconfig ) +{ + unsigned int fbcount; + int pixelFormat; + _GLFWfbconfig *fbconfigs; + const _GLFWfbconfig *closest; + + fbconfigs = getFBConfigs( &fbcount ); + if( !fbconfigs ) + { + fprintf( stderr, "Failed to find any usable GLFWFBConfigs\n" ); + return 0; + } + + closest = _glfwChooseFBConfig( fbconfig, fbconfigs, fbcount ); + if( !closest ) + { + fprintf( stderr, "Failed to select a GLFWFBConfig from the alternatives\n" ); + free( fbconfigs ); + return 0; + } + + pixelFormat = (int) closest->platformID; + + free( fbconfigs ); + fbconfigs = NULL; + closest = NULL; + + return pixelFormat; +} + + +//======================================================================== +// Creates the GLFW window and rendering context +//======================================================================== + +static int createWindow( const _GLFWwndconfig *wndconfig, + const _GLFWfbconfig *fbconfig ) +{ + DWORD dwStyle, dwExStyle; + int pixelFormat, fullWidth, fullHeight; + RECT wa; + POINT pos; + + _glfwWin.DC = NULL; + _glfwWin.context = NULL; + _glfwWin.window = NULL; + + // Set common window styles + dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE; + dwExStyle = WS_EX_APPWINDOW; + + // Set window style, depending on fullscreen mode + if( _glfwWin.fullscreen ) + { + dwStyle |= WS_POPUP; + + // Here's a trick for helping us getting window focus + // (SetForegroundWindow doesn't work properly under + // Win98/ME/2K/.NET/+) + /* + if( _glfwLibrary.Sys.WinVer != _GLFW_WIN_95 && + _glfwLibrary.Sys.WinVer != _GLFW_WIN_NT4 && + _glfwLibrary.Sys.WinVer != _GLFW_WIN_XP ) + { + dwStyle |= WS_MINIMIZE; + } + */ + } + else + { + dwStyle |= WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + + if( !wndconfig->windowNoResize ) + { + dwStyle |= ( WS_MAXIMIZEBOX | WS_SIZEBOX ); + dwExStyle |= WS_EX_WINDOWEDGE; + } + } + + // Remember window styles (used by getFullWindowSize) + _glfwWin.dwStyle = dwStyle; + _glfwWin.dwExStyle = dwExStyle; + + // Adjust window size for frame and title bar + getFullWindowSize( _glfwWin.width, _glfwWin.height, &fullWidth, &fullHeight ); + + // Adjust window position to working area (e.g. if the task bar is at + // the top of the display). Fullscreen windows are always opened in + // the upper left corner regardless of the desktop working area. + if( _glfwWin.fullscreen ) + { + wa.left = wa.top = 0; + } + else + { + SystemParametersInfo( SPI_GETWORKAREA, 0, &wa, 0 ); + } + + _glfwWin.window = CreateWindowEx( _glfwWin.dwExStyle, // Extended style + _GLFW_WNDCLASSNAME, // Class name + "GLFW Window", // Window title + _glfwWin.dwStyle, // Defined window style + wa.left, wa.top, // Window position + fullWidth, // Decorated window width + fullHeight, // Decorated window height + NULL, // No parent window + NULL, // No menu + _glfwLibrary.instance, // Instance + NULL ); // Nothing to WM_CREATE + + if( !_glfwWin.window ) + { + fprintf( stderr, "Unable to create Win32 window\n" ); + return GL_FALSE; + } + + _glfwWin.DC = GetDC( _glfwWin.window ); + if( !_glfwWin.DC ) + { + fprintf( stderr, "Unable to retrieve GLFW window DC\n" ); + return GL_FALSE; + } + + pixelFormat = choosePixelFormat( fbconfig ); + if( !pixelFormat ) + { + fprintf( stderr, "Unable to find a usable pixel format\n" ); + return GL_FALSE; + } + + _glfwWin.context = createContext( _glfwWin.DC, wndconfig, pixelFormat ); + if( !_glfwWin.context ) + { + fprintf( stderr, "Unable to create OpenGL context\n" ); + return GL_FALSE; + } + + if( !wglMakeCurrent( _glfwWin.DC, _glfwWin.context ) ) + { + fprintf( stderr, "Unable to make OpenGL context current\n" ); + return GL_FALSE; + } + + initWGLExtensions(); + + // Initialize mouse position data + GetCursorPos( &pos ); + ScreenToClient( _glfwWin.window, &pos ); + _glfwInput.OldMouseX = _glfwInput.MousePosX = pos.x; + _glfwInput.OldMouseY = _glfwInput.MousePosY = pos.y; + + return GL_TRUE; +} + + +//======================================================================== +// Destroys the GLFW window and rendering context +//======================================================================== + +static void destroyWindow( void ) +{ + if( _glfwWin.context ) + { + wglMakeCurrent( NULL, NULL ); + wglDeleteContext( _glfwWin.context ); + _glfwWin.context = NULL; + } + + if( _glfwWin.DC ) + { + ReleaseDC( _glfwWin.window, _glfwWin.DC ); + _glfwWin.DC = NULL; + } + + if( _glfwWin.window ) + { + if( _glfwLibrary.Sys.winVer <= _GLFW_WIN_NT4 ) + { + // Note: Hiding the window first fixes an annoying W98/NT4 + // remaining icon bug for fullscreen displays + ShowWindow( _glfwWin.window, SW_HIDE ); + } + + DestroyWindow( _glfwWin.window ); + _glfwWin.window = NULL; + } +} + + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Here is where the window is created, and the OpenGL rendering context is +// created +//======================================================================== + +int _glfwPlatformOpenWindow( int width, int height, + const _GLFWwndconfig *wndconfig, + const _GLFWfbconfig *fbconfig ) +{ + GLboolean recreateContext = GL_FALSE; + + // Clear platform specific GLFW window state + _glfwWin.classAtom = 0; + _glfwWin.oldMouseLockValid = GL_FALSE; + + _glfwWin.desiredRefreshRate = wndconfig->refreshRate; + + _glfwWin.classAtom = registerWindowClass(); + if( !_glfwWin.classAtom ) + { + fprintf( stderr, "Failed to register GLFW window class\n" ); + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + if( _glfwWin.fullscreen ) + { + _glfwSetVideoMode( &_glfwWin.width, &_glfwWin.height, + fbconfig->redBits, fbconfig->greenBits, fbconfig->blueBits, + wndconfig->refreshRate ); + } + + initWGLExtensions(); + + if( !createWindow( wndconfig, fbconfig ) ) + { + fprintf( stderr, "Failed to create GLFW window\n" ); + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + if( wndconfig->glMajor > 2 ) + { + if( !_glfwWin.has_WGL_ARB_create_context ) + { + fprintf( stderr, "OpenGL 3.0+ is not supported\n" ); + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + recreateContext = GL_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( _glfwWin.has_WGL_ARB_multisample && _glfwWin.has_WGL_ARB_pixel_format ) + { + // We appear to have both the FSAA extension and the means to ask for it + recreateContext = GL_TRUE; + } + } + + if( recreateContext ) + { + // 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 potential compatibility problems + + destroyWindow(); + + if( !createWindow( wndconfig, fbconfig ) ) + { + fprintf( stderr, "Unable to re-create GLFW window\n" ); + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + } + + if( _glfwWin.fullscreen ) + { + // Place the window above all topmost windows + SetWindowPos( _glfwWin.window, HWND_TOPMOST, 0,0,0,0, + SWP_NOMOVE | SWP_NOSIZE ); + } + + setForegroundWindow( _glfwWin.window ); + SetFocus( _glfwWin.window ); + + return GL_TRUE; +} + + +//======================================================================== +// Properly kill the window / video display +//======================================================================== + +void _glfwPlatformCloseWindow( void ) +{ + destroyWindow(); + + if( _glfwWin.classAtom ) + { + UnregisterClass( _GLFW_WNDCLASSNAME, _glfwLibrary.instance ); + _glfwWin.classAtom = 0; + } + + if( _glfwWin.fullscreen ) + { + // Restore original desktop resolution + ChangeDisplaySettings( NULL, CDS_FULLSCREEN ); + } +} + + +//======================================================================== +// Set the window title +//======================================================================== + +void _glfwPlatformSetWindowTitle( const char *title ) +{ + (void) SetWindowText( _glfwWin.window, title ); +} + + +//======================================================================== +// Set the window size. +//======================================================================== + +void _glfwPlatformSetWindowSize( int width, int height ) +{ + int bpp, mode = 0, refresh; + int sizechanged = GL_FALSE; + GLint drawbuffer; + GLfloat clearcolor[4]; + + if( _glfwWin.fullscreen ) + { + // Get some info about the current mode + + DEVMODE dm; + + // Get current BPP settings + dm.dmSize = sizeof( DEVMODE ); + if( EnumDisplaySettings( NULL, _glfwWin.modeID, &dm ) ) + { + // Get bpp + bpp = dm.dmBitsPerPel; + + // Get closest match for target video mode + refresh = _glfwWin.desiredRefreshRate; + mode = _glfwGetClosestVideoModeBPP( &width, &height, &bpp, + &refresh ); + } + else + { + mode = _glfwWin.modeID; + } + } + else + { + // If we are in windowed mode, adjust the window size to + // compensate for window decorations + getFullWindowSize( width, height, &width, &height ); + } + + // Change window size before changing fullscreen mode? + if( _glfwWin.fullscreen && (width > _glfwWin.width) ) + { + SetWindowPos( _glfwWin.window, HWND_TOP, 0, 0, width, height, + SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER ); + sizechanged = GL_TRUE; + } + + // Change fullscreen video mode? + if( _glfwWin.fullscreen && mode != _glfwWin.modeID ) + { + _glfwSetVideoModeMODE( mode ); + + // Clear the front buffer to black (avoid ugly desktop remains in + // our OpenGL window) + glGetIntegerv( GL_DRAW_BUFFER, &drawbuffer ); + glGetFloatv( GL_COLOR_CLEAR_VALUE, clearcolor ); + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + glClear( GL_COLOR_BUFFER_BIT ); + if( drawbuffer == GL_BACK ) + { + _glfw_SwapBuffers( _glfwWin.DC ); + } + glClearColor( clearcolor[0], clearcolor[1], clearcolor[2], + clearcolor[3] ); + } + + // Set window size (if not already changed) + if( !sizechanged ) + { + SetWindowPos( _glfwWin.window, HWND_TOP, 0, 0, width, height, + SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER ); + } +} + + +//======================================================================== +// Set the window position +//======================================================================== + +void _glfwPlatformSetWindowPos( int x, int y ) +{ + (void) SetWindowPos( _glfwWin.window, HWND_TOP, x, y, 0, 0, + SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER ); +} + + +//======================================================================== +// Window iconification +//======================================================================== + +void _glfwPlatformIconifyWindow( void ) +{ + // Iconify window + CloseWindow( _glfwWin.window ); + _glfwWin.iconified = GL_TRUE; + + // If we are in fullscreen mode we need to change video modes + if( _glfwWin.fullscreen ) + { + // Change display settings to the desktop resolution + ChangeDisplaySettings( NULL, CDS_FULLSCREEN ); + } + + // Unlock mouse + if( !_glfwWin.oldMouseLockValid ) + { + _glfwWin.oldMouseLock = _glfwWin.mouseLock; + _glfwWin.oldMouseLockValid = GL_TRUE; + glfwEnable( GLFW_MOUSE_CURSOR ); + } +} + + +//======================================================================== +// Window un-iconification +//======================================================================== + +void _glfwPlatformRestoreWindow( void ) +{ + // If we are in fullscreen mode we need to change video modes + if( _glfwWin.fullscreen ) + { + // Change display settings to the user selected mode + _glfwSetVideoModeMODE( _glfwWin.modeID ); + } + + // Un-iconify window + OpenIcon( _glfwWin.window ); + + // Make sure that our window ends up on top of things + ShowWindow( _glfwWin.window, SW_SHOW ); + setForegroundWindow( _glfwWin.window ); + SetFocus( _glfwWin.window ); + + // Window is no longer iconified + _glfwWin.iconified = GL_FALSE; + + // Lock mouse, if necessary + if( _glfwWin.oldMouseLockValid && _glfwWin.oldMouseLock ) + { + glfwDisable( GLFW_MOUSE_CURSOR ); + } + _glfwWin.oldMouseLockValid = GL_FALSE; +} + + +//======================================================================== +// Swap buffers (double-buffering) +//======================================================================== + +void _glfwPlatformSwapBuffers( void ) +{ + _glfw_SwapBuffers( _glfwWin.DC ); +} + + +//======================================================================== +// Set double buffering swap interval +//======================================================================== + +void _glfwPlatformSwapInterval( int interval ) +{ + if( _glfwWin.has_WGL_EXT_swap_control ) + { + _glfwWin.SwapIntervalEXT( interval ); + } +} + + +//======================================================================== +// Write back window parameters into GLFW window structure +//======================================================================== + +void _glfwPlatformRefreshWindowParams( void ) +{ + PIXELFORMATDESCRIPTOR pfd; + DEVMODE dm; + int pixelFormat, mode; + + // Obtain a detailed description of current pixel format + pixelFormat = _glfw_GetPixelFormat( _glfwWin.DC ); + + if( _glfwWin.has_WGL_ARB_pixel_format ) + { + if( getPixelFormatAttrib( pixelFormat, WGL_ACCELERATION_ARB ) != + WGL_NO_ACCELERATION_ARB ) + { + _glfwWin.accelerated = GL_TRUE; + } + else + { + _glfwWin.accelerated = GL_FALSE; + } + + _glfwWin.redBits = getPixelFormatAttrib( pixelFormat, WGL_RED_BITS_ARB ); + _glfwWin.greenBits = getPixelFormatAttrib( pixelFormat, WGL_GREEN_BITS_ARB ); + _glfwWin.blueBits = getPixelFormatAttrib( pixelFormat, WGL_BLUE_BITS_ARB ); + + _glfwWin.alphaBits = getPixelFormatAttrib( pixelFormat, WGL_ALPHA_BITS_ARB ); + _glfwWin.depthBits = getPixelFormatAttrib( pixelFormat, WGL_DEPTH_BITS_ARB ); + _glfwWin.stencilBits = getPixelFormatAttrib( pixelFormat, WGL_STENCIL_BITS_ARB ); + + _glfwWin.accumRedBits = getPixelFormatAttrib( pixelFormat, WGL_ACCUM_RED_BITS_ARB ); + _glfwWin.accumGreenBits = getPixelFormatAttrib( pixelFormat, WGL_ACCUM_GREEN_BITS_ARB ); + _glfwWin.accumBlueBits = getPixelFormatAttrib( pixelFormat, WGL_ACCUM_BLUE_BITS_ARB ); + _glfwWin.accumAlphaBits = getPixelFormatAttrib( pixelFormat, WGL_ACCUM_ALPHA_BITS_ARB ); + + _glfwWin.auxBuffers = getPixelFormatAttrib( pixelFormat, WGL_AUX_BUFFERS_ARB ); + _glfwWin.stereo = getPixelFormatAttrib( pixelFormat, WGL_STEREO_ARB ) ? GL_TRUE : GL_FALSE; + + if( _glfwWin.has_WGL_ARB_multisample ) + { + _glfwWin.samples = getPixelFormatAttrib( pixelFormat, WGL_SAMPLES_ARB ); + // Should we force 1 to 0 here for consistency, or keep 1 for transparency? + } + else + { + _glfwWin.samples = 0; + } + } + else + { + _glfw_DescribePixelFormat( _glfwWin.DC, pixelFormat, + sizeof(PIXELFORMATDESCRIPTOR), &pfd ); + + // Is current OpenGL context accelerated? + _glfwWin.accelerated = (pfd.dwFlags & PFD_GENERIC_ACCELERATED) || + !(pfd.dwFlags & PFD_GENERIC_FORMAT) ? 1 : 0; + + // "Standard" window parameters + _glfwWin.redBits = pfd.cRedBits; + _glfwWin.greenBits = pfd.cGreenBits; + _glfwWin.blueBits = pfd.cBlueBits; + _glfwWin.alphaBits = pfd.cAlphaBits; + _glfwWin.depthBits = pfd.cDepthBits; + _glfwWin.stencilBits = pfd.cStencilBits; + _glfwWin.accumRedBits = pfd.cAccumRedBits; + _glfwWin.accumGreenBits = pfd.cAccumGreenBits; + _glfwWin.accumBlueBits = pfd.cAccumBlueBits; + _glfwWin.accumAlphaBits = pfd.cAccumAlphaBits; + _glfwWin.auxBuffers = pfd.cAuxBuffers; + _glfwWin.stereo = (pfd.dwFlags & PFD_STEREO) ? GL_TRUE : GL_FALSE; + + // If we don't have WGL_ARB_pixel_format then we can't have created a + // multisampling context, so it's safe to hardcode zero here + _glfwWin.samples = 0; + } + + // Get refresh rate + mode = _glfwWin.fullscreen ? _glfwWin.modeID : ENUM_CURRENT_SETTINGS; + dm.dmSize = sizeof( DEVMODE ); + + if( EnumDisplaySettings( NULL, mode, &dm ) ) + { + _glfwWin.refreshRate = dm.dmDisplayFrequency; + if( _glfwWin.refreshRate <= 1 ) + { + _glfwWin.refreshRate = 0; + } + } + else + { + _glfwWin.refreshRate = 0; + } +} + + +//======================================================================== +// Poll for new window and input events +//======================================================================== + +void _glfwPlatformPollEvents( void ) +{ + MSG msg; + int winclosed = GL_FALSE; + + // Flag: mouse was not moved (will be changed by _glfwGetNextEvent if + // there was a mouse move event) + _glfwInput.MouseMoved = GL_FALSE; + if( _glfwWin.mouseLock ) + { + _glfwInput.OldMouseX = _glfwWin.width/2; + _glfwInput.OldMouseY = _glfwWin.height/2; + } + else + { + _glfwInput.OldMouseX = _glfwInput.MousePosX; + _glfwInput.OldMouseY = _glfwInput.MousePosY; + } + + // Check for new window messages + while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) + { + switch( msg.message ) + { + // QUIT-message (from close window)? + case WM_QUIT: + winclosed = GL_TRUE; + break; + + // Ok, send it to the window message handler + default: + DispatchMessage( &msg ); + break; + } + } + + // 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. + // Caveat: Does not work under Win 9x/ME. + if( _glfwLibrary.Sys.winVer >= _GLFW_WIN_NT4 ) + { + int lshift_down, rshift_down; + + // Get current state of left and right shift keys + lshift_down = (GetAsyncKeyState( VK_LSHIFT ) >> 15) & 1; + rshift_down = (GetAsyncKeyState( VK_RSHIFT ) >> 15) & 1; + + // See if this differs from our belief of what has happened + // (we only have to check for lost key up events) + if( !lshift_down && _glfwInput.Key[ GLFW_KEY_LSHIFT ] == 1 ) + { + _glfwInputKey( GLFW_KEY_LSHIFT, GLFW_RELEASE ); + } + if( !rshift_down && _glfwInput.Key[ GLFW_KEY_RSHIFT ] == 1 ) + { + _glfwInputKey( GLFW_KEY_RSHIFT, GLFW_RELEASE ); + } + } + + // Did we have mouse movement in locked cursor mode? + if( _glfwInput.MouseMoved && _glfwWin.mouseLock ) + { + _glfwPlatformSetMouseCursorPos( _glfwWin.width / 2, + _glfwWin.height / 2 ); + } + + // Was there a window close request? + if( winclosed && _glfwWin.windowCloseCallback ) + { + // Check if the program wants us to close the window + winclosed = _glfwWin.windowCloseCallback(); + } + if( winclosed ) + { + glfwCloseWindow(); + } +} + + +//======================================================================== +// _glfwPlatformWaitEvents() - Wait for new window and input events +//======================================================================== + +void _glfwPlatformWaitEvents( void ) +{ + WaitMessage(); + + _glfwPlatformPollEvents(); +} + + +//======================================================================== +// Hide mouse cursor (lock it) +//======================================================================== + +void _glfwPlatformHideMouseCursor( void ) +{ + RECT ClipWindowRect; + + ShowCursor( FALSE ); + + // Clip cursor to the window + if( GetWindowRect( _glfwWin.window, &ClipWindowRect ) ) + { + ClipCursor( &ClipWindowRect ); + } + + // Capture cursor to user window + SetCapture( _glfwWin.window ); +} + + +//======================================================================== +// Show mouse cursor (unlock it) +//======================================================================== + +void _glfwPlatformShowMouseCursor( void ) +{ + // Un-capture cursor + ReleaseCapture(); + + // Release the cursor from the window + ClipCursor( NULL ); + + ShowCursor( TRUE ); +} + + +//======================================================================== +// Set physical mouse cursor position +//======================================================================== + +void _glfwPlatformSetMouseCursorPos( int x, int y ) +{ + POINT pos; + + // Convert client coordinates to screen coordinates + pos.x = x; + pos.y = y; + ClientToScreen( _glfwWin.window, &pos ); + + SetCursorPos( pos.x, pos.y ); +} + diff --git a/lib/window.c b/lib/window.c new file mode 100644 index 00000000..cc6adcd9 --- /dev/null +++ b/lib/window.c @@ -0,0 +1,1030 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: Any +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + +#include + + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +static int Max(int a, int b) +{ + return (a > b) ? a : b; +} + +//======================================================================== +// Clear all open window hints +//======================================================================== + +void _glfwClearWindowHints( void ) +{ + memset( &_glfwLibrary.hints, 0, sizeof( _glfwLibrary.hints ) ); + _glfwLibrary.hints.glMajor = 1; +} + + +//======================================================================== +// Handle the input tracking part of window deactivation +//======================================================================== + +void _glfwInputDeactivation( void ) +{ + int i; + + // Release all keyboard keys + for( i = 0; i <= GLFW_KEY_LAST; i ++ ) + { + if( _glfwInput.Key[ i ] == GLFW_PRESS ) + { + _glfwInputKey( i, GLFW_RELEASE ); + } + } + + // Release all mouse buttons + for( i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i ++ ) + { + if( _glfwInput.MouseButton[ i ] == GLFW_PRESS ) + { + _glfwInputMouseClick( i, GLFW_RELEASE ); + } + } +} + + +//======================================================================== +// _glfwClearInput() - Clear all input state +//======================================================================== + +void _glfwClearInput( void ) +{ + int i; + + // Release all keyboard keys + for( i = 0; i <= GLFW_KEY_LAST; i ++ ) + { + _glfwInput.Key[ i ] = GLFW_RELEASE; + } + + // Clear last character + _glfwInput.LastChar = 0; + + // Release all mouse buttons + for( i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i ++ ) + { + _glfwInput.MouseButton[ i ] = GLFW_RELEASE; + } + + // Set mouse position to (0,0) + _glfwInput.MousePosX = 0; + _glfwInput.MousePosY = 0; + + // Set mouse wheel position to 0 + _glfwInput.WheelPos = 0; + + // The default is to use non sticky keys and mouse buttons + _glfwInput.StickyKeys = GL_FALSE; + _glfwInput.StickyMouseButtons = GL_FALSE; + + // The default is to disable key repeat + _glfwInput.KeyRepeat = GL_FALSE; +} + + +//======================================================================== +// _glfwInputKey() - Register keyboard activity +//======================================================================== + +void _glfwInputKey( int key, int action ) +{ + int keyrepeat = 0; + + if( key < 0 || key > GLFW_KEY_LAST ) + { + return; + } + + // Are we trying to release an already released key? + if( action == GLFW_RELEASE && _glfwInput.Key[ key ] != GLFW_PRESS ) + { + return; + } + + // Register key action + if( action == GLFW_RELEASE && _glfwInput.StickyKeys ) + { + _glfwInput.Key[ key ] = GLFW_STICK; + } + else + { + keyrepeat = (_glfwInput.Key[ key ] == GLFW_PRESS) && + (action == GLFW_PRESS); + _glfwInput.Key[ key ] = (char) action; + } + + // Call user callback function + if( _glfwWin.keyCallback && (_glfwInput.KeyRepeat || !keyrepeat) ) + { + _glfwWin.keyCallback( key, action ); + } +} + + +//======================================================================== +// Register (keyboard) character activity +//======================================================================== + +void _glfwInputChar( int character, int action ) +{ + int keyrepeat = 0; + + // Valid Unicode (ISO 10646) character? + if( !( (character >= 32 && character <= 126) || character >= 160 ) ) + { + return; + } + + // Is this a key repeat? + if( action == GLFW_PRESS && _glfwInput.LastChar == character ) + { + keyrepeat = 1; + } + + // Store this character as last character (or clear it, if released) + if( action == GLFW_PRESS ) + { + _glfwInput.LastChar = character; + } + else + { + _glfwInput.LastChar = 0; + } + + if( action != GLFW_PRESS ) + { + // This intentionally breaks release notifications for Unicode + // characters, partly to see if anyone cares but mostly because it's + // a nonsensical concept to begin with + // + // It will remain broken either until its removal in the 3.0 API or + // until someone explains, in a way that makes sense to people outside + // the US and Scandinavia, what "Unicode character up" actually means + // + // If what you want is "physical key up" then you should be using the + // key functions and/or the key callback, NOT the Unicode input + // + // However, if your particular application uses this misfeature for... + // something, you can re-enable it by removing this if-statement + return; + } + + if( _glfwWin.charCallback && (_glfwInput.KeyRepeat || !keyrepeat) ) + { + _glfwWin.charCallback( character, action ); + } +} + + +//======================================================================== +// _glfwInputMouseClick() - Register mouse button clicks +//======================================================================== + +void _glfwInputMouseClick( int button, int action ) +{ + if( button >= 0 && button <= GLFW_MOUSE_BUTTON_LAST ) + { + // Register mouse button action + if( action == GLFW_RELEASE && _glfwInput.StickyMouseButtons ) + { + _glfwInput.MouseButton[ button ] = GLFW_STICK; + } + else + { + _glfwInput.MouseButton[ button ] = (char) action; + } + + // Call user callback function + if( _glfwWin.mouseButtonCallback ) + { + _glfwWin.mouseButtonCallback( button, action ); + } + } +} + + +//======================================================================== +// Return the available framebuffer config closest to the desired values +// This is based on the manual GLX Visual selection from 2.6 +//======================================================================== + +const _GLFWfbconfig *_glfwChooseFBConfig( const _GLFWfbconfig *desired, + const _GLFWfbconfig *alternatives, + unsigned int count ) +{ + unsigned int i; + unsigned int missing, leastMissing = UINT_MAX; + unsigned int colorDiff, leastColorDiff = UINT_MAX; + unsigned int extraDiff, leastExtraDiff = UINT_MAX; + GLboolean desiresColor = GL_FALSE; + const _GLFWfbconfig *current; + const _GLFWfbconfig *closest = NULL; + + // Cache some long-winded preferences + + if( desired->redBits || desired->greenBits || desired->blueBits || + desired->alphaBits ) + { + desiresColor = GL_TRUE; + } + + for( i = 0; i < count; i++ ) + { + current = alternatives + i; + + if( desired->stereo > 0 && current->stereo == 0 ) + { + // Stereo is a hard constraint + continue; + } + + // Count number of missing buffers + { + missing = 0; + + if( desired->alphaBits > 0 && current->alphaBits == 0 ) + { + missing++; + } + + if( desired->depthBits > 0 && current->depthBits == 0 ) + { + missing++; + } + + if( desired->stencilBits > 0 && current->stencilBits == 0 ) + { + missing++; + } + + if( desired->auxBuffers > 0 && current->auxBuffers < desired->auxBuffers ) + { + missing += desired->auxBuffers - current->auxBuffers; + } + + if( desired->samples > 0 && current->samples == 0 ) + { + // Technically, several multisampling buffers could be + // involved, but that's a lower level implementation detail and + // not important to us here, so we count them as one + missing++; + } + } + + // These polynomials make many small channel size differences matter + // less than one large channel size difference + + // Calculate color channel size difference value + { + colorDiff = 0; + + if ( desired->redBits > 0 ) + { + colorDiff += ( desired->redBits - current->redBits ) * + ( desired->redBits - current->redBits ); + } + + if ( desired->greenBits > 0 ) + { + colorDiff += ( desired->greenBits - current->greenBits ) * + ( desired->greenBits - current->greenBits ); + } + + if ( desired->blueBits > 0 ) + { + colorDiff += ( desired->blueBits - current->blueBits ) * + ( desired->blueBits - current->blueBits ); + } + } + + // Calculate non-color channel size difference value + { + extraDiff = 0; + + if( desired->alphaBits > 0 ) + { + extraDiff += ( desired->alphaBits - current->alphaBits ) * + ( desired->alphaBits - current->alphaBits ); + } + + if( desired->depthBits > 0 ) + { + extraDiff += ( desired->depthBits - current->depthBits ) * + ( desired->depthBits - current->depthBits ); + } + + if( desired->stencilBits > 0 ) + { + extraDiff += ( desired->stencilBits - current->stencilBits ) * + ( desired->stencilBits - current->stencilBits ); + } + + if( desired->accumRedBits > 0 ) + { + extraDiff += ( desired->accumRedBits - current->accumRedBits ) * + ( desired->accumRedBits - current->accumRedBits ); + } + + if( desired->accumGreenBits > 0 ) + { + extraDiff += ( desired->accumGreenBits - current->accumGreenBits ) * + ( desired->accumGreenBits - current->accumGreenBits ); + } + + if( desired->accumBlueBits > 0 ) + { + extraDiff += ( desired->accumBlueBits - current->accumBlueBits ) * + ( desired->accumBlueBits - current->accumBlueBits ); + } + + if( desired->accumAlphaBits > 0 ) + { + extraDiff += ( desired->accumAlphaBits - current->accumAlphaBits ) * + ( desired->accumAlphaBits - current->accumAlphaBits ); + } + + if( desired->samples > 0 ) + { + extraDiff += ( desired->samples - current->samples ) * + ( desired->samples - current->samples ); + } + } + + // Figure out if the current one is better than the best one found so far + + if( missing < leastMissing ) + { + closest = current; + } + else if( missing == leastMissing ) + { + if( desiresColor ) + { + if( ( colorDiff < leastColorDiff ) || + ( colorDiff == leastColorDiff && extraDiff < leastExtraDiff ) ) + { + closest = current; + } + } + else + { + if( ( extraDiff < leastExtraDiff ) || + ( extraDiff == leastExtraDiff && colorDiff < leastColorDiff ) ) + { + closest = current; + } + } + } + + if( current == closest ) + { + leastMissing = missing; + leastColorDiff = colorDiff; + leastExtraDiff = extraDiff; + } + } + + return closest; +} + + +//************************************************************************ +//**** GLFW user functions **** +//************************************************************************ + +//======================================================================== +// Create the GLFW window and its associated context +//======================================================================== + +GLFWAPI int glfwOpenWindow( int width, int height, + int redbits, int greenbits, int bluebits, int alphabits, + int depthbits, int stencilbits, int mode ) +{ + _GLFWfbconfig fbconfig; + _GLFWwndconfig wndconfig; + + // Is GLFW initialized? + if( !_glfwInitialized || _glfwWin.opened ) + { + return GL_FALSE; + } + + // Set up desired framebuffer config + fbconfig.redBits = Max( redbits, 0 ); + fbconfig.greenBits = Max( greenbits, 0 ); + fbconfig.blueBits = Max( bluebits, 0 ); + fbconfig.alphaBits = Max( alphabits, 0 ); + fbconfig.depthBits = Max( depthbits, 0 ); + fbconfig.stencilBits = Max( stencilbits, 0 ); + fbconfig.accumRedBits = Max( _glfwLibrary.hints.accumRedBits, 0 ); + fbconfig.accumGreenBits = Max( _glfwLibrary.hints.accumGreenBits, 0 ); + fbconfig.accumBlueBits = Max( _glfwLibrary.hints.accumBlueBits, 0 ); + fbconfig.accumAlphaBits = Max( _glfwLibrary.hints.accumAlphaBits, 0 ); + fbconfig.auxBuffers = Max( _glfwLibrary.hints.auxBuffers, 0 ); + fbconfig.stereo = _glfwLibrary.hints.stereo ? GL_TRUE : GL_FALSE; + fbconfig.samples = Max( _glfwLibrary.hints.samples, 0 ); + + // Set up desired window config + wndconfig.mode = mode; + wndconfig.refreshRate = Max( _glfwLibrary.hints.refreshRate, 0 ); + wndconfig.windowNoResize = _glfwLibrary.hints.windowNoResize ? GL_TRUE : GL_FALSE; + wndconfig.glMajor = Max( _glfwLibrary.hints.glMajor, 1 ); + wndconfig.glMinor = Max( _glfwLibrary.hints.glMinor, 0 ); + wndconfig.glForward = _glfwLibrary.hints.glForward ? GL_TRUE : GL_FALSE; + wndconfig.glDebug = _glfwLibrary.hints.glDebug ? GL_TRUE : GL_FALSE; + wndconfig.glProfile = _glfwLibrary.hints.glProfile; + + if( wndconfig.glMajor == 1 && wndconfig.glMinor > 5 ) + { + // OpenGL 1.x series ended with version 1.5 + return GL_FALSE; + } + else if( wndconfig.glMajor == 2 && wndconfig.glMinor > 1 ) + { + // OpenGL 2.x series ended with version 2.1 + return GL_FALSE; + } + else if( wndconfig.glMajor == 3 && wndconfig.glMinor > 3 ) + { + // OpenGL 3.x series ended with version 3.3 + return GL_FALSE; + } + else + { + // For now, let everything else through + } + + if( wndconfig.glProfile && + ( wndconfig.glMajor < 3 || ( wndconfig.glMajor == 3 && wndconfig.glMinor < 2 ) ) ) + { + // Context profiles are only defined for OpenGL version 3.2 and above + return GL_FALSE; + } + + if( wndconfig.glForward && wndconfig.glMajor < 3 ) + { + // Forward-compatible contexts are only defined for OpenGL version 3.0 and above + return GL_FALSE; + } + + // Clear for next open call + _glfwClearWindowHints(); + + // Check input arguments + if( mode != GLFW_WINDOW && mode != GLFW_FULLSCREEN ) + { + return GL_FALSE; + } + + // Clear GLFW window state + _glfwWin.active = GL_TRUE; + _glfwWin.iconified = GL_FALSE; + _glfwWin.mouseLock = GL_FALSE; + _glfwWin.autoPollEvents = GL_TRUE; + _glfwClearInput(); + + // Unregister all callback functions + _glfwWin.windowSizeCallback = NULL; + _glfwWin.windowCloseCallback = NULL; + _glfwWin.windowRefreshCallback = NULL; + _glfwWin.keyCallback = NULL; + _glfwWin.charCallback = NULL; + _glfwWin.mousePosCallback = NULL; + _glfwWin.mouseButtonCallback = NULL; + _glfwWin.mouseWheelCallback = NULL; + + // Check width & height + if( width > 0 && height <= 0 ) + { + // Set the window aspect ratio to 4:3 + height = (width * 3) / 4; + } + else if( width <= 0 && height > 0 ) + { + // Set the window aspect ratio to 4:3 + width = (height * 4) / 3; + } + else if( width <= 0 && height <= 0 ) + { + // Default window size + width = 640; + height = 480; + } + + // Remember window settings + _glfwWin.width = width; + _glfwWin.height = height; + _glfwWin.fullscreen = (mode == GLFW_FULLSCREEN ? GL_TRUE : GL_FALSE); + + // Platform specific window opening routine + if( !_glfwPlatformOpenWindow( width, height, &wndconfig, &fbconfig ) ) + { + return GL_FALSE; + } + + // Flag that window is now opened + _glfwWin.opened = GL_TRUE; + + // Get window parameters (such as color buffer bits etc) + _glfwPlatformRefreshWindowParams(); + + // Get OpenGL version + _glfwParseGLVersion( &_glfwWin.glMajor, &_glfwWin.glMinor, + &_glfwWin.glRevision ); + + if( _glfwWin.glMajor < wndconfig.glMajor || + ( _glfwWin.glMajor == wndconfig.glMajor && + _glfwWin.glMinor < wndconfig.glMinor ) ) + { + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + // Do we have non-power-of-two textures (added to core in version 2.0)? + _glfwWin.has_GL_ARB_texture_non_power_of_two = + ( _glfwWin.glMajor >= 2 ) || + glfwExtensionSupported( "GL_ARB_texture_non_power_of_two" ); + + // Do we have automatic mipmap generation (added to core in version 1.4)? + _glfwWin.has_GL_SGIS_generate_mipmap = + ( _glfwWin.glMajor >= 2 ) || ( _glfwWin.glMinor >= 4 ) || + glfwExtensionSupported( "GL_SGIS_generate_mipmap" ); + + if( _glfwWin.glMajor > 2 ) + { + _glfwWin.GetStringi = (PFNGLGETSTRINGIPROC) glfwGetProcAddress( "glGetStringi" ); + if( !_glfwWin.GetStringi ) + { + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + } + + // If full-screen mode was requested, disable mouse cursor + if( mode == GLFW_FULLSCREEN ) + { + glfwDisable( GLFW_MOUSE_CURSOR ); + } + + // Start by clearing the front buffer to black (avoid ugly desktop + // remains in our OpenGL window) + glClear( GL_COLOR_BUFFER_BIT ); + _glfwPlatformSwapBuffers(); + + return GL_TRUE; +} + + +//======================================================================== +// Set hints for opening the window +//======================================================================== + +GLFWAPI void glfwOpenWindowHint( int target, int hint ) +{ + // Is GLFW initialized? + if( !_glfwInitialized ) + { + return; + } + + switch( target ) + { + case GLFW_REFRESH_RATE: + _glfwLibrary.hints.refreshRate = hint; + break; + case GLFW_ACCUM_RED_BITS: + _glfwLibrary.hints.accumRedBits = hint; + break; + case GLFW_ACCUM_GREEN_BITS: + _glfwLibrary.hints.accumGreenBits = hint; + break; + case GLFW_ACCUM_BLUE_BITS: + _glfwLibrary.hints.accumBlueBits = hint; + break; + case GLFW_ACCUM_ALPHA_BITS: + _glfwLibrary.hints.accumAlphaBits = hint; + break; + case GLFW_AUX_BUFFERS: + _glfwLibrary.hints.auxBuffers = hint; + break; + case GLFW_STEREO: + _glfwLibrary.hints.stereo = hint; + break; + case GLFW_WINDOW_NO_RESIZE: + _glfwLibrary.hints.windowNoResize = hint; + break; + case GLFW_FSAA_SAMPLES: + _glfwLibrary.hints.samples = hint; + break; + case GLFW_OPENGL_VERSION_MAJOR: + _glfwLibrary.hints.glMajor = hint; + break; + case GLFW_OPENGL_VERSION_MINOR: + _glfwLibrary.hints.glMinor = hint; + break; + case GLFW_OPENGL_FORWARD_COMPAT: + _glfwLibrary.hints.glForward = hint; + break; + case GLFW_OPENGL_DEBUG_CONTEXT: + _glfwLibrary.hints.glDebug = hint; + break; + case GLFW_OPENGL_PROFILE: + _glfwLibrary.hints.glProfile = hint; + break; + default: + break; + } +} + + +//======================================================================== +// Properly kill the window / video display +//======================================================================== + +GLFWAPI void glfwCloseWindow( void ) +{ + if( !_glfwInitialized ) + { + return; + } + + // Show mouse pointer again (if hidden) + glfwEnable( GLFW_MOUSE_CURSOR ); + + _glfwPlatformCloseWindow(); + + memset( &_glfwWin, 0, sizeof(_glfwWin) ); + _glfwWin.opened = GL_FALSE; +} + + +//======================================================================== +// glfwSetWindowTitle() - Set the window title +//======================================================================== + +GLFWAPI void glfwSetWindowTitle( const char *title ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Set window title + _glfwPlatformSetWindowTitle( title ); +} + + +//======================================================================== +// glfwGetWindowSize() - Get the window size +//======================================================================== + +GLFWAPI void glfwGetWindowSize( int *width, int *height ) +{ + if( width != NULL ) + { + *width = _glfwWin.width; + } + if( height != NULL ) + { + *height = _glfwWin.height; + } +} + + +//======================================================================== +// glfwSetWindowSize() - Set the window size +//======================================================================== + +GLFWAPI void glfwSetWindowSize( int width, int height ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened || _glfwWin.iconified ) + { + return; + } + + // Don't do anything if the window size did not change + if( width == _glfwWin.width && height == _glfwWin.height ) + { + return; + } + + // Change window size + _glfwPlatformSetWindowSize( width, height ); + + // Refresh window parameters (may have changed due to changed video + // modes) + _glfwPlatformRefreshWindowParams(); +} + + +//======================================================================== +// glfwSetWindowPos() - Set the window position +//======================================================================== + +GLFWAPI void glfwSetWindowPos( int x, int y ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened || _glfwWin.fullscreen || + _glfwWin.iconified ) + { + return; + } + + // Set window position + _glfwPlatformSetWindowPos( x, y ); +} + + +//======================================================================== +// glfwIconfyWindow() - Window iconification +//======================================================================== + +GLFWAPI void glfwIconifyWindow( void ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened || _glfwWin.iconified ) + { + return; + } + + // Iconify window + _glfwPlatformIconifyWindow(); +} + + +//======================================================================== +// glfwRestoreWindow() - Window un-iconification +//======================================================================== + +GLFWAPI void glfwRestoreWindow( void ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened || !_glfwWin.iconified ) + { + return; + } + + // Restore iconified window + _glfwPlatformRestoreWindow(); + + // Refresh window parameters + _glfwPlatformRefreshWindowParams(); +} + + +//======================================================================== +// Swap buffers (double-buffering) and poll any new events +//======================================================================== + +GLFWAPI void glfwSwapBuffers( void ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Update display-buffer + if( _glfwWin.opened ) + { + _glfwPlatformSwapBuffers(); + } + + // Check for window messages + if( _glfwWin.autoPollEvents ) + { + glfwPollEvents(); + } +} + + +//======================================================================== +// glfwSwapInterval() - Set double buffering swap interval (0 = vsync off) +//======================================================================== + +GLFWAPI void glfwSwapInterval( int interval ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Set double buffering swap interval + _glfwPlatformSwapInterval( interval ); +} + + +//======================================================================== +// glfwGetWindowParam() - Get window parameter +//======================================================================== + +GLFWAPI int glfwGetWindowParam( int param ) +{ + // Is GLFW initialized? + if( !_glfwInitialized ) + { + return 0; + } + + // Is the window opened? + if( !_glfwWin.opened ) + { + if( param == GLFW_OPENED ) + { + return GL_FALSE; + } + return 0; + } + + // Window parameters + switch( param ) + { + case GLFW_OPENED: + return GL_TRUE; + case GLFW_ACTIVE: + return _glfwWin.active; + case GLFW_ICONIFIED: + return _glfwWin.iconified; + case GLFW_ACCELERATED: + return _glfwWin.accelerated; + case GLFW_RED_BITS: + return _glfwWin.redBits; + case GLFW_GREEN_BITS: + return _glfwWin.greenBits; + case GLFW_BLUE_BITS: + return _glfwWin.blueBits; + case GLFW_ALPHA_BITS: + return _glfwWin.alphaBits; + case GLFW_DEPTH_BITS: + return _glfwWin.depthBits; + case GLFW_STENCIL_BITS: + return _glfwWin.stencilBits; + case GLFW_ACCUM_RED_BITS: + return _glfwWin.accumRedBits; + case GLFW_ACCUM_GREEN_BITS: + return _glfwWin.accumGreenBits; + case GLFW_ACCUM_BLUE_BITS: + return _glfwWin.accumBlueBits; + case GLFW_ACCUM_ALPHA_BITS: + return _glfwWin.accumAlphaBits; + case GLFW_AUX_BUFFERS: + return _glfwWin.auxBuffers; + case GLFW_STEREO: + return _glfwWin.stereo; + case GLFW_REFRESH_RATE: + return _glfwWin.refreshRate; + case GLFW_WINDOW_NO_RESIZE: + return _glfwWin.windowNoResize; + case GLFW_FSAA_SAMPLES: + return _glfwWin.samples; + case GLFW_OPENGL_VERSION_MAJOR: + return _glfwWin.glMajor; + case GLFW_OPENGL_VERSION_MINOR: + return _glfwWin.glMinor; + case GLFW_OPENGL_FORWARD_COMPAT: + return _glfwWin.glForward; + case GLFW_OPENGL_DEBUG_CONTEXT: + return _glfwWin.glDebug; + case GLFW_OPENGL_PROFILE: + return _glfwWin.glProfile; + default: + return 0; + } +} + + +//======================================================================== +// glfwSetWindowSizeCallback() - Set callback function for window size +// changes +//======================================================================== + +GLFWAPI void glfwSetWindowSizeCallback( GLFWwindowsizefun cbfun ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Set callback function + _glfwWin.windowSizeCallback = cbfun; + + // Call the callback function to let the application know the current + // window size + if( cbfun ) + { + cbfun( _glfwWin.width, _glfwWin.height ); + } +} + +//======================================================================== +// glfwSetWindowCloseCallback() - Set callback function for window close +// events +//======================================================================== + +GLFWAPI void glfwSetWindowCloseCallback( GLFWwindowclosefun cbfun ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Set callback function + _glfwWin.windowCloseCallback = cbfun; +} + + +//======================================================================== +// glfwSetWindowRefreshCallback() - Set callback function for window +// refresh events +//======================================================================== + +GLFWAPI void glfwSetWindowRefreshCallback( GLFWwindowrefreshfun cbfun ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Set callback function + _glfwWin.windowRefreshCallback = cbfun; +} + + +//======================================================================== +// glfwPollEvents() - Poll for new window and input events +//======================================================================== + +GLFWAPI void glfwPollEvents( void ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Poll for new events + _glfwPlatformPollEvents(); +} + + +//======================================================================== +// glfwWaitEvents() - Wait for new window and input events +//======================================================================== + +GLFWAPI void glfwWaitEvents( void ) +{ + // Is GLFW initialized? + if( !_glfwInitialized || !_glfwWin.opened ) + { + return; + } + + // Poll for new events + _glfwPlatformWaitEvents(); +} + diff --git a/lib/x11/CMakeLists.txt b/lib/x11/CMakeLists.txt new file mode 100644 index 00000000..fde2d773 --- /dev/null +++ b/lib/x11/CMakeLists.txt @@ -0,0 +1,36 @@ +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/x11_config.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/x11_config.h @ONLY) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/libglfw.pc.cmake + ${CMAKE_CURRENT_BINARY_DIR}/libglfw.pc @ONLY) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${GLFW_INCLUDE_DIR}) + +set(libglfw_SOURCES + ${common_SOURCES} + x11_enable.c + x11_fullscreen.c + x11_glext.c + x11_init.c + x11_joystick.c + x11_keysym2unicode.c + x11_time.c + x11_window.c) + +add_library(libglfwStatic STATIC ${libglfw_SOURCES}) +add_library(libglfwShared SHARED ${libglfw_SOURCES}) +target_link_libraries(libglfwShared ${GLFW_LIBRARIES}) +set_target_properties(libglfwStatic libglfwShared PROPERTIES + CLEAN_DIRECT_OUTPUT 1 + OUTPUT_NAME glfw +) + +install(TARGETS libglfwStatic libglfwShared DESTINATION lib) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libglfw.pc DESTINATION lib/pkgconfig) + diff --git a/lib/x11/libglfw.pc.cmake b/lib/x11/libglfw.pc.cmake new file mode 100644 index 00000000..adc455f8 --- /dev/null +++ b/lib/x11/libglfw.pc.cmake @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: GLFW +Description: A portable framework for OpenGL development +Version: 2.7 +URL: http://glfw.sourceforge.net/ +Libs: -L${libdir} -lglfw @GLFW_LIBRARIES@ +Cflags: -I${includedir} diff --git a/lib/x11/platform.h b/lib/x11/platform.h new file mode 100644 index 00000000..831f16f1 --- /dev/null +++ b/lib/x11/platform.h @@ -0,0 +1,433 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: X11/GLX +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 _platform_h_ +#define _platform_h_ + + +// This is the X11 version of GLFW +#define _GLFW_X11 + + +// Include files +#include +#include +#include +#include +#include +#include +#include + +#include "../../include/GL/glfw.h" +#include "x11_config.h" + +// We need declarations for GLX version 1.3 or above even if the server doesn't +// support version 1.3 +#ifndef GLX_VERSION_1_3 + #error "GLX header version 1.3 or above is required" +#endif + +#if defined( _GLFW_HAS_XF86VIDMODE ) && defined( _GLFW_HAS_XRANDR ) + #error "Xf86VidMode and RandR extensions cannot both be enabled" +#endif + +// With XFree86, we can use the XF86VidMode extension +#if defined( _GLFW_HAS_XF86VIDMODE ) + #include +#endif + +#if defined( _GLFW_HAS_XRANDR ) + #include +#endif + +// Do we have support for dlopen/dlsym? +#if defined( _GLFW_HAS_DLOPEN ) + #include +#endif + +// We support two different ways for getting the number of processors in +// the system: sysconf (POSIX) and sysctl (BSD?) +#if defined( _GLFW_HAS_SYSCONF ) + + // Use a single constant for querying number of online processors using + // the sysconf function (e.g. SGI defines _SC_NPROC_ONLN instead of + // _SC_NPROCESSORS_ONLN) + #ifndef _SC_NPROCESSORS_ONLN + #ifdef _SC_NPROC_ONLN + #define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN + #else + #error POSIX constant _SC_NPROCESSORS_ONLN not defined! + #endif + #endif + + // Macro for querying the number of processors + #define _glfw_numprocessors(n) n=(int)sysconf(_SC_NPROCESSORS_ONLN) + +#elif defined( _GLFW_HAS_SYSCTL ) + + #include + #include + + // Macro for querying the number of processors + #define _glfw_numprocessors(n) { \ + int mib[2], ncpu; \ + size_t len = 1; \ + mib[0] = CTL_HW; \ + mib[1] = HW_NCPU; \ + n = 1; \ + if( sysctl( mib, 2, &ncpu, &len, NULL, 0 ) != -1 ) \ + { \ + if( len > 0 ) \ + { \ + n = ncpu; \ + } \ + } \ + } + +#else + + // If neither sysconf nor sysctl is supported, assume single processor + // system + #define _glfw_numprocessors(n) n=1 + +#endif + +// Pointer length integer +// One day, this will most likely move into glfw.h +typedef intptr_t GLFWintptr; + + +#ifndef GLX_SGI_swap_control + +// Function signature for GLX_SGI_swap_control +typedef int ( * PFNGLXSWAPINTERVALSGIPROC) (int interval); + +#endif /*GLX_SGI_swap_control*/ + + +#ifndef GLX_SGIX_fbconfig + +/* Type definitions for GLX_SGIX_fbconfig */ +typedef XID GLXFBConfigIDSGIX; +typedef struct __GLXFBConfigRec *GLXFBConfigSGIX; + +/* Function signatures for GLX_SGIX_fbconfig */ +typedef int ( * PFNGLXGETFBCONFIGATTRIBSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, int attribute, int *value); +typedef GLXFBConfigSGIX * ( * PFNGLXCHOOSEFBCONFIGSGIXPROC) (Display *dpy, int screen, int *attrib_list, int *nelements); +typedef GLXContext ( * PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, Bool direct); +typedef XVisualInfo * ( * PFNGLXGETVISUALFROMFBCONFIGSGIXPROC) (Display *dpy, GLXFBConfigSGIX config); + +/* Tokens for GLX_SGIX_fbconfig */ +#define GLX_WINDOW_BIT_SGIX 0x00000001 +#define GLX_PIXMAP_BIT_SGIX 0x00000002 +#define GLX_RGBA_BIT_SGIX 0x00000001 +#define GLX_COLOR_INDEX_BIT_SGIX 0x00000002 +#define GLX_DRAWABLE_TYPE_SGIX 0x8010 +#define GLX_RENDER_TYPE_SGIX 0x8011 +#define GLX_X_RENDERABLE_SGIX 0x8012 +#define GLX_FBCONFIG_ID_SGIX 0x8013 +#define GLX_RGBA_TYPE_SGIX 0x8014 +#define GLX_COLOR_INDEX_TYPE_SGIX 0x8015 +#define GLX_SCREEN_EXT 0x800C + +#endif /*GLX_SGIX_fbconfig*/ + + +#ifndef GLX_ARB_create_context + +/* Tokens for glXCreateContextAttribsARB attributes */ +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 + +/* Bits for WGL_CONTEXT_FLAGS_ARB */ +#define GLX_CONTEXT_DEBUG_BIT_ARB 0x0001 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 + +/* Prototype for glXCreateContextAttribs */ +typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)( Display *display, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); + +#endif /*GLX_ARB_create_context*/ + + +#ifndef GLX_ARB_create_context_profile + +/* Tokens for glXCreateContextAttribsARB attributes */ +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 + +/* BIts for GLX_CONTEXT_PROFILE_MASK_ARB */ +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 + +#endif /*GLX_ARB_create_context_profile*/ + + +#ifndef GL_VERSION_3_0 + +typedef const GLubyte * (APIENTRY *PFNGLGETSTRINGIPROC) (GLenum, GLuint); + +#endif /*GL_VERSION_3_0*/ + + + +//======================================================================== +// Global variables (GLFW internals) +//======================================================================== + +//------------------------------------------------------------------------ +// Window structure +//------------------------------------------------------------------------ +typedef struct _GLFWwin_struct _GLFWwin; + +struct _GLFWwin_struct { + +// ========= PLATFORM INDEPENDENT MANDATORY PART ========================= + + // User callback functions + GLFWwindowsizefun windowSizeCallback; + GLFWwindowclosefun windowCloseCallback; + GLFWwindowrefreshfun windowRefreshCallback; + GLFWmousebuttonfun mouseButtonCallback; + GLFWmouseposfun mousePosCallback; + GLFWmousewheelfun mouseWheelCallback; + GLFWkeyfun keyCallback; + GLFWcharfun charCallback; + + // User selected window settings + int fullscreen; // Fullscreen flag + int mouseLock; // Mouse-lock flag + int autoPollEvents; // Auto polling flag + int sysKeysDisabled; // System keys disabled flag + int windowNoResize; // Resize- and maximize gadgets disabled flag + int refreshRate; // Vertical monitor refresh rate + + // Window status & parameters + int opened; // Flag telling if window is opened or not + int active; // Application active flag + int iconified; // Window iconified flag + int width, height; // Window width and heigth + int accelerated; // GL_TRUE if window is HW accelerated + + // Framebuffer attributes + int redBits; + int greenBits; + int blueBits; + int alphaBits; + int depthBits; + int stencilBits; + int accumRedBits; + int accumGreenBits; + int accumBlueBits; + int accumAlphaBits; + int auxBuffers; + int stereo; + int samples; + + // OpenGL extensions and context attributes + int has_GL_SGIS_generate_mipmap; + int has_GL_ARB_texture_non_power_of_two; + int glMajor, glMinor, glRevision; + int glForward, glDebug, glProfile; + + PFNGLGETSTRINGIPROC GetStringi; + + +// ========= PLATFORM SPECIFIC PART ====================================== + + // Platform specific window resources + Colormap colormap; // Window colormap + Window window; // Window + Window root; // Root window for screen + int screen; // Screen ID + XVisualInfo *visual; // Visual for selected GLXFBConfig + GLXFBConfigID fbconfigID; // ID of selected GLXFBConfig + GLXContext context; // OpenGL rendering context + Atom wmDeleteWindow; // WM_DELETE_WINDOW atom + Atom wmPing; // _NET_WM_PING atom + Atom wmState; // _NET_WM_STATE atom + Atom wmStateFullscreen; // _NET_WM_STATE_FULLSCREEN atom + Atom wmActiveWindow; // _NET_ACTIVE_WINDOW atom + Cursor cursor; // Invisible cursor for hidden cursor + + // GLX extensions + PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI; + PFNGLXGETFBCONFIGATTRIBSGIXPROC GetFBConfigAttribSGIX; + PFNGLXCHOOSEFBCONFIGSGIXPROC ChooseFBConfigSGIX; + PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC CreateContextWithConfigSGIX; + PFNGLXGETVISUALFROMFBCONFIGSGIXPROC GetVisualFromFBConfigSGIX; + PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + GLboolean has_GLX_SGIX_fbconfig; + GLboolean has_GLX_SGI_swap_control; + GLboolean has_GLX_ARB_multisample; + GLboolean has_GLX_ARB_create_context; + GLboolean has_GLX_ARB_create_context_profile; + + // Various platform specific internal variables + GLboolean hasEWMH; // True if window manager supports EWMH + GLboolean overrideRedirect; // True if window is OverrideRedirect + GLboolean keyboardGrabbed; // True if keyboard is currently grabbed + GLboolean pointerGrabbed; // True if pointer is currently grabbed + GLboolean pointerHidden; // True if pointer is currently hidden + + // Screensaver data + struct { + int changed; + int timeout; + int interval; + int blanking; + int exposure; + } Saver; + + // Fullscreen data + struct { + int modeChanged; +#if defined( _GLFW_HAS_XF86VIDMODE ) + XF86VidModeModeInfo oldMode; +#endif +#if defined( _GLFW_HAS_XRANDR ) + SizeID oldSizeID; + int oldWidth; + int oldHeight; + Rotation oldRotation; +#endif + } FS; +}; + +GLFWGLOBAL _GLFWwin _glfwWin; + + +//------------------------------------------------------------------------ +// User input status (most of this should go in _GLFWwin) +//------------------------------------------------------------------------ +GLFWGLOBAL struct { + +// ========= PLATFORM INDEPENDENT MANDATORY PART ========================= + + // Mouse status + int MousePosX, MousePosY; + int WheelPos; + char MouseButton[ GLFW_MOUSE_BUTTON_LAST+1 ]; + + // Keyboard status + char Key[ GLFW_KEY_LAST+1 ]; + int LastChar; + + // User selected settings + int StickyKeys; + int StickyMouseButtons; + int KeyRepeat; + + +// ========= PLATFORM SPECIFIC PART ====================================== + + // Platform specific internal variables + int MouseMoved, CursorPosX, CursorPosY; + +} _glfwInput; + + +//------------------------------------------------------------------------ +// Library global data +//------------------------------------------------------------------------ +GLFWGLOBAL struct { + +// ========= PLATFORM INDEPENDENT MANDATORY PART ========================= + + // Window opening hints + _GLFWhints hints; + +// ========= PLATFORM SPECIFIC PART ====================================== + + Display *display; + + // Server-side GLX version + int glxMajor, glxMinor; + + struct { + int available; + int eventBase; + int errorBase; + } XF86VidMode; + + struct { + int available; + int eventBase; + int errorBase; + } XRandR; + + // Timer data + struct { + double resolution; + long long t0; + } Timer; + +#if defined(_GLFW_DLOPEN_LIBGL) + struct { + void *libGL; // dlopen handle for libGL.so + } Libs; +#endif +} _glfwLibrary; + + +//------------------------------------------------------------------------ +// Joystick information & state +//------------------------------------------------------------------------ +GLFWGLOBAL struct { + int Present; + int fd; + int NumAxes; + int NumButtons; + float *Axis; + unsigned char *Button; +} _glfwJoy[ GLFW_JOYSTICK_LAST + 1 ]; + + +//======================================================================== +// Prototypes for platform specific internal functions +//======================================================================== + +// Time +void _glfwInitTimer( void ); + +// Fullscreen support +int _glfwGetClosestVideoMode( int screen, int *width, int *height, int *rate ); +void _glfwSetVideoModeMODE( int screen, int mode, int rate ); +void _glfwSetVideoMode( int screen, int *width, int *height, int *rate ); +void _glfwRestoreVideoMode( void ); + +// Joystick input +void _glfwInitJoysticks( void ); +void _glfwTerminateJoysticks( void ); + +// Unicode support +long _glfwKeySym2Unicode( KeySym keysym ); + + +#endif // _platform_h_ diff --git a/lib/x11/x11_config.h.cmake b/lib/x11/x11_config.h.cmake new file mode 100644 index 00000000..d578d574 --- /dev/null +++ b/lib/x11/x11_config.h.cmake @@ -0,0 +1,14 @@ +/* Configure build time options of GLFW */ + +/* Define this to 1 if XRandR is available */ +#cmakedefine _GLFW_HAS_XRANDR 1 +/* Define this to 1 if Xf86VidMode is available */ +#cmakedefine _GLFW_HAS_XF86VIDMODE 1 + +/* Define this to 1 if glXGetProcAddress is available */ +#cmakedefine _GLFW_HAS_GLXGETPROCADDRESS 1 +/* Define this to 1 if glXGetProcAddressARB is available */ +#cmakedefine _GLFW_HAS_GLXGETPROCADDRESSARB 1 +/* Define this to 1 if glXGetProcAddressEXT is available */ +#cmakedefine _GLFW_HAS_GLXGETPROCADDRESSEXT 1 + diff --git a/lib/x11/x11_enable.c b/lib/x11/x11_enable.c new file mode 100644 index 00000000..88308d5a --- /dev/null +++ b/lib/x11/x11_enable.c @@ -0,0 +1,64 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: X11 (Unix) +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Enable system keys +//======================================================================== + +void _glfwPlatformEnableSystemKeys( void ) +{ + if( _glfwWin.keyboardGrabbed ) + { + XUngrabKeyboard( _glfwLibrary.display, CurrentTime ); + _glfwWin.keyboardGrabbed = GL_FALSE; + } +} + +//======================================================================== +// Disable system keys +//======================================================================== + +void _glfwPlatformDisableSystemKeys( void ) +{ + if( XGrabKeyboard( _glfwLibrary.display, _glfwWin.window, True, + GrabModeAsync, GrabModeAsync, CurrentTime ) == + GrabSuccess ) + { + _glfwWin.keyboardGrabbed = GL_TRUE; + } +} + diff --git a/lib/x11/x11_fullscreen.c b/lib/x11/x11_fullscreen.c new file mode 100644 index 00000000..f720214b --- /dev/null +++ b/lib/x11/x11_fullscreen.c @@ -0,0 +1,572 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: X11/GLX +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + +#include + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +//======================================================================== +// Convert BPP to RGB bits (based on "best guess") +//======================================================================== + +static void BPP2RGB( int bpp, int *r, int *g, int *b ) +{ + int delta; + + // Special case: BPP = 32 (I don't think this is necessary for X11??) + if( bpp == 32 ) + bpp = 24; + + // Convert "bits per pixel" to red, green & blue sizes + *r = *g = *b = bpp / 3; + delta = bpp - (*r * 3); + if( delta >= 1 ) + { + *g = *g + 1; + } + if( delta == 2 ) + { + *r = *r + 1; + } +} + + +//======================================================================== +// Finds the video mode closest in size to the specified desired size +//======================================================================== + +int _glfwGetClosestVideoMode( int screen, int *width, int *height, int *rate ) +{ +#if defined( _GLFW_HAS_XRANDR ) + int i, match, bestmatch; + int sizecount, bestsize; + int ratecount, bestrate; + short *ratelist; + XRRScreenConfiguration *sc; + XRRScreenSize *sizelist; + + if( _glfwLibrary.XRandR.available ) + { + sc = XRRGetScreenInfo( _glfwLibrary.display, + RootWindow( _glfwLibrary.display, screen ) ); + + sizelist = XRRConfigSizes( sc, &sizecount ); + + // Find the best matching mode + bestsize = -1; + bestmatch = INT_MAX; + for( i = 0; i < sizecount; i++ ) + { + match = (*width - sizelist[i].width) * + (*width - sizelist[i].width) + + (*height - sizelist[i].height) * + (*height - sizelist[i].height); + if( match < bestmatch ) + { + bestmatch = match; + bestsize = i; + } + } + + if( bestsize != -1 ) + { + // Report width & height of best matching mode + *width = sizelist[bestsize].width; + *height = sizelist[bestsize].height; + + if( *rate > 0 ) + { + ratelist = XRRConfigRates( sc, bestsize, &ratecount ); + + bestrate = -1; + bestmatch = INT_MAX; + for( i = 0; i < ratecount; i++ ) + { + match = abs( ratelist[i] - *rate ); + if( match < bestmatch ) + { + bestmatch = match; + bestrate = ratelist[i]; + } + } + + if( bestrate != -1 ) + { + *rate = bestrate; + } + } + } + + // Free modelist + XRRFreeScreenConfigInfo( sc ); + + if( bestsize != -1 ) + { + return bestsize; + } + } +#elif defined( _GLFW_HAS_XF86VIDMODE ) + XF86VidModeModeInfo **modelist; + int modecount, i, bestmode, bestmatch, match; + + // Use the XF86VidMode extension to control video resolution + if( _glfwLibrary.XF86VidMode.available ) + { + // Get a list of all available display modes + XF86VidModeGetAllModeLines( _glfwLibrary.display, screen, + &modecount, &modelist ); + + // Find the best matching mode + bestmode = -1; + bestmatch = INT_MAX; + for( i = 0; i < modecount; i++ ) + { + match = (*width - modelist[i]->hdisplay) * + (*width - modelist[i]->hdisplay) + + (*height - modelist[i]->vdisplay) * + (*height - modelist[i]->vdisplay); + if( match < bestmatch ) + { + bestmatch = match; + bestmode = i; + } + } + + if( bestmode != -1 ) + { + // Report width & height of best matching mode + *width = modelist[ bestmode ]->hdisplay; + *height = modelist[ bestmode ]->vdisplay; + } + + // Free modelist + XFree( modelist ); + + if( bestmode != -1 ) + { + return bestmode; + } + } +#endif + + // Default: Simply use the screen resolution + *width = DisplayWidth( _glfwLibrary.display, screen ); + *height = DisplayHeight( _glfwLibrary.display, screen ); + + return 0; +} + + +//======================================================================== +// Change the current video mode +//======================================================================== + +void _glfwSetVideoModeMODE( int screen, int mode, int rate ) +{ +#if defined( _GLFW_HAS_XRANDR ) + XRRScreenConfiguration *sc; + Window root; + + if( _glfwLibrary.XRandR.available ) + { + root = RootWindow( _glfwLibrary.display, screen ); + sc = XRRGetScreenInfo( _glfwLibrary.display, root ); + + // Remember old size and flag that we have changed the mode + if( !_glfwWin.FS.modeChanged ) + { + _glfwWin.FS.oldSizeID = XRRConfigCurrentConfiguration( sc, &_glfwWin.FS.oldRotation ); + _glfwWin.FS.oldWidth = DisplayWidth( _glfwLibrary.display, screen ); + _glfwWin.FS.oldHeight = DisplayHeight( _glfwLibrary.display, screen ); + + _glfwWin.FS.modeChanged = GL_TRUE; + } + + if( rate > 0 ) + { + // Set desired configuration + XRRSetScreenConfigAndRate( _glfwLibrary.display, + sc, + root, + mode, + RR_Rotate_0, + (short) rate, + CurrentTime ); + } + else + { + // Set desired configuration + XRRSetScreenConfig( _glfwLibrary.display, + sc, + root, + mode, + RR_Rotate_0, + CurrentTime ); + } + + XRRFreeScreenConfigInfo( sc ); + } +#elif defined( _GLFW_HAS_XF86VIDMODE ) + XF86VidModeModeInfo **modelist; + int modecount; + + // Use the XF86VidMode extension to control video resolution + if( _glfwLibrary.XF86VidMode.available ) + { + // Get a list of all available display modes + XF86VidModeGetAllModeLines( _glfwLibrary.display, screen, + &modecount, &modelist ); + + // Unlock mode switch if necessary + if( _glfwWin.FS.modeChanged ) + { + XF86VidModeLockModeSwitch( _glfwLibrary.display, screen, 0 ); + } + + // Change the video mode to the desired mode + XF86VidModeSwitchToMode( _glfwLibrary.display, screen, + modelist[ mode ] ); + + // Set viewport to upper left corner (where our window will be) + XF86VidModeSetViewPort( _glfwLibrary.display, screen, 0, 0 ); + + // Lock mode switch + XF86VidModeLockModeSwitch( _glfwLibrary.display, screen, 1 ); + + // Remember old mode and flag that we have changed the mode + if( !_glfwWin.FS.modeChanged ) + { + _glfwWin.FS.oldMode = *modelist[ 0 ]; + _glfwWin.FS.modeChanged = GL_TRUE; + } + + // Free mode list + XFree( modelist ); + } +#endif +} + + +//======================================================================== +// Change the current video mode +//======================================================================== + +void _glfwSetVideoMode( int screen, int *width, int *height, int *rate ) +{ + int bestmode; + + // Find a best match mode + bestmode = _glfwGetClosestVideoMode( screen, width, height, rate ); + + // Change mode + _glfwSetVideoModeMODE( screen, bestmode, *rate ); +} + + +//======================================================================== +// Restore the previously saved (original) video mode +//======================================================================== + +void _glfwRestoreVideoMode( void ) +{ + if( _glfwWin.FS.modeChanged ) + { +#if defined( _GLFW_HAS_XRANDR ) + if( _glfwLibrary.XRandR.available ) + { + XRRScreenConfiguration *sc; + + if( _glfwLibrary.XRandR.available ) + { + sc = XRRGetScreenInfo( _glfwLibrary.display, _glfwWin.root ); + + XRRSetScreenConfig( _glfwLibrary.display, + sc, + _glfwWin.root, + _glfwWin.FS.oldSizeID, + _glfwWin.FS.oldRotation, + CurrentTime ); + + XRRFreeScreenConfigInfo( sc ); + } + } +#elif defined( _GLFW_HAS_XF86VIDMODE ) + if( _glfwLibrary.XF86VidMode.available ) + { + // Unlock mode switch + XF86VidModeLockModeSwitch( _glfwLibrary.display, _glfwWin.screen, 0 ); + + // Change the video mode back to the old mode + XF86VidModeSwitchToMode( _glfwLibrary.display, + _glfwWin.screen, + &_glfwWin.FS.oldMode ); + } +#endif + _glfwWin.FS.modeChanged = GL_FALSE; + } +} + + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +struct _glfwResolution +{ + int width; + int height; +}; + +//======================================================================== +// List available video modes +//======================================================================== + +int _glfwPlatformGetVideoModes( GLFWvidmode *list, int maxcount ) +{ + int count, k, l, r, g, b, rgba, gl; + int depth, screen; + Display *dpy; + XVisualInfo *vislist, dummy; + int viscount, rgbcount, rescount; + int *rgbarray; + struct _glfwResolution *resarray; +#if defined( _GLFW_HAS_XRANDR ) + XRRScreenConfiguration *sc; + XRRScreenSize *sizelist; + int sizecount; +#elif defined( _GLFW_HAS_XF86VIDMODE ) + XF86VidModeModeInfo **modelist; + int modecount, width, height; +#endif + + // Get display and screen + dpy = _glfwLibrary.display; + screen = DefaultScreen( dpy ); + + // Get list of visuals + vislist = XGetVisualInfo( dpy, 0, &dummy, &viscount ); + if( vislist == NULL ) + { + return 0; + } + + rgbarray = (int*) malloc( sizeof(int) * viscount ); + rgbcount = 0; + + // Build RGB array + for( k = 0; k < viscount; k++ ) + { + // Does the visual support OpenGL & true color? + glXGetConfig( dpy, &vislist[k], GLX_USE_GL, &gl ); + glXGetConfig( dpy, &vislist[k], GLX_RGBA, &rgba ); + if( gl && rgba ) + { + // Get color depth for this visual + depth = vislist[k].depth; + + // Convert to RGB + BPP2RGB( depth, &r, &g, &b ); + depth = (r<<16) | (g<<8) | b; + + // Is this mode unique? + for( l = 0; l < rgbcount; l++ ) + { + if( depth == rgbarray[ l ] ) + { + break; + } + } + if( l >= rgbcount ) + { + rgbarray[ rgbcount ] = depth; + rgbcount++; + } + } + } + + rescount = 0; + resarray = NULL; + + // Build resolution array +#if defined( _GLFW_HAS_XRANDR ) + if( _glfwLibrary.XRandR.available ) + { + sc = XRRGetScreenInfo( dpy, RootWindow( dpy, screen ) ); + sizelist = XRRConfigSizes( sc, &sizecount ); + + resarray = (struct _glfwResolution*) malloc( sizeof(struct _glfwResolution) * sizecount ); + + for( k = 0; k < sizecount; k++ ) + { + resarray[ rescount ].width = sizelist[ k ].width; + resarray[ rescount ].height = sizelist[ k ].height; + rescount++; + } + + XRRFreeScreenConfigInfo( sc ); + } +#elif defined( _GLFW_HAS_XF86VIDMODE ) + if( _glfwLibrary.XF86VidMode.available ) + { + XF86VidModeGetAllModeLines( dpy, screen, &modecount, &modelist ); + + resarray = (struct _glfwResolution*) malloc( sizeof(struct _glfwResolution) * modecount ); + + for( k = 0; k < modecount; k++ ) + { + width = modelist[ k ]->hdisplay; + height = modelist[ k ]->vdisplay; + + // Is this mode unique? + for( l = 0; l < rescount; l++ ) + { + if( width == resarray[ l ].width && height == resarray[ l ].height ) + { + break; + } + } + + if( l >= rescount ) + { + resarray[ rescount ].width = width; + resarray[ rescount ].height = height; + rescount++; + } + } + + XFree( modelist ); + } +#endif + + if( !resarray ) + { + rescount = 1; + resarray = (struct _glfwResolution*) malloc( sizeof(struct _glfwResolution) * rescount ); + + resarray[ 0 ].width = DisplayWidth( dpy, screen ); + resarray[ 0 ].height = DisplayHeight( dpy, screen ); + } + + // Build permutations of colors and resolutions + count = 0; + for( k = 0; k < rgbcount && count < maxcount; k++ ) + { + for( l = 0; l < rescount && count < maxcount; l++ ) + { + list[count].Width = resarray[ l ].width; + list[count].Height = resarray[ l ].height; + list[count].RedBits = (rgbarray[ k ] >> 16) & 255; + list[count].GreenBits = (rgbarray[ k ] >> 8) & 255; + list[count].BlueBits = rgbarray[ k ] & 255; + count++; + } + } + + // Free visuals list + XFree( vislist ); + + free( resarray ); + free( rgbarray ); + + return count; +} + + +//======================================================================== +// Get the desktop video mode +//======================================================================== + +void _glfwPlatformGetDesktopMode( GLFWvidmode *mode ) +{ + Display *dpy; + int bpp, screen; +#if defined( _GLFW_HAS_XF86VIDMODE ) + XF86VidModeModeInfo **modelist; + int modecount; +#endif + + // Get display and screen + dpy = _glfwLibrary.display; + screen = DefaultScreen( dpy ); + + // Get display depth + bpp = DefaultDepth( dpy, screen ); + + // Convert BPP to RGB bits + BPP2RGB( bpp, &mode->RedBits, &mode->GreenBits, &mode->BlueBits ); + +#if defined( _GLFW_HAS_XRANDR ) + if( _glfwLibrary.XRandR.available ) + { + if( _glfwWin.FS.modeChanged ) + { + mode->Width = _glfwWin.FS.oldWidth; + mode->Height = _glfwWin.FS.oldHeight; + return; + } + } +#elif defined( _GLFW_HAS_XF86VIDMODE ) + if( _glfwLibrary.XF86VidMode.available ) + { + if( _glfwWin.FS.modeChanged ) + { + // The old (desktop) mode is stored in _glfwWin.FS.oldMode + mode->Width = _glfwWin.FS.oldMode.hdisplay; + mode->Height = _glfwWin.FS.oldMode.vdisplay; + } + else + { + // Use the XF86VidMode extension to get list of video modes + XF86VidModeGetAllModeLines( dpy, screen, &modecount, + &modelist ); + + // The first mode in the list is the current (desktio) mode + mode->Width = modelist[ 0 ]->hdisplay; + mode->Height = modelist[ 0 ]->vdisplay; + + // Free list + XFree( modelist ); + } + + return; + } +#endif + + // Get current display width and height + mode->Width = DisplayWidth( dpy, screen ); + mode->Height = DisplayHeight( dpy, screen ); +} + diff --git a/lib/x11/x11_glext.c b/lib/x11/x11_glext.c new file mode 100644 index 00000000..bf77dcfa --- /dev/null +++ b/lib/x11/x11_glext.c @@ -0,0 +1,90 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: X11/GLX +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +void (*glXGetProcAddress(const GLubyte *procName))(); +void (*glXGetProcAddressARB(const GLubyte *procName))(); +void (*glXGetProcAddressEXT(const GLubyte *procName))(); + +// We support four different ways for getting addresses for GL/GLX +// extension functions: glXGetProcAddress, glXGetProcAddressARB, +// glXGetProcAddressEXT, and dlsym +#if defined( _GLFW_HAS_GLXGETPROCADDRESSARB ) + #define _glfw_glXGetProcAddress(x) glXGetProcAddressARB(x) +#elif defined( _GLFW_HAS_GLXGETPROCADDRESS ) + #define _glfw_glXGetProcAddress(x) glXGetProcAddress(x) +#elif defined( _GLFW_HAS_GLXGETPROCADDRESSEXT ) + #define _glfw_glXGetProcAddress(x) glXGetProcAddressEXT(x) +#elif defined( _GLFW_HAS_DLOPEN ) + #define _glfw_glXGetProcAddress(x) dlsym(_glfwLibs.libGL,x) + #define _GLFW_DLOPEN_LIBGL +#else +#define _glfw_glXGetProcAddress(x) NULL +#endif + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Check if an OpenGL extension is available at runtime +//======================================================================== + +int _glfwPlatformExtensionSupported( const char *extension ) +{ + const GLubyte *extensions; + + // Get list of GLX extensions + extensions = (const GLubyte*) glXQueryExtensionsString( _glfwLibrary.display, + _glfwWin.screen ); + if( extensions != NULL ) + { + if( _glfwStringInExtensionString( extension, extensions ) ) + { + return GL_TRUE; + } + } + + return GL_FALSE; +} + + +//======================================================================== +// Get the function pointer to an OpenGL function +//======================================================================== + +void * _glfwPlatformGetProcAddress( const char *procname ) +{ + return (void *) _glfw_glXGetProcAddress( (const GLubyte *) procname ); +} + diff --git a/lib/x11/x11_init.c b/lib/x11/x11_init.c new file mode 100644 index 00000000..612a524f --- /dev/null +++ b/lib/x11/x11_init.c @@ -0,0 +1,206 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: X11/GLX +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +//======================================================================== +// Dynamically load libraries +//======================================================================== + +static void initLibraries( void ) +{ +#ifdef _GLFW_DLOPEN_LIBGL + int i; + char *libGL_names[ ] = + { + "libGL.so", + "libGL.so.1", + "/usr/lib/libGL.so", + "/usr/lib/libGL.so.1", + NULL + }; + + _glfwLibrary.Libs.libGL = NULL; + for( i = 0; !libGL_names[ i ] != NULL; i ++ ) + { + _glfwLibrary.Libs.libGL = dlopen( libGL_names[ i ], + RTLD_LAZY | RTLD_GLOBAL ); + if( _glfwLibrary.Libs.libGL ) + break; + } +#endif +} + + +//======================================================================== +// Terminate GLFW when exiting application +//======================================================================== + +static void glfw_atexit( void ) +{ + glfwTerminate(); +} + + +//======================================================================== +// Initialize X11 display +//======================================================================== + +static int initDisplay( void ) +{ + // Open display + _glfwLibrary.display = XOpenDisplay( 0 ); + if( !_glfwLibrary.display ) + { + fprintf(stderr, "Failed to open X display\n"); + return GL_FALSE; + } + + // Check for XF86VidMode extension +#ifdef _GLFW_HAS_XF86VIDMODE + _glfwLibrary.XF86VidMode.available = + XF86VidModeQueryExtension( _glfwLibrary.display, + &_glfwLibrary.XF86VidMode.eventBase, + &_glfwLibrary.XF86VidMode.errorBase); +#else + _glfwLibrary.XF86VidMode.available = 0; +#endif + + // Check for XRandR extension +#ifdef _GLFW_HAS_XRANDR + _glfwLibrary.XRandR.available = + XRRQueryExtension( _glfwLibrary.display, + &_glfwLibrary.XRandR.eventBase, + &_glfwLibrary.XRandR.errorBase ); +#else + _glfwLibrary.XRandR.available = 0; +#endif + + // Fullscreen & screen saver settings + // Check if GLX is supported on this display + if( !glXQueryExtension( _glfwLibrary.display, NULL, NULL ) ) + { + fprintf(stderr, "GLX not supported\n"); + return GL_FALSE; + } + + // Retrieve GLX version + if( !glXQueryVersion( _glfwLibrary.display, + &_glfwLibrary.glxMajor, + &_glfwLibrary.glxMinor ) ) + { + fprintf(stderr, "Unable to query GLX version\n"); + return GL_FALSE; + } + + return GL_TRUE; +} + + +//======================================================================== +// Terminate X11 display +//======================================================================== + +static void terminateDisplay( void ) +{ + // Open display + if( _glfwLibrary.display ) + { + XCloseDisplay( _glfwLibrary.display ); + _glfwLibrary.display = NULL; + } +} + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Initialize various GLFW state +//======================================================================== + +int _glfwPlatformInit( void ) +{ + // Initialize display + if( !initDisplay() ) + { + return GL_FALSE; + } + + // Try to load libGL.so if necessary + initLibraries(); + + // Install atexit() routine + atexit( glfw_atexit ); + + // Initialize joysticks + _glfwInitJoysticks(); + + // Start the timer + _glfwInitTimer(); + + return GL_TRUE; +} + + +//======================================================================== +// Close window and shut down library +//======================================================================== + +int _glfwPlatformTerminate( void ) +{ + // Close OpenGL window + glfwCloseWindow(); + + // Terminate display + terminateDisplay(); + + // Terminate joysticks + _glfwTerminateJoysticks(); + + // Unload libGL.so if necessary +#ifdef _GLFW_DLOPEN_LIBGL + if( _glfwLibrary.Libs.libGL != NULL ) + { + dlclose( _glfwLibrary.Libs.libGL ); + _glfwLibrary.Libs.libGL = NULL; + } +#endif + + return GL_TRUE; +} + diff --git a/lib/x11/x11_joystick.c b/lib/x11/x11_joystick.c new file mode 100644 index 00000000..90c9c648 --- /dev/null +++ b/lib/x11/x11_joystick.c @@ -0,0 +1,367 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: X11/GLX +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//======================================================================== +// Note: Only Linux joystick input is supported at the moment. Other +// systems will behave as if there are no joysticks connected. +//======================================================================== + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +#ifdef _GLFW_USE_LINUX_JOYSTICKS + +//------------------------------------------------------------------------ +// Here are the Linux joystick driver v1.x interface definitions that we +// use (we do not want to rely on ): +//------------------------------------------------------------------------ + +#include +#include +#include + +// Joystick event types +#define JS_EVENT_BUTTON 0x01 /* button pressed/released */ +#define JS_EVENT_AXIS 0x02 /* joystick moved */ +#define JS_EVENT_INIT 0x80 /* initial state of device */ + +// Joystick event structure +struct js_event { + unsigned int time; /* (u32) event timestamp in milliseconds */ + signed short value; /* (s16) value */ + unsigned char type; /* (u8) event type */ + unsigned char number; /* (u8) axis/button number */ +}; + +// Joystick IOCTL commands +#define JSIOCGVERSION _IOR('j', 0x01, int) /* get driver version (u32) */ +#define JSIOCGAXES _IOR('j', 0x11, char) /* get number of axes (u8) */ +#define JSIOCGBUTTONS _IOR('j', 0x12, char) /* get number of buttons (u8) */ + +#endif // _GLFW_USE_LINUX_JOYSTICKS + + +//======================================================================== +// _glfwInitJoysticks() - Initialize joystick interface +//======================================================================== + +void _glfwInitJoysticks( void ) +{ +#ifdef _GLFW_USE_LINUX_JOYSTICKS + int k, n, fd, joy_count; + char *joy_base_name, joy_dev_name[ 20 ]; + int driver_version = 0x000800; + char ret_data; +#endif // _GLFW_USE_LINUX_JOYSTICKS + int i; + + // Start by saying that there are no sticks + for( i = 0; i <= GLFW_JOYSTICK_LAST; ++ i ) + { + _glfwJoy[ i ].Present = GL_FALSE; + } + +#ifdef _GLFW_USE_LINUX_JOYSTICKS + + // Try to open joysticks (nonblocking) + joy_count = 0; + for( k = 0; k <= 1 && joy_count <= GLFW_JOYSTICK_LAST; ++ k ) + { + // Pick joystick base name + switch( k ) + { + case 0: + joy_base_name = "/dev/input/js"; // USB sticks + break; + case 1: + joy_base_name = "/dev/js"; // "Legacy" sticks + break; + default: + continue; // (should never happen) + } + + // Try to open a few of these sticks + for( i = 0; i <= 50 && joy_count <= GLFW_JOYSTICK_LAST; ++ i ) + { + sprintf( joy_dev_name, "%s%d", joy_base_name, i ); + fd = open( joy_dev_name, O_NONBLOCK ); + if( fd != -1 ) + { + // Remember fd + _glfwJoy[ joy_count ].fd = fd; + + // Check that the joystick driver version is 1.0+ + ioctl( fd, JSIOCGVERSION, &driver_version ); + if( driver_version < 0x010000 ) + { + // It's an old 0.x interface (we don't support it) + close( fd ); + continue; + } + + // Get number of joystick axes + ioctl( fd, JSIOCGAXES, &ret_data ); + _glfwJoy[ joy_count ].NumAxes = (int) ret_data; + + // Get number of joystick buttons + ioctl( fd, JSIOCGBUTTONS, &ret_data ); + _glfwJoy[ joy_count ].NumButtons = (int) ret_data; + + // Allocate memory for joystick state + _glfwJoy[ joy_count ].Axis = + (float *) malloc( sizeof(float) * + _glfwJoy[ joy_count ].NumAxes ); + if( _glfwJoy[ joy_count ].Axis == NULL ) + { + close( fd ); + continue; + } + _glfwJoy[ joy_count ].Button = + (unsigned char *) malloc( sizeof(char) * + _glfwJoy[ joy_count ].NumButtons ); + if( _glfwJoy[ joy_count ].Button == NULL ) + { + free( _glfwJoy[ joy_count ].Axis ); + close( fd ); + continue; + } + + // Clear joystick state + for( n = 0; n < _glfwJoy[ joy_count ].NumAxes; ++ n ) + { + _glfwJoy[ joy_count ].Axis[ n ] = 0.0f; + } + for( n = 0; n < _glfwJoy[ joy_count ].NumButtons; ++ n ) + { + _glfwJoy[ joy_count ].Button[ n ] = GLFW_RELEASE; + } + + // The joystick is supported and connected + _glfwJoy[ joy_count ].Present = GL_TRUE; + joy_count ++; + } + } + } + +#endif // _GLFW_USE_LINUX_JOYSTICKS + +} + + +//======================================================================== +// _glfwTerminateJoysticks() - Close all opened joystick handles +//======================================================================== + +void _glfwTerminateJoysticks( void ) +{ + +#ifdef _GLFW_USE_LINUX_JOYSTICKS + + int i; + + // Close any opened joysticks + for( i = 0; i <= GLFW_JOYSTICK_LAST; ++ i ) + { + if( _glfwJoy[ i ].Present ) + { + close( _glfwJoy[ i ].fd ); + free( _glfwJoy[ i ].Axis ); + free( _glfwJoy[ i ].Button ); + _glfwJoy[ i ].Present = GL_FALSE; + } + } + +#endif // _GLFW_USE_LINUX_JOYSTICKS + +} + + +//======================================================================== +// Empty joystick event queue +//======================================================================== + +static void pollJoystickEvents( void ) +{ + +#ifdef _GLFW_USE_LINUX_JOYSTICKS + + struct js_event e; + int i; + + // Get joystick events for all GLFW joysticks + for( i = 0; i <= GLFW_JOYSTICK_LAST; ++ i ) + { + // Is the stick present? + if( _glfwJoy[ i ].Present ) + { + // Read all queued events (non-blocking) + while( read(_glfwJoy[i].fd, &e, sizeof(struct js_event)) > 0 ) + { + // We don't care if it's an init event or not + e.type &= ~JS_EVENT_INIT; + + // Check event type + switch( e.type ) + { + case JS_EVENT_AXIS: + _glfwJoy[ i ].Axis[ e.number ] = (float) e.value / + 32767.0f; + // We need to change the sign for the Y axes, so that + // positive = up/forward, according to the GLFW spec. + if( e.number & 1 ) + { + _glfwJoy[ i ].Axis[ e.number ] = + -_glfwJoy[ i ].Axis[ e.number ]; + } + break; + + case JS_EVENT_BUTTON: + _glfwJoy[ i ].Button[ e.number ] = + e.value ? GLFW_PRESS : GLFW_RELEASE; + break; + + default: + break; + } + } + } + } + +#endif // _GLFW_USE_LINUX_JOYSTICKS + +} + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// _glfwPlatformGetJoystickParam() - Determine joystick capabilities +//======================================================================== + +int _glfwPlatformGetJoystickParam( int joy, int param ) +{ + // Is joystick present? + if( !_glfwJoy[ joy ].Present ) + { + return 0; + } + + switch( param ) + { + case GLFW_PRESENT: + return GL_TRUE; + + case GLFW_AXES: + return _glfwJoy[ joy ].NumAxes; + + case GLFW_BUTTONS: + return _glfwJoy[ joy ].NumButtons; + + default: + break; + } + + return 0; +} + + +//======================================================================== +// _glfwPlatformGetJoystickPos() - Get joystick axis positions +//======================================================================== + +int _glfwPlatformGetJoystickPos( int joy, float *pos, int numaxes ) +{ + int i; + + // Is joystick present? + if( !_glfwJoy[ joy ].Present ) + { + return 0; + } + + // Update joystick state + pollJoystickEvents(); + + // Does the joystick support less axes than requested? + if( _glfwJoy[ joy ].NumAxes < numaxes ) + { + numaxes = _glfwJoy[ joy ].NumAxes; + } + + // Copy axis positions from internal state + for( i = 0; i < numaxes; ++ i ) + { + pos[ i ] = _glfwJoy[ joy ].Axis[ i ]; + } + + return numaxes; +} + + +//======================================================================== +// _glfwPlatformGetJoystickButtons() - Get joystick button states +//======================================================================== + +int _glfwPlatformGetJoystickButtons( int joy, unsigned char *buttons, + int numbuttons ) +{ + int i; + + // Is joystick present? + if( !_glfwJoy[ joy ].Present ) + { + return 0; + } + + // Update joystick state + pollJoystickEvents(); + + // Does the joystick support less buttons than requested? + if( _glfwJoy[ joy ].NumButtons < numbuttons ) + { + numbuttons = _glfwJoy[ joy ].NumButtons; + } + + // Copy button states from internal state + for( i = 0; i < numbuttons; ++ i ) + { + buttons[ i ] = _glfwJoy[ joy ].Button[ i ]; + } + + return numbuttons; +} + diff --git a/lib/x11/x11_keysym2unicode.c b/lib/x11/x11_keysym2unicode.c new file mode 100644 index 00000000..9701cb49 --- /dev/null +++ b/lib/x11/x11_keysym2unicode.c @@ -0,0 +1,901 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: X11/GLX +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +/* + * Marcus: This code was originally written by Markus G. Kuhn. + * I have made some slight changes (trimmed it down a bit from >60 KB to + * 20 KB), but the functionality is the same. + */ + +/* + * This module converts keysym values into the corresponding ISO 10646 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. The function + * _glfwKeySym2Unicode() maps a keysym onto a Unicode value using a binary + * search, therefore keysymtab[] must remain SORTED by keysym value. + * + * We allow to represent any UCS character in the range U-00000000 to + * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. + * This admittedly does not cover the entire 31-bit space of UCS, but + * it does cover all of the characters up to U-10FFFF, which can be + * represented by UTF-16, and more, and it is very unlikely that higher + * UCS codes will ever be assigned by ISO. So to get Unicode character + * U+ABCD you can directly use keysym 0x0100abcd. + * + * Original author: Markus G. Kuhn , University of + * Cambridge, April 2001 + * + * Special thanks to Richard Verhoeven for preparing + * an initial draft of the mapping table. + * + */ + + +//************************************************************************ +//**** KeySym to Unicode mapping table **** +//************************************************************************ + +static struct codepair { + unsigned short keysym; + unsigned short ucs; +} keysymtab[] = { + { 0x01a1, 0x0104 }, + { 0x01a2, 0x02d8 }, + { 0x01a3, 0x0141 }, + { 0x01a5, 0x013d }, + { 0x01a6, 0x015a }, + { 0x01a9, 0x0160 }, + { 0x01aa, 0x015e }, + { 0x01ab, 0x0164 }, + { 0x01ac, 0x0179 }, + { 0x01ae, 0x017d }, + { 0x01af, 0x017b }, + { 0x01b1, 0x0105 }, + { 0x01b2, 0x02db }, + { 0x01b3, 0x0142 }, + { 0x01b5, 0x013e }, + { 0x01b6, 0x015b }, + { 0x01b7, 0x02c7 }, + { 0x01b9, 0x0161 }, + { 0x01ba, 0x015f }, + { 0x01bb, 0x0165 }, + { 0x01bc, 0x017a }, + { 0x01bd, 0x02dd }, + { 0x01be, 0x017e }, + { 0x01bf, 0x017c }, + { 0x01c0, 0x0154 }, + { 0x01c3, 0x0102 }, + { 0x01c5, 0x0139 }, + { 0x01c6, 0x0106 }, + { 0x01c8, 0x010c }, + { 0x01ca, 0x0118 }, + { 0x01cc, 0x011a }, + { 0x01cf, 0x010e }, + { 0x01d0, 0x0110 }, + { 0x01d1, 0x0143 }, + { 0x01d2, 0x0147 }, + { 0x01d5, 0x0150 }, + { 0x01d8, 0x0158 }, + { 0x01d9, 0x016e }, + { 0x01db, 0x0170 }, + { 0x01de, 0x0162 }, + { 0x01e0, 0x0155 }, + { 0x01e3, 0x0103 }, + { 0x01e5, 0x013a }, + { 0x01e6, 0x0107 }, + { 0x01e8, 0x010d }, + { 0x01ea, 0x0119 }, + { 0x01ec, 0x011b }, + { 0x01ef, 0x010f }, + { 0x01f0, 0x0111 }, + { 0x01f1, 0x0144 }, + { 0x01f2, 0x0148 }, + { 0x01f5, 0x0151 }, + { 0x01f8, 0x0159 }, + { 0x01f9, 0x016f }, + { 0x01fb, 0x0171 }, + { 0x01fe, 0x0163 }, + { 0x01ff, 0x02d9 }, + { 0x02a1, 0x0126 }, + { 0x02a6, 0x0124 }, + { 0x02a9, 0x0130 }, + { 0x02ab, 0x011e }, + { 0x02ac, 0x0134 }, + { 0x02b1, 0x0127 }, + { 0x02b6, 0x0125 }, + { 0x02b9, 0x0131 }, + { 0x02bb, 0x011f }, + { 0x02bc, 0x0135 }, + { 0x02c5, 0x010a }, + { 0x02c6, 0x0108 }, + { 0x02d5, 0x0120 }, + { 0x02d8, 0x011c }, + { 0x02dd, 0x016c }, + { 0x02de, 0x015c }, + { 0x02e5, 0x010b }, + { 0x02e6, 0x0109 }, + { 0x02f5, 0x0121 }, + { 0x02f8, 0x011d }, + { 0x02fd, 0x016d }, + { 0x02fe, 0x015d }, + { 0x03a2, 0x0138 }, + { 0x03a3, 0x0156 }, + { 0x03a5, 0x0128 }, + { 0x03a6, 0x013b }, + { 0x03aa, 0x0112 }, + { 0x03ab, 0x0122 }, + { 0x03ac, 0x0166 }, + { 0x03b3, 0x0157 }, + { 0x03b5, 0x0129 }, + { 0x03b6, 0x013c }, + { 0x03ba, 0x0113 }, + { 0x03bb, 0x0123 }, + { 0x03bc, 0x0167 }, + { 0x03bd, 0x014a }, + { 0x03bf, 0x014b }, + { 0x03c0, 0x0100 }, + { 0x03c7, 0x012e }, + { 0x03cc, 0x0116 }, + { 0x03cf, 0x012a }, + { 0x03d1, 0x0145 }, + { 0x03d2, 0x014c }, + { 0x03d3, 0x0136 }, + { 0x03d9, 0x0172 }, + { 0x03dd, 0x0168 }, + { 0x03de, 0x016a }, + { 0x03e0, 0x0101 }, + { 0x03e7, 0x012f }, + { 0x03ec, 0x0117 }, + { 0x03ef, 0x012b }, + { 0x03f1, 0x0146 }, + { 0x03f2, 0x014d }, + { 0x03f3, 0x0137 }, + { 0x03f9, 0x0173 }, + { 0x03fd, 0x0169 }, + { 0x03fe, 0x016b }, + { 0x047e, 0x203e }, + { 0x04a1, 0x3002 }, + { 0x04a2, 0x300c }, + { 0x04a3, 0x300d }, + { 0x04a4, 0x3001 }, + { 0x04a5, 0x30fb }, + { 0x04a6, 0x30f2 }, + { 0x04a7, 0x30a1 }, + { 0x04a8, 0x30a3 }, + { 0x04a9, 0x30a5 }, + { 0x04aa, 0x30a7 }, + { 0x04ab, 0x30a9 }, + { 0x04ac, 0x30e3 }, + { 0x04ad, 0x30e5 }, + { 0x04ae, 0x30e7 }, + { 0x04af, 0x30c3 }, + { 0x04b0, 0x30fc }, + { 0x04b1, 0x30a2 }, + { 0x04b2, 0x30a4 }, + { 0x04b3, 0x30a6 }, + { 0x04b4, 0x30a8 }, + { 0x04b5, 0x30aa }, + { 0x04b6, 0x30ab }, + { 0x04b7, 0x30ad }, + { 0x04b8, 0x30af }, + { 0x04b9, 0x30b1 }, + { 0x04ba, 0x30b3 }, + { 0x04bb, 0x30b5 }, + { 0x04bc, 0x30b7 }, + { 0x04bd, 0x30b9 }, + { 0x04be, 0x30bb }, + { 0x04bf, 0x30bd }, + { 0x04c0, 0x30bf }, + { 0x04c1, 0x30c1 }, + { 0x04c2, 0x30c4 }, + { 0x04c3, 0x30c6 }, + { 0x04c4, 0x30c8 }, + { 0x04c5, 0x30ca }, + { 0x04c6, 0x30cb }, + { 0x04c7, 0x30cc }, + { 0x04c8, 0x30cd }, + { 0x04c9, 0x30ce }, + { 0x04ca, 0x30cf }, + { 0x04cb, 0x30d2 }, + { 0x04cc, 0x30d5 }, + { 0x04cd, 0x30d8 }, + { 0x04ce, 0x30db }, + { 0x04cf, 0x30de }, + { 0x04d0, 0x30df }, + { 0x04d1, 0x30e0 }, + { 0x04d2, 0x30e1 }, + { 0x04d3, 0x30e2 }, + { 0x04d4, 0x30e4 }, + { 0x04d5, 0x30e6 }, + { 0x04d6, 0x30e8 }, + { 0x04d7, 0x30e9 }, + { 0x04d8, 0x30ea }, + { 0x04d9, 0x30eb }, + { 0x04da, 0x30ec }, + { 0x04db, 0x30ed }, + { 0x04dc, 0x30ef }, + { 0x04dd, 0x30f3 }, + { 0x04de, 0x309b }, + { 0x04df, 0x309c }, + { 0x05ac, 0x060c }, + { 0x05bb, 0x061b }, + { 0x05bf, 0x061f }, + { 0x05c1, 0x0621 }, + { 0x05c2, 0x0622 }, + { 0x05c3, 0x0623 }, + { 0x05c4, 0x0624 }, + { 0x05c5, 0x0625 }, + { 0x05c6, 0x0626 }, + { 0x05c7, 0x0627 }, + { 0x05c8, 0x0628 }, + { 0x05c9, 0x0629 }, + { 0x05ca, 0x062a }, + { 0x05cb, 0x062b }, + { 0x05cc, 0x062c }, + { 0x05cd, 0x062d }, + { 0x05ce, 0x062e }, + { 0x05cf, 0x062f }, + { 0x05d0, 0x0630 }, + { 0x05d1, 0x0631 }, + { 0x05d2, 0x0632 }, + { 0x05d3, 0x0633 }, + { 0x05d4, 0x0634 }, + { 0x05d5, 0x0635 }, + { 0x05d6, 0x0636 }, + { 0x05d7, 0x0637 }, + { 0x05d8, 0x0638 }, + { 0x05d9, 0x0639 }, + { 0x05da, 0x063a }, + { 0x05e0, 0x0640 }, + { 0x05e1, 0x0641 }, + { 0x05e2, 0x0642 }, + { 0x05e3, 0x0643 }, + { 0x05e4, 0x0644 }, + { 0x05e5, 0x0645 }, + { 0x05e6, 0x0646 }, + { 0x05e7, 0x0647 }, + { 0x05e8, 0x0648 }, + { 0x05e9, 0x0649 }, + { 0x05ea, 0x064a }, + { 0x05eb, 0x064b }, + { 0x05ec, 0x064c }, + { 0x05ed, 0x064d }, + { 0x05ee, 0x064e }, + { 0x05ef, 0x064f }, + { 0x05f0, 0x0650 }, + { 0x05f1, 0x0651 }, + { 0x05f2, 0x0652 }, + { 0x06a1, 0x0452 }, + { 0x06a2, 0x0453 }, + { 0x06a3, 0x0451 }, + { 0x06a4, 0x0454 }, + { 0x06a5, 0x0455 }, + { 0x06a6, 0x0456 }, + { 0x06a7, 0x0457 }, + { 0x06a8, 0x0458 }, + { 0x06a9, 0x0459 }, + { 0x06aa, 0x045a }, + { 0x06ab, 0x045b }, + { 0x06ac, 0x045c }, + { 0x06ae, 0x045e }, + { 0x06af, 0x045f }, + { 0x06b0, 0x2116 }, + { 0x06b1, 0x0402 }, + { 0x06b2, 0x0403 }, + { 0x06b3, 0x0401 }, + { 0x06b4, 0x0404 }, + { 0x06b5, 0x0405 }, + { 0x06b6, 0x0406 }, + { 0x06b7, 0x0407 }, + { 0x06b8, 0x0408 }, + { 0x06b9, 0x0409 }, + { 0x06ba, 0x040a }, + { 0x06bb, 0x040b }, + { 0x06bc, 0x040c }, + { 0x06be, 0x040e }, + { 0x06bf, 0x040f }, + { 0x06c0, 0x044e }, + { 0x06c1, 0x0430 }, + { 0x06c2, 0x0431 }, + { 0x06c3, 0x0446 }, + { 0x06c4, 0x0434 }, + { 0x06c5, 0x0435 }, + { 0x06c6, 0x0444 }, + { 0x06c7, 0x0433 }, + { 0x06c8, 0x0445 }, + { 0x06c9, 0x0438 }, + { 0x06ca, 0x0439 }, + { 0x06cb, 0x043a }, + { 0x06cc, 0x043b }, + { 0x06cd, 0x043c }, + { 0x06ce, 0x043d }, + { 0x06cf, 0x043e }, + { 0x06d0, 0x043f }, + { 0x06d1, 0x044f }, + { 0x06d2, 0x0440 }, + { 0x06d3, 0x0441 }, + { 0x06d4, 0x0442 }, + { 0x06d5, 0x0443 }, + { 0x06d6, 0x0436 }, + { 0x06d7, 0x0432 }, + { 0x06d8, 0x044c }, + { 0x06d9, 0x044b }, + { 0x06da, 0x0437 }, + { 0x06db, 0x0448 }, + { 0x06dc, 0x044d }, + { 0x06dd, 0x0449 }, + { 0x06de, 0x0447 }, + { 0x06df, 0x044a }, + { 0x06e0, 0x042e }, + { 0x06e1, 0x0410 }, + { 0x06e2, 0x0411 }, + { 0x06e3, 0x0426 }, + { 0x06e4, 0x0414 }, + { 0x06e5, 0x0415 }, + { 0x06e6, 0x0424 }, + { 0x06e7, 0x0413 }, + { 0x06e8, 0x0425 }, + { 0x06e9, 0x0418 }, + { 0x06ea, 0x0419 }, + { 0x06eb, 0x041a }, + { 0x06ec, 0x041b }, + { 0x06ed, 0x041c }, + { 0x06ee, 0x041d }, + { 0x06ef, 0x041e }, + { 0x06f0, 0x041f }, + { 0x06f1, 0x042f }, + { 0x06f2, 0x0420 }, + { 0x06f3, 0x0421 }, + { 0x06f4, 0x0422 }, + { 0x06f5, 0x0423 }, + { 0x06f6, 0x0416 }, + { 0x06f7, 0x0412 }, + { 0x06f8, 0x042c }, + { 0x06f9, 0x042b }, + { 0x06fa, 0x0417 }, + { 0x06fb, 0x0428 }, + { 0x06fc, 0x042d }, + { 0x06fd, 0x0429 }, + { 0x06fe, 0x0427 }, + { 0x06ff, 0x042a }, + { 0x07a1, 0x0386 }, + { 0x07a2, 0x0388 }, + { 0x07a3, 0x0389 }, + { 0x07a4, 0x038a }, + { 0x07a5, 0x03aa }, + { 0x07a7, 0x038c }, + { 0x07a8, 0x038e }, + { 0x07a9, 0x03ab }, + { 0x07ab, 0x038f }, + { 0x07ae, 0x0385 }, + { 0x07af, 0x2015 }, + { 0x07b1, 0x03ac }, + { 0x07b2, 0x03ad }, + { 0x07b3, 0x03ae }, + { 0x07b4, 0x03af }, + { 0x07b5, 0x03ca }, + { 0x07b6, 0x0390 }, + { 0x07b7, 0x03cc }, + { 0x07b8, 0x03cd }, + { 0x07b9, 0x03cb }, + { 0x07ba, 0x03b0 }, + { 0x07bb, 0x03ce }, + { 0x07c1, 0x0391 }, + { 0x07c2, 0x0392 }, + { 0x07c3, 0x0393 }, + { 0x07c4, 0x0394 }, + { 0x07c5, 0x0395 }, + { 0x07c6, 0x0396 }, + { 0x07c7, 0x0397 }, + { 0x07c8, 0x0398 }, + { 0x07c9, 0x0399 }, + { 0x07ca, 0x039a }, + { 0x07cb, 0x039b }, + { 0x07cc, 0x039c }, + { 0x07cd, 0x039d }, + { 0x07ce, 0x039e }, + { 0x07cf, 0x039f }, + { 0x07d0, 0x03a0 }, + { 0x07d1, 0x03a1 }, + { 0x07d2, 0x03a3 }, + { 0x07d4, 0x03a4 }, + { 0x07d5, 0x03a5 }, + { 0x07d6, 0x03a6 }, + { 0x07d7, 0x03a7 }, + { 0x07d8, 0x03a8 }, + { 0x07d9, 0x03a9 }, + { 0x07e1, 0x03b1 }, + { 0x07e2, 0x03b2 }, + { 0x07e3, 0x03b3 }, + { 0x07e4, 0x03b4 }, + { 0x07e5, 0x03b5 }, + { 0x07e6, 0x03b6 }, + { 0x07e7, 0x03b7 }, + { 0x07e8, 0x03b8 }, + { 0x07e9, 0x03b9 }, + { 0x07ea, 0x03ba }, + { 0x07eb, 0x03bb }, + { 0x07ec, 0x03bc }, + { 0x07ed, 0x03bd }, + { 0x07ee, 0x03be }, + { 0x07ef, 0x03bf }, + { 0x07f0, 0x03c0 }, + { 0x07f1, 0x03c1 }, + { 0x07f2, 0x03c3 }, + { 0x07f3, 0x03c2 }, + { 0x07f4, 0x03c4 }, + { 0x07f5, 0x03c5 }, + { 0x07f6, 0x03c6 }, + { 0x07f7, 0x03c7 }, + { 0x07f8, 0x03c8 }, + { 0x07f9, 0x03c9 }, + { 0x08a1, 0x23b7 }, + { 0x08a2, 0x250c }, + { 0x08a3, 0x2500 }, + { 0x08a4, 0x2320 }, + { 0x08a5, 0x2321 }, + { 0x08a6, 0x2502 }, + { 0x08a7, 0x23a1 }, + { 0x08a8, 0x23a3 }, + { 0x08a9, 0x23a4 }, + { 0x08aa, 0x23a6 }, + { 0x08ab, 0x239b }, + { 0x08ac, 0x239d }, + { 0x08ad, 0x239e }, + { 0x08ae, 0x23a0 }, + { 0x08af, 0x23a8 }, + { 0x08b0, 0x23ac }, + { 0x08bc, 0x2264 }, + { 0x08bd, 0x2260 }, + { 0x08be, 0x2265 }, + { 0x08bf, 0x222b }, + { 0x08c0, 0x2234 }, + { 0x08c1, 0x221d }, + { 0x08c2, 0x221e }, + { 0x08c5, 0x2207 }, + { 0x08c8, 0x223c }, + { 0x08c9, 0x2243 }, + { 0x08cd, 0x21d4 }, + { 0x08ce, 0x21d2 }, + { 0x08cf, 0x2261 }, + { 0x08d6, 0x221a }, + { 0x08da, 0x2282 }, + { 0x08db, 0x2283 }, + { 0x08dc, 0x2229 }, + { 0x08dd, 0x222a }, + { 0x08de, 0x2227 }, + { 0x08df, 0x2228 }, + { 0x08ef, 0x2202 }, + { 0x08f6, 0x0192 }, + { 0x08fb, 0x2190 }, + { 0x08fc, 0x2191 }, + { 0x08fd, 0x2192 }, + { 0x08fe, 0x2193 }, + { 0x09e0, 0x25c6 }, + { 0x09e1, 0x2592 }, + { 0x09e2, 0x2409 }, + { 0x09e3, 0x240c }, + { 0x09e4, 0x240d }, + { 0x09e5, 0x240a }, + { 0x09e8, 0x2424 }, + { 0x09e9, 0x240b }, + { 0x09ea, 0x2518 }, + { 0x09eb, 0x2510 }, + { 0x09ec, 0x250c }, + { 0x09ed, 0x2514 }, + { 0x09ee, 0x253c }, + { 0x09ef, 0x23ba }, + { 0x09f0, 0x23bb }, + { 0x09f1, 0x2500 }, + { 0x09f2, 0x23bc }, + { 0x09f3, 0x23bd }, + { 0x09f4, 0x251c }, + { 0x09f5, 0x2524 }, + { 0x09f6, 0x2534 }, + { 0x09f7, 0x252c }, + { 0x09f8, 0x2502 }, + { 0x0aa1, 0x2003 }, + { 0x0aa2, 0x2002 }, + { 0x0aa3, 0x2004 }, + { 0x0aa4, 0x2005 }, + { 0x0aa5, 0x2007 }, + { 0x0aa6, 0x2008 }, + { 0x0aa7, 0x2009 }, + { 0x0aa8, 0x200a }, + { 0x0aa9, 0x2014 }, + { 0x0aaa, 0x2013 }, + { 0x0aae, 0x2026 }, + { 0x0aaf, 0x2025 }, + { 0x0ab0, 0x2153 }, + { 0x0ab1, 0x2154 }, + { 0x0ab2, 0x2155 }, + { 0x0ab3, 0x2156 }, + { 0x0ab4, 0x2157 }, + { 0x0ab5, 0x2158 }, + { 0x0ab6, 0x2159 }, + { 0x0ab7, 0x215a }, + { 0x0ab8, 0x2105 }, + { 0x0abb, 0x2012 }, + { 0x0abc, 0x2329 }, + { 0x0abe, 0x232a }, + { 0x0ac3, 0x215b }, + { 0x0ac4, 0x215c }, + { 0x0ac5, 0x215d }, + { 0x0ac6, 0x215e }, + { 0x0ac9, 0x2122 }, + { 0x0aca, 0x2613 }, + { 0x0acc, 0x25c1 }, + { 0x0acd, 0x25b7 }, + { 0x0ace, 0x25cb }, + { 0x0acf, 0x25af }, + { 0x0ad0, 0x2018 }, + { 0x0ad1, 0x2019 }, + { 0x0ad2, 0x201c }, + { 0x0ad3, 0x201d }, + { 0x0ad4, 0x211e }, + { 0x0ad6, 0x2032 }, + { 0x0ad7, 0x2033 }, + { 0x0ad9, 0x271d }, + { 0x0adb, 0x25ac }, + { 0x0adc, 0x25c0 }, + { 0x0add, 0x25b6 }, + { 0x0ade, 0x25cf }, + { 0x0adf, 0x25ae }, + { 0x0ae0, 0x25e6 }, + { 0x0ae1, 0x25ab }, + { 0x0ae2, 0x25ad }, + { 0x0ae3, 0x25b3 }, + { 0x0ae4, 0x25bd }, + { 0x0ae5, 0x2606 }, + { 0x0ae6, 0x2022 }, + { 0x0ae7, 0x25aa }, + { 0x0ae8, 0x25b2 }, + { 0x0ae9, 0x25bc }, + { 0x0aea, 0x261c }, + { 0x0aeb, 0x261e }, + { 0x0aec, 0x2663 }, + { 0x0aed, 0x2666 }, + { 0x0aee, 0x2665 }, + { 0x0af0, 0x2720 }, + { 0x0af1, 0x2020 }, + { 0x0af2, 0x2021 }, + { 0x0af3, 0x2713 }, + { 0x0af4, 0x2717 }, + { 0x0af5, 0x266f }, + { 0x0af6, 0x266d }, + { 0x0af7, 0x2642 }, + { 0x0af8, 0x2640 }, + { 0x0af9, 0x260e }, + { 0x0afa, 0x2315 }, + { 0x0afb, 0x2117 }, + { 0x0afc, 0x2038 }, + { 0x0afd, 0x201a }, + { 0x0afe, 0x201e }, + { 0x0ba3, 0x003c }, + { 0x0ba6, 0x003e }, + { 0x0ba8, 0x2228 }, + { 0x0ba9, 0x2227 }, + { 0x0bc0, 0x00af }, + { 0x0bc2, 0x22a5 }, + { 0x0bc3, 0x2229 }, + { 0x0bc4, 0x230a }, + { 0x0bc6, 0x005f }, + { 0x0bca, 0x2218 }, + { 0x0bcc, 0x2395 }, + { 0x0bce, 0x22a4 }, + { 0x0bcf, 0x25cb }, + { 0x0bd3, 0x2308 }, + { 0x0bd6, 0x222a }, + { 0x0bd8, 0x2283 }, + { 0x0bda, 0x2282 }, + { 0x0bdc, 0x22a2 }, + { 0x0bfc, 0x22a3 }, + { 0x0cdf, 0x2017 }, + { 0x0ce0, 0x05d0 }, + { 0x0ce1, 0x05d1 }, + { 0x0ce2, 0x05d2 }, + { 0x0ce3, 0x05d3 }, + { 0x0ce4, 0x05d4 }, + { 0x0ce5, 0x05d5 }, + { 0x0ce6, 0x05d6 }, + { 0x0ce7, 0x05d7 }, + { 0x0ce8, 0x05d8 }, + { 0x0ce9, 0x05d9 }, + { 0x0cea, 0x05da }, + { 0x0ceb, 0x05db }, + { 0x0cec, 0x05dc }, + { 0x0ced, 0x05dd }, + { 0x0cee, 0x05de }, + { 0x0cef, 0x05df }, + { 0x0cf0, 0x05e0 }, + { 0x0cf1, 0x05e1 }, + { 0x0cf2, 0x05e2 }, + { 0x0cf3, 0x05e3 }, + { 0x0cf4, 0x05e4 }, + { 0x0cf5, 0x05e5 }, + { 0x0cf6, 0x05e6 }, + { 0x0cf7, 0x05e7 }, + { 0x0cf8, 0x05e8 }, + { 0x0cf9, 0x05e9 }, + { 0x0cfa, 0x05ea }, + { 0x0da1, 0x0e01 }, + { 0x0da2, 0x0e02 }, + { 0x0da3, 0x0e03 }, + { 0x0da4, 0x0e04 }, + { 0x0da5, 0x0e05 }, + { 0x0da6, 0x0e06 }, + { 0x0da7, 0x0e07 }, + { 0x0da8, 0x0e08 }, + { 0x0da9, 0x0e09 }, + { 0x0daa, 0x0e0a }, + { 0x0dab, 0x0e0b }, + { 0x0dac, 0x0e0c }, + { 0x0dad, 0x0e0d }, + { 0x0dae, 0x0e0e }, + { 0x0daf, 0x0e0f }, + { 0x0db0, 0x0e10 }, + { 0x0db1, 0x0e11 }, + { 0x0db2, 0x0e12 }, + { 0x0db3, 0x0e13 }, + { 0x0db4, 0x0e14 }, + { 0x0db5, 0x0e15 }, + { 0x0db6, 0x0e16 }, + { 0x0db7, 0x0e17 }, + { 0x0db8, 0x0e18 }, + { 0x0db9, 0x0e19 }, + { 0x0dba, 0x0e1a }, + { 0x0dbb, 0x0e1b }, + { 0x0dbc, 0x0e1c }, + { 0x0dbd, 0x0e1d }, + { 0x0dbe, 0x0e1e }, + { 0x0dbf, 0x0e1f }, + { 0x0dc0, 0x0e20 }, + { 0x0dc1, 0x0e21 }, + { 0x0dc2, 0x0e22 }, + { 0x0dc3, 0x0e23 }, + { 0x0dc4, 0x0e24 }, + { 0x0dc5, 0x0e25 }, + { 0x0dc6, 0x0e26 }, + { 0x0dc7, 0x0e27 }, + { 0x0dc8, 0x0e28 }, + { 0x0dc9, 0x0e29 }, + { 0x0dca, 0x0e2a }, + { 0x0dcb, 0x0e2b }, + { 0x0dcc, 0x0e2c }, + { 0x0dcd, 0x0e2d }, + { 0x0dce, 0x0e2e }, + { 0x0dcf, 0x0e2f }, + { 0x0dd0, 0x0e30 }, + { 0x0dd1, 0x0e31 }, + { 0x0dd2, 0x0e32 }, + { 0x0dd3, 0x0e33 }, + { 0x0dd4, 0x0e34 }, + { 0x0dd5, 0x0e35 }, + { 0x0dd6, 0x0e36 }, + { 0x0dd7, 0x0e37 }, + { 0x0dd8, 0x0e38 }, + { 0x0dd9, 0x0e39 }, + { 0x0dda, 0x0e3a }, + { 0x0ddf, 0x0e3f }, + { 0x0de0, 0x0e40 }, + { 0x0de1, 0x0e41 }, + { 0x0de2, 0x0e42 }, + { 0x0de3, 0x0e43 }, + { 0x0de4, 0x0e44 }, + { 0x0de5, 0x0e45 }, + { 0x0de6, 0x0e46 }, + { 0x0de7, 0x0e47 }, + { 0x0de8, 0x0e48 }, + { 0x0de9, 0x0e49 }, + { 0x0dea, 0x0e4a }, + { 0x0deb, 0x0e4b }, + { 0x0dec, 0x0e4c }, + { 0x0ded, 0x0e4d }, + { 0x0df0, 0x0e50 }, + { 0x0df1, 0x0e51 }, + { 0x0df2, 0x0e52 }, + { 0x0df3, 0x0e53 }, + { 0x0df4, 0x0e54 }, + { 0x0df5, 0x0e55 }, + { 0x0df6, 0x0e56 }, + { 0x0df7, 0x0e57 }, + { 0x0df8, 0x0e58 }, + { 0x0df9, 0x0e59 }, + { 0x0ea1, 0x3131 }, + { 0x0ea2, 0x3132 }, + { 0x0ea3, 0x3133 }, + { 0x0ea4, 0x3134 }, + { 0x0ea5, 0x3135 }, + { 0x0ea6, 0x3136 }, + { 0x0ea7, 0x3137 }, + { 0x0ea8, 0x3138 }, + { 0x0ea9, 0x3139 }, + { 0x0eaa, 0x313a }, + { 0x0eab, 0x313b }, + { 0x0eac, 0x313c }, + { 0x0ead, 0x313d }, + { 0x0eae, 0x313e }, + { 0x0eaf, 0x313f }, + { 0x0eb0, 0x3140 }, + { 0x0eb1, 0x3141 }, + { 0x0eb2, 0x3142 }, + { 0x0eb3, 0x3143 }, + { 0x0eb4, 0x3144 }, + { 0x0eb5, 0x3145 }, + { 0x0eb6, 0x3146 }, + { 0x0eb7, 0x3147 }, + { 0x0eb8, 0x3148 }, + { 0x0eb9, 0x3149 }, + { 0x0eba, 0x314a }, + { 0x0ebb, 0x314b }, + { 0x0ebc, 0x314c }, + { 0x0ebd, 0x314d }, + { 0x0ebe, 0x314e }, + { 0x0ebf, 0x314f }, + { 0x0ec0, 0x3150 }, + { 0x0ec1, 0x3151 }, + { 0x0ec2, 0x3152 }, + { 0x0ec3, 0x3153 }, + { 0x0ec4, 0x3154 }, + { 0x0ec5, 0x3155 }, + { 0x0ec6, 0x3156 }, + { 0x0ec7, 0x3157 }, + { 0x0ec8, 0x3158 }, + { 0x0ec9, 0x3159 }, + { 0x0eca, 0x315a }, + { 0x0ecb, 0x315b }, + { 0x0ecc, 0x315c }, + { 0x0ecd, 0x315d }, + { 0x0ece, 0x315e }, + { 0x0ecf, 0x315f }, + { 0x0ed0, 0x3160 }, + { 0x0ed1, 0x3161 }, + { 0x0ed2, 0x3162 }, + { 0x0ed3, 0x3163 }, + { 0x0ed4, 0x11a8 }, + { 0x0ed5, 0x11a9 }, + { 0x0ed6, 0x11aa }, + { 0x0ed7, 0x11ab }, + { 0x0ed8, 0x11ac }, + { 0x0ed9, 0x11ad }, + { 0x0eda, 0x11ae }, + { 0x0edb, 0x11af }, + { 0x0edc, 0x11b0 }, + { 0x0edd, 0x11b1 }, + { 0x0ede, 0x11b2 }, + { 0x0edf, 0x11b3 }, + { 0x0ee0, 0x11b4 }, + { 0x0ee1, 0x11b5 }, + { 0x0ee2, 0x11b6 }, + { 0x0ee3, 0x11b7 }, + { 0x0ee4, 0x11b8 }, + { 0x0ee5, 0x11b9 }, + { 0x0ee6, 0x11ba }, + { 0x0ee7, 0x11bb }, + { 0x0ee8, 0x11bc }, + { 0x0ee9, 0x11bd }, + { 0x0eea, 0x11be }, + { 0x0eeb, 0x11bf }, + { 0x0eec, 0x11c0 }, + { 0x0eed, 0x11c1 }, + { 0x0eee, 0x11c2 }, + { 0x0eef, 0x316d }, + { 0x0ef0, 0x3171 }, + { 0x0ef1, 0x3178 }, + { 0x0ef2, 0x317f }, + { 0x0ef3, 0x3181 }, + { 0x0ef4, 0x3184 }, + { 0x0ef5, 0x3186 }, + { 0x0ef6, 0x318d }, + { 0x0ef7, 0x318e }, + { 0x0ef8, 0x11eb }, + { 0x0ef9, 0x11f0 }, + { 0x0efa, 0x11f9 }, + { 0x0eff, 0x20a9 }, + { 0x13a4, 0x20ac }, + { 0x13bc, 0x0152 }, + { 0x13bd, 0x0153 }, + { 0x13be, 0x0178 }, + { 0x20ac, 0x20ac }, + // Numeric keypad with numlock on + { XK_KP_Space, ' ' }, + { XK_KP_Equal, '=' }, + { XK_KP_Multiply, '*' }, + { XK_KP_Add, '+' }, + { XK_KP_Separator, ',' }, + { XK_KP_Subtract, '-' }, + { XK_KP_Decimal, '.' }, + { XK_KP_Divide, '/' }, + { XK_KP_0, 0x0030 }, + { XK_KP_1, 0x0031 }, + { XK_KP_2, 0x0032 }, + { XK_KP_3, 0x0033 }, + { XK_KP_4, 0x0034 }, + { XK_KP_5, 0x0035 }, + { XK_KP_6, 0x0036 }, + { XK_KP_7, 0x0037 }, + { XK_KP_8, 0x0038 }, + { XK_KP_9, 0x0039 } +}; + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +//======================================================================== +// _glfwKeySym2Unicode() - Convert X11 KeySym to Unicode +//======================================================================== + +long _glfwKeySym2Unicode( KeySym keysym ) +{ + int min = 0; + int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; + int mid; + + /* First check for Latin-1 characters (1:1 mapping) */ + if( (keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff) ) + { return keysym; + } + + /* Also check for directly encoded 24-bit UCS characters */ + if( (keysym & 0xff000000) == 0x01000000 ) + { + return keysym & 0x00ffffff; + } + + /* Binary search in table */ + while( max >= min ) + { + mid = (min + max) / 2; + if( keysymtab[mid].keysym < keysym ) + { + min = mid + 1; + } + else if( keysymtab[mid].keysym > keysym ) + { + max = mid - 1; + } + else + { + /* Found it! */ + return keysymtab[mid].ucs; + } + } + + /* No matching Unicode value found */ + return -1; +} diff --git a/lib/x11/x11_time.c b/lib/x11/x11_time.c new file mode 100644 index 00000000..0cff709f --- /dev/null +++ b/lib/x11/x11_time.c @@ -0,0 +1,89 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: X11/GLX +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + + +//======================================================================== +// Initialise timer +//======================================================================== + +void _glfwInitTimer( void ) +{ + struct timeval tv; + + // "Resolution" is 1 us + _glfwLibrary.Timer.resolution = 1e-6; + + // Set start-time for timer + gettimeofday( &tv, NULL ); + _glfwLibrary.Timer.t0 = (long long) tv.tv_sec * (long long) 1000000 + + (long long) tv.tv_usec; +} + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Return timer value in seconds +//======================================================================== + +double _glfwPlatformGetTime( void ) +{ + long long t; + struct timeval tv; + + gettimeofday( &tv, NULL ); + t = (long long) tv.tv_sec * (long long) 1000000 + + (long long) tv.tv_usec; + + return (double)(t - _glfwLibrary.Timer.t0) * _glfwLibrary.Timer.resolution; +} + + +//======================================================================== +// Set timer value in seconds +//======================================================================== + +void _glfwPlatformSetTime( double t ) +{ + long long t0; + struct timeval tv; + + gettimeofday( &tv, NULL ); + t0 = (long long) tv.tv_sec * (long long) 1000000 + + (long long) tv.tv_usec; + + // Calulate new starting time + _glfwLibrary.Timer.t0 = t0 - (long long)(t/_glfwLibrary.Timer.resolution); +} + diff --git a/lib/x11/x11_window.c b/lib/x11/x11_window.c new file mode 100644 index 00000000..e382e8ed --- /dev/null +++ b/lib/x11/x11_window.c @@ -0,0 +1,1875 @@ +//======================================================================== +// GLFW - An OpenGL framework +// Platform: X11/GLX +// API version: 2.7 +// WWW: http://www.glfw.org/ +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2010 Camilla Berglund +// +// 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 "internal.h" + +#include + + +/* Define GLX 1.4 FSAA tokens if not already defined */ +#ifndef GLX_VERSION_1_4 + +#define GLX_SAMPLE_BUFFERS 100000 +#define GLX_SAMPLES 100001 + +#endif /*GLX_VERSION_1_4*/ + +// Action for EWMH client messages +#define _NET_WM_STATE_REMOVE 0 +#define _NET_WM_STATE_ADD 1 +#define _NET_WM_STATE_TOGGLE 2 + + +//************************************************************************ +//**** GLFW internal functions **** +//************************************************************************ + +//======================================================================== +// Checks whether the event is a MapNotify for the specified window +//======================================================================== + +static Bool isMapNotify( Display *d, XEvent *e, char *arg ) +{ + return (e->type == MapNotify) && (e->xmap.window == (Window)arg); +} + + +//======================================================================== +// Retrieve a single window property of the specified type +// Inspired by fghGetWindowProperty from freeglut +//======================================================================== + +static unsigned long getWindowProperty( Window window, + Atom property, + Atom type, + unsigned char** value ) +{ + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + + XGetWindowProperty( _glfwLibrary.display, + window, + property, + 0, + LONG_MAX, + False, + type, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + value ); + + if( actualType != type ) + { + return 0; + } + + return itemCount; +} + + +//======================================================================== +// Check whether the specified atom is supported +//======================================================================== + +static Atom getSupportedAtom( Atom* supportedAtoms, + unsigned long atomCount, + const char* atomName ) +{ + Atom atom = XInternAtom( _glfwLibrary.display, atomName, True ); + if( atom != None ) + { + unsigned long i; + + for( i = 0; i < atomCount; i++ ) + { + if( supportedAtoms[i] == atom ) + { + return atom; + } + } + } + + return None; +} + + +//======================================================================== +// Check whether the running window manager is EWMH-compliant +//======================================================================== + +static GLboolean checkForEWMH( void ) +{ + Window *windowFromRoot = NULL; + Window *windowFromChild = NULL; + + // Hey kids; let's see if the window manager supports EWMH! + + // First we need a couple of atoms, which should already be there + Atom supportingWmCheck = XInternAtom( _glfwLibrary.display, + "_NET_SUPPORTING_WM_CHECK", + True ); + Atom wmSupported = XInternAtom( _glfwLibrary.display, + "_NET_SUPPORTED", + True ); + if( supportingWmCheck == None || wmSupported == None ) + { + return GL_FALSE; + } + + // Then we look for the _NET_SUPPORTING_WM_CHECK property of the root window + if( getWindowProperty( _glfwWin.root, + supportingWmCheck, + XA_WINDOW, + (unsigned char**) &windowFromRoot ) != 1 ) + { + XFree( windowFromRoot ); + return GL_FALSE; + } + + // It should be the ID of a child window (of the root) + // Then we look for the same property on the child window + if( getWindowProperty( *windowFromRoot, + supportingWmCheck, + XA_WINDOW, + (unsigned char**) &windowFromChild ) != 1 ) + { + XFree( windowFromRoot ); + XFree( windowFromChild ); + return GL_FALSE; + } + + // It should be the ID of that same child window + if( *windowFromRoot != *windowFromChild ) + { + XFree( windowFromRoot ); + XFree( windowFromChild ); + return GL_FALSE; + } + + XFree( windowFromRoot ); + XFree( windowFromChild ); + + // We are now fairly sure that an EWMH-compliant window manager is running + + Atom *supportedAtoms; + unsigned long atomCount; + + // Now we need to check the _NET_SUPPORTED property of the root window + atomCount = getWindowProperty( _glfwWin.root, + wmSupported, + XA_ATOM, + (unsigned char**) &supportedAtoms ); + + // See which of the atoms we support that are supported by the WM + + _glfwWin.wmState = getSupportedAtom( supportedAtoms, + atomCount, + "_NET_WM_STATE" ); + + _glfwWin.wmStateFullscreen = getSupportedAtom( supportedAtoms, + atomCount, + "_NET_WM_STATE_FULLSCREEN" ); + + _glfwWin.wmPing = getSupportedAtom( supportedAtoms, + atomCount, + "_NET_WM_PING" ); + + _glfwWin.wmActiveWindow = getSupportedAtom( supportedAtoms, + atomCount, + "_NET_ACTIVE_WINDOW" ); + + XFree( supportedAtoms ); + + return GL_TRUE; +} + +//======================================================================== +// Translates an X Window key to internal coding +//======================================================================== + +static int translateKey( int keycode ) +{ + KeySym key, key_lc, key_uc; + + // Try secondary keysym, for numeric keypad keys + // Note: This way we always force "NumLock = ON", which at least + // enables GLFW users to detect numeric keypad keys + key = XKeycodeToKeysym( _glfwLibrary.display, keycode, 1 ); + switch( key ) + { + // Numeric keypad + case XK_KP_0: return GLFW_KEY_KP_0; + case XK_KP_1: return GLFW_KEY_KP_1; + case XK_KP_2: return GLFW_KEY_KP_2; + case XK_KP_3: return GLFW_KEY_KP_3; + case XK_KP_4: return GLFW_KEY_KP_4; + case XK_KP_5: return GLFW_KEY_KP_5; + case XK_KP_6: return GLFW_KEY_KP_6; + case XK_KP_7: return GLFW_KEY_KP_7; + case XK_KP_8: return GLFW_KEY_KP_8; + case XK_KP_9: return GLFW_KEY_KP_9; + case XK_KP_Separator: + case XK_KP_Decimal: return GLFW_KEY_KP_DECIMAL; + case XK_KP_Equal: return GLFW_KEY_KP_EQUAL; + case XK_KP_Enter: return GLFW_KEY_KP_ENTER; + default: break; + } + + // Now try pimary keysym + key = XKeycodeToKeysym( _glfwLibrary.display, keycode, 0 ); + switch( key ) + { + // Special keys (non character keys) + case XK_Escape: return GLFW_KEY_ESC; + case XK_Tab: return GLFW_KEY_TAB; + case XK_Shift_L: return GLFW_KEY_LSHIFT; + case XK_Shift_R: return GLFW_KEY_RSHIFT; + case XK_Control_L: return GLFW_KEY_LCTRL; + case XK_Control_R: return GLFW_KEY_RCTRL; + case XK_Meta_L: + case XK_Alt_L: return GLFW_KEY_LALT; + case XK_Mode_switch: // Mapped to Alt_R on many keyboards + case XK_Meta_R: + case XK_ISO_Level3_Shift: // AltGr on at least some machines + case XK_Alt_R: return GLFW_KEY_RALT; + case XK_Super_L: return GLFW_KEY_LSUPER; + case XK_Super_R: return GLFW_KEY_RSUPER; + case XK_Menu: return GLFW_KEY_MENU; + case XK_Num_Lock: return GLFW_KEY_KP_NUM_LOCK; + case XK_Caps_Lock: return GLFW_KEY_CAPS_LOCK; + case XK_Scroll_Lock: return GLFW_KEY_SCROLL_LOCK; + case XK_Pause: return GLFW_KEY_PAUSE; + case XK_KP_Delete: + case XK_Delete: return GLFW_KEY_DEL; + case XK_BackSpace: return GLFW_KEY_BACKSPACE; + case XK_Return: return GLFW_KEY_ENTER; + case XK_KP_Home: + case XK_Home: return GLFW_KEY_HOME; + case XK_KP_End: + case XK_End: return GLFW_KEY_END; + case XK_KP_Page_Up: + case XK_Page_Up: return GLFW_KEY_PAGEUP; + case XK_KP_Page_Down: + case XK_Page_Down: return GLFW_KEY_PAGEDOWN; + case XK_KP_Insert: + case XK_Insert: return GLFW_KEY_INSERT; + case XK_KP_Left: + case XK_Left: return GLFW_KEY_LEFT; + case XK_KP_Right: + case XK_Right: return GLFW_KEY_RIGHT; + case XK_KP_Down: + case XK_Down: return GLFW_KEY_DOWN; + case XK_KP_Up: + case XK_Up: return GLFW_KEY_UP; + case XK_F1: return GLFW_KEY_F1; + case XK_F2: return GLFW_KEY_F2; + case XK_F3: return GLFW_KEY_F3; + case XK_F4: return GLFW_KEY_F4; + case XK_F5: return GLFW_KEY_F5; + case XK_F6: return GLFW_KEY_F6; + case XK_F7: return GLFW_KEY_F7; + case XK_F8: return GLFW_KEY_F8; + case XK_F9: return GLFW_KEY_F9; + case XK_F10: return GLFW_KEY_F10; + case XK_F11: return GLFW_KEY_F11; + case XK_F12: return GLFW_KEY_F12; + case XK_F13: return GLFW_KEY_F13; + case XK_F14: return GLFW_KEY_F14; + case XK_F15: return GLFW_KEY_F15; + case XK_F16: return GLFW_KEY_F16; + case XK_F17: return GLFW_KEY_F17; + case XK_F18: return GLFW_KEY_F18; + case XK_F19: return GLFW_KEY_F19; + case XK_F20: return GLFW_KEY_F20; + case XK_F21: return GLFW_KEY_F21; + case XK_F22: return GLFW_KEY_F22; + case XK_F23: return GLFW_KEY_F23; + case XK_F24: return GLFW_KEY_F24; + case XK_F25: return GLFW_KEY_F25; + + // Numeric keypad (should have been detected in secondary keysym!) + case XK_KP_Divide: return GLFW_KEY_KP_DIVIDE; + case XK_KP_Multiply: return GLFW_KEY_KP_MULTIPLY; + case XK_KP_Subtract: return GLFW_KEY_KP_SUBTRACT; + case XK_KP_Add: return GLFW_KEY_KP_ADD; + case XK_KP_Equal: return GLFW_KEY_KP_EQUAL; + case XK_KP_Enter: return GLFW_KEY_KP_ENTER; + + // The rest (should be printable keys) + default: + // Make uppercase + XConvertCase( key, &key_lc, &key_uc ); + key = key_uc; + + // Valid ISO 8859-1 character? + if( (key >= 32 && key <= 126) || + (key >= 160 && key <= 255) ) + { + return (int) key; + } + return GLFW_KEY_UNKNOWN; + } +} + + +//======================================================================== +// Translates an X Window event to Unicode +//======================================================================== + +static int translateChar( XKeyEvent *event ) +{ + KeySym keysym; + + // Get X11 keysym + XLookupString( event, NULL, 0, &keysym, NULL ); + + // Convert to Unicode (see x11_keysym2unicode.c) + return (int) _glfwKeySym2Unicode( keysym ); +} + + +//======================================================================== +// Create a blank cursor (for locked mouse mode) +//======================================================================== + +static Cursor createNULLCursor( Display *display, Window root ) +{ + Pixmap cursormask; + XGCValues xgc; + GC gc; + XColor col; + Cursor cursor; + + cursormask = XCreatePixmap( display, root, 1, 1, 1 ); + xgc.function = GXclear; + gc = XCreateGC( display, cursormask, GCFunction, &xgc ); + XFillRectangle( display, cursormask, gc, 0, 0, 1, 1 ); + col.pixel = 0; + col.red = 0; + col.flags = 4; + cursor = XCreatePixmapCursor( display, cursormask, cursormask, + &col,&col, 0,0 ); + XFreePixmap( display, cursormask ); + XFreeGC( display, gc ); + + return cursor; +} + + +//======================================================================== +// Returns the specified attribute of the specified GLXFBConfig +// NOTE: Do not call this unless we have found GLX 1.3+ or GLX_SGIX_fbconfig +//======================================================================== + +static int getFBConfigAttrib( GLXFBConfig fbconfig, int attrib ) +{ + int value; + + if( _glfwWin.has_GLX_SGIX_fbconfig ) + { + _glfwWin.GetFBConfigAttribSGIX( _glfwLibrary.display, fbconfig, attrib, &value ); + } + else + { + glXGetFBConfigAttrib( _glfwLibrary.display, fbconfig, attrib, &value ); + } + + return value; +} + + +//======================================================================== +// Return a list of available and usable framebuffer configs +//======================================================================== + +static _GLFWfbconfig *getFBConfigs( unsigned int *found ) +{ + GLXFBConfig *fbconfigs; + _GLFWfbconfig *result; + int i, count = 0; + + *found = 0; + + if( _glfwLibrary.glxMajor == 1 && _glfwLibrary.glxMinor < 3 ) + { + if( !_glfwWin.has_GLX_SGIX_fbconfig ) + { + fprintf( stderr, "GLXFBConfigs are not supported by the X server\n" ); + return NULL; + } + } + + if( _glfwWin.has_GLX_SGIX_fbconfig ) + { + fbconfigs = _glfwWin.ChooseFBConfigSGIX( _glfwLibrary.display, + _glfwWin.screen, + NULL, + &count ); + if( !count ) + { + fprintf( stderr, "No GLXFBConfigs returned\n" ); + return NULL; + } + } + else + { + fbconfigs = glXGetFBConfigs( _glfwLibrary.display, _glfwWin.screen, &count ); + if( !count ) + { + fprintf( stderr, "No GLXFBConfigs returned\n" ); + return NULL; + } + } + + result = (_GLFWfbconfig*) malloc( sizeof(_GLFWfbconfig) * count ); + if( !result ) + { + fprintf( stderr, "Out of memory\n" ); + return NULL; + } + + for( i = 0; i < count; i++ ) + { + if( !getFBConfigAttrib( fbconfigs[i], GLX_DOUBLEBUFFER ) || + !getFBConfigAttrib( fbconfigs[i], GLX_VISUAL_ID ) ) + { + // Only consider double-buffered GLXFBConfigs with associated visuals + continue; + } + + if( !( getFBConfigAttrib( fbconfigs[i], GLX_RENDER_TYPE ) & GLX_RGBA_BIT ) ) + { + // Only consider RGBA GLXFBConfigs + continue; + } + + if( !( getFBConfigAttrib( fbconfigs[i], GLX_DRAWABLE_TYPE ) & GLX_WINDOW_BIT ) ) + { + // Only consider window GLXFBConfigs + continue; + } + + result[*found].redBits = getFBConfigAttrib( fbconfigs[i], GLX_RED_SIZE ); + result[*found].greenBits = getFBConfigAttrib( fbconfigs[i], GLX_GREEN_SIZE ); + result[*found].blueBits = getFBConfigAttrib( fbconfigs[i], GLX_BLUE_SIZE ); + + result[*found].alphaBits = getFBConfigAttrib( fbconfigs[i], GLX_ALPHA_SIZE ); + result[*found].depthBits = getFBConfigAttrib( fbconfigs[i], GLX_DEPTH_SIZE ); + result[*found].stencilBits = getFBConfigAttrib( fbconfigs[i], GLX_STENCIL_SIZE ); + + result[*found].accumRedBits = getFBConfigAttrib( fbconfigs[i], GLX_ACCUM_RED_SIZE ); + result[*found].accumGreenBits = getFBConfigAttrib( fbconfigs[i], GLX_ACCUM_GREEN_SIZE ); + result[*found].accumBlueBits = getFBConfigAttrib( fbconfigs[i], GLX_ACCUM_BLUE_SIZE ); + result[*found].accumAlphaBits = getFBConfigAttrib( fbconfigs[i], GLX_ACCUM_ALPHA_SIZE ); + + result[*found].auxBuffers = getFBConfigAttrib( fbconfigs[i], GLX_AUX_BUFFERS ); + result[*found].stereo = getFBConfigAttrib( fbconfigs[i], GLX_STEREO ); + + if( _glfwWin.has_GLX_ARB_multisample ) + { + result[*found].samples = getFBConfigAttrib( fbconfigs[i], GLX_SAMPLES ); + } + else + { + result[*found].samples = 0; + } + + result[*found].platformID = (GLFWintptr) getFBConfigAttrib( fbconfigs[i], GLX_FBCONFIG_ID ); + + (*found)++; + } + + XFree( fbconfigs ); + + return result; +} + + +//======================================================================== +// Create the OpenGL context +//======================================================================== + +#define setGLXattrib( attribs, index, attribName, attribValue ) \ + attribs[index++] = attribName; \ + attribs[index++] = attribValue; + +static int createContext( const _GLFWwndconfig *wndconfig, GLXFBConfigID fbconfigID ) +{ + int attribs[40]; + int flags, dummy, index; + GLXFBConfig *fbconfig; + + // Retrieve the previously selected GLXFBConfig + { + index = 0; + + setGLXattrib( attribs, index, GLX_FBCONFIG_ID, (int) fbconfigID ); + setGLXattrib( attribs, index, None, None ); + + if( _glfwWin.has_GLX_SGIX_fbconfig ) + { + fbconfig = _glfwWin.ChooseFBConfigSGIX( _glfwLibrary.display, + _glfwWin.screen, + attribs, + &dummy ); + } + else + { + fbconfig = glXChooseFBConfig( _glfwLibrary.display, + _glfwWin.screen, + attribs, + &dummy ); + } + + if( fbconfig == NULL ) + { + fprintf(stderr, "Unable to retrieve the selected GLXFBConfig\n"); + return GL_FALSE; + } + } + + // Retrieve the corresponding visual + if( _glfwWin.has_GLX_SGIX_fbconfig ) + { + _glfwWin.visual = _glfwWin.GetVisualFromFBConfigSGIX( _glfwLibrary.display, + *fbconfig ); + } + else + { + _glfwWin.visual = glXGetVisualFromFBConfig( _glfwLibrary.display, *fbconfig ); + } + + if( _glfwWin.visual == NULL ) + { + XFree( fbconfig ); + + fprintf(stderr, "Unable to retrieve visual for GLXFBconfig\n"); + return GL_FALSE; + } + + if( _glfwWin.has_GLX_ARB_create_context ) + { + index = 0; + + if( wndconfig->glMajor != 1 || wndconfig->glMinor != 0 ) + { + // Request an explicitly versioned context + + setGLXattrib( attribs, index, GLX_CONTEXT_MAJOR_VERSION_ARB, wndconfig->glMajor ); + setGLXattrib( attribs, index, GLX_CONTEXT_MINOR_VERSION_ARB, wndconfig->glMinor ); + } + + if( wndconfig->glForward || wndconfig->glDebug ) + { + flags = 0; + + if( wndconfig->glForward ) + { + flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + } + + if( wndconfig->glDebug ) + { + flags |= GLX_CONTEXT_DEBUG_BIT_ARB; + } + + setGLXattrib( attribs, index, GLX_CONTEXT_FLAGS_ARB, flags ); + } + + if( wndconfig->glProfile ) + { + if( !_glfwWin.has_GLX_ARB_create_context_profile ) + { + fprintf( stderr, "OpenGL profile requested but GLX_ARB_create_context_profile " + "is unavailable\n" ); + return GL_FALSE; + } + + if( wndconfig->glProfile == GLFW_OPENGL_CORE_PROFILE ) + { + flags = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + } + else + { + flags = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + } + + setGLXattrib( attribs, index, GLX_CONTEXT_PROFILE_MASK_ARB, flags ); + } + + setGLXattrib( attribs, index, None, None ); + + _glfwWin.context = _glfwWin.CreateContextAttribsARB( _glfwLibrary.display, + *fbconfig, + NULL, + True, + attribs ); + } + else + { + if( _glfwWin.has_GLX_SGIX_fbconfig ) + { + _glfwWin.context = _glfwWin.CreateContextWithConfigSGIX( _glfwLibrary.display, + *fbconfig, + GLX_RGBA_TYPE, + NULL, + True ); + } + else + { + _glfwWin.context = glXCreateNewContext( _glfwLibrary.display, + *fbconfig, + GLX_RGBA_TYPE, + NULL, + True ); + } + } + + XFree( fbconfig ); + + if( _glfwWin.context == NULL ) + { + fprintf(stderr, "Unable to create OpenGL context\n"); + return GL_FALSE; + } + + _glfwWin.fbconfigID = fbconfigID; + + return GL_TRUE; +} + +#undef setGLXattrib + + +//======================================================================== +// Initialize GLX-specific extensions +//======================================================================== + +static void initGLXExtensions( void ) +{ + // This needs to include every function pointer loaded below + _glfwWin.SwapIntervalSGI = NULL; + _glfwWin.GetFBConfigAttribSGIX = NULL; + _glfwWin.ChooseFBConfigSGIX = NULL; + _glfwWin.CreateContextWithConfigSGIX = NULL; + _glfwWin.GetVisualFromFBConfigSGIX = NULL; + _glfwWin.CreateContextAttribsARB = NULL; + + // This needs to include every extension used below + _glfwWin.has_GLX_SGIX_fbconfig = GL_FALSE; + _glfwWin.has_GLX_SGI_swap_control = GL_FALSE; + _glfwWin.has_GLX_ARB_multisample = GL_FALSE; + _glfwWin.has_GLX_ARB_create_context = GL_FALSE; + _glfwWin.has_GLX_ARB_create_context_profile = GL_FALSE; + + if( _glfwPlatformExtensionSupported( "GLX_SGI_swap_control" ) ) + { + _glfwWin.SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) + _glfwPlatformGetProcAddress( "glXSwapIntervalSGI" ); + + if( _glfwWin.SwapIntervalSGI ) + { + _glfwWin.has_GLX_SGI_swap_control = GL_TRUE; + } + } + + if( _glfwPlatformExtensionSupported( "GLX_SGIX_fbconfig" ) ) + { + _glfwWin.GetFBConfigAttribSGIX = (PFNGLXGETFBCONFIGATTRIBSGIXPROC) + _glfwPlatformGetProcAddress( "glXGetFBConfigAttribSGIX" ); + _glfwWin.ChooseFBConfigSGIX = (PFNGLXCHOOSEFBCONFIGSGIXPROC) + _glfwPlatformGetProcAddress( "glXChooseFBConfigSGIX" ); + _glfwWin.CreateContextWithConfigSGIX = (PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC) + _glfwPlatformGetProcAddress( "glXCreateContextWithConfigSGIX" ); + _glfwWin.GetVisualFromFBConfigSGIX = (PFNGLXGETVISUALFROMFBCONFIGSGIXPROC) + _glfwPlatformGetProcAddress( "glXGetVisualFromFBConfigSGIX" ); + + if( _glfwWin.GetFBConfigAttribSGIX && + _glfwWin.ChooseFBConfigSGIX && + _glfwWin.CreateContextWithConfigSGIX && + _glfwWin.GetVisualFromFBConfigSGIX ) + { + _glfwWin.has_GLX_SGIX_fbconfig = GL_TRUE; + } + } + + if( _glfwPlatformExtensionSupported( "GLX_ARB_multisample" ) ) + { + _glfwWin.has_GLX_ARB_multisample = GL_TRUE; + } + + if( _glfwPlatformExtensionSupported( "GLX_ARB_create_context" ) ) + { + _glfwWin.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) + _glfwPlatformGetProcAddress( "glXCreateContextAttribsARB" ); + + if( _glfwWin.CreateContextAttribsARB ) + { + _glfwWin.has_GLX_ARB_create_context = GL_TRUE; + } + } + + if( _glfwPlatformExtensionSupported( "GLX_ARB_create_context_profile" ) ) + { + _glfwWin.has_GLX_ARB_create_context_profile = GL_TRUE; + } +} + + +//======================================================================== +// Create the X11 window (and its colormap) +//======================================================================== + +static GLboolean createWindow( int width, int height, + const _GLFWwndconfig *wndconfig ) +{ + XEvent event; + unsigned long wamask; + XSetWindowAttributes wa; + + // Every window needs a colormap + // Create one based on the visual used by the current context + + _glfwWin.colormap = XCreateColormap( _glfwLibrary.display, + _glfwWin.root, + _glfwWin.visual->visual, + AllocNone ); + + // Create the actual window + { + wamask = CWBorderPixel | CWColormap | CWEventMask; + + wa.colormap = _glfwWin.colormap; + wa.border_pixel = 0; + wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | + ExposureMask | FocusChangeMask | VisibilityChangeMask; + + if( wndconfig->mode == GLFW_WINDOW ) + { + // The /only/ reason we are setting the background pixel here is + // that otherwise our window wont get any decorations on systems + // using Compiz on Intel hardware + wa.background_pixel = BlackPixel( _glfwLibrary.display, _glfwWin.screen ); + wamask |= CWBackPixel; + } + + _glfwWin.window = XCreateWindow( + _glfwLibrary.display, + _glfwWin.root, + 0, 0, // Upper left corner of this window on root + _glfwWin.width, _glfwWin.height, + 0, // Border width + _glfwWin.visual->depth, // Color depth + InputOutput, + _glfwWin.visual->visual, + wamask, + &wa + ); + if( !_glfwWin.window ) + { + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + } + + // Check whether an EWMH-compliant window manager is running + _glfwWin.hasEWMH = checkForEWMH(); + + if( _glfwWin.fullscreen && !_glfwWin.hasEWMH ) + { + // This is the butcher's way of removing window decorations + // Setting the override-redirect attribute on a window makes the window + // manager ignore the window completely (ICCCM, section 4) + // The good thing is that this makes undecorated fullscreen windows + // easy to do; the bad thing is that we have to do everything manually + // and some things (like iconify/restore) won't work at all, as they're + // usually performed by the window manager + + XSetWindowAttributes attributes; + attributes.override_redirect = True; + XChangeWindowAttributes( _glfwLibrary.display, + _glfwWin.window, + CWOverrideRedirect, + &attributes ); + + _glfwWin.overrideRedirect = GL_TRUE; + } + + // Find or create the protocol atom for window close notifications + _glfwWin.wmDeleteWindow = XInternAtom( _glfwLibrary.display, + "WM_DELETE_WINDOW", + False ); + + // Declare the WM protocols we support + { + int count = 0; + Atom protocols[2]; + + // The WM_DELETE_WINDOW ICCCM protocol + // Basic window close notification protocol + if( _glfwWin.wmDeleteWindow != None ) + { + protocols[count++] = _glfwWin.wmDeleteWindow; + } + + // The _NET_WM_PING EWMH protocol + // Tells the WM to ping our window and flag us as unresponsive if we + // don't reply within a few seconds + if( _glfwWin.wmPing != None ) + { + protocols[count++] = _glfwWin.wmPing; + } + + if( count > 0 ) + { + XSetWMProtocols( _glfwLibrary.display, _glfwWin.window, + protocols, count ); + } + } + + // Set ICCCM WM_HINTS property + { + XWMHints *hints = XAllocWMHints(); + if( !hints ) + { + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + hints->flags = StateHint; + hints->initial_state = NormalState; + + XSetWMHints( _glfwLibrary.display, _glfwWin.window, hints ); + XFree( hints ); + } + + // Set ICCCM WM_NORMAL_HINTS property (even if no parts are set) + { + XSizeHints *hints = XAllocSizeHints(); + if( !hints ) + { + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + hints->flags = 0; + + if( wndconfig->windowNoResize ) + { + hints->flags |= (PMinSize | PMaxSize); + hints->min_width = hints->max_width = _glfwWin.width; + hints->min_height = hints->max_height = _glfwWin.height; + } + + XSetWMNormalHints( _glfwLibrary.display, _glfwWin.window, hints ); + XFree( hints ); + } + + _glfwPlatformSetWindowTitle( "GLFW Window" ); + + // Make sure the window is mapped before proceeding + XMapWindow( _glfwLibrary.display, _glfwWin.window ); + XPeekIfEvent( _glfwLibrary.display, &event, isMapNotify, + (char*)_glfwWin.window ); + + return GL_TRUE; +} + + +//======================================================================== +// Enter fullscreen mode +//======================================================================== + +static void enterFullscreenMode( void ) +{ + if( !_glfwWin.Saver.changed ) + { + // Remember old screen saver settings + XGetScreenSaver( _glfwLibrary.display, + &_glfwWin.Saver.timeout, &_glfwWin.Saver.interval, + &_glfwWin.Saver.blanking, &_glfwWin.Saver.exposure ); + + // Disable screen saver + XSetScreenSaver( _glfwLibrary.display, 0, 0, DontPreferBlanking, + DefaultExposures ); + + _glfwWin.Saver.changed = GL_TRUE; + } + + _glfwSetVideoMode( _glfwWin.screen, + &_glfwWin.width, &_glfwWin.height, + &_glfwWin.refreshRate ); + + if( _glfwWin.hasEWMH && + _glfwWin.wmState != None && + _glfwWin.wmStateFullscreen != None ) + { + if( _glfwWin.wmActiveWindow != None ) + { + // Ask the window manager to raise and focus the GLFW window + // Only focused windows with the _NET_WM_STATE_FULLSCREEN state end + // up on top of all other windows ("Stacking order" in EWMH spec) + + XEvent event; + memset( &event, 0, sizeof(event) ); + + event.type = ClientMessage; + event.xclient.window = _glfwWin.window; + event.xclient.format = 32; // Data is 32-bit longs + event.xclient.message_type = _glfwWin.wmActiveWindow; + event.xclient.data.l[0] = 1; // Sender is a normal application + event.xclient.data.l[1] = 0; // We don't really know the timestamp + + XSendEvent( _glfwLibrary.display, + _glfwWin.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event ); + } + + // Ask the window manager to make the GLFW window a fullscreen window + // Fullscreen windows are undecorated and, when focused, are kept + // on top of all other windows + + XEvent event; + memset( &event, 0, sizeof(event) ); + + event.type = ClientMessage; + event.xclient.window = _glfwWin.window; + event.xclient.format = 32; // Data is 32-bit longs + event.xclient.message_type = _glfwWin.wmState; + event.xclient.data.l[0] = _NET_WM_STATE_ADD; + event.xclient.data.l[1] = _glfwWin.wmStateFullscreen; + event.xclient.data.l[2] = 0; // No secondary property + event.xclient.data.l[3] = 1; // Sender is a normal application + + XSendEvent( _glfwLibrary.display, + _glfwWin.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event ); + } + else if( _glfwWin.overrideRedirect ) + { + // In override-redirect mode, we have divorced ourselves from the + // window manager, so we need to do everything manually + + XRaiseWindow( _glfwLibrary.display, _glfwWin.window ); + XSetInputFocus( _glfwLibrary.display, _glfwWin.window, + RevertToParent, CurrentTime ); + XMoveWindow( _glfwLibrary.display, _glfwWin.window, 0, 0 ); + XResizeWindow( _glfwLibrary.display, _glfwWin.window, + _glfwWin.width, _glfwWin.height ); + } + + if( _glfwWin.mouseLock ) + { + _glfwPlatformHideMouseCursor(); + } + + // HACK: Try to get window inside viewport (for virtual displays) by moving + // the mouse cursor to the upper left corner (and then to the center) + // This hack should be harmless on saner systems as well + XWarpPointer( _glfwLibrary.display, None, _glfwWin.window, 0,0,0,0, 0,0 ); + XWarpPointer( _glfwLibrary.display, None, _glfwWin.window, 0,0,0,0, + _glfwWin.width / 2, _glfwWin.height / 2 ); +} + +//======================================================================== +// Leave fullscreen mode +//======================================================================== + +static void leaveFullscreenMode( void ) +{ + _glfwRestoreVideoMode(); + + // Did we change the screen saver setting? + if( _glfwWin.Saver.changed ) + { + // Restore old screen saver settings + XSetScreenSaver( _glfwLibrary.display, + _glfwWin.Saver.timeout, + _glfwWin.Saver.interval, + _glfwWin.Saver.blanking, + _glfwWin.Saver.exposure ); + + _glfwWin.Saver.changed = GL_FALSE; + } + + if( _glfwWin.hasEWMH && + _glfwWin.wmState != None && + _glfwWin.wmStateFullscreen != None ) + { + // Ask the window manager to make the GLFW window a normal window + // Normal windows usually have frames and other decorations + + XEvent event; + memset( &event, 0, sizeof(event) ); + + event.type = ClientMessage; + event.xclient.window = _glfwWin.window; + event.xclient.format = 32; // Data is 32-bit longs + event.xclient.message_type = _glfwWin.wmState; + event.xclient.data.l[0] = _NET_WM_STATE_REMOVE; + event.xclient.data.l[1] = _glfwWin.wmStateFullscreen; + event.xclient.data.l[2] = 0; // No secondary property + event.xclient.data.l[3] = 1; // Sender is a normal application + + XSendEvent( _glfwLibrary.display, + _glfwWin.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event ); + } + + if( _glfwWin.mouseLock ) + { + _glfwPlatformShowMouseCursor(); + } +} + +//======================================================================== +// Get and process next X event (called by _glfwPlatformPollEvents) +// Returns GL_TRUE if a window close request was received +//======================================================================== + +static GLboolean processSingleEvent( void ) +{ + XEvent event; + XNextEvent( _glfwLibrary.display, &event ); + + switch( event.type ) + { + case KeyPress: + { + // A keyboard key was pressed + + // Translate and report key press + _glfwInputKey( translateKey( event.xkey.keycode ), GLFW_PRESS ); + + // Translate and report character input + if( _glfwWin.charCallback ) + { + _glfwInputChar( translateChar( &event.xkey ), GLFW_PRESS ); + } + break; + } + + case KeyRelease: + { + // A keyboard key was released + + // Do not report key releases for key repeats. For key repeats we + // will get KeyRelease/KeyPress pairs with similar or identical + // time stamps. User selected key repeat filtering is handled in + // _glfwInputKey()/_glfwInputChar(). + if( XEventsQueued( _glfwLibrary.display, QueuedAfterReading ) ) + { + XEvent nextEvent; + XPeekEvent( _glfwLibrary.display, &nextEvent ); + + if( nextEvent.type == KeyPress && + nextEvent.xkey.window == event.xkey.window && + nextEvent.xkey.keycode == event.xkey.keycode ) + { + // This last check is a hack to work around key repeats + // leaking through due to some sort of time drift + // Toshiyuki Takahashi can press a button 16 times per + // second so it's fairly safe to assume that no human is + // pressing the key 50 times per second (value is ms) + if( ( nextEvent.xkey.time - event.xkey.time ) < 20 ) + { + // Do not report anything for this event + break; + } + } + } + + // Translate and report key release + _glfwInputKey( translateKey( event.xkey.keycode ), GLFW_RELEASE ); + + // Translate and report character input + if( _glfwWin.charCallback ) + { + _glfwInputChar( translateChar( &event.xkey ), GLFW_RELEASE ); + } + break; + } + + case ButtonPress: + { + // A mouse button was pressed or a scrolling event occurred + + if( event.xbutton.button == Button1 ) + { + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS ); + } + else if( event.xbutton.button == Button2 ) + { + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS ); + } + else if( event.xbutton.button == Button3 ) + { + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS ); + } + + // XFree86 3.3.2 and later translates mouse wheel up/down into + // mouse button 4 & 5 presses + else if( event.xbutton.button == Button4 ) + { + _glfwInput.WheelPos++; // To verify: is this up or down? + if( _glfwWin.mouseWheelCallback ) + { + _glfwWin.mouseWheelCallback( _glfwInput.WheelPos ); + } + } + else if( event.xbutton.button == Button5 ) + { + _glfwInput.WheelPos--; + if( _glfwWin.mouseWheelCallback ) + { + _glfwWin.mouseWheelCallback( _glfwInput.WheelPos ); + } + } + break; + } + + case ButtonRelease: + { + // A mouse button was released + + if( event.xbutton.button == Button1 ) + { + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_LEFT, + GLFW_RELEASE ); + } + else if( event.xbutton.button == Button2 ) + { + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_MIDDLE, + GLFW_RELEASE ); + } + else if( event.xbutton.button == Button3 ) + { + _glfwInputMouseClick( GLFW_MOUSE_BUTTON_RIGHT, + GLFW_RELEASE ); + } + break; + } + + case MotionNotify: + { + // The mouse cursor was moved + + if( event.xmotion.x != _glfwInput.CursorPosX || + event.xmotion.y != _glfwInput.CursorPosY ) + { + // The mouse cursor was moved and we didn't do it + + if( _glfwWin.mouseLock ) + { + if( _glfwWin.pointerHidden ) + { + _glfwInput.MousePosX += event.xmotion.x - + _glfwInput.CursorPosX; + _glfwInput.MousePosY += event.xmotion.y - + _glfwInput.CursorPosY; + } + } + else + { + _glfwInput.MousePosX = event.xmotion.x; + _glfwInput.MousePosY = event.xmotion.y; + } + + _glfwInput.CursorPosX = event.xmotion.x; + _glfwInput.CursorPosY = event.xmotion.y; + _glfwInput.MouseMoved = GL_TRUE; + + if( _glfwWin.mousePosCallback ) + { + _glfwWin.mousePosCallback( _glfwInput.MousePosX, + _glfwInput.MousePosY ); + } + } + break; + } + + case ConfigureNotify: + { + if( event.xconfigure.width != _glfwWin.width || + event.xconfigure.height != _glfwWin.height ) + { + // The window was resized + + _glfwWin.width = event.xconfigure.width; + _glfwWin.height = event.xconfigure.height; + if( _glfwWin.windowSizeCallback ) + { + _glfwWin.windowSizeCallback( _glfwWin.width, + _glfwWin.height ); + } + } + break; + } + + case ClientMessage: + { + if( (Atom) event.xclient.data.l[ 0 ] == _glfwWin.wmDeleteWindow ) + { + // The window manager was asked to close the window, for example by + // the user pressing a 'close' window decoration button + + return GL_TRUE; + } + else if( _glfwWin.wmPing != None && + (Atom) event.xclient.data.l[ 0 ] == _glfwWin.wmPing ) + { + // The window manager is pinging us to make sure we are still + // responding to events + + event.xclient.window = _glfwWin.root; + XSendEvent( _glfwLibrary.display, + event.xclient.window, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event ); + } + + break; + } + + case MapNotify: + { + // The window was mapped + + _glfwWin.iconified = GL_FALSE; + break; + } + + case UnmapNotify: + { + // The window was unmapped + + _glfwWin.iconified = GL_TRUE; + break; + } + + case FocusIn: + { + // The window gained focus + + _glfwWin.active = GL_TRUE; + + if( _glfwWin.mouseLock ) + { + _glfwPlatformHideMouseCursor(); + } + + break; + } + + case FocusOut: + { + // The window lost focus + + _glfwWin.active = GL_FALSE; + _glfwInputDeactivation(); + + if( _glfwWin.mouseLock ) + { + _glfwPlatformShowMouseCursor(); + } + + break; + } + + case Expose: + { + // The window's contents was damaged + + if( _glfwWin.windowRefreshCallback ) + { + _glfwWin.windowRefreshCallback(); + } + break; + } + + // Was the window destroyed? + case DestroyNotify: + return GL_FALSE; + + default: + { +#if defined( _GLFW_HAS_XRANDR ) + switch( event.type - _glfwLibrary.XRandR.eventBase ) + { + case RRScreenChangeNotify: + { + // Show XRandR that we really care + XRRUpdateConfiguration( &event ); + break; + } + } +#endif + break; + } + } + + // The window was not destroyed + return GL_FALSE; +} + + + +//************************************************************************ +//**** Platform implementation functions **** +//************************************************************************ + +//======================================================================== +// Here is where the window is created, and +// the OpenGL rendering context is created +//======================================================================== + +int _glfwPlatformOpenWindow( int width, int height, + const _GLFWwndconfig* wndconfig, + const _GLFWfbconfig* fbconfig ) +{ + _GLFWfbconfig closest; + + // Clear platform specific GLFW window state + _glfwWin.visual = (XVisualInfo*)NULL; + _glfwWin.colormap = (Colormap)0; + _glfwWin.context = (GLXContext)NULL; + _glfwWin.window = (Window)0; + _glfwWin.pointerGrabbed = GL_FALSE; + _glfwWin.pointerHidden = GL_FALSE; + _glfwWin.keyboardGrabbed = GL_FALSE; + _glfwWin.overrideRedirect = GL_FALSE; + _glfwWin.FS.modeChanged = GL_FALSE; + _glfwWin.Saver.changed = GL_FALSE; + _glfwWin.refreshRate = wndconfig->refreshRate; + _glfwWin.windowNoResize = wndconfig->windowNoResize; + + _glfwWin.wmDeleteWindow = None; + _glfwWin.wmPing = None; + _glfwWin.wmState = None; + _glfwWin.wmStateFullscreen = None; + _glfwWin.wmActiveWindow = None; + + // As the 2.x API doesn't understand multiple display devices, we hardcode + // this choice and hope for the best + _glfwWin.screen = DefaultScreen( _glfwLibrary.display ); + _glfwWin.root = RootWindow( _glfwLibrary.display, _glfwWin.screen ); + + // Create the invisible cursor for hidden cursor mode + _glfwWin.cursor = createNULLCursor( _glfwLibrary.display, _glfwWin.root ); + + initGLXExtensions(); + + // Choose the best available fbconfig + { + unsigned int fbcount; + _GLFWfbconfig *fbconfigs; + const _GLFWfbconfig *result; + + fbconfigs = getFBConfigs( &fbcount ); + if( !fbconfigs ) + { + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + result = _glfwChooseFBConfig( fbconfig, fbconfigs, fbcount ); + if( !result ) + { + free( fbconfigs ); + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + closest = *result; + free( fbconfigs ); + } + + if( !createContext( wndconfig, (GLXFBConfigID) closest.platformID ) ) + { + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + if( !createWindow( width, height, wndconfig ) ) + { + _glfwPlatformCloseWindow(); + return GL_FALSE; + } + + if( wndconfig->mode == GLFW_FULLSCREEN ) + { +#if defined( _GLFW_HAS_XRANDR ) + // Request screen change notifications + if( _glfwLibrary.XRandR.available ) + { + XRRSelectInput( _glfwLibrary.display, + _glfwWin.window, + RRScreenChangeNotifyMask ); + } +#endif + enterFullscreenMode(); + } + + // Process the window map event and any other that may have arrived + _glfwPlatformPollEvents(); + + // Retrieve and set initial cursor position + { + Window window, root; + int windowX, windowY, rootX, rootY; + unsigned int mask; + + XQueryPointer( _glfwLibrary.display, + _glfwWin.window, + &root, + &window, + &rootX, &rootY, + &windowX, &windowY, + &mask ); + + // TODO: Probably check for some corner cases here. + + _glfwInput.MousePosX = windowX; + _glfwInput.MousePosY = windowY; + } + + // Connect the context to the window + glXMakeCurrent( _glfwLibrary.display, _glfwWin.window, _glfwWin.context ); + + return GL_TRUE; +} + + +//======================================================================== +// Properly kill the window/video display +//======================================================================== + +void _glfwPlatformCloseWindow( void ) +{ + if( _glfwWin.fullscreen ) + { + leaveFullscreenMode(); + } + + if( _glfwWin.context ) + { + // Release and destroy the context + glXMakeCurrent( _glfwLibrary.display, None, NULL ); + glXDestroyContext( _glfwLibrary.display, _glfwWin.context ); + _glfwWin.context = NULL; + } + + if( _glfwWin.visual ) + { + XFree( _glfwWin.visual ); + _glfwWin.visual = NULL; + } + + if( _glfwWin.window ) + { + XUnmapWindow( _glfwLibrary.display, _glfwWin.window ); + XDestroyWindow( _glfwLibrary.display, _glfwWin.window ); + _glfwWin.window = (Window) 0; + } + + if( _glfwWin.colormap ) + { + XFreeColormap( _glfwLibrary.display, _glfwWin.colormap ); + _glfwWin.colormap = (Colormap) 0; + } + + if( _glfwWin.cursor ) + { + XFreeCursor( _glfwLibrary.display, _glfwWin.cursor ); + _glfwWin.cursor = (Cursor) 0; + } +} + + +//======================================================================== +// Set the window title +//======================================================================== + +void _glfwPlatformSetWindowTitle( const char *title ) +{ + // Set window & icon title + XStoreName( _glfwLibrary.display, _glfwWin.window, title ); + XSetIconName( _glfwLibrary.display, _glfwWin.window, title ); +} + + +//======================================================================== +// Set the window size +//======================================================================== + +void _glfwPlatformSetWindowSize( int width, int height ) +{ + int mode = 0, rate, sizeChanged = GL_FALSE; + XSizeHints *sizehints; + + rate = _glfwWin.refreshRate; + + if( _glfwWin.fullscreen ) + { + // Get the closest matching video mode for the specified window size + mode = _glfwGetClosestVideoMode( _glfwWin.screen, &width, &height, &rate ); + } + + if( _glfwWin.windowNoResize ) + { + // Update window size restrictions to match new window size + + sizehints = XAllocSizeHints(); + sizehints->flags = 0; + + sizehints->min_width = sizehints->max_width = width; + sizehints->min_height = sizehints->max_height = height; + + XSetWMNormalHints( _glfwLibrary.display, _glfwWin.window, sizehints ); + XFree( sizehints ); + } + + // Change window size before changing fullscreen mode? + if( _glfwWin.fullscreen && (width > _glfwWin.width) ) + { + XResizeWindow( _glfwLibrary.display, _glfwWin.window, width, height ); + sizeChanged = GL_TRUE; + } + + if( _glfwWin.fullscreen ) + { + // Change video mode, keeping current refresh rate + _glfwSetVideoModeMODE( _glfwWin.screen, mode, _glfwWin.refreshRate ); + } + + // Set window size (if not already changed) + if( !sizeChanged ) + { + XResizeWindow( _glfwLibrary.display, _glfwWin.window, width, height ); + } +} + + +//======================================================================== +// Set the window position. +//======================================================================== + +void _glfwPlatformSetWindowPos( int x, int y ) +{ + XMoveWindow( _glfwLibrary.display, _glfwWin.window, x, y ); +} + + +//======================================================================== +// Window iconification +//======================================================================== + +void _glfwPlatformIconifyWindow( void ) +{ + if( _glfwWin.overrideRedirect ) + { + // We can't iconify/restore override-redirect windows, as that's + // performed by the window manager + return; + } + + XIconifyWindow( _glfwLibrary.display, _glfwWin.window, _glfwWin.screen ); +} + + +//======================================================================== +// Window un-iconification +//======================================================================== + +void _glfwPlatformRestoreWindow( void ) +{ + if( _glfwWin.overrideRedirect ) + { + // We can't iconify/restore override-redirect windows, as that's + // performed by the window manager + return; + } + + XMapWindow( _glfwLibrary.display, _glfwWin.window ); +} + + +//======================================================================== +// Swap OpenGL buffers and poll any new events +//======================================================================== + +void _glfwPlatformSwapBuffers( void ) +{ + // Update display-buffer + glXSwapBuffers( _glfwLibrary.display, _glfwWin.window ); +} + + +//======================================================================== +// Set double buffering swap interval +//======================================================================== + +void _glfwPlatformSwapInterval( int interval ) +{ + if( _glfwWin.has_GLX_SGI_swap_control ) + { + _glfwWin.SwapIntervalSGI( interval ); + } +} + + +//======================================================================== +// Read back framebuffer parameters from the context +//======================================================================== + +void _glfwPlatformRefreshWindowParams( void ) +{ + int dummy; + GLXFBConfig *fbconfig; +#if defined( _GLFW_HAS_XRANDR ) + XRRScreenConfiguration *sc; +#elif defined( _GLFW_HAS_XF86VIDMODE ) + XF86VidModeModeLine modeline; + int dotclock; + float pixels_per_second, pixels_per_frame; +#endif + int attribs[] = { GLX_FBCONFIG_ID, _glfwWin.fbconfigID, None }; + + if( _glfwWin.has_GLX_SGIX_fbconfig ) + { + fbconfig = _glfwWin.ChooseFBConfigSGIX( _glfwLibrary.display, + _glfwWin.screen, + attribs, + &dummy ); + } + else + { + fbconfig = glXChooseFBConfig( _glfwLibrary.display, + _glfwWin.screen, + attribs, + &dummy ); + } + + if( fbconfig == NULL ) + { + // This should never ever happen + // TODO: Figure out what to do when this happens + fprintf( stderr, "Cannot find known GLXFBConfig by ID. " + "This cannot happen. Have a nice day.\n"); + abort(); + } + + // There is no clear definition of an "accelerated" context on X11/GLX, and + // true sounds better than false, so we hardcode true here + _glfwWin.accelerated = GL_TRUE; + + _glfwWin.redBits = getFBConfigAttrib( *fbconfig, GLX_RED_SIZE ); + _glfwWin.greenBits = getFBConfigAttrib( *fbconfig, GLX_GREEN_SIZE ); + _glfwWin.blueBits = getFBConfigAttrib( *fbconfig, GLX_BLUE_SIZE ); + + _glfwWin.alphaBits = getFBConfigAttrib( *fbconfig, GLX_ALPHA_SIZE ); + _glfwWin.depthBits = getFBConfigAttrib( *fbconfig, GLX_DEPTH_SIZE ); + _glfwWin.stencilBits = getFBConfigAttrib( *fbconfig, GLX_STENCIL_SIZE ); + + _glfwWin.accumRedBits = getFBConfigAttrib( *fbconfig, GLX_ACCUM_RED_SIZE ); + _glfwWin.accumGreenBits = getFBConfigAttrib( *fbconfig, GLX_ACCUM_GREEN_SIZE ); + _glfwWin.accumBlueBits = getFBConfigAttrib( *fbconfig, GLX_ACCUM_BLUE_SIZE ); + _glfwWin.accumAlphaBits = getFBConfigAttrib( *fbconfig, GLX_ACCUM_ALPHA_SIZE ); + + _glfwWin.auxBuffers = getFBConfigAttrib( *fbconfig, GLX_AUX_BUFFERS ); + _glfwWin.stereo = getFBConfigAttrib( *fbconfig, GLX_STEREO ) ? 1 : 0; + + // Get FSAA buffer sample count + if( _glfwWin.has_GLX_ARB_multisample ) + { + _glfwWin.samples = getFBConfigAttrib( *fbconfig, GLX_SAMPLES ); + } + else + { + _glfwWin.samples = 0; + } + + // Default to refresh rate unknown (=0 according to GLFW spec) + _glfwWin.refreshRate = 0; + + // Retrieve refresh rate if possible +#if defined( _GLFW_HAS_XRANDR ) + if( _glfwLibrary.XRandR.available ) + { + sc = XRRGetScreenInfo( _glfwLibrary.display, _glfwWin.root ); + _glfwWin.refreshRate = XRRConfigCurrentRate( sc ); + XRRFreeScreenConfigInfo( sc ); + } +#elif defined( _GLFW_HAS_XF86VIDMODE ) + if( _glfwLibrary.XF86VidMode.available ) + { + // Use the XF86VidMode extension to get current video mode + XF86VidModeGetModeLine( _glfwLibrary.display, _glfwWin.screen, + &dotclock, &modeline ); + pixels_per_second = 1000.0f * (float) dotclock; + pixels_per_frame = (float) modeline.htotal * modeline.vtotal; + _glfwWin.refreshRate = (int)(pixels_per_second/pixels_per_frame+0.5); + } +#endif + + XFree( fbconfig ); +} + + +//======================================================================== +// Poll for new window and input events +//======================================================================== + +void _glfwPlatformPollEvents( void ) +{ + GLboolean closeRequested = GL_FALSE; + + // Flag that the cursor has not moved + _glfwInput.MouseMoved = GL_FALSE; + + // Process all pending events + while( XPending( _glfwLibrary.display ) ) + { + if( processSingleEvent() ) + { + closeRequested = GL_TRUE; + } + } + + // Did we get mouse movement in fully enabled hidden cursor mode? + if( _glfwInput.MouseMoved && _glfwWin.pointerHidden ) + { + _glfwPlatformSetMouseCursorPos( _glfwWin.width/2, + _glfwWin.height/2 ); + } + + if( closeRequested && _glfwWin.windowCloseCallback ) + { + closeRequested = _glfwWin.windowCloseCallback(); + } + if( closeRequested ) + { + glfwCloseWindow(); + } +} + + +//======================================================================== +// Wait for new window and input events +//======================================================================== + +void _glfwPlatformWaitEvents( void ) +{ + XEvent event; + + // Block waiting for an event to arrive + XNextEvent( _glfwLibrary.display, &event ); + XPutBackEvent( _glfwLibrary.display, &event ); + + _glfwPlatformPollEvents(); +} + + +//======================================================================== +// Hide mouse cursor (lock it) +//======================================================================== + +void _glfwPlatformHideMouseCursor( void ) +{ + // Hide cursor + if( !_glfwWin.pointerHidden ) + { + XDefineCursor( _glfwLibrary.display, _glfwWin.window, _glfwWin.cursor ); + _glfwWin.pointerHidden = GL_TRUE; + } + + // Grab cursor to user window + if( !_glfwWin.pointerGrabbed ) + { + if( XGrabPointer( _glfwLibrary.display, _glfwWin.window, True, + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask, GrabModeAsync, GrabModeAsync, + _glfwWin.window, None, CurrentTime ) == + GrabSuccess ) + { + _glfwWin.pointerGrabbed = GL_TRUE; + } + } +} + + +//======================================================================== +// Show mouse cursor (unlock it) +//======================================================================== + +void _glfwPlatformShowMouseCursor( void ) +{ + // Un-grab cursor (only in windowed mode: in fullscreen mode we still + // want the mouse grabbed in order to confine the cursor to the window + // area) + if( _glfwWin.pointerGrabbed ) + { + XUngrabPointer( _glfwLibrary.display, CurrentTime ); + _glfwWin.pointerGrabbed = GL_FALSE; + } + + // Show cursor + if( _glfwWin.pointerHidden ) + { + XUndefineCursor( _glfwLibrary.display, _glfwWin.window ); + _glfwWin.pointerHidden = GL_FALSE; + } +} + + +//======================================================================== +// Set physical mouse cursor position +//======================================================================== + +void _glfwPlatformSetMouseCursorPos( int x, int y ) +{ + // Store the new position so we can recognise it later + _glfwInput.CursorPosX = x; + _glfwInput.CursorPosY = y; + + XWarpPointer( _glfwLibrary.display, None, _glfwWin.window, 0,0,0,0, x, y ); +} + diff --git a/readme.html b/readme.html new file mode 100644 index 00000000..e304949d --- /dev/null +++ b/readme.html @@ -0,0 +1,821 @@ + + + + + + GLFW Readme File + + + + + +

GLFW 2.7 Lite

+ +
    +
  1. Introduction
  2. +
  3. Compiling GLFW and the example programs
  4. +
  5. Installing GLFW
  6. +
  7. Using GLFW
  8. +
  9. Frequently Asked Questions
  10. +
  11. Version history
  12. +
  13. Directory structure of the GLFW distribution
  14. +
  15. Contacting the project
  16. +
  17. Acknowledgements
  18. +
+ + +

1. Introduction

+ +

Welcome to version 2.7 Lite of the GLFW OpenGL framework. GLFW is +a free, open source, portable framework for OpenGL application development. In +short, it is a library that constitutes a powerful API for handling operating +system specific tasks, such as opening an OpenGL window, reading keyboard and +mouse input, and more.

+ +

Please note that this is the Lite version of GLFW, which +means that some areas of functionality present in 2.x mainline versions of GLFW +have been removed.

+ +
+

2. Compiling GLFW and the example programs

+ +

To compile GLFW and the accompanying example programs, you will need the CMake build system.

+ + +

3. Installing GLFW

+ +

A rudimentary installation target is provided for all supported platforms +via the CMake build system.

+ +

For Unix-like platforms, the command is:

+ +

$ make install

+ +

Note that you may need to run this command as root or via +sudo(1) in order to install GLFW into the various system +directories.

+ +

For Visual C++ and other integrated development environments, an installation +target should appear in the by CMake generated project files.

+ +
+

4. Using GLFW

+ +

There are two aspects to using GLFW: + +

+

    +
  1. How the GLFW API works
  2. +
  3. How to compile programs that use GLFW
  4. +
+ +

The first point is covered in the GLFW +Users Guide and the GLFW Reference +Manual, and we suggest that you read at least the Users Guide, since +it's a good introduction to the GLFW API.

+ +

Designing and compiling programs that use GLFW is not very difficult. +A few rules for successfully designing GLFW-based programs are presented +in the following sections.

+ +

4.1 Include GL/glfw.h

+ +

In your program, you should include GL/glfw.h like this:

+ +

#include <GL/glfw.h>

+ +

This include file defines all the necessary constants, types and prototypes +that are used to interact with the GLFW API. It also includes +GL/gl.h and GL/glu.h, and - it defines all the +necessary constants and types that are necessary for OpenGL to work on +different platforms.

+ +

For instance, under Windows you are normally required to include +windows.h before you include GL/gl.h. If you write +such a program, it would not compile under e.g. Linux since +windows.h does not exist under Linux. GL/glfw.h +takes care of these things for you. Note however that it does not actually +include windows.h, it merely mimics the parts of it that are +needed for GL/gl.h and GL/glu.h (this way we do not +get the thousands of constants, types and prototypes that could otherwise +possibly interfere with our own declarations).

+ +

In other words: +

    +
  • Do not include GL/gl.h or GL/glu.h + (GLFW does it for you)
  • +
  • Do not include windows.h unless you need + to write Win32-specific code
  • +
  • If you do need to include windows.h, do it + before including GL/glfw.h.
  • +
+ +

4.2 Link with the correct libraries

+ +

4.2.1 Windows static library

+ +

If you link with the static version of GLFW, it is also necessary to +link with some system libraries that GLFW uses.

+ +

When linking a program under Windows that uses the static version of GLFW, +you must also link with the following libraries: opengl32, +user32 and kernel32. Some of these libraries may be +linked with by default by your compiler. In the table below you can see the +minimum required link options for each supported Windows compiler (you may want +to add other libraries as well, such as glu32):

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CompilerLink options
Borland C++ Builderglfw.lib opengl32.lib
CygwinSee Unix static library below
LCC-Win32glfw.lib opengl32.lib
Microsoft Visual C++glfw.lib opengl32.lib user32.lib
MinGW32-lglfw -lopengl32
OpenWatcomglfw.lib opengl32.lib user32.lib
Pelles Cglfw.lib opengl32.lib user32.lib kernel32.lib
+ + +

4.2.2 Windows DLL

+ +

To compile a program that uses the DLL version of GLFW, you need to define +the GLFW_DLL constant. This can either be done with a compiler switch, +typically by adding -DGLFW_DLL to the list of compiler options. You can +also do it by adding the following line to all your source files that include +glfw.h, before including it:

+ +

#define GLFW_DLL

+ +

When linking a program under Windows that uses the DLL version of GLFW, +the only library you need to link with for GLFW to work is glfwdll. +In the table below you can see the minimum required link options for each +supported Windows compiler (you may want to add other libraries as well, +such as opengl32 and glu32):

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CompilerLink options
Borland C++ Builderglfwdll.lib
Cygwin-lglfwdll
LCC-Win32glfwdll.lib
Microsoft Visual C++glfwdll.lib
MinGW32-lglfwdll
OpenWatcomglfwdll.lib
Pelles Cglfwdll.lib
+ +

4.2.3 Unix static library

+ +

GLFW now supports +pkgconfig, +and a libglfw.pc file is generated and installed when you install the library. +For systems that do not provide pkgconfig, you should look in this file for the +proper compile and link flags for your system, as determined by compile.sh at +compile time.

+ +

A typical compile and link command line may look like this (using GCC):

+ +

gcc `pkg-config --cflags libglfw` -o myprog myprog.c `pkg-config --libs libglfw`

+ +

If you use GLU functions in your program then you should also add the +-lGLU flag. + + +

4.2.5 Mac OS X static library

+ +

When compiling and linking a program under Mac OS X that uses GLFW, you +must also link with the following frameworks: Carbon.framework, +AGL.framework and OpenGL.framework. + +

If you are using Xcode, you simply add the GLFW library +libglfw.a and these frameworks to your project. If, however, you +are building your program from the command line, there are two methods for +correctly linking your GLFW program.

+ +

GLFW now supports pkgconfig, and a pkgconfig +file named libglfw.pc is generated and installed when you install the library. +You can find pkgconfig in most packaging systems, such as Fink and DarwinPorts, so if you have one +of them installed, simply install pkgconfig. Once you have pkgconfig available, +the command line for compiling and linking your program is:

+ +

gcc `pkg-config --cflags libglfw` -o myprog myprog.c `pkg-config --libs libglfw`

+ +

If you do not wish to use pkgconfig, you will need to add the required +frameworks and libraries to your command line using the -l and +-framework switches, i.e.:

+ +

gcc -o myprog myprog.c -lglfw -framework Carbon -framework AGL -framework OpenGL

+ +

Note that you do not add the .framework extension to a framework when adding +it from the command line.

+ +

These frameworks contain all GL and GLU functions, so there is no need to +add additional libraries or frameworks when using GLU functionality. Also note +that even though your machine may have Unix-style GL libraries, they are for +use with the X Window System, and will not work with the Mac OS X +native version of GLFW.

+ + +

9. Frequently Asked Questions

+ +
+

5. Version history

+ +

2.7 Lite

+
    +
  • Removed all threading support
  • +
  • Removed all image and texture loading support
  • +
  • Removed all build files not related to CMake
  • +
  • Removed D, Delphi and Lua bindings
  • +
  • Imported CMake work from pre-3.0 branch
  • +
+ +

2.7

+
    +
  • Added GLFW_OPENGL_VERSION_MAJOR, + GLFW_OPENGL_VERSION_MINOR and + GLFW_OPENGL_FORWARD_COMPAT hints for versioned context + creation support
  • +
  • Added GLFW_NO_GLU macro for disabling the inclusion of the + GLU header
  • +
  • Added the proper DEF file to the Visual C++ DLL project file
  • +
  • Added a rudimentary joystick API testing example
  • +
  • Changed all comments in public header file to plain C style
  • +
  • Removed all deprecated platforms
  • +
  • [X11] Added x11-distro-install install target, intended for packagers
  • +
  • [X11] Removed support for GLX version 1.3 and below
  • +
  • [X11] Bugfix: Misspelt struct member in XF86VidMode code path
  • +
  • [MacOSX] Bugfix: Key repeat events were ignored on 10.5 Leopard
  • +
  • [Win32] Bugfix: Improper use of wParam for WM_SYSCOMMAND
  • +
  • [Win32] Bugfix: Derivatives of stream.c not cleaned up by compile.bat
  • +
  • [Win32] Bugfix: Pointer for GetExtensionsStringARB was not initialized
  • +
  • [Win32] Bugfix: Updated API version in makefiles
  • +
+ +

2.6

+
+ +

2.5

+
    +
  • Added the function glfwWaitEvents
  • +
  • Added window close callback, which enables a program to prevent a user + from closing a window with the window manager
  • +
  • Added window refresh callback, which is called when the window needs + to be refreshed
  • +
  • Added support for loading alpha textures (GLFW_ALPHA_MAP_BIT)
  • +
  • Added support for the Lua programming language
  • +
  • Added support for the D programming language
  • +
  • Added support for the Pelles C compiler for Windows
  • +
  • Added API level support for up to eight mouse buttons
  • +
  • [Win32] Added support for up to five mouse buttons
  • +
  • [Win32] Mouse down events capture mouse input
  • +
  • [Win32] Bugfix: The DLL now exports glfwSetTime
  • +
  • [Win32] Fix: The GLFW window is now placed in the upper left corner + of the desktop working area
  • +
  • [Win32/X11] Bugfix: More robust check for SwapInterval
  • +
  • [X11] Added support for USB joysticks under Linux (/dev/input/js*)
  • +
  • [X11] Bugfix: Added support for GLX extensions in glfwExtensionSupported
  • +
  • [X11] Bugfix: More robust fullscreen mode (?)
  • +
  • [X11] Bugfix: Runtime check of XF86VidMode support for the active + display
  • +
  • [X11] Bugfix: Some mouse button events were reported incorrectly
  • +
  • [MacOSX] Added support for the input char callback.
  • +
  • [MacOSX] Added video mode validation and duplicate elimination.
  • +
  • [MacOSX] Switched to a new MakeBundle.sh script.
  • +
  • [MacOSX] Added emulation of the window refresh callback.
  • +
  • [MacOSX] Bugfix: The window and its associated resources are now + properly released.
  • +
  • [MacOSX] Bugfix: Removed support for more than eight mouse buttons.
  • +
  • [x86 CPUs] Improved Intel mobile CPU detection (e.g. disables RDTSC + timing on Centrino systems)
  • +
+ +

2.4.2

+
    +
  • Preliminary native Mac OS X support (via the Carbon interface)
  • +
  • Preliminary DOS support (DJGPP + Mesa)
  • +
  • Changed license to the zlib license (almost identical to the previous + GLFW license), so now GLFW is OSI Certified
  • +
  • Rewrote the GLFW documentation in LaTeX, meaning several improvements + (both visual and practical)
  • +
  • Added the support folder to the distribution, which includes + support for various languages
  • +
  • [Win32] Added OpenWatcom compiler support (thanks Sebastian + Schuberth!)
  • +
  • [Win32] Changed fallback timer from GetTickCount to timeGetTime, + which usually provides better resolution
  • +
  • [Win32] Bugfix: Accumulator buffer selection should be more + robust
  • +
  • [Win32] Bugfix: If stereo rendering is requested, and no stereo pixel + format could be created, glfwOpenWindow now fails
  • +
  • [Win32] Bugfix: glfwSetWindowSize now sets the size of the client + area, NOT the entire window, meaning that there is a 1:1 relationship + between glfwSetWindowSize and glfwGetWindowSize
  • +
  • [X11] Added FreeBSD and QNX support
  • +
  • [X11] Added support for non-pthread capable systems
  • +
  • [X11] Hopefully more robust configuration script (compile.sh)
  • +
  • [X11] Bugfix: When mouse cursor is hidden, mouse sensitivity is no + longer degraded
  • +
  • [X11] Bugfix: Source files EOL was PC style (CR/LF) in 2.4.1 (blame + my WinCVS configuration)
  • +
  • [X11] Bugfix: When a GLFW window is closed, input focus is properly + released
  • +
  • [X11] Bugfix: Iconification of fullscreen windows should now work + properly
  • +
  • [x86 CPUs] Improved RDTSC timing (e.g. RDTSC support on single-CPU + Intel Hyper-Threading enabled systems)
  • +
  • [AmigaOS] Added joystick support
  • +
  • [AmigaOS] Mouse cursor positioning is now implemented
  • +
  • [AmigaOS] Added support for Geek Gadgets GCC
  • +
  • [AmigaOS] Bugfix: glfwGetWindowParam now returns proper values for + all parameters (except for GLFW_ACCELERATED)
  • +
+ +

2.4.1

+
    +
  • Added AmigaOS support (preliminary)
  • +
  • GLFW for the X Window System now works under Mac OS X
  • +
  • [Win32] Bugfix: glfwWaitCond treated the timeout as milliseconds + instead of seconds
  • +
  • [X11] Bugfix: GLFW should now compile under IRIX v5.3
  • +
  • [X11] Bugfix: GLFW should now compile with Kylix
  • +
+ +

2.4

+
    +
  • Major source code rearrangement - much code is now shared between + different platforms, and it should be easier to port GLFW to new + platforms
  • +
  • Added a Unicode keyboard text input interface (CharCallback)
  • +
  • Keyboard key input is now slightly more internationalized: GLFW now + uses 8-bit ISO-8859-1 encoding for keys representing printable + characters (e.g. "Ö", "§", etc.), as + opposed to the previous 7-bit US-ASCII encoding
  • +
  • Added more key constants (F13-F25, keypad '=')
  • +
  • Added an enable/disable switch for automatic event polling from + glfwSwapBuffers
  • +
  • [X11] Added support for sysctl for querying the number of processors + in the system (if POSIX sysconf is not supported)
  • +
  • [X11] Bugfix: compile.sh now works with Sun sh (and hopefully others + too)
  • +
  • [X11] Bugfix: compile.sh now detects the need for -ldl when dlopen is + used
  • +
  • [Win32] Bugfix: When closing a fullscreen window under Win 9x/NT4, + the task bar icon now disappears properly
  • +
  • [Win32] Bugfix: GLFW should now compile on a wider range of MSVC + compilers (e.g. .NET) - Thanks Tim Little!
  • +
+ +

2.3.2

+
    +
  • Removed the silly limitation of 100 threads (the thread information + is now kept in a linked list)
  • +
  • General source cleanup (window state is now kept in a single + struct, plus some other minor changes)
  • +
  • [X11] Added Solaris gethrtime() support (not tested yet), which + should give an improved timer for Sun/Solaris stations
  • +
  • [X11] Some fixes to the 'compile.sh' script (-O for non-gcc compilers + and 'make x11-gcc' should now really force GCC)
  • +
+ +

2.3.1

+
    +
  • [X11] A minimalist configuration script was added that solves the + issue with glXGetProcAddressARB, and unifies all Unix/X11 Makefiles + into one template Makefile (well, one for GLFW, and one for the + examples)
  • +
+ +

2.3

+
    +
  • Added OpenGL stereo rendering support
  • +
  • Added a function for parsing the OpenGL version string + (glfwGetGLVersion)
  • +
  • [x86] Bugfix: Hopefully the CPU core clock dependent timer RDTSC will + never be used on CPUs with variable core frequencies anymore
  • +
  • [X11] Bugfix: GLFW could create stereo rendering capable windows, + even if it was not requested (GLFW 2.2.x did not support selection + of stereo rendering)
  • +
  • [X11] Bugfix: glfwGetProcAddress returned NULL on most systems (even + on those that supported glXGetProcAddressARB). Now GLFW assumes that + glXGetProcAddressARB is supported on all systems, which solves the + bug, but may result in compiler errors on some systems (please let me + know if you have any related problems).
  • +
+ +

2.2.3

+
    +
  • Bugfix: Checking for GL_SGIS_generate_mipmap is more robust
  • +
  • Bugfix: glfwLoadTexture2D will now fail if no window is opened
  • +
  • [Win32] Bugfix: Right shift was not detected under Win 9x/ME (it is + still not as good as under NT/2K/XP, but at least you get right + shifts)
  • +
  • [X11] Bugfix: Visuals are now selected more accurately. For instance, + glfwOpenWindow will no longer fail if you request a 24-bit color + buffer if only 16-bit color visuals are available (which means that + pong3d should work on 16-bit displays).
  • +
+ +

2.2.2

+
    +
  • [Win32] Bugfix: Windows did not always get focus (this was a tough + one!)
  • +
  • [Win32] Bugfix: glfwGetWindowParam did not work with + GLFW_ACCUM_*_BITS or GLFW_AUX_BUFFERS
  • +
  • [X11] Bugfix: Linux joystick Y axis positions were reversed
  • +
+ +

2.2.1

+
    +
  • [X11] Added joystick support for Linux
  • +
+ +

2.2

+
    +
  • Added joystick support (only supported under Windows so far)
  • +
  • Added joystick controls to pong3d.c (only 3 more lines of code)
  • +
  • Added glfwOpenWindowHint() function
  • +
  • It is now possible to specify a desired vertical monitor refresh + rate (for fullscreen windows)
  • +
  • It is now possible to request an accumulator buffer and auxiliary + buffers
  • +
  • Added glfwSetTime() function
  • +
  • Added a GLFW conversion of the MESA/GLUT gears.c demo to the example + programs
  • +
  • [Win32] gdi32.dll and winmm.dll are now loaded dynamically when + glfwInit() is called. This means that there is no need to link with + gdi32.lib or winmm.lib when using the static version of GLFW, which + should make GLFW usage more convenient.
  • +
  • [Win32] Bugfix: Greatly improved keyboard input (detect left/right + CTRL etc)
  • +
  • [Win32] Bugfix: glfwExtensionSupported now detects all WGL extensions + (e.g. WGL_ARB_pbuffer)
  • +
  • [Win32] Bugfix: Mouse cursor was not re-hidden when a GLFW window was + deselected and then selected again (with ALT+TAB)
  • +
  • [X11] Bugfix: Minor bug in the SGI timer - and ugly (unintended) SGI + timer debug info removed
  • +
  • [X11] Bugfix: glfwGetDesktopMode and glfwGetVideoModes no longer give + segmentation faults if no X server is available
  • +
+ +

2.1

+
    +
  • Added image and texture loading capabilities (support for the TGA + file format at the moment)
  • +
  • Added a new example program (mipmaps.c) for showing off the automatic + mipmap generation and texture loading capabilities of GLFW 2.1
  • +
  • Removed the separate TGA loader (tga.c in the examples directory) + since texture loading is supported natively by GLFW. Also updated the + Pong3D demo to use GLFW texture loading instead of tga.c.
  • +
  • Improved keyboard handling (e.g. numeric keypad keys can be + detected)
  • +
  • Added a new example program, keytest.c
  • +
  • Changed the GLFWvidmode structure and the corresponding API functions + to report pure color bits instead of the confusing (and unportable) + "BPP" field
  • +
  • Changed glfwSetWindowSize so that it operates in fullscreen mode + too
  • +
  • Added mouse wheel support under Windows (not Win95) and X11
  • +
  • Added window iconification functions (glfwInconifyWindow and + glfwRestoreWindow)
  • +
  • Improved iconification and deactivation handling under both Windows + and X11
  • +
  • Made it possible to turn on/off key repeat (the default is now no key + repeat)
  • +
  • Added SGI hardware timer support (CLOCK_SGI_CYCLE) for improved + timer resolution for SGI computers
  • +
  • Added support for the free Borland C++ Builder 5.x compiler for + Windows
  • +
  • Made it possible to compiler GLFW as a Windows DLL using any of the + supported compilers
  • +
  • Some constants have changed names (e.g. GLFW_REDBITS is now called + GLFW_RED_BITS)
  • +
  • Updated GLFW documentation (GLFW Users Guide and GLFW Reference + Manual) to reflect the changes in the API
  • +
  • [Win32] Bugfix: Corrected Cygwin toplevel makefile entry
  • +
  • [Win32] Bugfix: Fixed event lag bug
  • +
  • [Win32] Bugfix: Fixed Radeon 8500 crash
  • +
  • [X11] Bugfix: Fixed the window close bug
  • +
  • [X11] Bugfix: Iconification/deactivation is now detected
  • +
  • [X11] Bugfix: Non-OpenGL visuals are not listed anymore
  • +
  • [XFree86] Bugfix: Undesired video mode changes are now prevented
  • +
+ +

2.0.3

+
    +
  • Added precise CPU cycle based timing support (RDTSC) for x86 + CPUs (under both Windows and Unix)
  • +
  • Added a makefile option for building for Windows with Cygwin
  • +
  • Corrected the CC for Unix/X11 makefiles (-Wall is usually not a + supported flag for CC, so it was removed from the CFLAGS list)
  • +
+ +

2.0.2

+
    +
  • Added a makefile option for building for X11 with 'cc' rather than + 'gcc' (useful for IRIX users for instance).
  • +
  • [Win32] Bugfix: Mouse coordinates are now relative to the window + upper left corner, which also means that disabling the mouse cursor + in windowed mode should work much better.
  • +
  • [X11] Bugfix: Added a bunch of more keys that are recognized by + GLFW.
  • +
  • [X11] Bugfix: glfwGetNumberOfProcessors now works for IRIX (earlier + versions of GLFW would not compile under IRIX).
  • +
+ +

2.0.1

+
    +
  • glfwTerminate() will now be called automatically upon normal program + termination (using atexit())
  • +
  • [Win32] Bugfix: Buffer-swapping did not work if a window lost + focus.
  • +
  • [Win32] Bugfix: Top level Makefile did not work under Windows + 9x.
  • +
  • [Win32] Bugfix: NULL declaration in glfw.h was not MSVC 7.x + compatible.
  • +
  • [X11] Bugfix: GLFW would not build with C++ (e.g. g++).
  • +
+ +

2.0

+
    +
  • GLFW is no longer a single source file, but an entire link library.
  • +
  • Added multi threading support.
  • +
  • Added more window control.
  • +
  • New distribution layout (both Win32 and X11 version in same archive).
  • +
  • Added GLFW Users Manual and GLFW Reference Manual as PDF files.
  • +
  • Some bugfixes.
  • +
+ +

1.0.2

+
    +
  • Improved fullscreen functionality.
  • +
  • Added fullscreen support for X11.
  • +
+ +

1.0.1

+
    +
  • Added support for the X Window System.
  • +
  • Fixed bugs.
  • +
+ +

1.0.0

+
    +
  • First release.
  • +
  • Only supported Windows.
  • +
+ + + +

6. Directory structure of the GLFW distribution

+ +

Here is an overview of the directory structure of the GLFW distribution: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DirectoryContents
docsGLFW manuals in PDF format
examplesSeveral example programs in C
include/GLThe GLFW C/C++ header file
libThe source code for GLFW
lib/macosx + Mac OS X-specific code
lib/win32 + Windows-specific code
lib/x11Unix/X11-specific code
+ + +
+

7. Contacting the project

+ +

The official GLFW web site can be found here: http://glfw.sourceforge.net/. It +contains the latest version of GLFW, news and other information that is useful +for OpenGL development.

+ +

If you have questions related to the use of GLFW, we have a user's web +forum, and a user's mailing +list on SF.net, and the IRC channel #glfw on Freenode.

+ +

If you have a bug to report or a feature you'd like to request, please file +it in the SF.net +trackers.

+ +Finally, if you're interested in helping out with the development of GLFW or +porting it to your favorite platform, we have a developer's +mailing list, or you could join us on #glfw. + + + +

8. Acknowledgements

+ +

GLFW would not be what it is today without the help from: +

    + +
  • Marcus Geelnard, the original author and long-time maintainer of GLFW, + without whose brilliant work none of this would have happened.

  • + +
  • Robin Leffmann, for his work on Mac OS X and other platforms, and his + invaluable support.

  • + +
  • Keith Bauer, for his invaluable help with porting and maintaining GLFW on + Mac OS X, and for his many ideas.

  • + +
  • Ozzy @ Orkysquad, + for his dedication to GLFW, for debugging my source, and for his + valuable experience with game development.

  • + +
  • Jeff Molofee, the author of the excellent OpenGL tutorials at NeHe Productions. + Much of the Windows code of GLFW was originally based on Jeff's + code.

  • + +
  • Douglas C. Schmidt and Irfan Pyarali, for their excellent article Strategies for + Implementing POSIX Condition Variables on Win32, which was the basis for + the Win32 condition variable implementation in GLFW.

  • + +
  • Bobyshev Alexander and Martins Mozeiko, for the original proposal of + an FSAA hint and their work on the Win32 implementation of FSAA.

  • + +
  • Gerald Franz, who made GLFW compile under IRIX, and supplied patches + for the X11 keyboard translation routine.

  • + +
  • Bradley Smith, for his updates of the D support and his ports of the + remaining examples to the D language.

  • + +
  • Olivier Delannoy, for the initial implementation of FSAA support on + X11, cross-compiling support for MinGW and general extreme usefulness.
  • + +
  • Glenn Lewis, for helping out with support for the D programming + language.

  • + +
  • David Medlock, for doing the initial Lua port.

  • + +
  • Frank Wille, for helping me with the AmigaOS port and making GLFW + compile under IRIX 5.3.

  • + +
  • Matt Sealey, for helping me with the MorphOS port.

  • + +
  • Paul R. Deppe, who helped me with Cygwin support, and made an + adaption of PLIB + so that it can use GLFW (instead of GLUT).

  • + +
  • Jarrod Davis, for the Delphi port of GLFW.

  • + +
  • Toni Jovanoski, for helping me with the MASM32 port of GLFW, and + supplying the example program and fixed OpenGL and GLU bindings for + MASM32.

  • + +
  • Sebastian Schuberth, for the OpenWatcom makefiles.

  • + +
  • Dmitri Shuralyov, Samuli Tuomola, Santi Zupancic, Sylvain + Hellegouarch, and many others for support, bug reports and + testing.

  • + +
  • Дмитри Малышев, for the idea of a GLFW_NO_GLU macro.

  • + +
  • OpenGL.org, and all the people on + the discussion forums there that have provided help during the development of + GLFW.

  • + +
  • The MSDN Online Library, + which was used extensively for Windows development.

  • + +
  • All the feedback from the GLFW community - thank you!

  • + +
  • Everyone we forgot to thank - thank you!

  • +
+ + + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..2a915405 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,36 @@ + +link_libraries(libglfwStatic ${GLFW_LIBRARIES} ${OPENGL_glu_LIBRARY}) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include ${OPENGL_INCLUDE_DIR}) + +add_executable(defaults defaults.c) +add_executable(events events.c) +add_executable(joysticks joysticks.c) +add_executable(peter peter.c) +add_executable(reopen reopen.c) +add_executable(version version.c) + +if(APPLE) + # Set fancy names for bundles + add_executable(Accuracy MACOSX_BUNDLE accuracy.c) + add_executable(FSAA MACOSX_BUNDLE fsaa.c) + add_executable(Tearing MACOSX_BUNDLE tearing.c) +else() + # Set boring names for executables + add_executable(accuracy WIN32 accuracy.c) + add_executable(tearing WIN32 tearing.c) + add_executable(fsaa WIN32 fsaa.c) +endif(APPLE) + +if(MSVC) + # Tell MSVC to use main instead of WinMain for Windows subsystem executables + set_target_properties(accuracy defaults events fsaa peter reopen tearing version PROPERTIES LINK_FLAGS "/ENTRY:mainCRTStartup") +endif(MSVC) + +if(CYGWIN) + # Set cross-compile and subsystem compile and link flags + set_target_properties(accuracy defaults events fsaa joysticks peter reopen tearing version PROPERTIES COMPILE_FLAGS "-mno-cygwin") + set_target_properties(accuracy fsaa tearing PROPERTIES LINK_FLAGS "-mno-cygwin -mwindows") + set_target_properties(events defaults joysticks peter reopen version PROPERTIES LINK_FLAGS "-mno-cygwin -mconsole") +endif(CYGWIN) + diff --git a/tests/accuracy.c b/tests/accuracy.c new file mode 100644 index 00000000..3811cd81 --- /dev/null +++ b/tests/accuracy.c @@ -0,0 +1,103 @@ +//======================================================================== +// Mouse cursor accuracy test +// Copyright (c) Camilla Berglund +// +// 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. +// +//======================================================================== +// +// This test came about as the result of bug #1867804 +// +// No sign of said bug has so far been detected +// +//======================================================================== + +#include + +#include +#include + +static int cursor_x = 0, cursor_y = 0; +static int window_width = 640, window_height = 480; + +static void GLFWCALL window_size_callback(int width, int height) +{ + window_width = width; + window_height = height; + + glViewport(0, 0, window_width, window_height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0.f, window_width, 0.f, window_height); +} + +static void GLFWCALL mouse_position_callback(int x, int y) +{ + cursor_x = x; + cursor_y = y; +} + +int main(void) +{ + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(EXIT_FAILURE); + } + + if (!glfwOpenWindow(window_width, window_height, 0, 0, 0, 0, 0, 0, GLFW_WINDOW)) + { + glfwTerminate(); + + fprintf(stderr, "Failed to open GLFW window\n"); + exit(EXIT_FAILURE); + } + + glfwSetWindowTitle("Cursor Inaccuracy Detector"); + glfwSetMousePosCallback(mouse_position_callback); + glfwSetWindowSizeCallback(window_size_callback); + glfwSwapInterval(1); + + glClearColor(0, 0, 0, 0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + while (glfwGetWindowParam(GLFW_OPENED)) + { + glClear(GL_COLOR_BUFFER_BIT); + + glColor3f(1.f, 1.f, 1.f); + + glBegin(GL_LINES); + glVertex2f(0.f, (GLfloat) window_height - cursor_y); + glVertex2f((GLfloat) window_width, (GLfloat) window_height - cursor_y); + glVertex2f((GLfloat) cursor_x, 0.f); + glVertex2f((GLfloat) cursor_x, (GLfloat) window_height); + glEnd(); + + glfwSwapBuffers(); + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/tests/defaults.c b/tests/defaults.c new file mode 100644 index 00000000..58b77b4c --- /dev/null +++ b/tests/defaults.c @@ -0,0 +1,98 @@ +//======================================================================== +// Default window/context test +// Copyright (c) Camilla Berglund +// +// 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. +// +//======================================================================== +// +// This test creates a windowed mode window with all parameters set to +// default values and then reports the actual parameters of the created +// window and context +// +//======================================================================== + +#include + +#include +#include + +typedef struct +{ + int param; + char* name; +} Param; + +static Param parameters[] = +{ + { GLFW_ACCELERATED, "accelerated" }, + { GLFW_RED_BITS, "red bits" }, + { GLFW_GREEN_BITS, "green bits" }, + { GLFW_BLUE_BITS, "blue bits" }, + { GLFW_ALPHA_BITS, "alpha bits" }, + { GLFW_DEPTH_BITS, "depth bits" }, + { GLFW_STENCIL_BITS, "stencil bits" }, + { GLFW_REFRESH_RATE, "refresh rate" }, + { GLFW_ACCUM_RED_BITS, "accum red bits" }, + { GLFW_ACCUM_GREEN_BITS, "accum green bits" }, + { GLFW_ACCUM_BLUE_BITS, "accum blue bits" }, + { GLFW_ACCUM_ALPHA_BITS, "accum alpha bits" }, + { GLFW_AUX_BUFFERS, "aux buffers" }, + { GLFW_STEREO, "stereo" }, + { GLFW_FSAA_SAMPLES, "FSAA samples" }, + { GLFW_OPENGL_VERSION_MAJOR, "OpenGL major" }, + { GLFW_OPENGL_VERSION_MINOR, "OpenGL minor" }, + { GLFW_OPENGL_FORWARD_COMPAT, "OpenGL forward compatible" }, + { GLFW_OPENGL_DEBUG_CONTEXT, "OpenGL debug context" }, + { GLFW_OPENGL_PROFILE, "OpenGL profile" }, +}; + +int main(void) +{ + int i, width, height; + + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(1); + } + + if (!glfwOpenWindow(0, 0, 0, 0, 0, 0, 0, 0, GLFW_WINDOW)) + { + glfwTerminate(); + + fprintf(stderr, "Failed to open GLFW default window\n"); + exit(1); + } + + glfwGetWindowSize(&width, &height); + + printf("window size: %ix%i\n", width, height); + + for (i = 0; (size_t) i < sizeof(parameters) / sizeof(parameters[0]); i++) + { + printf("%s: %i\n", parameters[i].name, glfwGetWindowParam(parameters[i].param)); + } + + glfwCloseWindow(); + glfwTerminate(); + exit(0); +} + diff --git a/tests/events.c b/tests/events.c new file mode 100644 index 00000000..0434844e --- /dev/null +++ b/tests/events.c @@ -0,0 +1,309 @@ +//======================================================================== +// Event linter (event spewer) +// Copyright (c) Camilla Berglund +// +// 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. +// +//======================================================================== +// +// This test hooks every available callback and outputs their arguments +// +// Log messages go to stdout, error messages to stderr +// +// Every event also gets a (sequential) number to aid discussion of logs +// +//======================================================================== + +#include + +#include +#include +#include +#include + +static GLboolean keyrepeat = 0; +static GLboolean systemkeys = 1; +static unsigned int counter = 0; + +static const char* get_key_name(int key) +{ + switch (key) + { + case GLFW_KEY_UNKNOWN: return "unknown"; + case GLFW_KEY_SPACE: return "space"; + case GLFW_KEY_ESC: return "escape"; + case GLFW_KEY_F1: return "F1"; + case GLFW_KEY_F2: return "F2"; + case GLFW_KEY_F3: return "F3"; + case GLFW_KEY_F4: return "F4"; + case GLFW_KEY_F5: return "F5"; + case GLFW_KEY_F6: return "F6"; + case GLFW_KEY_F7: return "F7"; + case GLFW_KEY_F8: return "F8"; + case GLFW_KEY_F9: return "F9"; + case GLFW_KEY_F10: return "F10"; + case GLFW_KEY_F11: return "F11"; + case GLFW_KEY_F12: return "F12"; + case GLFW_KEY_F13: return "F13"; + case GLFW_KEY_F14: return "F14"; + case GLFW_KEY_F15: return "F15"; + case GLFW_KEY_F16: return "F16"; + case GLFW_KEY_F17: return "F17"; + case GLFW_KEY_F18: return "F18"; + case GLFW_KEY_F19: return "F19"; + case GLFW_KEY_F20: return "F20"; + case GLFW_KEY_F21: return "F21"; + case GLFW_KEY_F22: return "F22"; + case GLFW_KEY_F23: return "F23"; + case GLFW_KEY_F24: return "F24"; + case GLFW_KEY_F25: return "F25"; + case GLFW_KEY_UP: return "up"; + case GLFW_KEY_DOWN: return "down"; + case GLFW_KEY_LEFT: return "left"; + case GLFW_KEY_RIGHT: return "right"; + case GLFW_KEY_LSHIFT: return "left shift"; + case GLFW_KEY_RSHIFT: return "right shift"; + case GLFW_KEY_LCTRL: return "left control"; + case GLFW_KEY_RCTRL: return "right control"; + case GLFW_KEY_LALT: return "left alt"; + case GLFW_KEY_RALT: return "right alt"; + case GLFW_KEY_TAB: return "tab"; + case GLFW_KEY_ENTER: return "enter"; + case GLFW_KEY_BACKSPACE: return "backspace"; + case GLFW_KEY_INSERT: return "insert"; + case GLFW_KEY_DEL: return "delete"; + case GLFW_KEY_PAGEUP: return "page up"; + case GLFW_KEY_PAGEDOWN: return "page down"; + case GLFW_KEY_HOME: return "home"; + case GLFW_KEY_END: return "end"; + case GLFW_KEY_KP_0: return "keypad 0"; + case GLFW_KEY_KP_1: return "keypad 1"; + case GLFW_KEY_KP_2: return "keypad 2"; + case GLFW_KEY_KP_3: return "keypad 3"; + case GLFW_KEY_KP_4: return "keypad 4"; + case GLFW_KEY_KP_5: return "keypad 5"; + case GLFW_KEY_KP_6: return "keypad 6"; + case GLFW_KEY_KP_7: return "keypad 7"; + case GLFW_KEY_KP_8: return "keypad 8"; + case GLFW_KEY_KP_9: return "keypad 9"; + case GLFW_KEY_KP_DIVIDE: return "keypad divide"; + case GLFW_KEY_KP_MULTIPLY: return "keypad multiply"; + case GLFW_KEY_KP_SUBTRACT: return "keypad subtract"; + case GLFW_KEY_KP_ADD: return "keypad add"; + case GLFW_KEY_KP_DECIMAL: return "keypad decimal"; + case GLFW_KEY_KP_EQUAL: return "keypad equal"; + case GLFW_KEY_KP_ENTER: return "keypad enter"; + case GLFW_KEY_KP_NUM_LOCK: return "keypad num lock"; + case GLFW_KEY_CAPS_LOCK: return "caps lock"; + case GLFW_KEY_SCROLL_LOCK: return "scroll lock"; + case GLFW_KEY_PAUSE: return "pause"; + case GLFW_KEY_LSUPER: return "left super"; + case GLFW_KEY_RSUPER: return "right super"; + case GLFW_KEY_MENU: return "menu"; + } + + return NULL; +} + +static const char* get_action_name(int action) +{ + switch (action) + { + case GLFW_PRESS: + return "was pressed"; + case GLFW_RELEASE: + return "was released"; + } + + return "caused unknown action"; +} + +static const char* get_button_name(int button) +{ + switch (button) + { + case GLFW_MOUSE_BUTTON_LEFT: + return "left"; + case GLFW_MOUSE_BUTTON_RIGHT: + return "right"; + case GLFW_MOUSE_BUTTON_MIDDLE: + return "middle"; + } + + return NULL; +} + +static const char* get_character_string(int character) +{ + static char result[6 + 1]; + + int length = wctomb(result, character); + if (length == -1) + length = 0; + + result[length] = '\0'; + return result; +} + +static void GLFWCALL window_size_callback(int width, int height) +{ + printf("%08x at %0.3f: Window size: %i %i\n", + counter++, + glfwGetTime(), + width, + height); + + glViewport(0, 0, width, height); +} + +static int GLFWCALL window_close_callback(void) +{ + printf("%08x at %0.3f: Window close\n", counter++, glfwGetTime()); + return 1; +} + +static void GLFWCALL window_refresh_callback(void) +{ + printf("%08x at %0.3f: Window refresh\n", counter++, glfwGetTime()); +} + +static void GLFWCALL mouse_button_callback(int button, int action) +{ + const char* name = get_button_name(button); + + printf("%08x at %0.3f: Mouse button %i", counter++, glfwGetTime(), button); + + if (name) + printf(" (%s) was %s\n", name, get_action_name(action)); + else + printf(" was %s\n", get_action_name(action)); +} + +static void GLFWCALL mouse_position_callback(int x, int y) +{ + printf("%08x at %0.3f: Mouse position: %i %i\n", counter++, glfwGetTime(), x, y); +} + +static void GLFWCALL mouse_wheel_callback(int position) +{ + printf("%08x at %0.3f: Mouse wheel: %i\n", counter++, glfwGetTime(), position); +} + +static void GLFWCALL key_callback(int key, int action) +{ + const char* name = get_key_name(key); + + printf("%08x at %0.3f: Key 0x%04x", counter++, glfwGetTime(), key); + + if (name) + printf(" (%s) was %s\n", name, get_action_name(action)); + else if (isgraph(key)) + printf(" (%c) was %s\n", key, get_action_name(action)); + else + printf(" was %s\n", get_action_name(action)); + + if (action != GLFW_PRESS) + return; + + switch (key) + { + case 'R': + { + keyrepeat = !keyrepeat; + if (keyrepeat) + glfwEnable(GLFW_KEY_REPEAT); + else + glfwDisable(GLFW_KEY_REPEAT); + + printf("(( key repeat %s ))\n", keyrepeat ? "enabled" : "disabled"); + break; + } + + case 'S': + { + systemkeys = !systemkeys; + if( systemkeys ) + glfwEnable(GLFW_SYSTEM_KEYS); + else + glfwDisable(GLFW_SYSTEM_KEYS); + + printf("(( system keys %s ))\n", systemkeys ? "enabled" : "disabled"); + break; + } + } +} + +static void GLFWCALL char_callback(int character, int action) +{ + printf("%08x at %0.3f: Character 0x%04x", counter++, glfwGetTime(), character); + + printf(" (%s) %s\n", get_character_string(character), get_action_name(action)); +} + +int main(void) +{ + setlocale(LC_ALL, ""); + + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(1); + } + + printf("Library initialized\n"); + + if (!glfwOpenWindow(0, 0, 0, 0, 0, 0, 0, 0, GLFW_WINDOW)) + { + glfwTerminate(); + + fprintf(stderr, "Failed to create GLFW window"); + exit(1); + } + + printf("Window opened\n"); + + glfwSetWindowTitle("Event Linter"); + glfwSwapInterval(1); + + glfwSetWindowSizeCallback(window_size_callback); + glfwSetWindowCloseCallback(window_close_callback); + glfwSetWindowRefreshCallback(window_refresh_callback); + glfwSetMouseButtonCallback(mouse_button_callback); + glfwSetMousePosCallback(mouse_position_callback); + glfwSetMouseWheelCallback(mouse_wheel_callback); + glfwSetKeyCallback(key_callback); + glfwSetCharCallback(char_callback); + + printf("Key repeat should be %s\n", keyrepeat ? "enabled" : "disabled"); + printf("System keys should be %s\n", systemkeys ? "enabled" : "disabled"); + + printf("Main loop starting\n"); + + while (glfwGetWindowParam(GLFW_OPENED) == GL_TRUE) + { + glfwWaitEvents(); + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(); + } + + glfwTerminate(); + exit(0); +} + diff --git a/tests/fsaa.c b/tests/fsaa.c new file mode 100644 index 00000000..91c66d91 --- /dev/null +++ b/tests/fsaa.c @@ -0,0 +1,105 @@ +//======================================================================== +// Fullscreen multisampling anti-aliasing test +// Copyright (c) Camilla Berglund +// +// 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. +// +//======================================================================== +// +// This test renders two high contrast, slowly rotating quads, one aliased +// and one (hopefully) anti-aliased, thus allowing for visual verification +// of whether FSAA is indeed enabled +// +//======================================================================== + +#include + +#include +#include + +#ifndef GL_ARB_multisample +#define GL_MULTISAMPLE_ARB 0x809D +#endif + +static void GLFWCALL window_size_callback(int width, int height) +{ + glViewport(0, 0, width, height); +} + +int main(void) +{ + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(EXIT_FAILURE); + } + + glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 4); + + if (!glfwOpenWindow(400, 400, 0, 0, 0, 0, 0, 0, GLFW_WINDOW)) + { + glfwTerminate(); + + fprintf(stderr, "Failed to open GLFW window\n"); + exit(EXIT_FAILURE); + } + + glfwSetWindowTitle("Aliasing Detector"); + glfwSetWindowSizeCallback(window_size_callback); + glfwSwapInterval(1); + + int samples = glfwGetWindowParam(GLFW_FSAA_SAMPLES); + if (samples) + printf("Context reports FSAA is supported with %i samples\n", samples); + else + printf("Context reports FSAA is unsupported\n"); + + glMatrixMode(GL_PROJECTION); + gluOrtho2D(0.f, 1.f, 0.f, 1.f); + + while (glfwGetWindowParam(GLFW_OPENED)) + { + GLfloat time = (GLfloat) glfwGetTime(); + + glClear(GL_COLOR_BUFFER_BIT); + + glLoadIdentity(); + glTranslatef(0.5f, 0.f, 0.f); + glRotatef(time, 0.f, 0.f, 1.f); + + glEnable(GL_MULTISAMPLE_ARB); + glColor3f(1.f, 1.f, 1.f); + glRectf(-0.25f, -0.25f, 0.25f, 0.25f); + + glLoadIdentity(); + glTranslatef(-0.5f, 0.f, 0.f); + glRotatef(time, 0.f, 0.f, 1.f); + + glDisable(GL_MULTISAMPLE_ARB); + glColor3f(1.f, 1.f, 1.f); + glRectf(-0.25f, -0.25f, 0.25f, 0.25f); + + glfwSwapBuffers(); + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/tests/getopt.c b/tests/getopt.c new file mode 100644 index 00000000..b891b0a5 --- /dev/null +++ b/tests/getopt.c @@ -0,0 +1,253 @@ +/***************************************************************************** +* getopt.c - competent and free getopt library. +* $Header: /cvsroot/freegetopt/freegetopt/getopt.c,v 1.2 2003/10/26 03:10:20 vindaci Exp $ +* +* Copyright (c)2002-2003 Mark K. Kim +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided with the +* distribution. +* +* * Neither the original author of this software nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +* DAMAGE. +*/ +#include +#include +#include +#include "getopt.h" + + +/* 2009-10-12 Camilla Berglund + * + * Removed unused global static variable 'ID'. + */ + + +char* optarg = NULL; +int optind = 0; +int opterr = 1; +int optopt = '?'; + + +static char** prev_argv = NULL; /* Keep a copy of argv and argc to */ +static int prev_argc = 0; /* tell if getopt params change */ +static int argv_index = 0; /* Option we're checking */ +static int argv_index2 = 0; /* Option argument we're checking */ +static int opt_offset = 0; /* Index into compounded "-option" */ +static int dashdash = 0; /* True if "--" option reached */ +static int nonopt = 0; /* How many nonopts we've found */ + +static void increment_index() +{ + /* Move onto the next option */ + if(argv_index < argv_index2) + { + while(prev_argv[++argv_index] && prev_argv[argv_index][0] != '-' + && argv_index < argv_index2+1); + } + else argv_index++; + opt_offset = 1; +} + + +/* +* Permutes argv[] so that the argument currently being processed is moved +* to the end. +*/ +static int permute_argv_once() +{ + /* Movability check */ + if(argv_index + nonopt >= prev_argc) return 1; + /* Move the current option to the end, bring the others to front */ + else + { + char* tmp = prev_argv[argv_index]; + + /* Move the data */ + memmove(&prev_argv[argv_index], &prev_argv[argv_index+1], + sizeof(char**) * (prev_argc - argv_index - 1)); + prev_argv[prev_argc - 1] = tmp; + + nonopt++; + return 0; + } +} + + +int getopt(int argc, char** argv, char* optstr) +{ + int c = 0; + + /* If we have new argv, reinitialize */ + if(prev_argv != argv || prev_argc != argc) + { + /* Initialize variables */ + prev_argv = argv; + prev_argc = argc; + argv_index = 1; + argv_index2 = 1; + opt_offset = 1; + dashdash = 0; + nonopt = 0; + } + + /* Jump point in case we want to ignore the current argv_index */ + getopt_top: + + /* Misc. initializations */ + optarg = NULL; + + /* Dash-dash check */ + if(argv[argv_index] && !strcmp(argv[argv_index], "--")) + { + dashdash = 1; + increment_index(); + } + + /* If we're at the end of argv, that's it. */ + if(argv[argv_index] == NULL) + { + c = -1; + } + /* Are we looking at a string? Single dash is also a string */ + else if(dashdash || argv[argv_index][0] != '-' || !strcmp(argv[argv_index], "-")) + { + /* If we want a string... */ + if(optstr[0] == '-') + { + c = 1; + optarg = argv[argv_index]; + increment_index(); + } + /* If we really don't want it (we're in POSIX mode), we're done */ + else if(optstr[0] == '+' || getenv("POSIXLY_CORRECT")) + { + c = -1; + + /* Everything else is a non-opt argument */ + nonopt = argc - argv_index; + } + /* If we mildly don't want it, then move it back */ + else + { + if(!permute_argv_once()) goto getopt_top; + else c = -1; + } + } + /* Otherwise we're looking at an option */ + else + { + char* opt_ptr = NULL; + + /* Grab the option */ + c = argv[argv_index][opt_offset++]; + + /* Is the option in the optstr? */ + if(optstr[0] == '-') opt_ptr = strchr(optstr+1, c); + else opt_ptr = strchr(optstr, c); + /* Invalid argument */ + if(!opt_ptr) + { + if(opterr) + { + fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c); + } + + optopt = c; + c = '?'; + + /* Move onto the next option */ + increment_index(); + } + /* Option takes argument */ + else if(opt_ptr[1] == ':') + { + /* ie, -oARGUMENT, -xxxoARGUMENT, etc. */ + if(argv[argv_index][opt_offset] != '\0') + { + optarg = &argv[argv_index][opt_offset]; + increment_index(); + } + /* ie, -o ARGUMENT (only if it's a required argument) */ + else if(opt_ptr[2] != ':') + { + /* One of those "you're not expected to understand this" moment */ + if(argv_index2 < argv_index) argv_index2 = argv_index; + while(argv[++argv_index2] && argv[argv_index2][0] == '-'); + optarg = argv[argv_index2]; + + /* Don't cross into the non-option argument list */ + if(argv_index2 + nonopt >= prev_argc) optarg = NULL; + + /* Move onto the next option */ + increment_index(); + } + else + { + /* Move onto the next option */ + increment_index(); + } + + /* In case we got no argument for an option with required argument */ + if(optarg == NULL && opt_ptr[2] != ':') + { + optopt = c; + c = '?'; + + if(opterr) + { + fprintf(stderr,"%s: option requires an argument -- %c\n", + argv[0], optopt); + } + } + } + /* Option does not take argument */ + else + { + /* Next argv_index */ + if(argv[argv_index][opt_offset] == '\0') + { + increment_index(); + } + } + } + + /* Calculate optind */ + if(c == -1) + { + optind = argc - nonopt; + } + else + { + optind = argv_index; + } + + return c; +} + + +/* vim:ts=3 +*/ diff --git a/tests/getopt.h b/tests/getopt.h new file mode 100644 index 00000000..0b78650a --- /dev/null +++ b/tests/getopt.h @@ -0,0 +1,63 @@ +/***************************************************************************** +* getopt.h - competent and free getopt library. +* $Header: /cvsroot/freegetopt/freegetopt/getopt.h,v 1.2 2003/10/26 03:10:20 vindaci Exp $ +* +* Copyright (c)2002-2003 Mark K. Kim +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided with the +* distribution. +* +* * Neither the original author of this software nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +* DAMAGE. +*/ +#ifndef GETOPT_H_ +#define GETOPT_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + + +extern char* optarg; +extern int optind; +extern int opterr; +extern int optopt; + +int getopt(int argc, char** argv, char* optstr); + + +#ifdef __cplusplus +} +#endif + + +#endif /* GETOPT_H_ */ + + +/* vim:ts=3 +*/ diff --git a/tests/iconify.c b/tests/iconify.c new file mode 100644 index 00000000..14c6e726 --- /dev/null +++ b/tests/iconify.c @@ -0,0 +1,158 @@ +//======================================================================== +// Iconify/restore test program +// Copyright (c) Camilla Berglund +// +// 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. +// +//======================================================================== +// +// This program is used to test the iconify/restore functionality for +// both fullscreen and windowed mode windows +// +//======================================================================== + +#include + +#include +#include + +#include "getopt.h" + +static void usage(void) +{ + printf("iconify [-h] [-f]\n"); +} + +static void GLFWCALL key_callback(int key, int action) +{ + printf("%0.2f Key %s\n", + glfwGetTime(), + action == GLFW_PRESS ? "pressed" : "released"); + + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_SPACE: + glfwIconifyWindow(); + break; + case GLFW_KEY_ESC: + glfwCloseWindow(); + break; + } +} + +static void GLFWCALL size_callback(int width, int height) +{ + glViewport(0, 0, width, height); +} + +int main(int argc, char** argv) +{ + int width, height, ch; + int mode = GLFW_WINDOW; + GLboolean active = -1, iconified = -1; + + while ((ch = getopt(argc, argv, "fh")) != -1) + { + switch (ch) + { + case 'h': + usage(); + exit(EXIT_SUCCESS); + + case 'f': + mode = GLFW_FULLSCREEN; + break; + + default: + usage(); + exit(EXIT_FAILURE); + } + } + + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(EXIT_FAILURE); + } + + if (mode == GLFW_FULLSCREEN) + { + GLFWvidmode mode; + glfwGetDesktopMode(&mode); + width = mode.Width; + height = mode.Height; + } + else + { + width = 0; + height = 0; + } + + if (!glfwOpenWindow(width, height, 0, 0, 0, 0, 0, 0, mode)) + { + glfwTerminate(); + + fprintf(stderr, "Failed to open GLFW window\n"); + exit(EXIT_FAILURE); + } + + glfwSetWindowTitle("Iconify"); + glfwSwapInterval(1); + glfwSetKeyCallback(key_callback); + glfwSetWindowSizeCallback(size_callback); + + glEnable(GL_SCISSOR_TEST); + + while (glfwGetWindowParam(GLFW_OPENED)) + { + int width, height; + + if (iconified != glfwGetWindowParam(GLFW_ICONIFIED) || + active != glfwGetWindowParam(GLFW_ACTIVE)) + { + iconified = glfwGetWindowParam(GLFW_ICONIFIED); + active = glfwGetWindowParam(GLFW_ACTIVE); + + printf("%0.2f %s %s\n", + glfwGetTime(), + iconified ? "Iconified" : "Restored", + active ? "Active" : "Inactive"); + } + + glfwGetWindowSize(&width, &height); + + glScissor(0, 0, width, height); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + glScissor(0, 0, 640, 480); + glClearColor(1, 1, 1, 0); + glClear(GL_COLOR_BUFFER_BIT); + + glfwSwapBuffers(); + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/tests/joysticks.c b/tests/joysticks.c new file mode 100644 index 00000000..7015e131 --- /dev/null +++ b/tests/joysticks.c @@ -0,0 +1,136 @@ +/*======================================================================== + * This is a small test application for GLFW. + * joystick input test. + *========================================================================*/ + +#include + +#include +#include + +#define MAX_AXES 10 +#define MAX_BUTTONS 30 + +struct JoystickState +{ + int present; + int num_axes; + int num_buttons; + float axes[MAX_AXES]; + unsigned char buttons[MAX_BUTTONS]; +}; + +static struct JoystickState states[GLFW_JOYSTICK_LAST + 1]; + +int running; +int keyrepeat = 0; +int systemkeys = 1; + + +/*======================================================================== + * Retrieve joystick states + *========================================================================*/ +static void updateJoysticksState(void) +{ + int joy; + + for (joy = GLFW_JOYSTICK_1; joy < GLFW_JOYSTICK_LAST + 1; joy++) + { + printf("Updating information for joystick %d\n", joy); + states[joy].present = glfwGetJoystickParam(joy, GLFW_PRESENT); + if (states[joy].present == GL_TRUE) + { + states[joy].num_axes = glfwGetJoystickPos(joy, states[joy].axes, MAX_AXES); + states[joy].num_buttons = glfwGetJoystickButtons(joy, states[joy].buttons, MAX_BUTTONS); + } + } +} + +/*======================================================================== + * Print out the state of all joysticks on the standard output + *========================================================================*/ +static void displayJoysticksState(void) +{ + int joy; + int i; + + for (joy = GLFW_JOYSTICK_1; joy < GLFW_JOYSTICK_LAST + 1; joy++) + { + printf("Joystick %d: %s\n", joy, (states[joy].present == GL_TRUE ? "present" : "not connected")); + + if (states[joy].present == GL_TRUE) + { + if (states[joy].num_axes > 0) + { + printf(" axes: %.3f", states[joy].axes[0]); + for (i = 1; i < states[joy].num_axes; i++) + printf(", %.3f", states[joy].axes[i]); + + printf("\n"); + } + else + printf(" axes: none\n"); + + if (states[joy].num_buttons > 0) + { + printf(" buttons: 00 => %c", ((states[joy].buttons[0] == GLFW_PRESS) ? 'P' : 'R')); + + for (i = 1; i < states[joy].num_buttons; i++) + printf(", %02d => %c", i, ((states[joy].buttons[i] == GLFW_PRESS) ? 'P' : 'R')); + + printf("\n"); + } + else + printf(" buttons: none\n"); + } + } +} + +int main(void) +{ + double start; + double t; + double update; + + /* Initialise GLFW */ + glfwInit(); + printf("The program will work for 20 seconds and display every seconds the state of the joysticks\n"); + printf("Your computer is going to be very slow as the program is doing an active loop .....\n"); + + start = glfwGetTime(); + update = start; + + /* print the initial state of all joysticks */ + updateJoysticksState(); + printf("\n"); + displayJoysticksState(); + + running = GL_TRUE; + + /* Main loop */ + while (running) + { + /* Get time */ + t = glfwGetTime(); + + /* Display the state of all connected joysticks every secons */ + if ((t - update) > 1.0) + { + update = t; + printf("\n"); + updateJoysticksState(); + printf("\n"); + displayJoysticksState(); + } + + /* Check if the window was closed */ + if ((t - start) > 20.0) + running = GL_FALSE; + } + + /* Close OpenGL window and terminate GLFW */ + glfwTerminate(); + + return 0; +} + diff --git a/tests/peter.c b/tests/peter.c new file mode 100644 index 00000000..4d516614 --- /dev/null +++ b/tests/peter.c @@ -0,0 +1,137 @@ +//======================================================================== +// Mouse cursor bug test +// Copyright (c) Camilla Berglund +// +// 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. +// +//======================================================================== +// +// This test came about as the result of bugs #1262764, #1726540 and +// #1726592, all reported by the user peterpp, hence the name +// +// The utility of this test outside of these bugs is uncertain +// +//======================================================================== + +#include + +#include +#include + +static GLboolean cursor_enabled = GL_TRUE; + +static GLboolean open_window(void); + +static void toggle_mouse_cursor(void) +{ + if (cursor_enabled) + glfwDisable(GLFW_MOUSE_CURSOR); + else + glfwEnable(GLFW_MOUSE_CURSOR); + + cursor_enabled = !cursor_enabled; +} + +static void GLFWCALL mouse_position_callback(int x, int y) +{ + printf("Mouse moved to: %i %i\n", x, y); +} + +static void GLFWCALL key_callback(int key, int action) +{ + switch (key) + { + case GLFW_KEY_SPACE: + { + if (action == GLFW_PRESS) + toggle_mouse_cursor(); + + break; + } + + case 'R': + { + if (action == GLFW_PRESS) + { + glfwCloseWindow(); + open_window(); + } + + break; + } + } +} + +static void GLFWCALL window_size_callback(int width, int height) +{ + glViewport(0, 0, width, height); +} + +static GLboolean open_window(void) +{ + int x, y; + + if (!glfwOpenWindow(0, 0, 0, 0, 0, 0, 0, 0, GLFW_WINDOW)) + return GL_FALSE; + + glfwSetWindowTitle("Peter Detector"); + + glfwGetMousePos(&x, &y); + printf("Mouse position: %i %i\n", x, y); + + glfwDisable(GLFW_AUTO_POLL_EVENTS); + glfwSetWindowSizeCallback(window_size_callback); + glfwSetMousePosCallback(mouse_position_callback); + glfwSetKeyCallback(key_callback); + glfwSwapInterval(1); + + return GL_TRUE; +} + +int main(void) +{ + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(1); + } + + if (!open_window()) + { + glfwTerminate(); + + fprintf(stderr, "Failed to open GLFW window\n"); + exit(1); + } + + glClearColor(0.f, 0.f, 0.f, 0.f); + + while (glfwGetWindowParam(GLFW_OPENED)) + { + glClear(GL_COLOR_BUFFER_BIT); + + glfwSwapBuffers(); + glfwWaitEvents(); + } + + glfwTerminate(); + exit(0); +} + diff --git a/tests/reopen.c b/tests/reopen.c new file mode 100644 index 00000000..bfdda374 --- /dev/null +++ b/tests/reopen.c @@ -0,0 +1,170 @@ +//======================================================================== +// Window re-opener (open/close stress test) +// Copyright (c) Camilla Berglund +// +// 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. +// +//======================================================================== +// +// This test came about as the result of bug #1262773 +// +// It closes and re-opens the GLFW window every five seconds, alternating +// between windowed and fullscreen mode +// +// It also times and logs opening and closing actions and attempts to separate +// user initiated window closing from its own +// +//======================================================================== + +#include + +#include +#include + +static GLboolean closed = GL_FALSE; + +static const char* get_mode_name(int mode) +{ + switch (mode) + { + case GLFW_WINDOW: + return "windowed"; + case GLFW_FULLSCREEN: + return "fullscreen"; + default: + return "unknown"; + } +} + +static void GLFWCALL window_size_callback(int width, int height) +{ + glViewport(0, 0, width, height); +} + +static int GLFWCALL window_close_callback(void) +{ + printf("Close callback triggered\n"); + closed = GL_TRUE; + return 0; +} + +static void GLFWCALL key_callback(int key, int action) +{ + if (action != GLFW_PRESS) + return; + + switch (key) + { + case 'Q': + case GLFW_KEY_ESC: + closed = GL_TRUE; + break; + } +} + +static int open_window(int width, int height, int mode) +{ + double base = glfwGetTime(); + + if (!glfwOpenWindow(width, height, 0, 0, 0, 0, 16, 0, mode)) + { + fprintf(stderr, "Failed to create %s mode GLFW window\n", get_mode_name(mode)); + return 0; + } + + glfwSetWindowTitle("Window Re-opener"); + glfwSetWindowSizeCallback(window_size_callback); + glfwSetWindowCloseCallback(window_close_callback); + glfwSetKeyCallback(key_callback); + glfwSwapInterval(1); + + printf("Opening %s mode window took %0.3f seconds\n", + get_mode_name(mode), + glfwGetTime() - base); + + return 1; +} + +static void close_window(void) +{ + double base = glfwGetTime(); + + glfwCloseWindow(); + + printf("Closing window took %0.3f seconds\n", glfwGetTime() - base); +} + +int main(int argc, char** argv) +{ + int count = 0; + + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(1); + } + + for (;;) + { + if (!open_window(640, 480, (count & 1) ? GLFW_FULLSCREEN : GLFW_WINDOW)) + { + glfwTerminate(); + exit(1); + } + + glMatrixMode(GL_PROJECTION); + glOrtho(-1.f, 1.f, -1.f, 1.f, 1.f, -1.f); + glMatrixMode(GL_MODELVIEW); + + glClearColor(0.f, 0.f, 0.f, 0.f); + glColor3f(1.f, 1.f, 1.f); + + glfwSetTime(0.0); + + while (glfwGetTime() < 5.0) + { + glClear(GL_COLOR_BUFFER_BIT); + + glPushMatrix(); + glRotatef((GLfloat) glfwGetTime() * 100.f, 0.f, 0.f, 1.f); + glRectf(-0.5f, -0.5f, 1.f, 1.f); + glPopMatrix(); + + glfwSwapBuffers(); + + if (closed) + close_window(); + + if (!glfwGetWindowParam(GLFW_OPENED)) + { + printf("User closed window\n"); + + glfwTerminate(); + exit(0); + } + } + + printf("Closing window\n"); + close_window(); + + count++; + } +} + diff --git a/tests/tearing.c b/tests/tearing.c new file mode 100644 index 00000000..8e9f06a1 --- /dev/null +++ b/tests/tearing.c @@ -0,0 +1,84 @@ +//======================================================================== +// Vsync enabling test +// Copyright (c) Camilla Berglund +// +// 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. +// +//======================================================================== +// +// This test renders a high contrast, horizontally moving bar, allowing for +// visual verification of whether the set swap interval is indeed obeyed +// +//======================================================================== + +#include + +#include +#include +#include + +static void GLFWCALL window_size_callback(int width, int height) +{ + glViewport(0, 0, width, height); +} + +int main(void) +{ + float position; + + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(1); + } + + if (!glfwOpenWindow(0, 0, 0, 0, 0, 0, 0, 0, GLFW_WINDOW)) + { + glfwTerminate(); + + fprintf(stderr, "Failed to open GLFW window\n"); + exit(1); + } + + glfwSetWindowTitle("Tearing Detector"); + glfwSetWindowSizeCallback(window_size_callback); + glfwSwapInterval(1); + + glClearColor(0.f, 0.f, 0.f, 0.f); + glColor3f(1.f, 1.f, 1.f); + + glMatrixMode(GL_PROJECTION); + glOrtho(-1.f, 1.f, -1.f, 1.f, 1.f, -1.f); + glMatrixMode(GL_MODELVIEW); + + while (glfwGetWindowParam(GLFW_OPENED) == GL_TRUE) + { + glClear(GL_COLOR_BUFFER_BIT); + + position = cosf(glfwGetTime() * 4.f) * 0.75f; + glRectf(position - 0.25f, -1.f, position + 0.25f, 1.f); + + glfwSwapBuffers(); + } + + glfwTerminate(); + exit(0); +} + diff --git a/tests/version.c b/tests/version.c new file mode 100644 index 00000000..28f41b76 --- /dev/null +++ b/tests/version.c @@ -0,0 +1,253 @@ +//======================================================================== +// Version information dumper +// Copyright (c) Camilla Berglund +// +// 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. +// +//======================================================================== +// +// This test is a pale imitation of glxinfo(1), except not really +// +// It dumps GLFW and OpenGL version information +// +//======================================================================== + +#include + +#ifndef GL_VERSION_3_2 +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001 +#endif + +typedef const GLubyte * (APIENTRY *PFNGLGETSTRINGI) (GLenum, GLuint); + +#ifndef GL_VERSION_2_0 +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#endif + +#ifdef _MSC_VER +#define strcasecmp(x, y) _stricmp(x, y) +#endif + +#include +#include +#include + +#include "getopt.h" + +static void usage(void) +{ + printf("version [-h] [-m MAJOR] [-n MINOR] [-d] [-l] [-f] [-p PROFILE]\n"); + printf("available profiles: core compat\n"); +} + +static const char* get_profile_name(GLint mask) +{ + if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) + return "compatibility"; + if (mask & GL_CONTEXT_CORE_PROFILE_BIT) + return "core"; + + return "unknown"; +} + +static void list_extensions(int major, int minor) +{ + int i; + GLint count; + const GLubyte* extensions; + + printf("OpenGL context supported extensions:\n"); + + if (major > 2) + { + PFNGLGETSTRINGI glGetStringi = (PFNGLGETSTRINGI) glfwGetProcAddress("glGetStringi"); + if (!glGetStringi) + { + fprintf(stderr, "Failed to retrieve glGetStringi entry point"); + exit(EXIT_FAILURE); + } + + glGetIntegerv(GL_NUM_EXTENSIONS, &count); + + for (i = 0; i < count; i++) + puts((const char*) glGetStringi(GL_EXTENSIONS, i)); + } + else + { + extensions = glGetString(GL_EXTENSIONS); + while (*extensions != '\0') + { + if (*extensions == ' ') + putchar('\n'); + else + putchar(*extensions); + + extensions++; + } + } + + putchar('\n'); +} + +int main(int argc, char** argv) +{ + int ch, profile = 0, major = 1, minor = 1, revision; + GLboolean debug = GL_FALSE, forward = GL_FALSE, list = GL_FALSE; + GLint flags, mask; + + while ((ch = getopt(argc, argv, "dfhlm:n:p:")) != -1) + { + switch (ch) + { + case 'd': + debug = GL_TRUE; + break; + case 'f': + forward = GL_TRUE; + break; + case 'h': + usage(); + exit(0); + case 'l': + list = GL_TRUE; + break; + case 'm': + major = atoi(optarg); + break; + case 'n': + minor = atoi(optarg); + break; + case 'p': + if (strcasecmp(optarg, "core") == 0) + profile = GLFW_OPENGL_CORE_PROFILE; + else if (strcasecmp(optarg, "compat") == 0) + profile = GLFW_OPENGL_COMPAT_PROFILE; + else + { + usage(); + exit(EXIT_FAILURE); + } + break; + default: + usage(); + exit(EXIT_FAILURE); + } + } + + argc -= optind; + argv += optind; + + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(EXIT_FAILURE); + } + + if (major != 1 || minor != 1) + { + glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, major); + glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, minor); + } + + if (debug) + glfwOpenWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); + + if (forward) + glfwOpenWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + + if (profile != 0) + glfwOpenWindowHint(GLFW_OPENGL_PROFILE, profile); + + // We assume here that we stand a better chance of success by leaving all + // possible details of pixel format selection to GLFW + + if (!glfwOpenWindow(0, 0, 0, 0, 0, 0, 0, 0, GLFW_WINDOW)) + { + glfwTerminate(); + + fprintf(stderr, "Failed to open GLFW window\n"); + exit(EXIT_FAILURE); + } + + // Report GLFW version + + glfwGetVersion(&major, &minor, &revision); + + printf("GLFW header version: %u.%u.%u\n", + GLFW_VERSION_MAJOR, + GLFW_VERSION_MINOR, + GLFW_VERSION_REVISION); + + printf("GLFW library version: %u.%u.%u\n", major, minor, revision); + + if (major != GLFW_VERSION_MAJOR || + minor != GLFW_VERSION_MINOR || + revision != GLFW_VERSION_REVISION) + printf("*** WARNING: GLFW version mismatch! ***\n"); + + // Report OpenGL version + + printf("OpenGL context version string: \"%s\"\n", glGetString(GL_VERSION)); + + glfwGetGLVersion(&major, &minor, &revision); + + printf("OpenGL context version parsed by GLFW: %u.%u.%u\n", major, minor, revision); + + // Report OpenGL context properties + + if (major >= 3) + { + glGetIntegerv(GL_CONTEXT_FLAGS, &flags); + printf("OpenGL context flags:"); + + if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) + puts(" forward-compatible"); + else + puts(" none"); + } + + if (major > 3 || (major == 3 && minor >= 2)) + { + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); + printf("OpenGL profile mask: 0x%08x (%s)\n", mask, get_profile_name(mask)); + } + + printf("OpenGL context renderer string: \"%s\"\n", glGetString(GL_RENDERER)); + printf("OpenGL context vendor string: \"%s\"\n", glGetString(GL_VENDOR)); + + if (major > 1) + { + printf("OpenGL context shading language version: \"%s\"\n", + glGetString(GL_SHADING_LANGUAGE_VERSION)); + } + + // Report OpenGL extensions + if (list) + list_extensions(major, minor); + + glfwTerminate(); + exit(EXIT_SUCCESS); +} +