Add Chromium-only Blender WebEngine parity work
This commit is contained in:
640
blender-5.2.0/intern/ghost/CMakeLists.txt
Normal file
640
blender-5.2.0/intern/ghost/CMakeLists.txt
Normal file
@@ -0,0 +1,640 @@
|
||||
# SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
PUBLIC .
|
||||
../guardedalloc
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
intern/GHOST_Buttons.cc
|
||||
intern/GHOST_CallbackEventConsumer.cc
|
||||
intern/GHOST_Context.cc
|
||||
intern/GHOST_ContextNone.cc
|
||||
intern/GHOST_EventManager.cc
|
||||
intern/GHOST_ISystem.cc
|
||||
intern/GHOST_ISystemPaths.cc
|
||||
intern/GHOST_ModifierKeys.cc
|
||||
intern/GHOST_PathUtils.cc
|
||||
intern/GHOST_Rect.cc
|
||||
intern/GHOST_System.cc
|
||||
intern/GHOST_TimerManager.cc
|
||||
intern/GHOST_Window.cc
|
||||
intern/GHOST_WindowManager.cc
|
||||
|
||||
GHOST_CallbackEventConsumer.hh
|
||||
GHOST_IContext.hh
|
||||
GHOST_IEvent.hh
|
||||
GHOST_IEventConsumer.hh
|
||||
GHOST_ISystem.hh
|
||||
GHOST_ISystemPaths.hh
|
||||
GHOST_ITimerTask.hh
|
||||
GHOST_IWindow.hh
|
||||
GHOST_Rect.hh
|
||||
GHOST_Types.hh
|
||||
|
||||
intern/GHOST_Buttons.hh
|
||||
intern/GHOST_Context.hh
|
||||
intern/GHOST_ContextNone.hh
|
||||
intern/GHOST_Debug.hh
|
||||
intern/GHOST_Event.hh
|
||||
intern/GHOST_EventButton.hh
|
||||
intern/GHOST_EventCursor.hh
|
||||
intern/GHOST_EventDragnDrop.hh
|
||||
intern/GHOST_EventKey.hh
|
||||
intern/GHOST_EventManager.hh
|
||||
intern/GHOST_EventString.hh
|
||||
intern/GHOST_EventTrackpad.hh
|
||||
intern/GHOST_EventWheel.hh
|
||||
intern/GHOST_ModifierKeys.hh
|
||||
intern/GHOST_PathUtils.hh
|
||||
intern/GHOST_System.hh
|
||||
intern/GHOST_SystemPaths.hh
|
||||
intern/GHOST_TimerManager.hh
|
||||
intern/GHOST_TimerTask.hh
|
||||
intern/GHOST_Util.hh
|
||||
intern/GHOST_Window.hh
|
||||
intern/GHOST_WindowManager.hh
|
||||
intern/GHOST_utildefines.hh
|
||||
intern/GHOST_utildefines_variadic.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::intern::clog
|
||||
)
|
||||
|
||||
if(WITH_INPUT_IME)
|
||||
add_definitions(-DWITH_INPUT_IME)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENGL_BACKEND)
|
||||
list(APPEND LIB
|
||||
PRIVATE bf::dependencies::epoxy
|
||||
)
|
||||
add_definitions(-DWITH_OPENGL_BACKEND)
|
||||
endif()
|
||||
|
||||
if(WITH_VULKAN_BACKEND)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_ContextVK.cc
|
||||
|
||||
intern/GHOST_ContextVK.hh
|
||||
)
|
||||
list(APPEND LIB
|
||||
PUBLIC bf::dependencies::optional::vulkan
|
||||
PRIVATE bf::extern::vulkan_memory_allocator
|
||||
)
|
||||
|
||||
add_definitions(-DWITH_VULKAN_BACKEND)
|
||||
endif()
|
||||
|
||||
if(WITH_GHOST_DEBUG)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_EventPrinter.cc
|
||||
|
||||
intern/GHOST_EventPrinter.hh
|
||||
)
|
||||
add_definitions(-DWITH_GHOST_DEBUG)
|
||||
endif()
|
||||
|
||||
if(WITH_INPUT_NDOF)
|
||||
add_definitions(-DWITH_INPUT_NDOF)
|
||||
|
||||
list(APPEND SRC
|
||||
intern/GHOST_NDOFManager.cc
|
||||
|
||||
intern/GHOST_EventNDOF.hh
|
||||
intern/GHOST_NDOFManager.hh
|
||||
)
|
||||
|
||||
# Only some platforms define this.
|
||||
if(DEFINED NDOF_INCLUDE_DIRS)
|
||||
list(APPEND INC_SYS
|
||||
${NDOF_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
if(DEFINED NDOF_LIBRARIES)
|
||||
list(APPEND LIB
|
||||
${NDOF_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(APPEND SRC
|
||||
intern/GHOST_SystemHeadless.hh
|
||||
intern/GHOST_WindowNULL.hh
|
||||
)
|
||||
|
||||
if(WITH_HEADLESS)
|
||||
add_definitions(-DWITH_HEADLESS)
|
||||
elseif(WITH_GHOST_SDL)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_ContextSDL.cc
|
||||
intern/GHOST_SystemSDL.cc
|
||||
intern/GHOST_WindowSDL.cc
|
||||
|
||||
intern/GHOST_ContextSDL.hh
|
||||
intern/GHOST_SystemSDL.hh
|
||||
intern/GHOST_WindowSDL.hh
|
||||
)
|
||||
add_definitions(-DWITH_GHOST_SDL)
|
||||
|
||||
list(APPEND LIB
|
||||
bf::dependencies::optional::sdl
|
||||
)
|
||||
elseif(APPLE AND NOT WITH_GHOST_X11)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_SystemCocoa.mm
|
||||
intern/GHOST_WindowCocoa.mm
|
||||
|
||||
intern/GHOST_SystemCocoa.hh
|
||||
intern/GHOST_WindowCocoa.hh
|
||||
intern/GHOST_WindowViewCocoa.hh
|
||||
)
|
||||
|
||||
if(WITH_INPUT_NDOF)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_NDOFManagerCocoa.mm
|
||||
|
||||
intern/GHOST_NDOFManagerCocoa.hh
|
||||
)
|
||||
endif()
|
||||
|
||||
elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
||||
if(WITH_GHOST_X11)
|
||||
list(APPEND INC_SYS
|
||||
${X11_X11_INCLUDE_PATH}
|
||||
)
|
||||
|
||||
list(APPEND LIB
|
||||
${X11_X11_LIB}
|
||||
${X11_Xrender_LIB}
|
||||
)
|
||||
|
||||
list(APPEND SRC
|
||||
intern/GHOST_SystemX11.cc
|
||||
intern/GHOST_WindowX11.cc
|
||||
|
||||
intern/GHOST_IconX11.hh
|
||||
intern/GHOST_SystemX11.hh
|
||||
intern/GHOST_WindowX11.hh
|
||||
)
|
||||
|
||||
if(WITH_OPENGL_BACKEND)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_ContextGLX.cc
|
||||
|
||||
intern/GHOST_ContextGLX.hh
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_GHOST_XDND)
|
||||
add_definitions(-DWITH_XDND)
|
||||
|
||||
list(APPEND LIB
|
||||
extern_xdnd
|
||||
)
|
||||
|
||||
list(APPEND INC
|
||||
../../extern/xdnd
|
||||
)
|
||||
|
||||
list(APPEND SRC
|
||||
intern/GHOST_DropTargetX11.cc
|
||||
|
||||
intern/GHOST_DropTargetX11.hh
|
||||
)
|
||||
endif()
|
||||
|
||||
if(X11_XF86keysym_INCLUDE_PATH)
|
||||
add_definitions(-DWITH_XF86KEYSYM)
|
||||
list(APPEND INC_SYS
|
||||
${X11_XF86keysym_INCLUDE_PATH}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_X11_XFIXES)
|
||||
add_definitions(-DWITH_X11_XFIXES)
|
||||
list(APPEND INC_SYS
|
||||
${X11_Xfixes_INCLUDE_PATH}
|
||||
)
|
||||
list(APPEND LIB
|
||||
${X11_Xfixes_LIB}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_X11_XINPUT)
|
||||
add_definitions(-DWITH_X11_XINPUT)
|
||||
list(APPEND INC_SYS
|
||||
${X11_Xinput_INCLUDE_PATH}
|
||||
)
|
||||
list(APPEND LIB
|
||||
${X11_Xinput_LIB}
|
||||
)
|
||||
endif()
|
||||
|
||||
add_definitions(-DWITH_GHOST_X11)
|
||||
endif()
|
||||
|
||||
if(WITH_GHOST_WAYLAND)
|
||||
list(APPEND INC_SYS
|
||||
${wayland-client_INCLUDE_DIRS}
|
||||
${wayland-egl_INCLUDE_DIRS}
|
||||
${xkbcommon_INCLUDE_DIRS}
|
||||
${wayland-cursor_INCLUDE_DIRS}
|
||||
)
|
||||
list(APPEND LIB
|
||||
${xkbcommon_LINK_LIBRARIES}
|
||||
)
|
||||
|
||||
if(WITH_GHOST_WAYLAND_DYNLOAD)
|
||||
list(APPEND INC_SYS
|
||||
../wayland_dynload/extern
|
||||
)
|
||||
list(APPEND LIB
|
||||
bf_intern_wayland_dynload
|
||||
)
|
||||
add_definitions(-DWITH_GHOST_WAYLAND_DYNLOAD)
|
||||
else()
|
||||
list(APPEND LIB
|
||||
${wayland-client_LINK_LIBRARIES}
|
||||
${wayland-egl_LINK_LIBRARIES}
|
||||
${wayland-cursor_LINK_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_GHOST_CSD)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_WindowWaylandCSD.cc
|
||||
|
||||
intern/GHOST_WindowWaylandCSD.hh
|
||||
)
|
||||
add_definitions(-DWITH_GHOST_CSD)
|
||||
endif()
|
||||
|
||||
include(CheckSymbolExists)
|
||||
set(CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE")
|
||||
check_symbol_exists(memfd_create "sys/mman.h" HAVE_MEMFD_CREATE)
|
||||
unset(CMAKE_REQUIRED_DEFINITIONS)
|
||||
if(HAVE_MEMFD_CREATE)
|
||||
add_definitions(-DHAVE_MEMFD_CREATE)
|
||||
endif()
|
||||
|
||||
check_symbol_exists(poll "poll.h" HAVE_POLL)
|
||||
if(HAVE_POLL)
|
||||
add_definitions(-DHAVE_POLL)
|
||||
endif()
|
||||
|
||||
list(APPEND SRC
|
||||
intern/GHOST_SystemWayland.cc
|
||||
intern/GHOST_WindowWayland.cc
|
||||
|
||||
intern/GHOST_SystemWayland.hh
|
||||
intern/GHOST_WaylandUtils.hh
|
||||
intern/GHOST_WindowWayland.hh
|
||||
)
|
||||
|
||||
set(INC_DST ${CMAKE_CURRENT_BINARY_DIR}/libwayland)
|
||||
|
||||
# Generate protocols bindings.
|
||||
# Modifies in parent scope:
|
||||
# - `SRC`: appended with generated protocol source and header files.
|
||||
function(generate_protocol_bindings PROT_DEF)
|
||||
# File name without directory or extension (use for header name).
|
||||
get_filename_component(_name ${PROT_DEF} NAME_WLE)
|
||||
add_custom_command(
|
||||
OUTPUT ${INC_DST}/${_name}-client-protocol.h
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${INC_DST}
|
||||
COMMAND ${WAYLAND_SCANNER} client-header ${PROT_DEF} ${INC_DST}/${_name}-client-protocol.h
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT ${INC_DST}/${_name}-client-protocol.c
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${INC_DST}
|
||||
COMMAND ${WAYLAND_SCANNER} private-code ${PROT_DEF} ${INC_DST}/${_name}-client-protocol.c
|
||||
DEPENDS ${INC_DST}/${_name}-client-protocol.h
|
||||
)
|
||||
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
# Prevent warnings/failure to compile with generated `WL_PRIVATE` declarations.
|
||||
set_source_files_properties(
|
||||
"${INC_DST}/${_name}-client-protocol.c"
|
||||
PROPERTIES COMPILE_FLAGS "-Wno-missing-variable-declarations"
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND SRC
|
||||
${INC_DST}/${_name}-client-protocol.c
|
||||
${INC_DST}/${_name}-client-protocol.h
|
||||
)
|
||||
set(SRC "${SRC}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
list(APPEND INC_SYS
|
||||
${INC_DST}
|
||||
)
|
||||
|
||||
# `xdg-shell`.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml"
|
||||
)
|
||||
# `xdg-decoration`.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
|
||||
)
|
||||
# `xdg-output`.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-output/xdg-output-unstable-v1.xml"
|
||||
)
|
||||
# `xdg-activation`.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/staging/xdg-activation/xdg-activation-v1.xml"
|
||||
)
|
||||
# Fractional scale.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/staging/fractional-scale/fractional-scale-v1.xml"
|
||||
)
|
||||
# Viewporter (only required when fractional scale is in use).
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/stable/viewporter/viewporter.xml"
|
||||
)
|
||||
# Color management (HDR properties)
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/staging/color-management/color-management-v1.xml"
|
||||
)
|
||||
# Pointer-constraints.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
|
||||
)
|
||||
# Relative-pointer.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
|
||||
)
|
||||
# Pointer-gestures (multi-touch).
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/pointer-gestures/pointer-gestures-unstable-v1.xml"
|
||||
)
|
||||
# Tablet.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/stable/tablet/tablet-v2.xml"
|
||||
)
|
||||
# Primary-selection.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/primary-selection/primary-selection-unstable-v1.xml"
|
||||
)
|
||||
if(WITH_INPUT_IME)
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/text-input/text-input-unstable-v3.xml"
|
||||
)
|
||||
endif()
|
||||
# Desktop compositor controlled cursor rendering.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/staging/cursor-shape/cursor-shape-v1.xml"
|
||||
)
|
||||
# Support setting the window icon directly (replace legacy `.desktop` files).
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml"
|
||||
)
|
||||
|
||||
unset(INC_DST)
|
||||
|
||||
add_definitions(-DWITH_GHOST_WAYLAND)
|
||||
if(NOT WITH_GHOST_WAYLAND_APP_ID STREQUAL "")
|
||||
add_definitions(-DWITH_GHOST_WAYLAND_APP_ID=${WITH_GHOST_WAYLAND_APP_ID})
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
if(WITH_INPUT_NDOF)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_NDOFManagerUnix.cc
|
||||
|
||||
intern/GHOST_NDOFManagerUnix.hh
|
||||
)
|
||||
endif()
|
||||
|
||||
elseif(WIN32)
|
||||
# # Warnings as errors, this is too strict!
|
||||
# if(MSVC)
|
||||
# string(APPEND CMAKE_CXX_FLAGS " /WX")
|
||||
# endif()
|
||||
|
||||
list(APPEND INC_SYS
|
||||
${WINTAB_INC}
|
||||
)
|
||||
|
||||
list(APPEND SRC
|
||||
intern/GHOST_ContextD3D.cc
|
||||
intern/GHOST_DropTargetWin32.cc
|
||||
intern/GHOST_SystemWin32.cc
|
||||
intern/GHOST_TrackpadWin32.cc
|
||||
intern/GHOST_WindowWin32.cc
|
||||
intern/GHOST_Wintab.cc
|
||||
|
||||
intern/GHOST_ContextD3D.hh
|
||||
intern/GHOST_DropTargetWin32.hh
|
||||
intern/GHOST_SystemWin32.hh
|
||||
intern/GHOST_TaskbarWin32.hh
|
||||
intern/GHOST_TrackpadWin32.hh
|
||||
intern/GHOST_WindowWin32.hh
|
||||
intern/GHOST_Wintab.hh
|
||||
)
|
||||
|
||||
list(APPEND SRC
|
||||
intern/GHOST_ContextWGL.cc
|
||||
|
||||
intern/GHOST_ContextWGL.hh
|
||||
)
|
||||
|
||||
if(WITH_INPUT_IME)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_ImeWin32.cc
|
||||
|
||||
intern/GHOST_ImeWin32.hh
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_INPUT_NDOF)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_NDOFManagerWin32.cc
|
||||
|
||||
intern/GHOST_NDOFManagerWin32.hh
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(WITH_OPENGL_BACKEND)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_ContextEGL.cc
|
||||
|
||||
intern/GHOST_ContextEGL.hh
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
if(WITH_METAL_BACKEND)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_ContextMTL.mm
|
||||
|
||||
intern/GHOST_ContextMTL.hh
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_SystemPathsCocoa.hh
|
||||
intern/GHOST_SystemPathsCocoa.mm
|
||||
)
|
||||
|
||||
elseif(UNIX)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_SystemPathsUnix.cc
|
||||
intern/GHOST_SystemPathsUnix.hh
|
||||
)
|
||||
|
||||
# System-wide (non-portable) installs locate architecture-dependent libraries under `lib`
|
||||
# (typically `/usr/lib`), following the FHS convention - alongside `share/`.
|
||||
if(NOT WITH_INSTALL_PORTABLE)
|
||||
add_definitions(-DPREFIX="${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
# NOTE: A Python module is itself a library: its wrapper libraries are bundled in the package
|
||||
# beside the add-on, so it doesn't need a separate system libraries path.
|
||||
if(NOT WITH_PYTHON_MODULE)
|
||||
# The standard variable for this path is `CMAKE_INSTALL_LIBDIR` (typically `lib` or `lib64`).
|
||||
# It isn't set by default, but systems that build packages may define it.
|
||||
#
|
||||
# NOTE: `include(GNUInstallDirs)` could auto-detect this path.
|
||||
# Since default builds aren't system-wide, leave it to the
|
||||
# package-build scripts to pass in the correct directory.
|
||||
set(_lib "lib")
|
||||
if(DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
if(CMAKE_INSTALL_LIBDIR)
|
||||
set(_lib "${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_definitions(-DBLENDER_INSTALL_LIBDIR="${_lib}")
|
||||
|
||||
unset(_lib)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
elseif(WIN32)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_SystemPathsWin32.cc
|
||||
intern/GHOST_SystemPathsWin32.hh
|
||||
)
|
||||
|
||||
list(APPEND INC
|
||||
../utfconv
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
if(WITH_XR_OPENXR)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_Xr.cc
|
||||
intern/GHOST_XrAction.cc
|
||||
intern/GHOST_XrContext.cc
|
||||
intern/GHOST_XrControllerModel.cc
|
||||
intern/GHOST_XrEvent.cc
|
||||
intern/GHOST_XrGraphicsBinding.cc
|
||||
intern/GHOST_XrSession.cc
|
||||
intern/GHOST_XrSwapchain.cc
|
||||
|
||||
GHOST_IXrContext.hh
|
||||
GHOST_Xr-api.hh
|
||||
intern/GHOST_IXrGraphicsBinding.hh
|
||||
intern/GHOST_XrAction.hh
|
||||
intern/GHOST_XrContext.hh
|
||||
intern/GHOST_XrControllerModel.hh
|
||||
intern/GHOST_XrException.hh
|
||||
intern/GHOST_XrSession.hh
|
||||
intern/GHOST_XrSwapchain.hh
|
||||
intern/GHOST_Xr_intern.hh
|
||||
intern/GHOST_Xr_openxr_includes.hh
|
||||
|
||||
# Header only library.
|
||||
../../extern/tinygltf/tiny_gltf.h
|
||||
)
|
||||
if(APPLE)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_XrGraphicsBindingMetal.mm
|
||||
|
||||
intern/GHOST_XrGraphicsBindingMetal.hh
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_XrGraphicsBindingD3D.cc
|
||||
|
||||
intern/GHOST_XrGraphicsBindingD3D.hh
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND INC_SYS
|
||||
../../extern/json/include
|
||||
../../extern/tinygltf
|
||||
)
|
||||
list(APPEND INC_SYS
|
||||
${XR_OPENXR_SDK_INCLUDE_DIR}
|
||||
)
|
||||
list(APPEND LIB
|
||||
${XR_OPENXR_SDK_LIBRARIES}
|
||||
PRIVATE bf::dependencies::eigen
|
||||
)
|
||||
|
||||
set(XR_PLATFORM_DEFINES
|
||||
)
|
||||
if(WITH_OPENGL_BACKEND)
|
||||
list(APPEND XR_PLATFORM_DEFINES -DXR_USE_GRAPHICS_API_OPENGL)
|
||||
endif()
|
||||
if(WITH_VULKAN_BACKEND)
|
||||
list(APPEND XR_PLATFORM_DEFINES -DXR_USE_GRAPHICS_API_VULKAN)
|
||||
list(APPEND SRC
|
||||
intern/GHOST_XrGraphicsBindingVulkan.cc
|
||||
|
||||
intern/GHOST_XrGraphicsBindingVulkan.hh
|
||||
)
|
||||
endif()
|
||||
if(WITH_METAL_BACKEND)
|
||||
list(APPEND XR_PLATFORM_DEFINES -DXR_USE_GRAPHICS_API_METAL)
|
||||
endif()
|
||||
|
||||
# Add compiler defines as required by the OpenXR specification.
|
||||
if(WIN32)
|
||||
list(APPEND XR_PLATFORM_DEFINES
|
||||
-DXR_USE_PLATFORM_WIN32
|
||||
-DXR_USE_GRAPHICS_API_D3D11
|
||||
)
|
||||
list(APPEND LIB
|
||||
shlwapi
|
||||
)
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
list(APPEND XR_PLATFORM_DEFINES -DXR_OS_LINUX)
|
||||
if(WITH_GHOST_WAYLAND)
|
||||
list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_WAYLAND)
|
||||
endif()
|
||||
if(WITH_GHOST_X11)
|
||||
list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_EGL)
|
||||
list(APPEND XR_PLATFORM_DEFINES -DXR_USE_PLATFORM_XLIB)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_definitions(-DWITH_XR_OPENXR ${XR_PLATFORM_DEFINES})
|
||||
|
||||
unset(XR_PLATFORM_DEFINES)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_intern_ghost "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
56
blender-5.2.0/intern/ghost/GHOST_CallbackEventConsumer.hh
Normal file
56
blender-5.2.0/intern/ghost/GHOST_CallbackEventConsumer.hh
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_CallbackEventConsumer class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_IEventConsumer.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
/**
|
||||
* Definition of a callback routine that receives events.
|
||||
* \param event: The event received.
|
||||
* \param user_data: The callback's user data, supplied to #GHOST_CreateSystem.
|
||||
*/
|
||||
using GHOST_EventCallbackProcPtr = bool (*)(const GHOST_IEvent *event,
|
||||
GHOST_TUserDataPtr user_data);
|
||||
|
||||
/**
|
||||
* Event consumer that will forward events to a call-back routine.
|
||||
* Especially useful for the C-API.
|
||||
*/
|
||||
class GHOST_CallbackEventConsumer : public GHOST_IEventConsumer {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param eventCallback: The call-back routine invoked.
|
||||
* \param user_data: The data passed back through the call-back routine.
|
||||
*/
|
||||
GHOST_CallbackEventConsumer(GHOST_EventCallbackProcPtr eventCallback,
|
||||
GHOST_TUserDataPtr user_data);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_CallbackEventConsumer() override = default;
|
||||
|
||||
/**
|
||||
* This method is called by an event producer when an event is available.
|
||||
* \param event: The event that can be handled or ignored.
|
||||
* \return Indication as to whether the event was handled.
|
||||
*/
|
||||
bool processEvent(const GHOST_IEvent *event) override;
|
||||
|
||||
protected:
|
||||
/** The call-back routine invoked. */
|
||||
GHOST_EventCallbackProcPtr event_callback_;
|
||||
/** The data passed back through the call-back routine. */
|
||||
GHOST_TUserDataPtr user_data_;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_CallbackEventConsumer")
|
||||
};
|
||||
111
blender-5.2.0/intern/ghost/GHOST_IContext.hh
Normal file
111
blender-5.2.0/intern/ghost/GHOST_IContext.hh
Normal file
@@ -0,0 +1,111 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_IContext interface class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
# include <functional>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Interface for GHOST context.
|
||||
*
|
||||
* You can create a off-screen context (windowless) with the system's
|
||||
* #GHOST_ISystem::createOffscreenContext method.
|
||||
* \see GHOST_ISystem#createOffscreenContext
|
||||
*/
|
||||
class GHOST_IContext {
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~GHOST_IContext() = default;
|
||||
|
||||
/**
|
||||
* Returns the thread's currently active drawing context.
|
||||
*/
|
||||
static GHOST_IContext *getActiveDrawingContext();
|
||||
|
||||
/**
|
||||
* Activates the drawing context.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
virtual GHOST_TSuccess activateDrawingContext() = 0;
|
||||
|
||||
/**
|
||||
* Release the drawing context of the calling thread.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
virtual GHOST_TSuccess releaseDrawingContext() = 0;
|
||||
|
||||
/**
|
||||
* Gets the OpenGL frame-buffer associated with the OpenGL context
|
||||
* \return The ID of an OpenGL frame-buffer object.
|
||||
*/
|
||||
virtual unsigned int getDefaultFramebuffer() = 0;
|
||||
/**
|
||||
* Acquire next buffer for drawing.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
virtual GHOST_TSuccess swapBufferAcquire() = 0;
|
||||
|
||||
/**
|
||||
* Swaps front and back buffers of a window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
virtual GHOST_TSuccess swapBufferRelease() = 0;
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
/**
|
||||
* Get Vulkan handles for the given context.
|
||||
*
|
||||
* These handles are the same for a given context.
|
||||
* Should only be called when using a Vulkan context.
|
||||
* Other contexts will not return any handles and leave the
|
||||
* handles where the parameters are referring to unmodified.
|
||||
*
|
||||
* \param r_handles: After calling this structure is filled with
|
||||
* the vulkan handles of the context.
|
||||
*/
|
||||
virtual GHOST_TSuccess getVulkanHandles(GHOST_VulkanHandles &r_handles) = 0;
|
||||
|
||||
/**
|
||||
* Acquire the current swap-chain format.
|
||||
*
|
||||
* \param r_swap_chain_data: After calling this function the structure
|
||||
* referenced by this parameter will contain the surface format and
|
||||
* the size of the frame buffer and image in pixels.
|
||||
*/
|
||||
virtual GHOST_TSuccess getVulkanSwapChainFormat(
|
||||
GHOST_VulkanSwapChainData *r_swap_chain_data) = 0;
|
||||
|
||||
/**
|
||||
* Set the pre and post callbacks for vulkan swap-chain in the given context.
|
||||
*
|
||||
* \param swap_buffer_draw_callback: Function pointer to be called when acquired swap buffer is
|
||||
* released, allowing Vulkan backend to update the swap chain.
|
||||
* \param swap_buffer_acquired_callback: Function to be called at when swap buffer is acquired.
|
||||
* Allowing Vulkan backend to update the frame-buffer. It is also called when no swap chain
|
||||
* exists indicating that the window was minimized.
|
||||
* \param openxr_acquire_framebuffer_image_callback: Function to be called when an image needs to
|
||||
* be acquired to be drawn to an OpenXR swap-chain.
|
||||
* \param openxr_release_framebuffer_image_callback: Function to be called after an image has
|
||||
* been drawn to the OpenXR swap-chain.
|
||||
*/
|
||||
virtual GHOST_TSuccess setVulkanSwapBuffersCallbacks(
|
||||
std::function<void(const GHOST_VulkanSwapChainData *, bool)> swap_buffer_draw_callback,
|
||||
std::function<void(void)> swap_buffer_acquired_callback,
|
||||
std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback,
|
||||
std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback) = 0;
|
||||
#endif
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_IContext")
|
||||
};
|
||||
60
blender-5.2.0/intern/ghost/GHOST_IEvent.hh
Normal file
60
blender-5.2.0/intern/ghost/GHOST_IEvent.hh
Normal file
@@ -0,0 +1,60 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_IEvent interface class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
#include <cstddef>
|
||||
|
||||
class GHOST_IWindow;
|
||||
|
||||
/**
|
||||
* Interface class for events received from GHOST.
|
||||
* You should not need to inherit this class. The system will pass these events
|
||||
* to the #GHOST_IEventConsumer::processEvent() method of event consumers.<br>
|
||||
* Use the #getType() method to retrieve the type of event and the #getData()
|
||||
* method to get the event data out. Using the event type you can cast the
|
||||
* event data to the correct event data structure.
|
||||
* \see GHOST_IEventConsumer#processEvent
|
||||
* \see GHOST_TEventType
|
||||
*/
|
||||
class GHOST_IEvent {
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~GHOST_IEvent() = default;
|
||||
|
||||
/**
|
||||
* Returns the event type.
|
||||
* \return The event type.
|
||||
*/
|
||||
virtual GHOST_TEventType getType() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the time this event was generated.
|
||||
* \return The event generation time.
|
||||
*/
|
||||
virtual uint64_t getTime() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the window this event was generated on,
|
||||
* or nullptr if it is a 'system' event.
|
||||
* \return The generating window.
|
||||
*/
|
||||
virtual GHOST_IWindow *getWindow() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the event data.
|
||||
* \return The event data.
|
||||
*/
|
||||
virtual GHOST_TEventDataPtr getData() const = 0;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_IEvent")
|
||||
};
|
||||
38
blender-5.2.0/intern/ghost/GHOST_IEventConsumer.hh
Normal file
38
blender-5.2.0/intern/ghost/GHOST_IEventConsumer.hh
Normal file
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_IEventConsumer interface class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_IEvent.hh"
|
||||
|
||||
/**
|
||||
* Interface class for objects interested in receiving events.
|
||||
* Objects interested in events should inherit this class and implement the
|
||||
* processEvent() method. They should then be registered with the system that
|
||||
* they want to receive events. The system will call the processEvent() method
|
||||
* for every installed event consumer to pass events.
|
||||
* \see GHOST_ISystem#addEventConsumer
|
||||
*/
|
||||
class GHOST_IEventConsumer {
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~GHOST_IEventConsumer() = default;
|
||||
|
||||
/**
|
||||
* This method is called by the system when it has events to dispatch.
|
||||
* \see GHOST_ISystem#dispatchEvents
|
||||
* \param event: The event that can be handled or ignored.
|
||||
* \return Indication as to whether the event was handled.
|
||||
*/
|
||||
virtual bool processEvent(const GHOST_IEvent *event) = 0;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_IEventConsumer")
|
||||
};
|
||||
557
blender-5.2.0/intern/ghost/GHOST_ISystem.hh
Normal file
557
blender-5.2.0/intern/ghost/GHOST_ISystem.hh
Normal file
@@ -0,0 +1,557 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* %Main interface file for C++ API with declaration of GHOST_ISystem interface
|
||||
* class.
|
||||
* Contains the DOXYGEN documentation main page.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "GHOST_IContext.hh"
|
||||
#include "GHOST_ITimerTask.hh"
|
||||
#include "GHOST_IWindow.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
class GHOST_IEventConsumer;
|
||||
|
||||
/**
|
||||
* \page GHOSTPage GHOST
|
||||
*
|
||||
* \section intro Introduction
|
||||
*
|
||||
* GHOST is yet another acronym. It stands for "Generic Handy Operating System
|
||||
* Toolkit". It has been created to replace the OpenGL utility tool kit
|
||||
* <a href="http://www.opengl.org/resources/libraries/glut/">GLUT</a>.
|
||||
* GLUT was used in <a href="http://www.blender3d.com">Blender</a> until the
|
||||
* point that Blender needed to be ported to Apple's Mac OSX. Blender needed a
|
||||
* number of modifications in GLUT to work but the GLUT sources for OSX were
|
||||
* unavailable at the time. The decision was made to build our own replacement
|
||||
* for GLUT. In those days, NaN Technologies BV was the company that developed
|
||||
* Blender.
|
||||
* <br><br>
|
||||
* Enough history. What does GHOST have to offer?<br>
|
||||
* In short: everything that Blender needed from GLUT to run on all its supported
|
||||
* operating systems and some extra's.
|
||||
* This includes :
|
||||
*
|
||||
* - Time(r) management.
|
||||
* - Display/window management (windows are only created on the main display).
|
||||
* - Event management.
|
||||
* - Cursor shape management (no custom cursors for now).
|
||||
* - Access to the state of the mouse buttons and the keyboard.
|
||||
* - Menus for windows with events generated when they are accessed (this is
|
||||
* work in progress).
|
||||
* - Video mode switching.
|
||||
* - Copy/Paste buffers.
|
||||
* - System paths.
|
||||
*
|
||||
* Font management has been moved to a separate library.
|
||||
*
|
||||
* \section platforms Platforms
|
||||
*
|
||||
* GHOST supports the following platforms:
|
||||
*
|
||||
* - macOS Cocoa.
|
||||
* - Microsoft Windows.
|
||||
* - X11 (Linux).
|
||||
* - Wayland (Linux).
|
||||
* - SDL3.
|
||||
* - null (headless mode).
|
||||
*
|
||||
* \section Building GHOST
|
||||
*
|
||||
* GHOST is not build standalone however there are tests in intern/ghost/test
|
||||
*
|
||||
* \section interface Interface
|
||||
* The GHOST C++ API consists of the following files:
|
||||
*
|
||||
* - GHOST_IEvent.hh
|
||||
* - GHOST_IEventConsumer.hh
|
||||
* - GHOST_ISystem.hh
|
||||
* - GHOST_ITimerTask.hh
|
||||
* - GHOST_IWindow.hh
|
||||
* - GHOST_Rect.hh
|
||||
* - GHOST_Types.hh
|
||||
* - GHOST_Xr-api.hh
|
||||
*
|
||||
* For an example of using the GHOST API, have a look at the GHOST_Test.cpp
|
||||
* program in the ?/ghost/test/gears/ directory.
|
||||
*
|
||||
* \section work Work in progress
|
||||
* \todo write WIP section
|
||||
*/
|
||||
|
||||
/** \interface GHOST_ISystem
|
||||
* Interface for classes that provide access to the operating system.
|
||||
* There should be only one system class in an application.
|
||||
* Therefore, the routines to create and dispose the system are static.
|
||||
* Provides:
|
||||
* -# Time(r) management.
|
||||
* -# Display/window management (windows are only created on the main display).
|
||||
* -# Event management.
|
||||
* -# Cursor shape management (no custom cursors for now).
|
||||
* -# Access to the state of the mouse buttons and the keyboard.
|
||||
* -# Menus for windows with events generated when they are accessed (this is
|
||||
* work in progress).
|
||||
*/
|
||||
class GHOST_ISystem {
|
||||
public:
|
||||
/**
|
||||
* Creates the one and only system.
|
||||
* \param verbose: report back-ends that were attempted no back-end could be loaded.
|
||||
* \param background: loading the system for background rendering (no visible windows).
|
||||
* \return An indication of success.
|
||||
*/
|
||||
|
||||
static GHOST_TSuccess createSystem(bool verbose = true, bool background = false);
|
||||
static GHOST_TSuccess createSystemBackground();
|
||||
|
||||
/**
|
||||
* Disposes the one and only system.
|
||||
* \return An indication of success.
|
||||
*/
|
||||
static GHOST_TSuccess disposeSystem();
|
||||
|
||||
/**
|
||||
* Returns a pointer to the one and only system (nil if it hasn't been created).
|
||||
* \return A pointer to the system.
|
||||
*/
|
||||
static GHOST_ISystem *getSystem();
|
||||
/**
|
||||
* Return an identifier for the one and only system.
|
||||
* \warning while it may be tempting this should never be used to check for supported features,
|
||||
* in that case, the GHOST API should be extended to query capabilities.
|
||||
* This is needed for X11/WAYLAND on Unix, without this - there is no convenient way for users to
|
||||
* check if WAYLAND or XWAYLAND are in use since they are dynamically selected at startup.
|
||||
* When dynamically switching between X11/WAYLAND is removed, this function can go too.
|
||||
*/
|
||||
static const char *getSystemBackend();
|
||||
|
||||
static GHOST_TBacktraceFn getBacktraceFn();
|
||||
static void setBacktraceFn(GHOST_TBacktraceFn backtrace_fn);
|
||||
|
||||
static bool getUseWindowFrame();
|
||||
static void setUseWindowFrame(bool use_window_frame);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor.
|
||||
* Protected default constructor to force use of static createSystem member.
|
||||
*/
|
||||
GHOST_ISystem() {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
* Protected default constructor to force use of static dispose member.
|
||||
*/
|
||||
virtual ~GHOST_ISystem() = default;
|
||||
|
||||
public:
|
||||
/***************************************************************************************
|
||||
* Time(r) functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns the system time.
|
||||
* Returns the number of milliseconds since the start of the system.
|
||||
* \note The exact method used is platform dependent however monotonic methods should be used
|
||||
* instead of wall-clock time.
|
||||
*
|
||||
* \return The number of milliseconds.
|
||||
*/
|
||||
virtual uint64_t getMilliSeconds() const = 0;
|
||||
|
||||
/**
|
||||
* Installs a timer.
|
||||
*
|
||||
* \note On most operating systems, messages need to be processed in order
|
||||
* for the timer callbacks to be invoked.
|
||||
*
|
||||
* \param delay: The time to wait for the first call to the #timer_proc (in milliseconds).
|
||||
* \param interval: The interval between calls to the #timer_proc.
|
||||
* \param timer_proc: The callback invoked when the interval expires.
|
||||
* \param user_data: Placeholder for user data.
|
||||
* \return A timer task (0 if timer task installation failed).
|
||||
*/
|
||||
virtual GHOST_ITimerTask *installTimer(uint64_t delay,
|
||||
uint64_t interval,
|
||||
GHOST_TimerProcPtr timer_proc,
|
||||
GHOST_TUserDataPtr user_data = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* Removes a timer.
|
||||
* \param timerTask: Timer task to be removed.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess removeTimer(GHOST_ITimerTask *timerTask) = 0;
|
||||
|
||||
/***************************************************************************************
|
||||
* Display/window management functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns the number of displays on this system.
|
||||
* \return The number of displays.
|
||||
*/
|
||||
virtual uint8_t getNumDisplays() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the dimensions of the main display on this system.
|
||||
* \return The dimension of the main display.
|
||||
*/
|
||||
virtual void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const = 0;
|
||||
|
||||
/**
|
||||
* Returns the combine dimensions of all monitors.
|
||||
* \return The dimension of the workspace.
|
||||
*/
|
||||
virtual void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const = 0;
|
||||
|
||||
/**
|
||||
* Create a new window.
|
||||
* The new window is added to the list of windows managed.
|
||||
* Never explicitly delete the window, use disposeWindow() instead.
|
||||
* \param title: The name of the window
|
||||
* (displayed in the title bar of the window if the OS supports it).
|
||||
* \param left: The coordinate of the left edge of the window.
|
||||
* \param top: The coordinate of the top edge of the window.
|
||||
* \param width: The width the window.
|
||||
* \param height: The height the window.
|
||||
* \param state: The state of the window when opened.
|
||||
* \param gpu_settings: Misc GPU settings.
|
||||
* \param exclusive: Use to show the window on top and ignore others (used full-screen).
|
||||
* \param is_dialog: Stay on top of parent window, no icon in taskbar, can't be minimized.
|
||||
* \param parent_window: Parent (embedder) window
|
||||
* \return The new window (or 0 if creation failed).
|
||||
*/
|
||||
virtual GHOST_IWindow *createWindow(const char *title,
|
||||
int32_t left,
|
||||
int32_t top,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
GHOST_TWindowState state,
|
||||
GHOST_GPUSettings gpu_settings,
|
||||
const bool exclusive = false,
|
||||
const bool is_dialog = false,
|
||||
const GHOST_IWindow *parent_window = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* Dispose a window.
|
||||
* \param window: Pointer to the window to be disposed.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess disposeWindow(GHOST_IWindow *window) = 0;
|
||||
|
||||
/**
|
||||
* Create a new off-screen context.
|
||||
* Never explicitly delete the context, use #disposeContext() instead.
|
||||
* \return The new context (or 0 if creation failed).
|
||||
*/
|
||||
virtual GHOST_IContext *createOffscreenContext(GHOST_GPUSettings gpu_settings) = 0;
|
||||
|
||||
/**
|
||||
* Dispose of a context.
|
||||
* \param context: Pointer to the context to be disposed.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess disposeContext(GHOST_IContext *context) = 0;
|
||||
|
||||
/**
|
||||
* Returns whether a window is valid.
|
||||
* \param window: Pointer to the window to be checked.
|
||||
* \return Indication of validity.
|
||||
*/
|
||||
virtual bool validWindow(GHOST_IWindow *window) = 0;
|
||||
|
||||
/**
|
||||
* Native pixel size support (MacBook 'retina').
|
||||
* \return The pixel size in float.
|
||||
*/
|
||||
virtual bool useNativePixel() = 0;
|
||||
|
||||
/**
|
||||
* Return features supported by the system back-end.
|
||||
*
|
||||
* The resulting value doesn't change at run-time.
|
||||
*/
|
||||
virtual GHOST_TCapabilityFlag getCapabilities() const = 0;
|
||||
|
||||
/**
|
||||
* Focus window after opening, or put them in the background.
|
||||
*/
|
||||
virtual void useWindowFocus(const bool use_focus) = 0;
|
||||
|
||||
/**
|
||||
* Focus and raise windows on mouse hover.
|
||||
*/
|
||||
virtual void setAutoFocus(const bool auto_focus) = 0;
|
||||
|
||||
/**
|
||||
* Get the Window under the cursor. Although coordinates of the mouse are supplied, platform-
|
||||
* specific implementations are free to ignore these and query the mouse location themselves, due
|
||||
* to them possibly being incorrect under certain conditions, for example when using multiple
|
||||
* monitors that vary in scale and/or DPI.
|
||||
* \param x: The x-coordinate of the cursor.
|
||||
* \param y: The y-coordinate of the cursor.
|
||||
* \return The window under the cursor or nullptr if none.
|
||||
*/
|
||||
virtual GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y) = 0;
|
||||
|
||||
/***************************************************************************************
|
||||
* Event management functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Retrieves events from the system and stores them in the queue.
|
||||
* \param waitForEvent: Flag to wait for an event (or return immediately).
|
||||
* \return Indication of the presence of events.
|
||||
*/
|
||||
virtual bool processEvents(bool waitForEvent) = 0;
|
||||
|
||||
/**
|
||||
* Retrieves events from the queue and send them to the event consumers.
|
||||
* The event stack will be empty afterwards.
|
||||
*/
|
||||
virtual void dispatchEvents() = 0;
|
||||
|
||||
/**
|
||||
* Adds the given event consumer to our list.
|
||||
* \param consumer: The event consumer to add.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess addEventConsumer(GHOST_IEventConsumer *consumer) = 0;
|
||||
|
||||
/**
|
||||
* Removes the given event consumer to our list.
|
||||
* \param consumer: The event consumer to remove.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess removeEventConsumer(GHOST_IEventConsumer *consumer) = 0;
|
||||
|
||||
/***************************************************************************************
|
||||
* Cursor management functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns the current location of the cursor (location in window coordinates)
|
||||
* \param x: The x-coordinate of the cursor.
|
||||
* \param y: The y-coordinate of the cursor.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess getCursorPositionClientRelative(const GHOST_IWindow *window,
|
||||
int32_t &x,
|
||||
int32_t &y) const = 0;
|
||||
/**
|
||||
* Updates the location of the cursor (location in window coordinates).
|
||||
* \param x: The x-coordinate of the cursor.
|
||||
* \param y: The y-coordinate of the cursor.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess setCursorPositionClientRelative(GHOST_IWindow *window,
|
||||
int32_t x,
|
||||
int32_t y) = 0;
|
||||
|
||||
/**
|
||||
* Returns the current location of the cursor (location in screen coordinates)
|
||||
* \param x: The x-coordinate of the cursor.
|
||||
* \param y: The y-coordinate of the cursor.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const = 0;
|
||||
|
||||
/**
|
||||
* \return the size of the cursor in logical pixels (before Hi-DPI scaling is applied).
|
||||
*/
|
||||
virtual uint32_t getCursorPreferredLogicalSize() const = 0;
|
||||
|
||||
/**
|
||||
* Updates the location of the cursor (location in screen coordinates).
|
||||
* Not all operating systems allow the cursor to be moved (without the input device being moved).
|
||||
* \param x: The x-coordinate of the cursor.
|
||||
* \param y: The y-coordinate of the cursor.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) = 0;
|
||||
|
||||
/***************************************************************************************
|
||||
* Access to mouse button and keyboard states.
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns the state of a modifier key (outside the message queue).
|
||||
* \param mask: The modifier key state to retrieve.
|
||||
* \param is_down: The state of a modifier key (true == pressed).
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess getModifierKeyState(GHOST_TModifierKey mask, bool &is_down) const = 0;
|
||||
|
||||
/**
|
||||
* Returns the state of a mouse button (outside the message queue).
|
||||
* \param mask: The button state to retrieve.
|
||||
* \param is_down: Button state.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess getButtonState(GHOST_TButton mask, bool &is_down) const = 0;
|
||||
|
||||
/**
|
||||
* Enable multi-touch gestures if supported.
|
||||
* \param use: Enable or disable.
|
||||
*/
|
||||
virtual void setMultitouchGestures(const bool use) = 0;
|
||||
|
||||
/**
|
||||
* Set which tablet API to use. Only affects Windows, other platforms have a single API.
|
||||
* \param api: Enum indicating which API to use.
|
||||
*/
|
||||
virtual void setTabletAPI(GHOST_TTabletAPI api) = 0;
|
||||
|
||||
/**
|
||||
* Get the color of the pixel at the current mouse cursor location
|
||||
* \param r_color: returned sRGB float colors
|
||||
* \return Success value (true == successful and supported by platform)
|
||||
*/
|
||||
virtual GHOST_TSuccess getPixelAtCursor(float r_color[3]) const = 0;
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
/**
|
||||
* Sets 3D mouse dead-zone.
|
||||
* \param deadzone: Dead-zone of the 3D mouse (both for rotation and pan) relative to full range
|
||||
*/
|
||||
virtual void setNDOFDeadZone(float deadzone) = 0;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Set the Console State
|
||||
* \param action: console state
|
||||
* \return current status (true: visible, 0: hidden)
|
||||
*/
|
||||
virtual bool setConsoleWindowState(GHOST_TConsoleWindowState action) = 0;
|
||||
|
||||
/***************************************************************************************
|
||||
* Access to clipboard.
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Return the clipboard buffer or null.
|
||||
*
|
||||
* \param selection: Use the "primary" selection.
|
||||
* Check the #GHOST_kCapabilityClipboardPrimary for backends that support this.
|
||||
* \return Returns the clipboard data as a null terminated string or null when unavailable.
|
||||
*/
|
||||
virtual char *getClipboard(bool selection) const = 0;
|
||||
|
||||
/**
|
||||
* Put data to the Clipboard
|
||||
* \param buffer: The buffer to copy to the clipboard.
|
||||
* \param selection: The clipboard to copy too only used on X11.
|
||||
*/
|
||||
virtual void putClipboard(const char *buffer, bool selection) const = 0;
|
||||
|
||||
/**
|
||||
* Returns GHOST_kSuccess if the clipboard contains an image.
|
||||
*/
|
||||
virtual GHOST_TSuccess hasClipboardImage() const = 0;
|
||||
|
||||
/**
|
||||
* Get image data from the Clipboard
|
||||
* \param r_width: the returned image width in pixels.
|
||||
* \param r_height: the returned image height in pixels.
|
||||
* \return pointer uint array in RGBA byte order. Caller must free.
|
||||
*/
|
||||
virtual uint *getClipboardImage(int *r_width, int *r_height) const = 0;
|
||||
|
||||
/**
|
||||
* Put image data to the Clipboard
|
||||
* \param rgba: uint array in RGBA byte order.
|
||||
* \param width: the image width in pixels.
|
||||
* \param height: the image height in pixels.
|
||||
*/
|
||||
virtual GHOST_TSuccess putClipboardImage(uint *rgba, int width, int height) const = 0;
|
||||
|
||||
/***************************************************************************************
|
||||
* Window "Client Side Decorations" (CSD)
|
||||
***************************************************************************************/
|
||||
|
||||
virtual void setWindowCSD(const GHOST_CSD_Params ¶ms) = 0;
|
||||
virtual const GHOST_CSD_Layout &getWindowCSD_Layout() const = 0;
|
||||
|
||||
/***************************************************************************************
|
||||
* Window Icon Generator.
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Set a callback used to generate window icons.
|
||||
*
|
||||
* Back-ends that support window icons will call this to fill pixel data.
|
||||
*/
|
||||
virtual void setIconGenerator(const GHOST_IconGenerator *icon_generator) = 0;
|
||||
|
||||
/***************************************************************************************
|
||||
* System Message Box.
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Show a system message box
|
||||
*
|
||||
* \param title: The title of the message box.
|
||||
* \param message: The message to display.
|
||||
* \param help_label: Help button label.
|
||||
* \param continue_label: Continue button label.
|
||||
* \param link: An optional hyperlink.
|
||||
* \param dialog_options: Options how to display the message.
|
||||
*/
|
||||
virtual GHOST_TSuccess showMessageBox(const char * /*title*/,
|
||||
const char * /*message*/,
|
||||
const char * /*help_label*/,
|
||||
const char * /*continue_label*/,
|
||||
const char * /*link*/,
|
||||
GHOST_DialogOptions /*dialog_options*/) const = 0;
|
||||
|
||||
/***************************************************************************************
|
||||
* Debugging
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Specify whether debug messages are to be shown.
|
||||
* \param debug: Flag for systems to debug.
|
||||
*/
|
||||
virtual void initDebug(GHOST_Debug debug) = 0;
|
||||
|
||||
/**
|
||||
* Check whether debug messages are to be shown.
|
||||
*/
|
||||
virtual bool isDebugEnabled() = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Initialize the system.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess init() = 0;
|
||||
|
||||
/**
|
||||
* Shut the system down.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess exit() = 0;
|
||||
|
||||
/** The one and only system */
|
||||
static GHOST_ISystem *system_;
|
||||
static const char *system_backend_id_;
|
||||
|
||||
/** Function to call that sets the back-trace. */
|
||||
static GHOST_TBacktraceFn backtrace_fn_;
|
||||
|
||||
/**
|
||||
* When false, don't use window frame.
|
||||
*/
|
||||
static bool use_window_frame_;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_ISystem")
|
||||
};
|
||||
101
blender-5.2.0/intern/ghost/GHOST_ISystemPaths.hh
Normal file
101
blender-5.2.0/intern/ghost/GHOST_ISystemPaths.hh
Normal file
@@ -0,0 +1,101 @@
|
||||
/* SPDX-FileCopyrightText: 2009 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
class GHOST_ISystemPaths {
|
||||
public:
|
||||
/**
|
||||
* Creates the one and only system.
|
||||
* \return An indication of success.
|
||||
*/
|
||||
static GHOST_TSuccess create();
|
||||
|
||||
/**
|
||||
* Disposes the one and only system.
|
||||
* \return An indication of success.
|
||||
*/
|
||||
static GHOST_TSuccess dispose();
|
||||
|
||||
/**
|
||||
* Returns a pointer to the one and only system.
|
||||
* \return A pointer to the system.
|
||||
*/
|
||||
static GHOST_ISystemPaths *get();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor.
|
||||
* Protected default constructor to force use of static createSystem member.
|
||||
*/
|
||||
GHOST_ISystemPaths() {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
* Protected default constructor to force use of static dispose member.
|
||||
*/
|
||||
virtual ~GHOST_ISystemPaths() = default;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Determine the base directory in which shared resources are located. It will first try to use
|
||||
* "unpack and run" path, then look for properly installed path, including versioning.
|
||||
* \return Unsigned char string pointing to system directory (eg `/usr/share/blender/`).
|
||||
*/
|
||||
virtual const char *getSystemDir(int version, const char *versionstr) const = 0;
|
||||
|
||||
/**
|
||||
* The base directory where architecture-dependent files are located,
|
||||
* mirroring #getSystemDir under the install lib tree (eg `/usr/lib/blender/`).
|
||||
*
|
||||
* \return String pointing to the system libraries directory,
|
||||
* or null when this build has no separate system library directory.
|
||||
*
|
||||
* For portable builds this function always returns null, for specific details
|
||||
* see CMake's `BLENDER_INSTALL_LIBDIR` define for when this is/isn't used and why.
|
||||
*/
|
||||
virtual const char *getSystemLibsDir(int /*version*/, const char * /*versionstr*/) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the base directory in which user configuration is stored, including versioning.
|
||||
* If needed, it will create the base directory.
|
||||
* \return Unsigned char string pointing to user directory (eg `~/.blender/`).
|
||||
*/
|
||||
virtual const char *getUserDir(int version, const char *versionstr) const = 0;
|
||||
|
||||
/**
|
||||
* Determine a special ("well known") and easy to reach user directory.
|
||||
* \return If successfull, a string containing the user directory path (eg `~/Documents/`).
|
||||
*/
|
||||
virtual std::optional<std::string> getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const = 0;
|
||||
|
||||
/**
|
||||
* Determine the directory of the current binary
|
||||
* \return Unsigned char string pointing to the binary directory.
|
||||
*/
|
||||
virtual const char *getBinaryDir() const = 0;
|
||||
|
||||
/**
|
||||
* Add the file to the operating system most recently used files
|
||||
*/
|
||||
virtual void addToSystemRecentFiles(const char *filepath) const = 0;
|
||||
|
||||
private:
|
||||
/** The one and only system paths. */
|
||||
static GHOST_ISystemPaths *system_paths_;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_ISystemPaths")
|
||||
};
|
||||
58
blender-5.2.0/intern/ghost/GHOST_ITimerTask.hh
Normal file
58
blender-5.2.0/intern/ghost/GHOST_ITimerTask.hh
Normal file
@@ -0,0 +1,58 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_ITimerTask interface class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
/**
|
||||
* Interface for a timer task.
|
||||
* Timer tasks are created by the system and can be installed by the system.
|
||||
* After installation, the timer callback-procedure or "timer_proc" will be called
|
||||
* periodically. You should not need to inherit this class. It is passed to the
|
||||
* application in the timer-callback.<br>
|
||||
* <br>
|
||||
* Note that GHOST processes timers in the UI thread. You should ask GHOST
|
||||
* process messages in order for the timer-callbacks to be called.
|
||||
* \see GHOST_ISystem#installTimer
|
||||
* \see GHOST_TimerProcPtr
|
||||
*/
|
||||
class GHOST_ITimerTask {
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~GHOST_ITimerTask() = default;
|
||||
|
||||
/**
|
||||
* Returns the timer callback.
|
||||
* \return The timer callback.
|
||||
*/
|
||||
virtual GHOST_TimerProcPtr getTimerProc() const = 0;
|
||||
|
||||
/**
|
||||
* Changes the timer callback.
|
||||
* \param timer_proc: The timer callback.
|
||||
*/
|
||||
virtual void setTimerProc(const GHOST_TimerProcPtr timer_proc) = 0;
|
||||
|
||||
/**
|
||||
* Returns the timer user data.
|
||||
* \return The timer user data.
|
||||
*/
|
||||
virtual GHOST_TUserDataPtr getUserData() const = 0;
|
||||
|
||||
/**
|
||||
* Changes the time user data.
|
||||
* \param user_data: The timer user data.
|
||||
*/
|
||||
virtual void setUserData(const GHOST_TUserDataPtr user_data) = 0;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_ITimerTask")
|
||||
};
|
||||
387
blender-5.2.0/intern/ghost/GHOST_IWindow.hh
Normal file
387
blender-5.2.0/intern/ghost/GHOST_IWindow.hh
Normal file
@@ -0,0 +1,387 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_IWindow interface class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Rect.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
class GHOST_IContext;
|
||||
|
||||
/**
|
||||
* Interface for GHOST windows.
|
||||
*
|
||||
* You can create a window with the system's GHOST_ISystem::createWindow
|
||||
* method.
|
||||
* \see GHOST_ISystem#createWindow
|
||||
*
|
||||
* There are two coordinate systems:
|
||||
*
|
||||
* - The screen coordinate system. The origin of the screen is located in the
|
||||
* upper left corner of the screen.
|
||||
* - The client rectangle coordinate system. The client rectangle of a window
|
||||
* is the area that is drawable by the application (excluding title bars etc.).
|
||||
*/
|
||||
class GHOST_IWindow {
|
||||
public:
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~GHOST_IWindow() = default;
|
||||
|
||||
/**
|
||||
* Returns indication as to whether the window is valid.
|
||||
* \return The validity of the window.
|
||||
*/
|
||||
virtual bool getValid() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the type of drawing context used in this window.
|
||||
* \return The current type of drawing context.
|
||||
*/
|
||||
virtual GHOST_TDrawingContextType getDrawingContextType() = 0;
|
||||
|
||||
/**
|
||||
* Tries to install a rendering context in this window.
|
||||
* \param type: The type of rendering context installed.
|
||||
* \return Indication as to whether installation has succeeded.
|
||||
*/
|
||||
virtual GHOST_TSuccess setDrawingContextType(GHOST_TDrawingContextType type) = 0;
|
||||
|
||||
/**
|
||||
* Returns the drawing context used in this window.
|
||||
* \return The current drawing context.
|
||||
*/
|
||||
virtual GHOST_IContext *getDrawingContext() = 0;
|
||||
|
||||
/**
|
||||
* Sets the title displayed in the title bar.
|
||||
* \param title: The title to display in the title bar.
|
||||
*/
|
||||
virtual void setTitle(const char *title) = 0;
|
||||
|
||||
/**
|
||||
* Returns the title displayed in the title bar.
|
||||
* \return The title displayed in the title bar.
|
||||
*/
|
||||
virtual std::string getTitle() const = 0;
|
||||
|
||||
/**
|
||||
* Sets the file name represented by this window.
|
||||
* \param filepath: The file directory.
|
||||
*/
|
||||
virtual void setPath(const char *filepath) = 0;
|
||||
|
||||
/**
|
||||
* Return the current window decoration style flags.
|
||||
*/
|
||||
virtual GHOST_TWindowDecorationStyleFlags getWindowDecorationStyleFlags() = 0;
|
||||
|
||||
/**
|
||||
* Set the window decoration style flags.
|
||||
* \param style_flags: Window decoration style flags.
|
||||
*/
|
||||
virtual void setWindowDecorationStyleFlags(GHOST_TWindowDecorationStyleFlags style_flags) = 0;
|
||||
|
||||
/**
|
||||
* Set the window decoration style settings.
|
||||
* \param decoration_settings: Window decoration style settings.
|
||||
*/
|
||||
virtual void setWindowDecorationStyleSettings(
|
||||
GHOST_WindowDecorationStyleSettings decoration_settings) = 0;
|
||||
|
||||
/**
|
||||
* Apply the window decoration style using the current flags and settings.
|
||||
*/
|
||||
virtual GHOST_TSuccess applyWindowDecorationStyle() = 0;
|
||||
|
||||
/**
|
||||
* Returns the window rectangle dimensions.
|
||||
* These are screen coordinates.
|
||||
* \param bounds: The bounding rectangle of the window.
|
||||
*/
|
||||
virtual void getWindowBounds(GHOST_Rect &bounds) const = 0;
|
||||
|
||||
/**
|
||||
* Returns the client rectangle dimensions.
|
||||
* The left and top members of the rectangle are always zero.
|
||||
* \param bounds: The bounding rectangle of the client area of the window.
|
||||
*/
|
||||
virtual void getClientBounds(GHOST_Rect &bounds) const = 0;
|
||||
|
||||
/**
|
||||
* Resizes client rectangle width.
|
||||
* \param width: The new width of the client area of the window.
|
||||
*/
|
||||
virtual GHOST_TSuccess setClientWidth(uint32_t width) = 0;
|
||||
|
||||
/**
|
||||
* Resizes client rectangle height.
|
||||
* \param height: The new height of the client area of the window.
|
||||
*/
|
||||
virtual GHOST_TSuccess setClientHeight(uint32_t height) = 0;
|
||||
|
||||
/**
|
||||
* Resizes client rectangle.
|
||||
* \param width: The new width of the client area of the window.
|
||||
* \param height: The new height of the client area of the window.
|
||||
*/
|
||||
virtual GHOST_TSuccess setClientSize(uint32_t width, uint32_t height) = 0;
|
||||
|
||||
/**
|
||||
* Converts a point in screen coordinates to client rectangle coordinates
|
||||
* \param inX: The x-coordinate on the screen.
|
||||
* \param inY: The y-coordinate on the screen.
|
||||
* \param outX: The x-coordinate in the client rectangle.
|
||||
* \param outY: The y-coordinate in the client rectangle.
|
||||
*/
|
||||
virtual void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const = 0;
|
||||
|
||||
/**
|
||||
* Converts a point in client rectangle coordinates to screen coordinates.
|
||||
* \param inX: The x-coordinate in the client rectangle.
|
||||
* \param inY: The y-coordinate in the client rectangle.
|
||||
* \param outX: The x-coordinate on the screen.
|
||||
* \param outY: The y-coordinate on the screen.
|
||||
*/
|
||||
virtual void clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const = 0;
|
||||
|
||||
/**
|
||||
* Tells if the ongoing drag & drop object can be accepted upon mouse drop
|
||||
*/
|
||||
virtual void setAcceptDragOperation(bool can_accept) = 0;
|
||||
|
||||
/**
|
||||
* Returns the state of the window (normal, minimized, maximized).
|
||||
* \return The state of the window.
|
||||
*/
|
||||
virtual GHOST_TWindowState getState() const = 0;
|
||||
|
||||
/**
|
||||
* Sets the state of the window (normal, minimized, maximized).
|
||||
* \param state: The state of the window.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess setState(GHOST_TWindowState state) = 0;
|
||||
|
||||
/**
|
||||
* Sets the window "modified" status, indicating unsaved changes.
|
||||
* \param is_unsaved_changes: Unsaved changes or not.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess setModifiedState(bool is_unsaved_changes) = 0;
|
||||
|
||||
/**
|
||||
* Sets the order of the window (bottom, top).
|
||||
* \param order: The order of the window.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess setOrder(GHOST_TWindowOrder order) = 0;
|
||||
|
||||
/**
|
||||
* Acquire the next buffer of the swap chain.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
virtual GHOST_TSuccess swapBufferAcquire() = 0;
|
||||
|
||||
/**
|
||||
* Swaps front and back buffers of a window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
virtual GHOST_TSuccess swapBufferRelease() = 0;
|
||||
|
||||
/**
|
||||
* Sets the swap interval for #swapBuffers.
|
||||
* \param interval: The swap interval to use.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
virtual GHOST_TSuccess setSwapInterval(int interval) = 0;
|
||||
|
||||
/**
|
||||
* Gets the current swap interval for #swapBuffers.
|
||||
* \param interval_out: pointer to location to return swap interval.
|
||||
* (left untouched if there is an error)
|
||||
* \return A boolean success indicator of if swap interval was successfully read.
|
||||
*/
|
||||
virtual GHOST_TSuccess getSwapInterval(int &interval_out) = 0;
|
||||
|
||||
/**
|
||||
* Activates the drawing context of this window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
virtual GHOST_TSuccess activateDrawingContext() = 0;
|
||||
|
||||
/**
|
||||
* Gets the OpenGL frame-buffer associated with the window's contents.
|
||||
* \return The name of an OpenGL frame-buffer object.
|
||||
*/
|
||||
virtual unsigned int getDefaultFramebuffer() = 0;
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
/** \copydoc #GHOST_GetVulkanSwapChainFormat */
|
||||
virtual GHOST_TSuccess getVulkanSwapChainFormat(
|
||||
GHOST_VulkanSwapChainData *r_swap_chain_data) = 0;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Invalidates the contents of this window.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess invalidate() = 0;
|
||||
|
||||
/**
|
||||
* Returns the window user data.
|
||||
* \return The window user data.
|
||||
*/
|
||||
virtual GHOST_TUserDataPtr getUserData() const = 0;
|
||||
|
||||
/**
|
||||
* Changes the window user data.
|
||||
* \param user_data: The window user data.
|
||||
*/
|
||||
virtual void setUserData(const GHOST_TUserDataPtr user_data) = 0;
|
||||
|
||||
virtual bool isDialog() const = 0;
|
||||
|
||||
/***************************************************************************************
|
||||
* Progress bar functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Sets the progress bar value displayed in the window/application icon
|
||||
* \param progress: The progress percentage (0.0 to 1.0).
|
||||
*/
|
||||
virtual GHOST_TSuccess setProgressBar(float progress) = 0;
|
||||
|
||||
/**
|
||||
* Hides the progress bar in the icon.
|
||||
*/
|
||||
virtual GHOST_TSuccess endProgressBar() = 0;
|
||||
|
||||
/***************************************************************************************
|
||||
* Cursor management functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns the current cursor shape.
|
||||
* \return The current cursor shape.
|
||||
*/
|
||||
virtual GHOST_TStandardCursor getCursorShape() const = 0;
|
||||
|
||||
/**
|
||||
* Set the shape of the cursor.
|
||||
* \param cursor_shape: The new cursor shape type id.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess setCursorShape(GHOST_TStandardCursor cursor_shape) = 0;
|
||||
|
||||
virtual void getCursorGrabState(GHOST_TGrabCursorMode &mode,
|
||||
GHOST_TAxisFlag &axis_flag,
|
||||
GHOST_Rect &bounds,
|
||||
bool &use_software_cursor) = 0;
|
||||
|
||||
/**
|
||||
* Test if the standard cursor shape is supported by current platform.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor cursor_shape) = 0;
|
||||
|
||||
/**
|
||||
* Set the shape of the cursor to a custom cursor.
|
||||
* \param bitmap: The bitmap data for the cursor.
|
||||
* \param mask: The mask data for the cursor.
|
||||
* \param size: The X,Y size of the cursor in pixels.
|
||||
* \param hot_spot: The X,Y coordinate of the cursor hot-spot.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess setCustomCursorShape(const uint8_t *bitmap,
|
||||
const uint8_t *mask,
|
||||
const int size[2],
|
||||
const int hot_spot[2],
|
||||
bool can_invert_color) = 0;
|
||||
|
||||
/**
|
||||
* Set the cursor generator.
|
||||
*
|
||||
* \param cursor_generator: An object which generates cursors.
|
||||
* Ownership is transferred to GHOST which is responsible for calling it's free method.
|
||||
*/
|
||||
virtual GHOST_TSuccess setCustomCursorGenerator(GHOST_CursorGenerator *cursor_generator) = 0;
|
||||
|
||||
virtual GHOST_TSuccess getCursorBitmap(GHOST_CursorBitmapRef *bitmap) = 0;
|
||||
|
||||
/**
|
||||
* Returns the visibility state of the cursor.
|
||||
* \return The visibility state of the cursor.
|
||||
*/
|
||||
virtual bool getCursorVisibility() const = 0;
|
||||
|
||||
/**
|
||||
* Shows or hides the cursor.
|
||||
* \param visible: The new visibility state of the cursor.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess setCursorVisibility(bool visible) = 0;
|
||||
|
||||
/**
|
||||
* Grabs the cursor for a modal operation.
|
||||
* \param mode: The new grab mode of the cursor.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess setCursorGrab(GHOST_TGrabCursorMode /*mode*/,
|
||||
GHOST_TAxisFlag /*wrap_axis*/,
|
||||
GHOST_Rect * /*bounds*/,
|
||||
int32_t /*mouse_ungrab_xy*/[2])
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this window was opened using native pixel size, return the scaling factor.
|
||||
*/
|
||||
virtual float getNativePixelSize() = 0;
|
||||
|
||||
/**
|
||||
* Returns the recommended DPI for this window.
|
||||
* \return The recommended DPI for this window.
|
||||
*/
|
||||
virtual uint16_t getDPIHint() = 0;
|
||||
|
||||
/**
|
||||
* Returns high dynamic range color information about this window.
|
||||
* \return HDR info.
|
||||
* */
|
||||
virtual GHOST_WindowHDRInfo getHDRInfo() = 0;
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/**
|
||||
* Enable IME attached to the given window, i.e. allows user-input
|
||||
* events to be dispatched to the IME.
|
||||
* \param x: Requested x-coordinate of the rectangle.
|
||||
* \param y: Requested y-coordinate of the rectangle.
|
||||
* \param w: Requested width of the rectangle.
|
||||
* \param h: Requested height of the rectangle.
|
||||
* \param completed: Whether or not to complete the ongoing composition.
|
||||
* - true: Start a new composition
|
||||
* - false: Move the IME windows to the given position without finishing it.
|
||||
*/
|
||||
virtual void beginIME(int32_t x, int32_t y, int32_t w, int32_t h, bool completed) = 0;
|
||||
|
||||
/**
|
||||
* Disable the IME attached to the given window, i.e. prohibits any user-input
|
||||
* events from being dispatched to the IME.
|
||||
*/
|
||||
virtual void endIME() = 0;
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_IWindow")
|
||||
};
|
||||
38
blender-5.2.0/intern/ghost/GHOST_IXrContext.hh
Normal file
38
blender-5.2.0/intern/ghost/GHOST_IXrContext.hh
Normal file
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2002-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
class GHOST_XrSession;
|
||||
struct GHOST_XrSessionBeginInfo;
|
||||
|
||||
class GHOST_IXrContext {
|
||||
public:
|
||||
virtual ~GHOST_IXrContext() = default;
|
||||
|
||||
virtual void startSession(const GHOST_XrSessionBeginInfo *begin_info) = 0;
|
||||
virtual void endSession() = 0;
|
||||
virtual bool isSessionRunning() const = 0;
|
||||
virtual void drawSessionViews(void *draw_customdata) = 0;
|
||||
|
||||
/* Needed for the GHOST C API. */
|
||||
virtual GHOST_XrSession *getSession() = 0;
|
||||
virtual const GHOST_XrSession *getSession() const = 0;
|
||||
|
||||
virtual void dispatchErrorMessage(const class GHOST_XrException *) const = 0;
|
||||
|
||||
virtual void setGraphicsContextBindFuncs(GHOST_XrGraphicsContextBindFn bind_fn,
|
||||
GHOST_XrGraphicsContextUnbindFn unbind_fn) = 0;
|
||||
virtual void setDrawViewFunc(GHOST_XrDrawViewFn draw_view_fn) = 0;
|
||||
virtual void setPassthroughEnabledFunc(GHOST_XrPassthroughEnabledFn passthrough_enabled_fn) = 0;
|
||||
virtual void setDisablePassthroughFunc(GHOST_XrDisablePassthroughFn disable_passthrough_fn) = 0;
|
||||
|
||||
virtual bool needsUpsideDownDrawing() const = 0;
|
||||
};
|
||||
273
blender-5.2.0/intern/ghost/GHOST_Rect.hh
Normal file
273
blender-5.2.0/intern/ghost/GHOST_Rect.hh
Normal file
@@ -0,0 +1,273 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Macro's used in GHOST debug target.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
/**
|
||||
* Implements rectangle functionality.
|
||||
* The four extreme coordinates are stored as left, top, right and bottom.
|
||||
* To be valid, a rectangle should have a left coordinate smaller than or equal to right.
|
||||
* To be valid, a rectangle should have a top coordinate smaller than or equal to bottom.
|
||||
*/
|
||||
class GHOST_Rect {
|
||||
public:
|
||||
/**
|
||||
* Constructs a rectangle with the given values.
|
||||
* \param l: requested left coordinate of the rectangle.
|
||||
* \param t: requested top coordinate of the rectangle.
|
||||
* \param r: requested right coordinate of the rectangle.
|
||||
* \param b: requested bottom coordinate of the rectangle.
|
||||
*/
|
||||
GHOST_Rect(int32_t l = 0, int32_t t = 0, int32_t r = 0, int32_t b = 0)
|
||||
: l_(l), t_(t), r_(r), b_(b)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~GHOST_Rect() = default;
|
||||
|
||||
/**
|
||||
* Access to rectangle width.
|
||||
* \return width of the rectangle.
|
||||
*/
|
||||
virtual inline int32_t getWidth() const;
|
||||
|
||||
/**
|
||||
* Access to rectangle height.
|
||||
* \return height of the rectangle.
|
||||
*/
|
||||
virtual inline int32_t getHeight() const;
|
||||
|
||||
/**
|
||||
* Sets all members of the rectangle.
|
||||
* \param l: requested left coordinate of the rectangle.
|
||||
* \param t: requested top coordinate of the rectangle.
|
||||
* \param r: requested right coordinate of the rectangle.
|
||||
* \param b: requested bottom coordinate of the rectangle.
|
||||
*/
|
||||
virtual inline void set(int32_t l, int32_t t, int32_t r, int32_t b);
|
||||
|
||||
/**
|
||||
* Returns whether this rectangle is empty.
|
||||
* Empty rectangles are rectangles that have width==0 and/or height==0.
|
||||
* \return boolean value (true==empty rectangle)
|
||||
*/
|
||||
virtual inline bool isEmpty() const;
|
||||
|
||||
/**
|
||||
* Returns whether this rectangle is valid.
|
||||
* Valid rectangles are rectangles that have l_ <= r_ and t_ <= b_.
|
||||
* Thus, empty rectangles are valid.
|
||||
* \return boolean value (true==valid rectangle)
|
||||
*/
|
||||
virtual inline bool isValid() const;
|
||||
|
||||
/**
|
||||
* Grows (or shrinks the rectangle).
|
||||
* The method avoids negative insets making the rectangle invalid
|
||||
* \param i: The amount of offset given to each extreme (negative values shrink the rectangle).
|
||||
*/
|
||||
virtual void inset(int32_t i);
|
||||
|
||||
/**
|
||||
* Does a union of the rectangle given and this rectangle.
|
||||
* The result is stored in this rectangle.
|
||||
* \param r: The rectangle that is input for the union operation.
|
||||
*/
|
||||
virtual inline void unionRect(const GHOST_Rect &r);
|
||||
|
||||
/**
|
||||
* Grows the rectangle to included a point.
|
||||
* \param x: The x-coordinate of the point.
|
||||
* \param y: The y-coordinate of the point.
|
||||
*/
|
||||
virtual inline void unionPoint(int32_t x, int32_t y);
|
||||
|
||||
/**
|
||||
* Grows the rectangle to included a point.
|
||||
* \param x: The x-coordinate of the point.
|
||||
* \param y: The y-coordinate of the point.
|
||||
*/
|
||||
virtual inline void wrapPoint(int32_t &x, int32_t &y, int32_t ofs, GHOST_TAxisFlag axis);
|
||||
/**
|
||||
* Confine x & y within the rectangle (inclusive).
|
||||
* \param x: The x-coordinate of the point.
|
||||
* \param y: The y-coordinate of the point.
|
||||
*/
|
||||
virtual inline void clampPoint(int32_t &x, int32_t &y);
|
||||
|
||||
/**
|
||||
* Returns whether the point is inside this rectangle.
|
||||
* Point on the boundary is considered inside.
|
||||
* \param x: x-coordinate of point to test.
|
||||
* \param y: y-coordinate of point to test.
|
||||
* \return boolean value (true if point is inside).
|
||||
*/
|
||||
virtual inline bool isInside(int32_t x, int32_t y) const;
|
||||
|
||||
/**
|
||||
* Returns whether the rectangle is inside this rectangle.
|
||||
* \param r: rectangle to test.
|
||||
* \return visibility (not, partially or fully visible).
|
||||
*/
|
||||
virtual GHOST_TVisibility getVisibility(GHOST_Rect &r) const;
|
||||
|
||||
/**
|
||||
* Sets rectangle members.
|
||||
* Sets rectangle members such that it is centered at the given location.
|
||||
* \param cx: requested center x-coordinate of the rectangle.
|
||||
* \param cy: requested center y-coordinate of the rectangle.
|
||||
*/
|
||||
virtual void setCenter(int32_t cx, int32_t cy);
|
||||
|
||||
/**
|
||||
* Sets rectangle members.
|
||||
* Sets rectangle members such that it is centered at the given location,
|
||||
* with the width requested.
|
||||
* \param cx: requested center x-coordinate of the rectangle.
|
||||
* \param cy: requested center y-coordinate of the rectangle.
|
||||
* \param w: requested width of the rectangle.
|
||||
* \param h: requested height of the rectangle.
|
||||
*/
|
||||
virtual void setCenter(int32_t cx, int32_t cy, int32_t w, int32_t h);
|
||||
|
||||
/**
|
||||
* Clips a rectangle.
|
||||
* Updates the rectangle given such that it will fit within this one.
|
||||
* This can result in an empty rectangle.
|
||||
* \param r: the rectangle to clip.
|
||||
* \return whether clipping has occurred
|
||||
*/
|
||||
virtual bool clip(GHOST_Rect &r) const;
|
||||
|
||||
/** Left coordinate of the rectangle */
|
||||
int32_t l_;
|
||||
/** Top coordinate of the rectangle */
|
||||
int32_t t_;
|
||||
/** Right coordinate of the rectangle */
|
||||
int32_t r_;
|
||||
/** Bottom coordinate of the rectangle */
|
||||
int32_t b_;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_Rect")
|
||||
};
|
||||
|
||||
inline int32_t GHOST_Rect::getWidth() const
|
||||
{
|
||||
return r_ - l_;
|
||||
}
|
||||
|
||||
inline int32_t GHOST_Rect::getHeight() const
|
||||
{
|
||||
return b_ - t_;
|
||||
}
|
||||
|
||||
inline void GHOST_Rect::set(int32_t l, int32_t t, int32_t r, int32_t b)
|
||||
{
|
||||
l_ = l;
|
||||
t_ = t;
|
||||
r_ = r;
|
||||
b_ = b;
|
||||
}
|
||||
|
||||
inline bool GHOST_Rect::isEmpty() const
|
||||
{
|
||||
return (getWidth() == 0) || (getHeight() == 0);
|
||||
}
|
||||
|
||||
inline bool GHOST_Rect::isValid() const
|
||||
{
|
||||
return (l_ <= r_) && (t_ <= b_);
|
||||
}
|
||||
|
||||
inline void GHOST_Rect::unionRect(const GHOST_Rect &r)
|
||||
{
|
||||
if (r.l_ < l_) {
|
||||
l_ = r.l_;
|
||||
}
|
||||
if (r.r_ > r_) {
|
||||
r_ = r.r_;
|
||||
}
|
||||
if (r.t_ < t_) {
|
||||
t_ = r.t_;
|
||||
}
|
||||
if (r.b_ > b_) {
|
||||
b_ = r.b_;
|
||||
}
|
||||
}
|
||||
|
||||
inline void GHOST_Rect::unionPoint(int32_t x, int32_t y)
|
||||
{
|
||||
if (x < l_) {
|
||||
l_ = x;
|
||||
}
|
||||
if (x > r_) {
|
||||
r_ = x;
|
||||
}
|
||||
if (y < t_) {
|
||||
t_ = y;
|
||||
}
|
||||
if (y > b_) {
|
||||
b_ = y;
|
||||
}
|
||||
}
|
||||
|
||||
inline void GHOST_Rect::wrapPoint(int32_t &x, int32_t &y, int32_t ofs, GHOST_TAxisFlag axis)
|
||||
{
|
||||
int32_t w = getWidth();
|
||||
int32_t h = getHeight();
|
||||
|
||||
/* highly unlikely but avoid eternal loop */
|
||||
if (w - ofs * 2 <= 0 || h - ofs * 2 <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (axis & GHOST_kAxisX) {
|
||||
while (x - ofs < l_) {
|
||||
x += w - (ofs * 2);
|
||||
}
|
||||
while (x + ofs > r_) {
|
||||
x -= w - (ofs * 2);
|
||||
}
|
||||
}
|
||||
if (axis & GHOST_kAxisY) {
|
||||
while (y - ofs < t_) {
|
||||
y += h - (ofs * 2);
|
||||
}
|
||||
while (y + ofs > b_) {
|
||||
y -= h - (ofs * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void GHOST_Rect::clampPoint(int32_t &x, int32_t &y)
|
||||
{
|
||||
if (x < l_) {
|
||||
x = l_;
|
||||
}
|
||||
else if (x > r_) {
|
||||
x = r_;
|
||||
}
|
||||
|
||||
if (y < t_) {
|
||||
y = t_;
|
||||
}
|
||||
else if (y > b_) {
|
||||
y = b_;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool GHOST_Rect::isInside(int32_t x, int32_t y) const
|
||||
{
|
||||
return (x >= l_) && (x <= r_) && (y >= t_) && (y <= b_);
|
||||
}
|
||||
1403
blender-5.2.0/intern/ghost/GHOST_Types.hh
Normal file
1403
blender-5.2.0/intern/ghost/GHOST_Types.hh
Normal file
File diff suppressed because it is too large
Load Diff
245
blender-5.2.0/intern/ghost/GHOST_Xr-api.hh
Normal file
245
blender-5.2.0/intern/ghost/GHOST_Xr-api.hh
Normal file
@@ -0,0 +1,245 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* \brief Abstraction for XR (VR, AR, MR, ..) access via OpenXR.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_IXrContext.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
/* XR-context */
|
||||
|
||||
/**
|
||||
* Set a custom callback to be executed whenever an error occurs. Should be set before calling
|
||||
* #GHOST_XrContextCreate() to get error handling during context creation too.
|
||||
*
|
||||
* \param customdata: Handle to some data that will get passed to \a handler_fn should an error be
|
||||
* thrown.
|
||||
*/
|
||||
void GHOST_XrErrorHandler(GHOST_XrErrorHandlerFn handler_fn, void *customdata);
|
||||
|
||||
/**
|
||||
* \brief Initialize the Ghost XR-context.
|
||||
*
|
||||
* Includes setting up the OpenXR runtime link, querying available extensions and API layers,
|
||||
* enabling extensions and API layers.
|
||||
*
|
||||
* \param create_info: Options for creating the XR-context, e.g. debug-flags and ordered array of
|
||||
* graphics bindings to try enabling.
|
||||
*/
|
||||
GHOST_IXrContext *GHOST_XrContextCreate(const GHOST_XrContextCreateInfo *create_info);
|
||||
/**
|
||||
* Free a XR-context involving OpenXR runtime link destruction and freeing of all internal data.
|
||||
*/
|
||||
void GHOST_XrContextDestroy(GHOST_IXrContext *xr_context);
|
||||
|
||||
/**
|
||||
* Set callbacks for binding and unbinding a graphics context for a session. The binding callback
|
||||
* may create a new graphics context thereby. In fact that's the sole reason for this callback
|
||||
* approach to binding. Just make sure to have an unbind function set that properly destructs.
|
||||
*
|
||||
* \param bind_fn: Function to retrieve (possibly create) a graphics context.
|
||||
* \param unbind_fn: Function to release (possibly free) a graphics context.
|
||||
*/
|
||||
void GHOST_XrGraphicsContextBindFuncs(GHOST_IXrContext *xr_context,
|
||||
GHOST_XrGraphicsContextBindFn bind_fn,
|
||||
GHOST_XrGraphicsContextUnbindFn unbind_fn);
|
||||
|
||||
/**
|
||||
* Set the drawing callback for views. A view would typically be either the left or the right eye,
|
||||
* although other configurations are possible. When #GHOST_XrSessionDrawViews() is called to draw
|
||||
* an XR frame, \a draw_view_fn is executed for each view.
|
||||
*
|
||||
* \param draw_view_fn: The callback to draw a single view for an XR frame.
|
||||
*/
|
||||
void GHOST_XrDrawViewFunc(GHOST_IXrContext *xr_context, GHOST_XrDrawViewFn draw_view_fn);
|
||||
|
||||
/**
|
||||
* Set the callback to check if passthrough is enabled.
|
||||
* If enabled, the passthrough composition layer is added in GHOST_XrSession::draw().
|
||||
*
|
||||
* \param passthrough_enabled_fn: The callback to check if passthrough is enabled.
|
||||
*/
|
||||
void GHOST_XrPassthroughEnabledFunc(GHOST_IXrContext *xr_context,
|
||||
GHOST_XrPassthroughEnabledFn passthrough_enabled_fn);
|
||||
|
||||
/**
|
||||
* Set the callback to force disable passthrough in case it is not supported.
|
||||
* Called in GHOST_XrSession::draw().
|
||||
*
|
||||
* \param disable_passthrough_fn: The callback to disable passthrough.
|
||||
*/
|
||||
void GHOST_XrDisablePassthroughFunc(GHOST_IXrContext *xr_context,
|
||||
GHOST_XrDisablePassthroughFn disable_passthrough_fn);
|
||||
|
||||
/* sessions */
|
||||
/**
|
||||
* Create internal session data for \a xr_context and ask the OpenXR runtime to invoke a session.
|
||||
*
|
||||
* \param begin_info: Options for the session creation.
|
||||
*/
|
||||
void GHOST_XrSessionStart(GHOST_IXrContext *xr_context,
|
||||
const GHOST_XrSessionBeginInfo *begin_info);
|
||||
/**
|
||||
* Destruct internal session data for \a xr_context and ask the OpenXR runtime to stop a session.
|
||||
*/
|
||||
void GHOST_XrSessionEnd(GHOST_IXrContext *xr_context);
|
||||
/**
|
||||
* Draw a single frame by calling the view drawing callback defined by #GHOST_XrDrawViewFunc() for
|
||||
* each view and submit it to the OpenXR runtime.
|
||||
*
|
||||
* \param customdata: Handle to some data that will get passed to the view drawing callback.
|
||||
*/
|
||||
void GHOST_XrSessionDrawViews(GHOST_IXrContext *xr_context, void *customdata);
|
||||
/**
|
||||
* Check if a \a xr_context has a session that, according to the OpenXR definition would be
|
||||
* considered to be 'running'
|
||||
* (https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#session_running).
|
||||
*/
|
||||
int GHOST_XrSessionIsRunning(const GHOST_IXrContext *xr_context);
|
||||
|
||||
/**
|
||||
* Check if \a xr_context has a session that requires an upside-down frame-buffer (compared to
|
||||
* GPU). If true, the render result should be flipped vertically for correct output.
|
||||
* \note Only to be called after session start, may otherwise result in a false negative.
|
||||
*/
|
||||
int GHOST_XrSessionNeedsUpsideDownDrawing(const GHOST_IXrContext *xr_context);
|
||||
|
||||
/* events */
|
||||
/**
|
||||
* Invoke handling of all OpenXR events for \a xr_context. Should be called on every main-loop
|
||||
* iteration and will early-exit if \a xr_context is nullptr (so caller doesn't have to check).
|
||||
*
|
||||
* \returns GHOST_kSuccess if any event was handled, otherwise GHOST_kFailure.
|
||||
*/
|
||||
GHOST_TSuccess GHOST_XrEventsHandle(GHOST_IXrContext *xr_context);
|
||||
|
||||
/* actions */
|
||||
/**
|
||||
* Create an OpenXR action set for input/output.
|
||||
*/
|
||||
int GHOST_XrCreateActionSet(GHOST_IXrContext *xr_context, const GHOST_XrActionSetInfo *info);
|
||||
|
||||
/**
|
||||
* Destroy a previously created OpenXR action set.
|
||||
*/
|
||||
void GHOST_XrDestroyActionSet(GHOST_IXrContext *xr_context, const char *action_set_name);
|
||||
|
||||
/**
|
||||
* Create OpenXR input/output actions.
|
||||
*/
|
||||
int GHOST_XrCreateActions(GHOST_IXrContext *xr_context,
|
||||
const char *action_set_name,
|
||||
uint32_t count,
|
||||
const GHOST_XrActionInfo *infos);
|
||||
|
||||
/**
|
||||
* Destroy previously created OpenXR actions.
|
||||
*/
|
||||
void GHOST_XrDestroyActions(GHOST_IXrContext *xr_context,
|
||||
const char *action_set_name,
|
||||
uint32_t count,
|
||||
const char *const *action_names);
|
||||
|
||||
/**
|
||||
* Create input/output path bindings for OpenXR actions.
|
||||
*/
|
||||
int GHOST_XrCreateActionBindings(GHOST_IXrContext *xr_context,
|
||||
const char *action_set_name,
|
||||
uint32_t count,
|
||||
const GHOST_XrActionProfileInfo *infos);
|
||||
|
||||
/**
|
||||
* Destroy previously created bindings for OpenXR actions.
|
||||
*/
|
||||
void GHOST_XrDestroyActionBindings(GHOST_IXrContext *xr_context,
|
||||
const char *action_set_name,
|
||||
uint32_t count,
|
||||
const char *const *action_names,
|
||||
const char *const *profile_paths);
|
||||
|
||||
/**
|
||||
* Attach all created action sets to the current OpenXR session.
|
||||
*/
|
||||
int GHOST_XrAttachActionSets(GHOST_IXrContext *xr_context);
|
||||
|
||||
/**
|
||||
* Update button/tracking states for OpenXR actions.
|
||||
*
|
||||
* \param action_set_name: The name of the action set to sync. If nullptr, all action sets
|
||||
* attached to the session will be synced.
|
||||
*/
|
||||
int GHOST_XrSyncActions(GHOST_IXrContext *xr_context, const char *action_set_name);
|
||||
|
||||
/**
|
||||
* Apply an OpenXR haptic output action.
|
||||
*/
|
||||
int GHOST_XrApplyHapticAction(GHOST_IXrContext *xr_context_handle,
|
||||
const char *action_set_name,
|
||||
const char *action_name,
|
||||
const char *subaction_path,
|
||||
const int64_t *duration,
|
||||
const float *frequency,
|
||||
const float *amplitude);
|
||||
|
||||
/**
|
||||
* Stop a previously applied OpenXR haptic output action.
|
||||
*/
|
||||
void GHOST_XrStopHapticAction(GHOST_IXrContext *xr_context_handle,
|
||||
const char *action_set_name,
|
||||
const char *action_name,
|
||||
const char *subaction_path);
|
||||
|
||||
/**
|
||||
* Get action set custom data (owned by Blender, not GHOST).
|
||||
*/
|
||||
void *GHOST_XrGetActionSetCustomdata(GHOST_IXrContext *xr_context, const char *action_set_name);
|
||||
|
||||
/**
|
||||
* Get action custom data (owned by Blender, not GHOST).
|
||||
*/
|
||||
void *GHOST_XrGetActionCustomdata(GHOST_IXrContext *xr_context,
|
||||
const char *action_set_name,
|
||||
const char *action_name);
|
||||
|
||||
/**
|
||||
* Get the number of actions in an action set.
|
||||
*/
|
||||
unsigned int GHOST_XrGetActionCount(GHOST_IXrContext *xr_context, const char *action_set_name);
|
||||
|
||||
/**
|
||||
* Get custom data for all actions in an action set.
|
||||
*/
|
||||
void GHOST_XrGetActionCustomdataArray(GHOST_IXrContext *xr_context,
|
||||
const char *action_set_name,
|
||||
void **r_customdata_array);
|
||||
|
||||
/* controller model */
|
||||
/**
|
||||
* Load the OpenXR controller model.
|
||||
*/
|
||||
int GHOST_XrLoadControllerModel(GHOST_IXrContext *xr_context, const char *subaction_path);
|
||||
|
||||
/**
|
||||
* Unload the OpenXR controller model.
|
||||
*/
|
||||
void GHOST_XrUnloadControllerModel(GHOST_IXrContext *xr_context, const char *subaction_path);
|
||||
|
||||
/**
|
||||
* Update component transforms for the OpenXR controller model.
|
||||
*/
|
||||
int GHOST_XrUpdateControllerModelComponents(GHOST_IXrContext *xr_context,
|
||||
const char *subaction_path);
|
||||
|
||||
/**
|
||||
* Get vertex data for the OpenXR controller model.
|
||||
*/
|
||||
int GHOST_XrGetControllerModelData(GHOST_IXrContext *xr_context,
|
||||
const char *subaction_path,
|
||||
GHOST_XrControllerModelData *r_data);
|
||||
78
blender-5.2.0/intern/ghost/intern/GHOST_Buttons.cc
Normal file
78
blender-5.2.0/intern/ghost/intern/GHOST_Buttons.cc
Normal file
@@ -0,0 +1,78 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include "GHOST_Buttons.hh"
|
||||
|
||||
GHOST_Buttons::GHOST_Buttons()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
bool GHOST_Buttons::get(GHOST_TButton mask) const
|
||||
{
|
||||
switch (mask) {
|
||||
case GHOST_kButtonMaskLeft:
|
||||
return button_left_;
|
||||
case GHOST_kButtonMaskMiddle:
|
||||
return button_middle_;
|
||||
case GHOST_kButtonMaskRight:
|
||||
return button_right_;
|
||||
case GHOST_kButtonMaskButton4:
|
||||
return button4_;
|
||||
case GHOST_kButtonMaskButton5:
|
||||
return button5_;
|
||||
case GHOST_kButtonMaskButton6:
|
||||
return button6_;
|
||||
case GHOST_kButtonMaskButton7:
|
||||
return button7_;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_Buttons::set(GHOST_TButton mask, bool down)
|
||||
{
|
||||
switch (mask) {
|
||||
case GHOST_kButtonMaskLeft:
|
||||
button_left_ = down;
|
||||
break;
|
||||
case GHOST_kButtonMaskMiddle:
|
||||
button_middle_ = down;
|
||||
break;
|
||||
case GHOST_kButtonMaskRight:
|
||||
button_right_ = down;
|
||||
break;
|
||||
case GHOST_kButtonMaskButton4:
|
||||
button4_ = down;
|
||||
break;
|
||||
case GHOST_kButtonMaskButton5:
|
||||
button5_ = down;
|
||||
break;
|
||||
case GHOST_kButtonMaskButton6:
|
||||
button6_ = down;
|
||||
break;
|
||||
case GHOST_kButtonMaskButton7:
|
||||
button7_ = down;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_Buttons::clear()
|
||||
{
|
||||
button_left_ = false;
|
||||
button_middle_ = false;
|
||||
button_right_ = false;
|
||||
button4_ = false;
|
||||
button5_ = false;
|
||||
button6_ = false;
|
||||
button7_ = false;
|
||||
}
|
||||
|
||||
GHOST_Buttons::~GHOST_Buttons() = default;
|
||||
52
blender-5.2.0/intern/ghost/intern/GHOST_Buttons.hh
Normal file
52
blender-5.2.0/intern/ghost/intern/GHOST_Buttons.hh
Normal file
@@ -0,0 +1,52 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_Buttons struct.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
/**
|
||||
* This struct stores the state of the mouse buttons.
|
||||
* Buttons can be set using button masks.
|
||||
*/
|
||||
struct GHOST_Buttons {
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_Buttons();
|
||||
|
||||
~GHOST_Buttons();
|
||||
|
||||
/**
|
||||
* Returns the state of a single button.
|
||||
* \param mask: Key button to return.
|
||||
* \return The state of the button (pressed == true).
|
||||
*/
|
||||
bool get(GHOST_TButton mask) const;
|
||||
|
||||
/**
|
||||
* Updates the state of a single button.
|
||||
* \param mask: Button state to update.
|
||||
* \param down: The new state of the button.
|
||||
*/
|
||||
void set(GHOST_TButton mask, bool down);
|
||||
|
||||
/**
|
||||
* Sets the state of all buttons to up.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
uint8_t button_left_ : 1;
|
||||
uint8_t button_middle_ : 1;
|
||||
uint8_t button_right_ : 1;
|
||||
uint8_t button4_ : 1;
|
||||
uint8_t button5_ : 1;
|
||||
uint8_t button6_ : 1;
|
||||
uint8_t button7_ : 1;
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright (C) 2001 NaN Technologies B.V.
|
||||
*/
|
||||
|
||||
#include "GHOST_CallbackEventConsumer.hh"
|
||||
|
||||
GHOST_CallbackEventConsumer::GHOST_CallbackEventConsumer(GHOST_EventCallbackProcPtr eventCallback,
|
||||
GHOST_TUserDataPtr user_data)
|
||||
{
|
||||
event_callback_ = eventCallback;
|
||||
user_data_ = user_data;
|
||||
}
|
||||
|
||||
bool GHOST_CallbackEventConsumer::processEvent(const GHOST_IEvent *event)
|
||||
{
|
||||
return event_callback_(event, user_data_);
|
||||
}
|
||||
143
blender-5.2.0/intern/ghost/intern/GHOST_Context.cc
Normal file
143
blender-5.2.0/intern/ghost/intern/GHOST_Context.cc
Normal file
@@ -0,0 +1,143 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* Definition of GHOST_Context class.
|
||||
*/
|
||||
|
||||
#include "GHOST_Context.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
|
||||
# include <epoxy/wgl.h>
|
||||
# include <tchar.h>
|
||||
#
|
||||
# ifndef ERROR_PROFILE_DOES_NOT_MATCH_DEVICE
|
||||
# define ERROR_PROFILE_DOES_NOT_MATCH_DEVICE 0x7E7
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
# include <epoxy/gl.h>
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
GHOST_IContext *GHOST_IContext::getActiveDrawingContext()
|
||||
{
|
||||
return GHOST_Context::getActiveDrawingContext();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
bool win32_silent_chk(bool result)
|
||||
{
|
||||
if (!result) {
|
||||
SetLastError(NO_ERROR);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool win32_chk(bool result, const char *file, int line, const char *text)
|
||||
{
|
||||
if (!result) {
|
||||
LPTSTR formattedMsg = nullptr;
|
||||
|
||||
DWORD error = GetLastError();
|
||||
|
||||
const char *msg;
|
||||
|
||||
DWORD count = 0;
|
||||
|
||||
/* Some drivers returns a HRESULT instead of a standard error message.
|
||||
* i.e: 0xC0072095 instead of 0x2095 for ERROR_INVALID_VERSION_ARB
|
||||
* So strip down the error to the valid error code range. */
|
||||
switch (error & 0x0000FFFF) {
|
||||
case ERROR_INVALID_VERSION_ARB:
|
||||
msg =
|
||||
"The specified OpenGL version and feature set are either invalid or not supported.\n";
|
||||
break;
|
||||
|
||||
case ERROR_INVALID_PROFILE_ARB:
|
||||
msg =
|
||||
"The specified OpenGL profile and feature set are either invalid or not supported.\n";
|
||||
break;
|
||||
|
||||
case ERROR_INVALID_PIXEL_TYPE_ARB:
|
||||
msg = "The specified pixel type is invalid.\n";
|
||||
break;
|
||||
|
||||
case ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB:
|
||||
msg =
|
||||
("The device contexts specified are not compatible. "
|
||||
"This can occur if the device contexts are managed by "
|
||||
"different drivers or possibly on different graphics adapters.\n");
|
||||
break;
|
||||
|
||||
# ifdef WITH_GLEW_ES
|
||||
case ERROR_INCOMPATIBLE_AFFINITY_MASKS_NV:
|
||||
msg = "The device context(s) and rendering context have non-matching affinity masks.\n";
|
||||
break;
|
||||
|
||||
case ERROR_MISSING_AFFINITY_MASK_NV:
|
||||
msg = "The rendering context does not have an affinity mask set.\n";
|
||||
break;
|
||||
# endif
|
||||
|
||||
case ERROR_PROFILE_DOES_NOT_MATCH_DEVICE:
|
||||
msg =
|
||||
("The specified profile is intended for a device of a "
|
||||
"different type than the specified device.\n");
|
||||
break;
|
||||
|
||||
default: {
|
||||
count = FormatMessage((FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS),
|
||||
nullptr,
|
||||
error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)(&formattedMsg),
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
msg = count > 0 ? formattedMsg : "<no system message>\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
# ifndef NDEBUG
|
||||
_ftprintf(
|
||||
stderr, "%s:%d: [%s] -> Win32 Error# (%lu): %s", file, line, text, ulong(error), msg);
|
||||
# else
|
||||
(void)file;
|
||||
(void)line;
|
||||
(void)text;
|
||||
_ftprintf(stderr, "Win32 Error# (%lu): %s", ulong(error), msg);
|
||||
# endif
|
||||
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
if (count != 0) {
|
||||
LocalFree(formattedMsg);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
void GHOST_Context::initClearGL()
|
||||
{
|
||||
glClearColor(0.294, 0.294, 0.294, 0.000);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glClearColor(0.000, 0.000, 0.000, 0.000);
|
||||
}
|
||||
#endif
|
||||
195
blender-5.2.0/intern/ghost/intern/GHOST_Context.hh
Normal file
195
blender-5.2.0/intern/ghost/intern/GHOST_Context.hh
Normal file
@@ -0,0 +1,195 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_Context class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_IContext.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
#include <cstdlib> /* For `nullptr`. */
|
||||
|
||||
class GHOST_Context : public GHOST_IContext {
|
||||
protected:
|
||||
static thread_local inline GHOST_Context *active_context_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param context_params: Parameters to use when initializing the context.
|
||||
*/
|
||||
GHOST_Context(const GHOST_ContextParams &context_params) : context_params_(context_params) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_Context() override
|
||||
{
|
||||
if (active_context_ == this) {
|
||||
active_context_ = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the thread's currently active drawing context.
|
||||
*/
|
||||
static GHOST_Context *getActiveDrawingContext()
|
||||
{
|
||||
return active_context_;
|
||||
}
|
||||
|
||||
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
|
||||
GHOST_TSuccess swapBufferAcquire() override = 0;
|
||||
|
||||
/** \copydoc #GHOST_IContext::swapBuffers */
|
||||
GHOST_TSuccess swapBufferRelease() override = 0;
|
||||
|
||||
/** \copydoc #GHOST_IContext::activateDrawingContext */
|
||||
GHOST_TSuccess activateDrawingContext() override = 0;
|
||||
|
||||
/** \copydoc #GHOST_IContext::releaseDrawingContext */
|
||||
GHOST_TSuccess releaseDrawingContext() override = 0;
|
||||
|
||||
/**
|
||||
* Call immediately after new to initialize. If this fails then immediately delete the object.
|
||||
* \return Indication as to whether initialization has succeeded.
|
||||
*/
|
||||
virtual GHOST_TSuccess initializeDrawingContext() = 0;
|
||||
|
||||
/**
|
||||
* Updates the drawing context of this window. Needed
|
||||
* whenever the window is changed.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess updateDrawingContext()
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if it is OK for a remove the native display
|
||||
* \return Indication as to whether removal has succeeded.
|
||||
*/
|
||||
virtual GHOST_TSuccess releaseNativeHandles() = 0;
|
||||
|
||||
/**
|
||||
* Sets the swap interval for #swapBuffers.
|
||||
* \param interval: The swap interval to use.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
virtual GHOST_TSuccess setSwapInterval(int /*interval*/)
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current swap interval for #swapBuffers.
|
||||
* \param interval_out: Variable to store the swap interval if it can be read.
|
||||
* \return Whether the swap interval can be read.
|
||||
*/
|
||||
virtual GHOST_TSuccess getSwapInterval(int & /*interval_out*/)
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user data.
|
||||
*/
|
||||
void *getUserData()
|
||||
{
|
||||
return user_data_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user data (intended for the caller to use as needed).
|
||||
*/
|
||||
void setUserData(void *user_data)
|
||||
{
|
||||
user_data_ = user_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stereo visual created. Only necessary for 'real' stereo support,
|
||||
* ie quad buffered stereo. This is not always possible, depends on
|
||||
* the graphics h/w
|
||||
*/
|
||||
bool isStereoVisual() const
|
||||
{
|
||||
return context_params_.is_stereo_visual;
|
||||
}
|
||||
|
||||
/** Get the VSync value. */
|
||||
virtual GHOST_TVSyncModes getVSync()
|
||||
{
|
||||
return context_params_.vsync;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the context is rendered upside down compared to OpenGL.
|
||||
*/
|
||||
virtual bool isUpsideDown() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \copydoc #GHOST_IContext::getDefaultFramebuffer */
|
||||
unsigned int getDefaultFramebuffer() override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
/** \copydoc #GHOST_IContext::getVulkanHandles */
|
||||
virtual GHOST_TSuccess getVulkanHandles(GHOST_VulkanHandles & /* r_handles */) override
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
};
|
||||
/** \copydoc #GHOST_IContext::getVulkanSwapChainFormat */
|
||||
virtual GHOST_TSuccess getVulkanSwapChainFormat(
|
||||
GHOST_VulkanSwapChainData * /*r_swap_chain_data*/) override
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/** \copydoc #GHOST_IContext::setVulkanSwapBuffersCallbacks */
|
||||
virtual GHOST_TSuccess setVulkanSwapBuffersCallbacks(
|
||||
std::function<void(const GHOST_VulkanSwapChainData *, bool)> /*swap_buffer_draw_callback*/,
|
||||
std::function<void(void)> /*swap_buffer_acquired_callback*/,
|
||||
std::function<void(GHOST_VulkanOpenXRData *)> /*openxr_acquire_framebuffer_image_callback*/,
|
||||
std::function<void(GHOST_VulkanOpenXRData *)> /*openxr_release_framebuffer_image_callback*/)
|
||||
override
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
GHOST_ContextParams context_params_;
|
||||
|
||||
/** Caller specified, not for internal use. */
|
||||
void *user_data_ = nullptr;
|
||||
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
static void initClearGL();
|
||||
#endif
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_Context")
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
bool win32_chk(bool result, const char *file = nullptr, int line = 0, const char *text = nullptr);
|
||||
bool win32_silent_chk(bool result);
|
||||
|
||||
# ifndef NDEBUG
|
||||
# define WIN32_CHK(x) win32_chk((x), __FILE__, __LINE__, #x)
|
||||
# else
|
||||
# define WIN32_CHK(x) win32_chk(x)
|
||||
# endif
|
||||
|
||||
# define WIN32_CHK_SILENT(x, silent) ((silent) ? win32_silent_chk(x) : WIN32_CHK(x))
|
||||
#endif /* _WIN32 */
|
||||
406
blender-5.2.0/intern/ghost/intern/GHOST_ContextD3D.cc
Normal file
406
blender-5.2.0/intern/ghost/intern/GHOST_ContextD3D.cc
Normal file
@@ -0,0 +1,406 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* For testing purposes, it can be useful to do some DirectX only drawing. A patch for this can be
|
||||
* found here: https://developer.blender.org/P1284
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <epoxy/wgl.h>
|
||||
|
||||
#include "GHOST_ContextD3D.hh"
|
||||
#include "GHOST_ContextWGL.hh" /* For shared drawing */
|
||||
|
||||
HMODULE GHOST_ContextD3D::s_d3d_lib = nullptr;
|
||||
PFN_D3D11_CREATE_DEVICE GHOST_ContextD3D::s_D3D11CreateDeviceFn = nullptr;
|
||||
|
||||
GHOST_ContextD3D::GHOST_ContextD3D(const GHOST_ContextParams &context_params, HWND hWnd)
|
||||
: GHOST_Context(context_params), h_wnd_(hWnd)
|
||||
{
|
||||
}
|
||||
|
||||
GHOST_ContextD3D::~GHOST_ContextD3D()
|
||||
{
|
||||
device_->Release();
|
||||
device_ctx_->ClearState();
|
||||
device_ctx_->Release();
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextD3D::swapBufferRelease()
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextD3D::activateDrawingContext()
|
||||
{
|
||||
active_context_ = this;
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextD3D::releaseDrawingContext()
|
||||
{
|
||||
active_context_ = nullptr;
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextD3D::setupD3DLib()
|
||||
{
|
||||
if (s_d3d_lib == nullptr) {
|
||||
s_d3d_lib = LoadLibraryA("d3d11.dll");
|
||||
|
||||
WIN32_CHK(s_d3d_lib != nullptr);
|
||||
|
||||
if (s_d3d_lib == nullptr) {
|
||||
fprintf(stderr, "LoadLibrary(\"d3d11.dll\") failed!\n");
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_D3D11CreateDeviceFn == nullptr) {
|
||||
s_D3D11CreateDeviceFn = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(s_d3d_lib,
|
||||
"D3D11CreateDevice");
|
||||
|
||||
WIN32_CHK(s_D3D11CreateDeviceFn != nullptr);
|
||||
|
||||
if (s_D3D11CreateDeviceFn == nullptr) {
|
||||
fprintf(stderr, "GetProcAddress(s_d3d_lib, \"D3D11CreateDevice\") failed!\n");
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
}
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextD3D::initializeDrawingContext()
|
||||
{
|
||||
if (setupD3DLib() == GHOST_kFailure) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
HRESULT hres = s_D3D11CreateDeviceFn(
|
||||
nullptr,
|
||||
D3D_DRIVER_TYPE_HARDWARE,
|
||||
nullptr,
|
||||
/* For debugging you may want to pass D3D11_CREATE_DEVICE_DEBUG here, but that requires
|
||||
* additional setup, see
|
||||
* https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-devices-layers#debug-layer.
|
||||
*/
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
D3D11_SDK_VERSION,
|
||||
&device_,
|
||||
nullptr,
|
||||
&device_ctx_);
|
||||
|
||||
WIN32_CHK(hres == S_OK);
|
||||
|
||||
active_context_ = this;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextD3D::releaseNativeHandles()
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
class GHOST_SharedOpenGLResource {
|
||||
struct SharedData {
|
||||
HANDLE device;
|
||||
GLuint fbo;
|
||||
HANDLE render_target{nullptr};
|
||||
} shared_;
|
||||
|
||||
enum RenderTarget { TARGET_RENDERBUF, TARGET_TEX2D };
|
||||
|
||||
public:
|
||||
GHOST_SharedOpenGLResource(ID3D11Device *device,
|
||||
ID3D11DeviceContext *device_ctx,
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
DXGI_FORMAT format,
|
||||
ID3D11RenderTargetView *render_target = nullptr)
|
||||
: device_(device), device_ctx_(device_ctx), cur_width_(width), cur_height_(height)
|
||||
{
|
||||
if (!render_target) {
|
||||
D3D11_TEXTURE2D_DESC texDesc{};
|
||||
D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc{};
|
||||
ID3D11Texture2D *tex;
|
||||
|
||||
texDesc.Width = width;
|
||||
texDesc.Height = height;
|
||||
texDesc.Format = format;
|
||||
texDesc.SampleDesc.Count = 1;
|
||||
texDesc.ArraySize = 1;
|
||||
texDesc.MipLevels = 1;
|
||||
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
|
||||
|
||||
device->CreateTexture2D(&texDesc, nullptr, &tex);
|
||||
if (!tex) {
|
||||
/* If texture creation fails, we just return and leave the render target unset. So it needs
|
||||
* to be nullptr-checked before use. */
|
||||
fprintf(stderr, "Error creating texture for shared DirectX-OpenGL resource\n");
|
||||
return;
|
||||
}
|
||||
|
||||
renderTargetViewDesc.Format = texDesc.Format;
|
||||
renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
|
||||
renderTargetViewDesc.Texture2D.MipSlice = 0;
|
||||
|
||||
device->CreateRenderTargetView(tex, &renderTargetViewDesc, &render_target);
|
||||
|
||||
tex->Release();
|
||||
}
|
||||
|
||||
render_target_ = render_target;
|
||||
if (render_target_) {
|
||||
ID3D11Resource *backbuffer_res = nullptr;
|
||||
render_target_->GetResource(&backbuffer_res);
|
||||
if (backbuffer_res) {
|
||||
backbuffer_res->QueryInterface<ID3D11Texture2D>(&render_target_tex_);
|
||||
backbuffer_res->Release();
|
||||
}
|
||||
}
|
||||
|
||||
if (!render_target_ || !render_target_tex_) {
|
||||
fprintf(stderr, "Error creating render target for shared DirectX-OpenGL resource\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
~GHOST_SharedOpenGLResource()
|
||||
{
|
||||
if (render_target_tex_) {
|
||||
render_target_tex_->Release();
|
||||
}
|
||||
if (render_target_) {
|
||||
render_target_->Release();
|
||||
}
|
||||
|
||||
if (is_initialized_) {
|
||||
#if 0 /* TODO: Causes an access violation since Blender 3.4 (a296b8f694d1). */
|
||||
if (shared_.render_target
|
||||
# if 1
|
||||
/* TODO: #wglDXUnregisterObjectNV() causes an access violation on AMD when the shared
|
||||
* resource is a GL texture. Since there is currently no good alternative, just skip
|
||||
* unregistering the shared resource. */
|
||||
&& !use_gl_texture2d_
|
||||
# endif
|
||||
) {
|
||||
wglDXUnregisterObjectNV(shared_.device, shared_.render_target);
|
||||
}
|
||||
if (shared_.device) {
|
||||
wglDXCloseDeviceNV(shared_.device);
|
||||
}
|
||||
glDeleteFramebuffers(1, &shared_.fbo);
|
||||
if (use_gl_texture2d_) {
|
||||
glDeleteTextures(1, &gl_render_target_);
|
||||
}
|
||||
else {
|
||||
glDeleteRenderbuffers(1, &gl_render_target_);
|
||||
}
|
||||
#else
|
||||
glDeleteFramebuffers(1, &shared_.fbo);
|
||||
if (use_gl_texture2d_) {
|
||||
glDeleteTextures(1, &gl_render_target_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if the shared object was successfully registered, false otherwise. */
|
||||
bool reregisterSharedObject(RenderTarget target)
|
||||
{
|
||||
if (shared_.render_target) {
|
||||
wglDXUnregisterObjectNV(shared_.device, shared_.render_target);
|
||||
}
|
||||
|
||||
if (!render_target_tex_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target == TARGET_TEX2D) {
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RGBA8,
|
||||
cur_width_,
|
||||
cur_height_,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
shared_.render_target = wglDXRegisterObjectNV(shared_.device,
|
||||
render_target_tex_,
|
||||
gl_render_target_,
|
||||
(target == TARGET_TEX2D) ? GL_TEXTURE_2D :
|
||||
GL_RENDERBUFFER,
|
||||
WGL_ACCESS_READ_WRITE_NV);
|
||||
if (!shared_.render_target) {
|
||||
fprintf(stderr, "Error registering shared object using wglDXRegisterObjectNV()\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GHOST_TSuccess initialize()
|
||||
{
|
||||
shared_.device = wglDXOpenDeviceNV(device_);
|
||||
if (shared_.device == nullptr) {
|
||||
fprintf(stderr, "Error opening shared device using wglDXOpenDeviceNV()\n");
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/* Build the render-buffer. */
|
||||
glGenRenderbuffers(1, &gl_render_target_);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, gl_render_target_);
|
||||
|
||||
if (!reregisterSharedObject(TARGET_RENDERBUF)) {
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
if (gl_render_target_) {
|
||||
glDeleteRenderbuffers(1, &gl_render_target_);
|
||||
}
|
||||
/* Fall back to texture 2d. */
|
||||
use_gl_texture2d_ = true;
|
||||
glGenTextures(1, &gl_render_target_);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_render_target_);
|
||||
|
||||
reregisterSharedObject(TARGET_TEX2D);
|
||||
}
|
||||
|
||||
/* Build the frame-buffer. */
|
||||
glGenFramebuffers(1, &shared_.fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, shared_.fbo);
|
||||
if (use_gl_texture2d_) {
|
||||
glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl_render_target_, 0);
|
||||
}
|
||||
else {
|
||||
glFramebufferRenderbuffer(
|
||||
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gl_render_target_);
|
||||
}
|
||||
is_initialized_ = true;
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
void ensureUpdated(unsigned int width, unsigned int height)
|
||||
{
|
||||
if (is_initialized_ == false) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
if ((cur_width_ != width) || (cur_height_ != height)) {
|
||||
cur_width_ = width;
|
||||
cur_height_ = height;
|
||||
reregisterSharedObject(use_gl_texture2d_ ? TARGET_TEX2D : TARGET_RENDERBUF);
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TSuccess blit(unsigned int width, unsigned int height)
|
||||
{
|
||||
GLint fbo;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fbo);
|
||||
|
||||
if (!render_target_ || !render_target_tex_) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
ensureUpdated(width, height);
|
||||
|
||||
#ifdef NDEBUG
|
||||
const float clear_col[] = {0.8f, 0.5f, 1.0f, 1.0f};
|
||||
device_ctx_->ClearRenderTargetView(render_target_, clear_col);
|
||||
#endif
|
||||
device_ctx_->OMSetRenderTargets(1, &render_target_, nullptr);
|
||||
|
||||
beginGLOnly();
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shared_.fbo);
|
||||
GLenum err = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (err != GL_FRAMEBUFFER_COMPLETE) {
|
||||
fprintf(
|
||||
stderr, "Error: Framebuffer for shared DirectX-OpenGL resource incomplete %u\n", err);
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/* No glBlitNamedFramebuffer, gotta be 3.3 compatible. */
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
|
||||
endGLOnly();
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
ID3D11RenderTargetView *render_target_{nullptr};
|
||||
ID3D11Texture2D *render_target_tex_{nullptr};
|
||||
|
||||
private:
|
||||
void beginGLOnly()
|
||||
{
|
||||
wglDXLockObjectsNV(shared_.device, 1, &shared_.render_target);
|
||||
}
|
||||
void endGLOnly()
|
||||
{
|
||||
wglDXUnlockObjectsNV(shared_.device, 1, &shared_.render_target);
|
||||
}
|
||||
|
||||
ID3D11Device *device_;
|
||||
ID3D11DeviceContext *device_ctx_;
|
||||
GLuint gl_render_target_;
|
||||
unsigned int cur_width_, cur_height_;
|
||||
bool is_initialized_{false};
|
||||
bool use_gl_texture2d_{false};
|
||||
};
|
||||
|
||||
GHOST_SharedOpenGLResource *GHOST_ContextD3D::createSharedOpenGLResource(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
DXGI_FORMAT format,
|
||||
ID3D11RenderTargetView *render_target)
|
||||
{
|
||||
if (!(WGL_NV_DX_interop && WGL_NV_DX_interop2)) {
|
||||
fprintf(stderr,
|
||||
"Error: Can't render OpenGL framebuffer using Direct3D. NV_DX_interop extension not "
|
||||
"available.");
|
||||
return nullptr;
|
||||
}
|
||||
GHOST_SharedOpenGLResource *shared_res = new GHOST_SharedOpenGLResource(
|
||||
device_, device_ctx_, width, height, format, render_target);
|
||||
|
||||
return shared_res;
|
||||
}
|
||||
GHOST_SharedOpenGLResource *GHOST_ContextD3D::createSharedOpenGLResource(unsigned int width,
|
||||
unsigned int height,
|
||||
DXGI_FORMAT format)
|
||||
{
|
||||
return createSharedOpenGLResource(width, height, format, nullptr);
|
||||
}
|
||||
|
||||
void GHOST_ContextD3D::disposeSharedOpenGLResource(GHOST_SharedOpenGLResource *shared_res)
|
||||
{
|
||||
delete shared_res;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextD3D::blitFromOpenGLContext(GHOST_SharedOpenGLResource *shared_res,
|
||||
unsigned int width,
|
||||
unsigned int height)
|
||||
{
|
||||
return shared_res->blit(width, height);
|
||||
}
|
||||
|
||||
ID3D11Texture2D *GHOST_ContextD3D::getSharedTexture2D(GHOST_SharedOpenGLResource *shared_res)
|
||||
{
|
||||
return shared_res->render_target_tex_;
|
||||
}
|
||||
133
blender-5.2.0/intern/ghost/intern/GHOST_ContextD3D.hh
Normal file
133
blender-5.2.0/intern/ghost/intern/GHOST_ContextD3D.hh
Normal file
@@ -0,0 +1,133 @@
|
||||
/* SPDX-FileCopyrightText: 2014-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef WIN32
|
||||
# error WIN32 only!
|
||||
#endif // WIN32
|
||||
|
||||
#include <D3D11.h>
|
||||
|
||||
#include "GHOST_Context.hh"
|
||||
|
||||
class GHOST_ContextD3D : public GHOST_Context {
|
||||
/* XR code needs low level graphics data to send to OpenXR. */
|
||||
friend class GHOST_XrGraphicsBindingD3D;
|
||||
friend class GHOST_XrGraphicsBindingOpenGLD3D;
|
||||
friend class GHOST_XrGraphicsBindingVulkanD3D;
|
||||
|
||||
public:
|
||||
GHOST_ContextD3D(const GHOST_ContextParams &context_params, HWND hWnd);
|
||||
~GHOST_ContextD3D() override;
|
||||
|
||||
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
|
||||
GHOST_TSuccess swapBufferAcquire() override
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps front and back buffers of a window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess swapBufferRelease() override;
|
||||
|
||||
/**
|
||||
* Activates the drawing context of this window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess activateDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Release the drawing context of the calling thread.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess releaseDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Call immediately after new to initialize. If this fails then immediately delete the object.
|
||||
* \return Indication as to whether initialization has succeeded.
|
||||
*/
|
||||
GHOST_TSuccess initializeDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Updates the drawing context of this window. Needed
|
||||
* whenever the window is changed.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess updateDrawingContext() override
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if it is OK for a remove the native display
|
||||
* \return Indication as to whether removal has succeeded.
|
||||
*/
|
||||
GHOST_TSuccess releaseNativeHandles() override;
|
||||
|
||||
/**
|
||||
* Sets the swap interval for #swapBuffers.
|
||||
* \param interval: The swap interval to use.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess setSwapInterval(int /*interval*/) override
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current swap interval for #swapBuffers.
|
||||
* \param interval_out: Variable to store the swap interval if it can be read.
|
||||
* \return Whether the swap interval can be read.
|
||||
*/
|
||||
GHOST_TSuccess getSwapInterval(int & /*interval_out*/) override
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the OpenGL frame-buffer associated with the OpenGL context
|
||||
* \return The ID of an OpenGL frame-buffer object.
|
||||
*/
|
||||
unsigned int getDefaultFramebuffer() override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
class GHOST_SharedOpenGLResource *createSharedOpenGLResource(
|
||||
unsigned int width,
|
||||
unsigned int height,
|
||||
DXGI_FORMAT format,
|
||||
ID3D11RenderTargetView *render_target);
|
||||
class GHOST_SharedOpenGLResource *createSharedOpenGLResource(unsigned int width,
|
||||
unsigned int height,
|
||||
DXGI_FORMAT format);
|
||||
void disposeSharedOpenGLResource(class GHOST_SharedOpenGLResource *shared_res);
|
||||
GHOST_TSuccess blitFromOpenGLContext(class GHOST_SharedOpenGLResource *shared_res,
|
||||
unsigned int width,
|
||||
unsigned int height);
|
||||
ID3D11Texture2D *getSharedTexture2D(class GHOST_SharedOpenGLResource *shared_res);
|
||||
|
||||
bool isUpsideDown() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
GHOST_TSuccess setupD3DLib();
|
||||
|
||||
static HMODULE s_d3d_lib;
|
||||
static PFN_D3D11_CREATE_DEVICE s_D3D11CreateDeviceFn;
|
||||
|
||||
HWND h_wnd_;
|
||||
|
||||
ID3D11Device *device_;
|
||||
ID3D11DeviceContext *device_ctx_;
|
||||
};
|
||||
659
blender-5.2.0/intern/ghost/intern/GHOST_ContextEGL.cc
Normal file
659
blender-5.2.0/intern/ghost/intern/GHOST_ContextEGL.cc
Normal file
@@ -0,0 +1,659 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* Definition of GHOST_ContextEGL class.
|
||||
*/
|
||||
|
||||
#include "GHOST_ContextEGL.hh"
|
||||
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#define CASE_CODE_RETURN_STR(code) \
|
||||
case code: \
|
||||
return #code;
|
||||
|
||||
static const char *get_egl_error_enum_string(EGLint error)
|
||||
{
|
||||
switch (error) {
|
||||
CASE_CODE_RETURN_STR(EGL_SUCCESS)
|
||||
CASE_CODE_RETURN_STR(EGL_NOT_INITIALIZED)
|
||||
CASE_CODE_RETURN_STR(EGL_BAD_ACCESS)
|
||||
CASE_CODE_RETURN_STR(EGL_BAD_ALLOC)
|
||||
CASE_CODE_RETURN_STR(EGL_BAD_ATTRIBUTE)
|
||||
CASE_CODE_RETURN_STR(EGL_BAD_CONTEXT)
|
||||
CASE_CODE_RETURN_STR(EGL_BAD_CONFIG)
|
||||
CASE_CODE_RETURN_STR(EGL_BAD_CURRENT_SURFACE)
|
||||
CASE_CODE_RETURN_STR(EGL_BAD_DISPLAY)
|
||||
CASE_CODE_RETURN_STR(EGL_BAD_SURFACE)
|
||||
CASE_CODE_RETURN_STR(EGL_BAD_MATCH)
|
||||
CASE_CODE_RETURN_STR(EGL_BAD_PARAMETER)
|
||||
CASE_CODE_RETURN_STR(EGL_BAD_NATIVE_PIXMAP)
|
||||
CASE_CODE_RETURN_STR(EGL_BAD_NATIVE_WINDOW)
|
||||
CASE_CODE_RETURN_STR(EGL_CONTEXT_LOST)
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *get_egl_error_message_string(EGLint error)
|
||||
{
|
||||
switch (error) {
|
||||
case EGL_SUCCESS:
|
||||
return "The last function succeeded without error.";
|
||||
|
||||
case EGL_NOT_INITIALIZED:
|
||||
return (
|
||||
"EGL is not initialized, or could not be initialized, "
|
||||
"for the specified EGL display connection.");
|
||||
|
||||
case EGL_BAD_ACCESS:
|
||||
return (
|
||||
"EGL cannot access a requested resource "
|
||||
"(for example a context is bound in another thread).");
|
||||
|
||||
case EGL_BAD_ALLOC:
|
||||
return "EGL failed to allocate resources for the requested operation.";
|
||||
|
||||
case EGL_BAD_ATTRIBUTE:
|
||||
return "An unrecognized attribute or attribute value was passed in the attribute list.";
|
||||
|
||||
case EGL_BAD_CONTEXT:
|
||||
return "An EGLContext argument does not name a valid EGL rendering context.";
|
||||
|
||||
case EGL_BAD_CONFIG:
|
||||
return "An EGLConfig argument does not name a valid EGL frame buffer configuration.";
|
||||
|
||||
case EGL_BAD_CURRENT_SURFACE:
|
||||
return (
|
||||
"The current surface of the calling thread is a window, "
|
||||
"pixel buffer or pixmap that is no longer valid.");
|
||||
|
||||
case EGL_BAD_DISPLAY:
|
||||
return "An EGLDisplay argument does not name a valid EGL display connection.";
|
||||
|
||||
case EGL_BAD_SURFACE:
|
||||
return (
|
||||
"An EGLSurface argument does not name a valid surface "
|
||||
"(window, pixel buffer or pixmap) configured for GL rendering.");
|
||||
|
||||
case EGL_BAD_MATCH:
|
||||
return (
|
||||
"Arguments are inconsistent "
|
||||
"(for example, a valid context requires buffers not supplied by a valid surface).");
|
||||
|
||||
case EGL_BAD_PARAMETER:
|
||||
return "One or more argument values are invalid.";
|
||||
|
||||
case EGL_BAD_NATIVE_PIXMAP:
|
||||
return "A NativePixmapType argument does not refer to a valid native pixmap.";
|
||||
|
||||
case EGL_BAD_NATIVE_WINDOW:
|
||||
return "A NativeWindowType argument does not refer to a valid native window.";
|
||||
|
||||
case EGL_CONTEXT_LOST:
|
||||
return (
|
||||
"A power management event has occurred. "
|
||||
"The application must destroy all contexts and reinitialize OpenGL ES state "
|
||||
"and objects to continue rendering.");
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void egl_print_error(const char *message, const EGLint error)
|
||||
{
|
||||
const char *code = get_egl_error_enum_string(error);
|
||||
const char *msg = get_egl_error_message_string(error);
|
||||
|
||||
fprintf(stderr,
|
||||
"%sEGL Error (0x%04X): %s: %s\n",
|
||||
message,
|
||||
uint(error),
|
||||
code ? code : "<Unknown>",
|
||||
msg ? msg : "<Unknown>");
|
||||
}
|
||||
|
||||
static bool egl_chk(bool result,
|
||||
const char *file = nullptr,
|
||||
int line = 0,
|
||||
const char *text = nullptr)
|
||||
{
|
||||
if (!result) {
|
||||
const EGLint error = eglGetError();
|
||||
#ifndef NDEBUG
|
||||
const char *code = get_egl_error_enum_string(error);
|
||||
const char *msg = get_egl_error_message_string(error);
|
||||
fprintf(stderr,
|
||||
"%s:%d: [%s] -> EGL Error (0x%04X): %s: %s\n",
|
||||
file,
|
||||
line,
|
||||
text,
|
||||
uint(error),
|
||||
code ? code : "<Unknown>",
|
||||
msg ? msg : "<Unknown>");
|
||||
#else
|
||||
egl_print_error("", error);
|
||||
(void)(file);
|
||||
(void)(line);
|
||||
(void)(text);
|
||||
#endif
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
# define EGL_CHK(x) egl_chk((x), __FILE__, __LINE__, #x)
|
||||
#else
|
||||
# define EGL_CHK(x) egl_chk(x)
|
||||
#endif
|
||||
|
||||
EGLContext GHOST_ContextEGL::s_gl_sharedContext = EGL_NO_CONTEXT;
|
||||
EGLint GHOST_ContextEGL::s_gl_sharedCount = 0;
|
||||
|
||||
EGLContext GHOST_ContextEGL::s_gles_sharedContext = EGL_NO_CONTEXT;
|
||||
EGLint GHOST_ContextEGL::s_gles_sharedCount = 0;
|
||||
|
||||
EGLContext GHOST_ContextEGL::s_vg_sharedContext = EGL_NO_CONTEXT;
|
||||
EGLint GHOST_ContextEGL::s_vg_sharedCount = 0;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(disable : 4715)
|
||||
#endif
|
||||
|
||||
template<typename T> T &choose_api(EGLenum api, T &a, T &b, T &c)
|
||||
{
|
||||
switch (api) {
|
||||
case EGL_OPENGL_API:
|
||||
return a;
|
||||
case EGL_OPENGL_ES_API:
|
||||
return b;
|
||||
case EGL_OPENVG_API:
|
||||
return c;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_ContextEGL::GHOST_ContextEGL(const GHOST_System *const system,
|
||||
const GHOST_ContextParams &context_params,
|
||||
EGLNativeWindowType nativeWindow,
|
||||
EGLNativeDisplayType nativeDisplay,
|
||||
EGLint contextProfileMask,
|
||||
EGLint contextMajorVersion,
|
||||
EGLint contextMinorVersion,
|
||||
EGLint contextFlags,
|
||||
EGLint contextResetNotificationStrategy,
|
||||
EGLenum api)
|
||||
: GHOST_Context(context_params),
|
||||
system_(system),
|
||||
native_display_(nativeDisplay),
|
||||
native_window_(nativeWindow),
|
||||
context_profile_mask_(contextProfileMask),
|
||||
context_major_version_(contextMajorVersion),
|
||||
context_minor_version_(contextMinorVersion),
|
||||
context_flags_(contextFlags),
|
||||
context_reset_notification_strategy_(contextResetNotificationStrategy),
|
||||
api_(api),
|
||||
context_(EGL_NO_CONTEXT),
|
||||
surface_(EGL_NO_SURFACE),
|
||||
display_(EGL_NO_DISPLAY),
|
||||
config_(EGL_NO_CONFIG_KHR),
|
||||
swap_interval_(1),
|
||||
shared_context_(
|
||||
choose_api(api, s_gl_sharedContext, s_gles_sharedContext, s_vg_sharedContext)),
|
||||
shared_count_(choose_api(api, s_gl_sharedCount, s_gles_sharedCount, s_vg_sharedCount)),
|
||||
surface_from_native_window_(false)
|
||||
{
|
||||
}
|
||||
|
||||
GHOST_ContextEGL::~GHOST_ContextEGL()
|
||||
{
|
||||
if (display_ != EGL_NO_DISPLAY) {
|
||||
|
||||
bindAPI(api_);
|
||||
|
||||
if (context_ != EGL_NO_CONTEXT) {
|
||||
if (context_ == ::eglGetCurrentContext()) {
|
||||
EGL_CHK(::eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
|
||||
}
|
||||
if (context_ != shared_context_ || shared_count_ == 1) {
|
||||
assert(shared_count_ > 0);
|
||||
|
||||
shared_count_--;
|
||||
|
||||
if (shared_count_ == 0) {
|
||||
shared_context_ = EGL_NO_CONTEXT;
|
||||
}
|
||||
EGL_CHK(::eglDestroyContext(display_, context_));
|
||||
}
|
||||
}
|
||||
|
||||
if (surface_ != EGL_NO_SURFACE) {
|
||||
EGL_CHK(::eglDestroySurface(display_, surface_));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextEGL::swapBufferRelease()
|
||||
{
|
||||
return EGL_CHK(::eglSwapBuffers(display_, surface_)) ? GHOST_kSuccess : GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextEGL::setSwapInterval(int interval)
|
||||
{
|
||||
if (epoxy_egl_version(display_) >= 11) {
|
||||
if (EGL_CHK(::eglSwapInterval(display_, interval))) {
|
||||
swap_interval_ = interval;
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextEGL::getSwapInterval(int &interval_out)
|
||||
{
|
||||
/* This is a bit of a kludge because there does not seem to
|
||||
* be a way to query the swap interval with EGL. */
|
||||
interval_out = swap_interval_;
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
EGLDisplay GHOST_ContextEGL::getDisplay() const
|
||||
{
|
||||
return display_;
|
||||
}
|
||||
|
||||
EGLConfig GHOST_ContextEGL::getConfig() const
|
||||
{
|
||||
return config_;
|
||||
}
|
||||
|
||||
EGLContext GHOST_ContextEGL::getContext() const
|
||||
{
|
||||
return context_;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextEGL::activateDrawingContext()
|
||||
{
|
||||
if (display_) {
|
||||
active_context_ = this;
|
||||
bindAPI(api_);
|
||||
return EGL_CHK(::eglMakeCurrent(display_, surface_, surface_, context_)) ? GHOST_kSuccess :
|
||||
GHOST_kFailure;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextEGL::releaseDrawingContext()
|
||||
{
|
||||
if (display_) {
|
||||
active_context_ = nullptr;
|
||||
bindAPI(api_);
|
||||
|
||||
return EGL_CHK(::eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) ?
|
||||
GHOST_kSuccess :
|
||||
GHOST_kFailure;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
inline bool GHOST_ContextEGL::bindAPI(EGLenum api)
|
||||
{
|
||||
if (epoxy_egl_version(display_) >= 12) {
|
||||
return (EGL_CHK(eglBindAPI(api)) == EGL_TRUE);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const std::string &api_string(EGLenum api)
|
||||
{
|
||||
static const std::string a("OpenGL");
|
||||
static const std::string b("OpenGL ES");
|
||||
static const std::string c("OpenVG");
|
||||
|
||||
return choose_api(api, a, b, c);
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext()
|
||||
{
|
||||
/* Objects have to be declared here due to the use of `goto`. */
|
||||
std::vector<EGLint> attrib_list;
|
||||
EGLint num_config = 0;
|
||||
|
||||
if (context_params_.is_stereo_visual) {
|
||||
fprintf(stderr, "Warning! Stereo OpenGL ES contexts are not supported.\n");
|
||||
}
|
||||
context_params_.is_stereo_visual = false; /* It doesn't matter what the Window wants. */
|
||||
|
||||
EGLDisplay prev_display = eglGetCurrentDisplay();
|
||||
EGLSurface prev_draw = eglGetCurrentSurface(EGL_DRAW);
|
||||
EGLSurface prev_read = eglGetCurrentSurface(EGL_READ);
|
||||
EGLContext prev_context = eglGetCurrentContext();
|
||||
|
||||
EGLint egl_major = 0, egl_minor = 0;
|
||||
|
||||
if (!EGL_CHK((display_ = ::eglGetDisplay(native_display_)) != EGL_NO_DISPLAY)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
{
|
||||
const EGLBoolean init_display_result = ::eglInitialize(display_, &egl_major, &egl_minor);
|
||||
const EGLint init_display_error = (init_display_result) ? 0 : eglGetError();
|
||||
|
||||
if (!init_display_result || (egl_major == 0 && egl_minor == 0)) {
|
||||
/* We failed to create a regular render window, retry and see if we can create a headless
|
||||
* render context. */
|
||||
::eglTerminate(display_);
|
||||
|
||||
const char *egl_extension_st = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
||||
assert(egl_extension_st != nullptr);
|
||||
assert(egl_extension_st == nullptr ||
|
||||
strstr(egl_extension_st, "EGL_MESA_platform_surfaceless") != nullptr);
|
||||
if (egl_extension_st == nullptr ||
|
||||
strstr(egl_extension_st, "EGL_MESA_platform_surfaceless") == nullptr)
|
||||
{
|
||||
egl_print_error("Failed to create display GPU context: ", init_display_error);
|
||||
fprintf(
|
||||
stderr,
|
||||
"Failed to create headless GPU context: No EGL_MESA_platform_surfaceless extension");
|
||||
goto error;
|
||||
}
|
||||
|
||||
display_ = eglGetPlatformDisplayEXT(
|
||||
EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, nullptr);
|
||||
|
||||
const EGLBoolean headless_result = ::eglInitialize(display_, &egl_major, &egl_minor);
|
||||
const EGLint init_headless_error = (headless_result) ? 0 : eglGetError();
|
||||
|
||||
if (!headless_result) {
|
||||
egl_print_error("Failed to create display GPU context: ", init_display_error);
|
||||
egl_print_error("Failed to create headless GPU context: ", init_headless_error);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_GHOST_DEBUG
|
||||
fprintf(stderr, "EGL Version %d.%d\n", egl_major, egl_minor);
|
||||
#endif
|
||||
|
||||
if (!EGL_CHK(::eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))) {
|
||||
goto error;
|
||||
}
|
||||
if (!bindAPI(api_)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Build attribute list. */
|
||||
|
||||
attrib_list.reserve(20);
|
||||
|
||||
if (api_ == EGL_OPENGL_ES_API && epoxy_egl_version(display_) >= 12) {
|
||||
/* According to the spec it seems that you are required to set EGL_RENDERABLE_TYPE,
|
||||
* but some implementations (ANGLE) do not seem to care. */
|
||||
|
||||
if (context_major_version_ == 1) {
|
||||
attrib_list.push_back(EGL_RENDERABLE_TYPE);
|
||||
attrib_list.push_back(EGL_OPENGL_ES_BIT);
|
||||
}
|
||||
else if (context_major_version_ == 2) {
|
||||
attrib_list.push_back(EGL_RENDERABLE_TYPE);
|
||||
attrib_list.push_back(EGL_OPENGL_ES2_BIT);
|
||||
}
|
||||
else if (context_major_version_ == 3) {
|
||||
attrib_list.push_back(EGL_RENDERABLE_TYPE);
|
||||
attrib_list.push_back(EGL_OPENGL_ES3_BIT_KHR);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr,
|
||||
"Warning! Unable to request an ES context of version %d.%d\n",
|
||||
context_major_version_,
|
||||
context_minor_version_);
|
||||
}
|
||||
|
||||
if (!((context_major_version_ == 1) ||
|
||||
(context_major_version_ == 2 && epoxy_egl_version(display_) >= 13) ||
|
||||
(context_major_version_ == 3 &&
|
||||
epoxy_has_egl_extension(display_, "KHR_create_context")) ||
|
||||
(context_major_version_ == 3 && epoxy_egl_version(display_) >= 15)))
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Warning! May not be able to create a version %d.%d ES context with version %d.%d "
|
||||
"of EGL\n",
|
||||
context_major_version_,
|
||||
context_minor_version_,
|
||||
egl_major,
|
||||
egl_minor);
|
||||
}
|
||||
}
|
||||
else {
|
||||
attrib_list.push_back(EGL_RENDERABLE_TYPE);
|
||||
attrib_list.push_back(EGL_OPENGL_BIT);
|
||||
}
|
||||
|
||||
attrib_list.push_back(EGL_RED_SIZE);
|
||||
attrib_list.push_back(8);
|
||||
|
||||
attrib_list.push_back(EGL_GREEN_SIZE);
|
||||
attrib_list.push_back(8);
|
||||
|
||||
attrib_list.push_back(EGL_BLUE_SIZE);
|
||||
attrib_list.push_back(8);
|
||||
|
||||
if (native_window_ == 0) {
|
||||
/* Off-screen surface. */
|
||||
attrib_list.push_back(EGL_SURFACE_TYPE);
|
||||
attrib_list.push_back(EGL_PBUFFER_BIT);
|
||||
}
|
||||
|
||||
attrib_list.push_back(EGL_NONE);
|
||||
|
||||
if (!EGL_CHK(::eglChooseConfig(display_, &(attrib_list[0]), &config_, 1, &num_config))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* A common error is to assume that ChooseConfig worked because it returned EGL_TRUE. */
|
||||
if (num_config != 1) { /* `num_config` should be exactly 1. */
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (native_window_ != 0) {
|
||||
std::vector<EGLint> surface_attrib_list;
|
||||
surface_attrib_list.reserve(3);
|
||||
#ifdef WITH_GHOST_WAYLAND
|
||||
/* Fix transparency issue on: `Wayland + Nouveau/Zink+NVK`. Due to unsupported texture formats
|
||||
* drivers can hit transparency code-paths resulting in showing the desktop in viewports.
|
||||
*
|
||||
* See #102994. */
|
||||
/* EGL_EXT_present_opaque isn't added to the latest release of epoxy, but is part of the latest
|
||||
* EGL https://github.com/KhronosGroup/EGL-Registry/blob/main/api/egl.xml */
|
||||
if (epoxy_has_egl_extension(display_, "EGL_EXT_present_opaque")) {
|
||||
# ifndef EGL_PRESENT_OPAQUE_EXT
|
||||
# define EGL_PRESENT_OPAQUE_EXT 0x31DF
|
||||
# endif
|
||||
surface_attrib_list.push_back(EGL_PRESENT_OPAQUE_EXT);
|
||||
surface_attrib_list.push_back(EGL_TRUE);
|
||||
}
|
||||
#endif
|
||||
surface_attrib_list.push_back(EGL_NONE);
|
||||
|
||||
surface_ = ::eglCreateWindowSurface(
|
||||
display_, config_, native_window_, surface_attrib_list.data());
|
||||
surface_from_native_window_ = true;
|
||||
}
|
||||
else {
|
||||
static const EGLint pb_attrib_list[] = {
|
||||
EGL_WIDTH,
|
||||
1,
|
||||
EGL_HEIGHT,
|
||||
1,
|
||||
EGL_NONE,
|
||||
};
|
||||
surface_ = ::eglCreatePbufferSurface(display_, config_, pb_attrib_list);
|
||||
}
|
||||
|
||||
if (!EGL_CHK(surface_ != EGL_NO_SURFACE)) {
|
||||
goto error;
|
||||
}
|
||||
attrib_list.clear();
|
||||
|
||||
if (epoxy_egl_version(display_) >= 15 || epoxy_has_egl_extension(display_, "KHR_create_context"))
|
||||
{
|
||||
if (api_ == EGL_OPENGL_API || api_ == EGL_OPENGL_ES_API) {
|
||||
if (context_major_version_ != 0) {
|
||||
attrib_list.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
|
||||
attrib_list.push_back(context_major_version_);
|
||||
}
|
||||
|
||||
if (context_minor_version_ != 0) {
|
||||
attrib_list.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
|
||||
attrib_list.push_back(context_minor_version_);
|
||||
}
|
||||
|
||||
if (context_flags_ != 0) {
|
||||
attrib_list.push_back(EGL_CONTEXT_FLAGS_KHR);
|
||||
attrib_list.push_back(context_flags_);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (context_major_version_ != 0 || context_minor_version_ != 0) {
|
||||
fprintf(stderr,
|
||||
"Warning! Cannot request specific versions of %s contexts.",
|
||||
api_string(api_).c_str());
|
||||
}
|
||||
|
||||
if (context_flags_ != 0) {
|
||||
fprintf(stderr, "Warning! Flags cannot be set on %s contexts.", api_string(api_).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (api_ == EGL_OPENGL_API) {
|
||||
if (context_profile_mask_ != 0) {
|
||||
attrib_list.push_back(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR);
|
||||
attrib_list.push_back(context_profile_mask_);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (context_profile_mask_ != 0) {
|
||||
fprintf(
|
||||
stderr, "Warning! Cannot select profile for %s contexts.", api_string(api_).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (api_ == EGL_OPENGL_API || epoxy_egl_version(display_) >= 15) {
|
||||
if (context_reset_notification_strategy_ != 0) {
|
||||
attrib_list.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR);
|
||||
attrib_list.push_back(context_reset_notification_strategy_);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (context_reset_notification_strategy_ != 0) {
|
||||
fprintf(stderr,
|
||||
"Warning! EGL %d.%d cannot set the reset notification strategy on %s contexts.",
|
||||
egl_major,
|
||||
egl_minor,
|
||||
api_string(api_).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (api_ == EGL_OPENGL_ES_API) {
|
||||
if (context_major_version_ != 0) {
|
||||
attrib_list.push_back(EGL_CONTEXT_CLIENT_VERSION);
|
||||
attrib_list.push_back(context_major_version_);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (context_major_version_ != 0 || context_minor_version_ != 0) {
|
||||
fprintf(stderr,
|
||||
"Warning! EGL %d.%d is unable to select between versions of %s.",
|
||||
egl_major,
|
||||
egl_minor,
|
||||
api_string(api_).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (context_flags_ != 0) {
|
||||
fprintf(stderr, "Warning! EGL %d.%d is unable to set context flags.", egl_major, egl_minor);
|
||||
}
|
||||
if (context_profile_mask_ != 0) {
|
||||
fprintf(stderr,
|
||||
"Warning! EGL %d.%d is unable to select between profiles.",
|
||||
egl_major,
|
||||
egl_minor);
|
||||
}
|
||||
if (context_reset_notification_strategy_ != 0) {
|
||||
fprintf(stderr,
|
||||
"Warning! EGL %d.%d is unable to set the reset notification strategies.",
|
||||
egl_major,
|
||||
egl_minor);
|
||||
}
|
||||
}
|
||||
|
||||
attrib_list.push_back(EGL_NONE);
|
||||
|
||||
context_ = ::eglCreateContext(display_, config_, shared_context_, &(attrib_list[0]));
|
||||
|
||||
if (!EGL_CHK(context_ != EGL_NO_CONTEXT)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (shared_context_ == EGL_NO_CONTEXT) {
|
||||
shared_context_ = context_;
|
||||
}
|
||||
|
||||
shared_count_++;
|
||||
|
||||
if (!EGL_CHK(::eglMakeCurrent(display_, surface_, surface_, context_))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
{
|
||||
const GHOST_TVSyncModes vsync = getVSync();
|
||||
if (vsync != GHOST_kVSyncModeUnset) {
|
||||
setSwapInterval(int(vsync));
|
||||
}
|
||||
}
|
||||
|
||||
if (native_window_ != 0) {
|
||||
initClearGL();
|
||||
::eglSwapBuffers(display_, surface_);
|
||||
}
|
||||
|
||||
active_context_ = this;
|
||||
return GHOST_kSuccess;
|
||||
|
||||
error:
|
||||
if (prev_display != EGL_NO_DISPLAY) {
|
||||
EGL_CHK(eglMakeCurrent(prev_display, prev_draw, prev_read, prev_context));
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextEGL::releaseNativeHandles()
|
||||
{
|
||||
native_display_ = nullptr;
|
||||
|
||||
native_window_ = 0;
|
||||
if (surface_from_native_window_) {
|
||||
surface_ = EGL_NO_SURFACE;
|
||||
}
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
149
blender-5.2.0/intern/ghost/intern/GHOST_ContextEGL.hh
Normal file
149
blender-5.2.0/intern/ghost/intern/GHOST_ContextEGL.hh
Normal file
@@ -0,0 +1,149 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef WITH_OPENGL_BACKEND
|
||||
# error "ContextEGL requires WITH_OPENGL_BACKEND"
|
||||
#endif
|
||||
|
||||
#include "GHOST_Context.hh"
|
||||
#include "GHOST_System.hh"
|
||||
|
||||
#include <epoxy/egl.h>
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
#ifndef GHOST_OPENGL_EGL_CONTEXT_FLAGS
|
||||
# define GHOST_OPENGL_EGL_CONTEXT_FLAGS 0
|
||||
#endif
|
||||
|
||||
#ifndef GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY
|
||||
# define GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY 0
|
||||
#endif
|
||||
|
||||
class GHOST_ContextEGL : public GHOST_Context {
|
||||
/* XR code needs low level graphics data to send to OpenXR. */
|
||||
friend class GHOST_XrGraphicsBindingOpenGL;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_ContextEGL(const GHOST_System *const system,
|
||||
const GHOST_ContextParams &context_params,
|
||||
EGLNativeWindowType nativeWindow,
|
||||
EGLNativeDisplayType nativeDisplay,
|
||||
EGLint contextProfileMask,
|
||||
EGLint contextMajorVersion,
|
||||
EGLint contextMinorVersion,
|
||||
EGLint contextFlags,
|
||||
EGLint contextResetNotificationStrategy,
|
||||
EGLenum api);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_ContextEGL() override;
|
||||
|
||||
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
|
||||
GHOST_TSuccess swapBufferAcquire() override
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps front and back buffers of a window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess swapBufferRelease() override;
|
||||
|
||||
/**
|
||||
* Activates the drawing context of this window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess activateDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Release the drawing context of the calling thread.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess releaseDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Call immediately after new to initialize. If this fails then immediately delete the object.
|
||||
* \return Indication as to whether initialization has succeeded.
|
||||
*/
|
||||
GHOST_TSuccess initializeDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Removes references to native handles from this context and then returns
|
||||
* \return GHOST_kSuccess if it is OK for the parent to release the handles and
|
||||
* GHOST_kFailure if releasing the handles will interfere with sharing
|
||||
*/
|
||||
GHOST_TSuccess releaseNativeHandles() override;
|
||||
|
||||
/**
|
||||
* Sets the swap interval for #swapBuffers.
|
||||
* \param interval: The swap interval to use.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess setSwapInterval(int interval) override;
|
||||
|
||||
/**
|
||||
* Gets the current swap interval for #swapBuffers.
|
||||
* \param interval_out: Variable to store the swap interval if it can be read.
|
||||
* \return Whether the swap interval can be read.
|
||||
*/
|
||||
GHOST_TSuccess getSwapInterval(int &interval_out) override;
|
||||
|
||||
EGLDisplay getDisplay() const;
|
||||
|
||||
EGLConfig getConfig() const;
|
||||
|
||||
EGLContext getContext() const;
|
||||
|
||||
private:
|
||||
bool bindAPI(EGLenum api);
|
||||
|
||||
const GHOST_System *const system_;
|
||||
|
||||
EGLNativeDisplayType native_display_;
|
||||
EGLNativeWindowType native_window_;
|
||||
|
||||
const EGLint context_profile_mask_;
|
||||
const EGLint context_major_version_;
|
||||
const EGLint context_minor_version_;
|
||||
const EGLint context_flags_;
|
||||
const EGLint context_reset_notification_strategy_;
|
||||
|
||||
const EGLenum api_;
|
||||
|
||||
EGLContext context_;
|
||||
EGLSurface surface_;
|
||||
EGLDisplay display_;
|
||||
EGLConfig config_;
|
||||
|
||||
EGLint swap_interval_;
|
||||
|
||||
EGLContext &shared_context_;
|
||||
EGLint &shared_count_;
|
||||
|
||||
/**
|
||||
* True when the surface is created from `native_window_`.
|
||||
*/
|
||||
bool surface_from_native_window_;
|
||||
|
||||
static EGLContext s_gl_sharedContext;
|
||||
static EGLint s_gl_sharedCount;
|
||||
|
||||
static EGLContext s_gles_sharedContext;
|
||||
static EGLint s_gles_sharedCount;
|
||||
|
||||
static EGLContext s_vg_sharedContext;
|
||||
static EGLint s_vg_sharedCount;
|
||||
};
|
||||
453
blender-5.2.0/intern/ghost/intern/GHOST_ContextGLX.cc
Normal file
453
blender-5.2.0/intern/ghost/intern/GHOST_ContextGLX.cc
Normal file
@@ -0,0 +1,453 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* Definition of GHOST_ContextGLX class.
|
||||
*/
|
||||
|
||||
#include "GHOST_ContextGLX.hh"
|
||||
#include "GHOST_SystemX11.hh"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
/* Needed for Intel drivers (works with MESA-software-rasterizer (`swrast`) & NVIDIA). */
|
||||
#define USE_GLXEW_INIT_WORKAROUND
|
||||
|
||||
#ifdef USE_GLXEW_INIT_WORKAROUND
|
||||
static GLuint _glewStrLen(const GLubyte *s);
|
||||
static GLboolean _glewSearchExtension(const char *name, const GLubyte *start, const GLubyte *end);
|
||||
#endif
|
||||
|
||||
GLXContext GHOST_ContextGLX::s_sharedContext = None;
|
||||
int GHOST_ContextGLX::s_sharedCount = 0;
|
||||
|
||||
GHOST_ContextGLX::GHOST_ContextGLX(const GHOST_ContextParams &context_params,
|
||||
Window window,
|
||||
Display *display,
|
||||
GLXFBConfig fbconfig,
|
||||
int contextProfileMask,
|
||||
int contextMajorVersion,
|
||||
int contextMinorVersion,
|
||||
int contextFlags,
|
||||
int contextResetNotificationStrategy)
|
||||
: GHOST_Context(context_params),
|
||||
display_(display),
|
||||
fbconfig_(fbconfig),
|
||||
window_(window),
|
||||
context_profile_mask_(contextProfileMask),
|
||||
context_major_version_(contextMajorVersion),
|
||||
context_minor_version_(contextMinorVersion),
|
||||
context_flags_(contextFlags),
|
||||
context_reset_notification_strategy_(contextResetNotificationStrategy),
|
||||
context_(None)
|
||||
{
|
||||
assert(display_ != nullptr);
|
||||
}
|
||||
|
||||
GHOST_ContextGLX::~GHOST_ContextGLX()
|
||||
{
|
||||
if (display_ != nullptr) {
|
||||
if (context_ != None) {
|
||||
if (window_ != 0 && context_ == ::glXGetCurrentContext()) {
|
||||
::glXMakeCurrent(display_, None, nullptr);
|
||||
}
|
||||
if (context_ != s_sharedContext || s_sharedCount == 1) {
|
||||
assert(s_sharedCount > 0);
|
||||
|
||||
s_sharedCount--;
|
||||
|
||||
if (s_sharedCount == 0) {
|
||||
s_sharedContext = nullptr;
|
||||
}
|
||||
|
||||
::glXDestroyContext(display_, context_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextGLX::swapBufferRelease()
|
||||
{
|
||||
::glXSwapBuffers(display_, window_);
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextGLX::activateDrawingContext()
|
||||
{
|
||||
if (display_ == nullptr) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
active_context_ = this;
|
||||
return ::glXMakeCurrent(display_, window_, context_) ? GHOST_kSuccess : GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextGLX::releaseDrawingContext()
|
||||
{
|
||||
if (display_ == nullptr) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
active_context_ = nullptr;
|
||||
return ::glXMakeCurrent(display_, None, nullptr) ? GHOST_kSuccess : GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext()
|
||||
{
|
||||
GHOST_X11_ERROR_HANDLERS_OVERRIDE(handler_store);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Begin Inline GLEW. */
|
||||
|
||||
#ifdef USE_GLXEW_INIT_WORKAROUND
|
||||
const GLubyte *extStart = (GLubyte *)"";
|
||||
const GLubyte *extEnd;
|
||||
if (glXQueryExtension(display_, nullptr, nullptr)) {
|
||||
extStart = (const GLubyte *)glXGetClientString(display_, GLX_EXTENSIONS);
|
||||
if ((extStart == nullptr) ||
|
||||
(glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC)glXGetProcAddressARB(
|
||||
(const GLubyte *)"glXChooseFBConfig")) == nullptr ||
|
||||
(glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddressARB(
|
||||
(const GLubyte *)"glXCreateContextAttribsARB")) == nullptr ||
|
||||
(glXCreatePbuffer = (PFNGLXCREATEPBUFFERPROC)glXGetProcAddressARB(
|
||||
(const GLubyte *)"glXCreatePbuffer")) == nullptr)
|
||||
{
|
||||
extStart = (GLubyte *)"";
|
||||
}
|
||||
}
|
||||
extEnd = extStart + _glewStrLen(extStart);
|
||||
|
||||
# undef GLXEW_ARB_create_context
|
||||
const bool GLXEW_ARB_create_context = _glewSearchExtension(
|
||||
"GLX_ARB_create_context", extStart, extEnd);
|
||||
# undef GLXEW_ARB_create_context_profile
|
||||
const bool GLXEW_ARB_create_context_profile = _glewSearchExtension(
|
||||
"GLX_ARB_create_context_profile", extStart, extEnd);
|
||||
# undef GLXEW_ARB_create_context_robustness
|
||||
const bool GLXEW_ARB_create_context_robustness = _glewSearchExtension(
|
||||
"GLX_ARB_create_context_robustness", extStart, extEnd);
|
||||
# ifdef WITH_GLEW_ES
|
||||
# undef GLXEW_EXT_create_context_es_profile
|
||||
const bool GLXEW_EXT_create_context_es_profile = _glewSearchExtension(
|
||||
"GLX_EXT_create_context_es_profile", extStart, extEnd);
|
||||
# undef GLXEW_EXT_create_context_es2_profile
|
||||
const bool GLXEW_EXT_create_context_es2_profile = _glewSearchExtension(
|
||||
"GLX_EXT_create_context_es2_profile", extStart, extEnd);
|
||||
# endif /* WITH_GLEW_ES */
|
||||
|
||||
/* End Inline GLEW. */
|
||||
/* -------------------------------------------------------------------- */
|
||||
#else
|
||||
/* Important to initialize only GLXEW (_not_ GLEW),
|
||||
* since this breaks w/ Mesa's `swrast`, see: #46431. */
|
||||
glxewInit();
|
||||
#endif /* USE_GLXEW_INIT_WORKAROUND */
|
||||
|
||||
if (GLXEW_ARB_create_context) {
|
||||
int profileBitCore = context_profile_mask_ & GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
|
||||
int profileBitCompat = context_profile_mask_ & GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
|
||||
|
||||
#ifdef WITH_GLEW_ES
|
||||
int profileBitES = context_profile_mask_ & GLX_CONTEXT_ES_PROFILE_BIT_EXT;
|
||||
#endif
|
||||
|
||||
if (!GLXEW_ARB_create_context_profile && profileBitCore) {
|
||||
fprintf(stderr, "Warning! OpenGL core profile not available.\n");
|
||||
}
|
||||
if (!GLXEW_ARB_create_context_profile && profileBitCompat) {
|
||||
fprintf(stderr, "Warning! OpenGL compatibility profile not available.\n");
|
||||
}
|
||||
|
||||
#ifdef WITH_GLEW_ES
|
||||
if (!GLXEW_EXT_create_context_es_profile && profileBitES && context_major_version_ == 1) {
|
||||
fprintf(stderr, "Warning! OpenGL ES profile not available.\n");
|
||||
}
|
||||
|
||||
if (!GLXEW_EXT_create_context_es2_profile && profileBitES && context_major_version_ == 2) {
|
||||
fprintf(stderr, "Warning! OpenGL ES2 profile not available.\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
int profileMask = 0;
|
||||
|
||||
if (GLXEW_ARB_create_context_profile && profileBitCore) {
|
||||
profileMask |= profileBitCore;
|
||||
}
|
||||
if (GLXEW_ARB_create_context_profile && profileBitCompat) {
|
||||
profileMask |= profileBitCompat;
|
||||
}
|
||||
|
||||
#ifdef WITH_GLEW_ES
|
||||
if (GLXEW_EXT_create_context_es_profile && profileBitES) {
|
||||
profileMask |= profileBitES;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (profileMask != context_profile_mask_) {
|
||||
fprintf(stderr, "Warning! Ignoring untested OpenGL context profile mask bits.");
|
||||
}
|
||||
/* max 10 attributes plus terminator */
|
||||
int attribs[11];
|
||||
int i = 0;
|
||||
|
||||
if (profileMask) {
|
||||
attribs[i++] = GLX_CONTEXT_PROFILE_MASK_ARB;
|
||||
attribs[i++] = profileMask;
|
||||
}
|
||||
|
||||
if (context_major_version_ != 0) {
|
||||
attribs[i++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
|
||||
attribs[i++] = context_major_version_;
|
||||
attribs[i++] = GLX_CONTEXT_MINOR_VERSION_ARB;
|
||||
attribs[i++] = context_minor_version_;
|
||||
}
|
||||
|
||||
if (context_flags_ != 0) {
|
||||
attribs[i++] = GLX_CONTEXT_FLAGS_ARB;
|
||||
attribs[i++] = context_flags_;
|
||||
}
|
||||
|
||||
if (context_reset_notification_strategy_ != 0) {
|
||||
if (GLXEW_ARB_create_context_robustness) {
|
||||
attribs[i++] = GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB;
|
||||
attribs[i++] = context_reset_notification_strategy_;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Warning! Cannot set the reset notification strategy.");
|
||||
}
|
||||
}
|
||||
attribs[i++] = 0;
|
||||
|
||||
/* Some drivers don't like having a true off-screen context.
|
||||
* Create a pixel buffer instead of a window to render to.
|
||||
* even if it will never be used for drawing. */
|
||||
int pbuffer_attribs[] = {GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, None};
|
||||
|
||||
/* Create a GL 3.x context */
|
||||
if (fbconfig_) {
|
||||
context_ = glXCreateContextAttribsARB(display_, fbconfig_, s_sharedContext, true, attribs);
|
||||
|
||||
if (!window_) {
|
||||
window_ = (Window)glXCreatePbuffer(display_, fbconfig_, pbuffer_attribs);
|
||||
}
|
||||
}
|
||||
else {
|
||||
GLXFBConfig *framebuffer_config = nullptr;
|
||||
{
|
||||
int glx_attribs[64];
|
||||
int fbcount = 0;
|
||||
|
||||
GHOST_X11_GL_GetAttributes(glx_attribs, 64, context_params_.is_stereo_visual, false, true);
|
||||
|
||||
framebuffer_config = glXChooseFBConfig(
|
||||
display_, DefaultScreen(display_), glx_attribs, &fbcount);
|
||||
}
|
||||
|
||||
if (framebuffer_config) {
|
||||
context_ = glXCreateContextAttribsARB(
|
||||
display_, framebuffer_config[0], s_sharedContext, True, attribs);
|
||||
|
||||
if (!window_) {
|
||||
window_ = (Window)glXCreatePbuffer(display_, framebuffer_config[0], pbuffer_attribs);
|
||||
}
|
||||
|
||||
fbconfig_ = framebuffer_config[0];
|
||||
XFree(framebuffer_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Don't create legacy context */
|
||||
fprintf(stderr, "Error! GLX_ARB_create_context not available.\n");
|
||||
}
|
||||
|
||||
GHOST_TSuccess success;
|
||||
|
||||
if (context_ != nullptr) {
|
||||
const uchar *version;
|
||||
|
||||
if (!s_sharedContext) {
|
||||
s_sharedContext = context_;
|
||||
}
|
||||
s_sharedCount++;
|
||||
|
||||
glXMakeCurrent(display_, window_, context_);
|
||||
|
||||
/* For performance measurements with VSync disabled. */
|
||||
{
|
||||
const GHOST_TVSyncModes vsync = getVSync();
|
||||
if (vsync != GHOST_kVSyncModeUnset) {
|
||||
setSwapInterval(int(vsync));
|
||||
}
|
||||
}
|
||||
|
||||
if (window_) {
|
||||
initClearGL();
|
||||
::glXSwapBuffers(display_, window_);
|
||||
}
|
||||
|
||||
version = glGetString(GL_VERSION);
|
||||
|
||||
if (!version || version[0] < '3' || ((version[0] == '3') && (version[2] < '3'))) {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
else {
|
||||
success = GHOST_kSuccess;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* freeing well clean up the context initialized above */
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_X11_ERROR_HANDLERS_RESTORE(handler_store);
|
||||
|
||||
active_context_ = this;
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextGLX::releaseNativeHandles()
|
||||
{
|
||||
window_ = 0;
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextGLX::setSwapInterval(int interval)
|
||||
{
|
||||
if (epoxy_has_glx_extension(display_, DefaultScreen(display_), "GLX_EXT_swap_control")) {
|
||||
::glXSwapIntervalEXT(display_, window_, interval);
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextGLX::getSwapInterval(int &interval_out)
|
||||
{
|
||||
if (epoxy_has_glx_extension(display_, DefaultScreen(display_), "GLX_EXT_swap_control")) {
|
||||
uint interval = 0;
|
||||
|
||||
::glXQueryDrawable(display_, window_, GLX_SWAP_INTERVAL_EXT, &interval);
|
||||
|
||||
interval_out = int(interval);
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to get GLX attributes.
|
||||
*
|
||||
* \param for_fb_config: There are some small differences in
|
||||
* #glXChooseVisual and #glXChooseFBConfig's attribute encoding.
|
||||
*
|
||||
* \note Similar to SDL's 'X11_GL_GetAttributes'
|
||||
*/
|
||||
int GHOST_X11_GL_GetAttributes(
|
||||
int *attribs, int attribs_max, bool is_stereo_visual, bool need_alpha, bool for_fb_config)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (is_stereo_visual) {
|
||||
attribs[i++] = GLX_STEREO;
|
||||
if (for_fb_config) {
|
||||
attribs[i++] = True;
|
||||
}
|
||||
}
|
||||
|
||||
if (for_fb_config) {
|
||||
attribs[i++] = GLX_RENDER_TYPE;
|
||||
attribs[i++] = GLX_RGBA_BIT;
|
||||
}
|
||||
else {
|
||||
attribs[i++] = GLX_RGBA;
|
||||
}
|
||||
|
||||
attribs[i++] = GLX_DOUBLEBUFFER;
|
||||
if (for_fb_config) {
|
||||
attribs[i++] = True;
|
||||
}
|
||||
|
||||
attribs[i++] = GLX_RED_SIZE;
|
||||
attribs[i++] = True;
|
||||
|
||||
attribs[i++] = GLX_BLUE_SIZE;
|
||||
attribs[i++] = True;
|
||||
|
||||
attribs[i++] = GLX_GREEN_SIZE;
|
||||
attribs[i++] = True;
|
||||
|
||||
if (need_alpha) {
|
||||
attribs[i++] = GLX_ALPHA_SIZE;
|
||||
attribs[i++] = True;
|
||||
}
|
||||
|
||||
attribs[i++] = 0;
|
||||
|
||||
GHOST_ASSERT(i <= attribs_max, "attribute size too small");
|
||||
|
||||
(void)attribs_max;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Excuse inlining part of GLEW. */
|
||||
#ifdef USE_GLXEW_INIT_WORKAROUND
|
||||
static GLuint _glewStrLen(const GLubyte *s)
|
||||
{
|
||||
GLuint i = 0;
|
||||
if (s == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
while (s[i] != '\0') {
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static GLuint _glewStrCLen(const GLubyte *s, GLubyte c)
|
||||
{
|
||||
GLuint i = 0;
|
||||
if (s == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
while (s[i] != '\0' && s[i] != c) {
|
||||
i++;
|
||||
}
|
||||
return (s[i] == '\0' || s[i] == c) ? i : 0;
|
||||
}
|
||||
|
||||
static GLboolean _glewStrSame(const GLubyte *a, const GLubyte *b, GLuint n)
|
||||
{
|
||||
GLuint i = 0;
|
||||
if (a == nullptr || b == nullptr) {
|
||||
return (a == nullptr && b == nullptr && n == 0) ? GL_TRUE : GL_FALSE;
|
||||
}
|
||||
while (i < n && a[i] != '\0' && b[i] != '\0' && a[i] == b[i]) {
|
||||
i++;
|
||||
}
|
||||
return i == n ? GL_TRUE : GL_FALSE;
|
||||
}
|
||||
|
||||
static GLboolean _glewSearchExtension(const char *name, const GLubyte *start, const GLubyte *end)
|
||||
{
|
||||
const GLubyte *p;
|
||||
GLuint len = _glewStrLen((const GLubyte *)name);
|
||||
p = start;
|
||||
while (p < end) {
|
||||
GLuint n = _glewStrCLen(p, ' ');
|
||||
if (len == n && _glewStrSame((const GLubyte *)name, p, n)) {
|
||||
return GL_TRUE;
|
||||
}
|
||||
p += n + 1;
|
||||
}
|
||||
return GL_FALSE;
|
||||
}
|
||||
#endif /* USE_GLXEW_INIT_WORKAROUND */
|
||||
118
blender-5.2.0/intern/ghost/intern/GHOST_ContextGLX.hh
Normal file
118
blender-5.2.0/intern/ghost/intern/GHOST_ContextGLX.hh
Normal file
@@ -0,0 +1,118 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Context.hh"
|
||||
|
||||
#include <epoxy/glx.h>
|
||||
|
||||
#ifndef GHOST_OPENGL_GLX_CONTEXT_FLAGS
|
||||
/* leave as convenience define for the future */
|
||||
# define GHOST_OPENGL_GLX_CONTEXT_FLAGS 0
|
||||
#endif
|
||||
|
||||
#ifndef GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY
|
||||
# define GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY 0
|
||||
#endif
|
||||
|
||||
class GHOST_ContextGLX : public GHOST_Context {
|
||||
/* XR code needs low level graphics data to send to OpenXR. */
|
||||
friend class GHOST_XrGraphicsBindingOpenGL;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_ContextGLX(const GHOST_ContextParams &context_params,
|
||||
Window window,
|
||||
Display *display,
|
||||
GLXFBConfig fbconfig,
|
||||
int contextProfileMask,
|
||||
int contextMajorVersion,
|
||||
int contextMinorVersion,
|
||||
int contextFlags,
|
||||
int contextResetNotificationStrategy);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_ContextGLX() override;
|
||||
|
||||
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
|
||||
GHOST_TSuccess swapBufferAcquire() override
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps front and back buffers of a window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess swapBufferRelease() override;
|
||||
|
||||
/**
|
||||
* Activates the drawing context of this window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess activateDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Release the drawing context of the calling thread.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess releaseDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Call immediately after new to initialize. If this fails then immediately delete the object.
|
||||
* \return Indication as to whether initialization has succeeded.
|
||||
*/
|
||||
GHOST_TSuccess initializeDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Removes references to native handles from this context and then returns
|
||||
* \return GHOST_kSuccess if it is OK for the parent to release the handles and
|
||||
* GHOST_kFailure if releasing the handles will interfere with sharing
|
||||
*/
|
||||
GHOST_TSuccess releaseNativeHandles() override;
|
||||
|
||||
/**
|
||||
* Sets the swap interval for #swapBuffers.
|
||||
* \param interval: The swap interval to use.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess setSwapInterval(int interval) override;
|
||||
|
||||
/**
|
||||
* Gets the current swap interval for #swapBuffers.
|
||||
* \param interval_out: Variable to store the swap interval if it can be read.
|
||||
* \return Whether the swap interval can be read.
|
||||
*/
|
||||
GHOST_TSuccess getSwapInterval(int &interval_out) override;
|
||||
|
||||
private:
|
||||
Display *display_;
|
||||
GLXFBConfig fbconfig_;
|
||||
Window window_;
|
||||
|
||||
const int context_profile_mask_;
|
||||
const int context_major_version_;
|
||||
const int context_minor_version_;
|
||||
const int context_flags_;
|
||||
const int context_reset_notification_strategy_;
|
||||
|
||||
GLXContext context_;
|
||||
|
||||
/** The first created OpenGL context (for sharing display lists) */
|
||||
static GLXContext s_sharedContext;
|
||||
static int s_sharedCount;
|
||||
};
|
||||
|
||||
/* used to get GLX info */
|
||||
int GHOST_X11_GL_GetAttributes(
|
||||
int *attribs, int attribs_max, bool is_stereo_visual, bool need_alpha, bool for_fb_config);
|
||||
187
blender-5.2.0/intern/ghost/intern/GHOST_ContextMTL.hh
Normal file
187
blender-5.2.0/intern/ghost/intern/GHOST_ContextMTL.hh
Normal file
@@ -0,0 +1,187 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Context.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <Metal/Metal.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
|
||||
@class CAMetalLayer;
|
||||
@class MTLCommandQueue;
|
||||
@class MTLDevice;
|
||||
@class MTLRenderPipelineState;
|
||||
@class MTLTexture;
|
||||
@class NSView;
|
||||
|
||||
class GHOST_ContextMTL : public GHOST_Context {
|
||||
friend class GHOST_XrGraphicsBindingMetal;
|
||||
|
||||
public:
|
||||
/* Defines the number of simultaneous command buffers which can be in flight.
|
||||
* The default limit of `64` is considered to be optimal for Blender. Too many command buffers
|
||||
* will result in workload fragmentation and additional system-level overhead. This limit should
|
||||
* also only be increased if the application is consistently exceeding the limit, and there are
|
||||
* no command buffer leaks.
|
||||
*
|
||||
* If this limit is reached, starting a new command buffer will fail. The Metal back-end will
|
||||
* therefore stall until completion and log a warning when this limit is reached in order to
|
||||
* ensure correct function of the app.
|
||||
*
|
||||
* It is generally preferable to reduce the prevalence of GPU_flush or GPU Context switches
|
||||
* (which will both break command submissions), rather than increasing this limit. */
|
||||
static const int max_command_buffer_count = 64;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_ContextMTL(const GHOST_ContextParams &context_params,
|
||||
NSView *metalView,
|
||||
CAMetalLayer *metalLayer);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_ContextMTL() override;
|
||||
|
||||
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
|
||||
GHOST_TSuccess swapBufferAcquire() override
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps front and back buffers of a window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess swapBufferRelease() override;
|
||||
|
||||
/**
|
||||
* Activates the drawing context of this window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess activateDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Release the drawing context of the calling thread.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess releaseDrawingContext() override;
|
||||
|
||||
unsigned int getDefaultFramebuffer() override;
|
||||
|
||||
/**
|
||||
* Call immediately after new to initialize. If this fails then immediately delete the object.
|
||||
* \return Indication as to whether initialization has succeeded.
|
||||
*/
|
||||
GHOST_TSuccess initializeDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Removes references to native handles from this context and then returns
|
||||
* \return GHOST_kSuccess if it is OK for the parent to release the handles and
|
||||
* GHOST_kFailure if releasing the handles will interfere with sharing
|
||||
*/
|
||||
GHOST_TSuccess releaseNativeHandles() override;
|
||||
|
||||
/**
|
||||
* Sets the swap interval for #swapBuffers.
|
||||
* \param interval: The swap interval to use.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess setSwapInterval(int interval) override;
|
||||
|
||||
/**
|
||||
* Gets the current swap interval for #swapBuffers.
|
||||
* \param interval_out: Variable to store the swap interval if it can be read.
|
||||
* \return Whether the swap interval can be read.
|
||||
*/
|
||||
GHOST_TSuccess getSwapInterval(int &interval_out) override;
|
||||
|
||||
/**
|
||||
* Updates the drawing context of this window.
|
||||
* Needed whenever the window is changed.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess updateDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Returns a texture that Metal code can use as a render target. The current
|
||||
* contents of this texture will be composited on top of the frame-buffer
|
||||
* each time `swapBuffers` is called.
|
||||
*/
|
||||
id<MTLTexture> metalOverlayTexture();
|
||||
|
||||
/**
|
||||
* Return a pointer to the Metal command queue used by this context.
|
||||
*/
|
||||
MTLCommandQueue *metalCommandQueue();
|
||||
|
||||
/**
|
||||
* Return a pointer to the Metal device associated with this context.
|
||||
*/
|
||||
MTLDevice *metalDevice();
|
||||
|
||||
/**
|
||||
* Callback registration.
|
||||
*/
|
||||
void metalRegisterPresentCallback(void (*callback)(
|
||||
MTLRenderPassDescriptor *, id<MTLRenderPipelineState>, id<MTLTexture>, id<CAMetalDrawable>));
|
||||
void metalRegisterXrBlitCallback(void (*callback)(id<MTLTexture>, int, int, int, int));
|
||||
|
||||
private:
|
||||
/** Metal state */
|
||||
NSView *metal_view_;
|
||||
CAMetalLayer *metal_layer_;
|
||||
MTLRenderPipelineState *metal_render_pipeline_;
|
||||
bool owns_metal_device_;
|
||||
|
||||
/** The virtualized default frame-buffer's texture. */
|
||||
/**
|
||||
* Texture that you can render into with Metal. The texture will be
|
||||
* composited on top of `default_framebuffer_metal_texture_` whenever
|
||||
* `swapBuffers` is called.
|
||||
*/
|
||||
static const int METAL_SWAPCHAIN_SIZE = 3;
|
||||
struct MTLSwapchainTexture {
|
||||
id<MTLTexture> texture;
|
||||
unsigned int index;
|
||||
};
|
||||
MTLSwapchainTexture default_framebuffer_metal_texture_[METAL_SWAPCHAIN_SIZE];
|
||||
unsigned int current_swapchain_index = 0;
|
||||
|
||||
/* Present callback.
|
||||
* We use this such that presentation can be controlled from within the Metal
|
||||
* Context. This is required for optimal performance and clean control flow.
|
||||
* Also helps ensure flickering does not occur by present being dependent
|
||||
* on existing submissions. */
|
||||
void (*contextPresentCallback)(MTLRenderPassDescriptor *,
|
||||
id<MTLRenderPipelineState>,
|
||||
id<MTLTexture>,
|
||||
id<CAMetalDrawable>);
|
||||
/* XR texture blitting callback. */
|
||||
void (*xrBlitCallback)(id<MTLTexture>, int, int, int, int);
|
||||
|
||||
int mtl_SwapInterval;
|
||||
|
||||
static int s_sharedCount;
|
||||
|
||||
/* Single device queue for multiple contexts. */
|
||||
static MTLCommandQueue *s_sharedMetalCommandQueue;
|
||||
|
||||
/* Metal functions */
|
||||
void metalInit();
|
||||
void metalFree();
|
||||
void metalInitFramebuffer();
|
||||
void metalUpdateFramebuffer();
|
||||
void metalSwapBuffers();
|
||||
void initClear() {};
|
||||
};
|
||||
437
blender-5.2.0/intern/ghost/intern/GHOST_ContextMTL.mm
Normal file
437
blender-5.2.0/intern/ghost/intern/GHOST_ContextMTL.mm
Normal file
@@ -0,0 +1,437 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* Definition of GHOST_ContextMTL class.
|
||||
*/
|
||||
|
||||
/* Don't generate OpenGL deprecation warning. This is a known thing, and is not something easily
|
||||
* solvable in a short term. */
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
#include "GHOST_ContextMTL.hh"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
static const MTLPixelFormat METAL_FRAMEBUFFERPIXEL_FORMAT_EDR = MTLPixelFormatRGBA16Float;
|
||||
|
||||
static void ghost_fatal_error_dialog(const char *msg)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSString *message = [NSString stringWithFormat:@"Error opening window:\n%s", msg];
|
||||
|
||||
NSAlert *alert = [[NSAlert alloc] init];
|
||||
|
||||
alert.messageText = @"Blender";
|
||||
alert.informativeText = message;
|
||||
alert.alertStyle = NSAlertStyleCritical;
|
||||
|
||||
[alert addButtonWithTitle:@"Quit"];
|
||||
[alert runModal];
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MTLCommandQueue *GHOST_ContextMTL::s_sharedMetalCommandQueue = nil;
|
||||
int GHOST_ContextMTL::s_sharedCount = 0;
|
||||
|
||||
GHOST_ContextMTL::GHOST_ContextMTL(const GHOST_ContextParams &context_params,
|
||||
NSView *metalView,
|
||||
CAMetalLayer *metalLayer)
|
||||
: GHOST_Context(context_params),
|
||||
metal_view_(metalView),
|
||||
metal_layer_(metalLayer),
|
||||
metal_render_pipeline_(nil)
|
||||
{
|
||||
@autoreleasepool {
|
||||
/* Initialize Metal Swap-chain. */
|
||||
current_swapchain_index = 0;
|
||||
for (int i = 0; i < METAL_SWAPCHAIN_SIZE; i++) {
|
||||
default_framebuffer_metal_texture_[i].texture = nil;
|
||||
default_framebuffer_metal_texture_[i].index = i;
|
||||
}
|
||||
|
||||
if (metal_view_) {
|
||||
owns_metal_device_ = false;
|
||||
metalInit();
|
||||
}
|
||||
else {
|
||||
/* Prepare offscreen GHOST Context Metal device. */
|
||||
id<MTLDevice> metalDevice = MTLCreateSystemDefaultDevice();
|
||||
|
||||
if (context_params_.is_debug) {
|
||||
printf("Selected Metal Device: %s\n", [metalDevice.name UTF8String]);
|
||||
}
|
||||
|
||||
owns_metal_device_ = true;
|
||||
if (metalDevice) {
|
||||
metal_layer_ = [[CAMetalLayer alloc] init];
|
||||
metal_layer_.edgeAntialiasingMask = 0;
|
||||
metal_layer_.masksToBounds = NO;
|
||||
metal_layer_.opaque = YES;
|
||||
metal_layer_.framebufferOnly = YES;
|
||||
metal_layer_.presentsWithTransaction = NO;
|
||||
[metal_layer_ removeAllAnimations];
|
||||
metal_layer_.device = metalDevice;
|
||||
metal_layer_.allowsNextDrawableTimeout = NO;
|
||||
|
||||
{
|
||||
const GHOST_TVSyncModes vsync = getVSync();
|
||||
if (vsync != GHOST_kVSyncModeUnset) {
|
||||
metal_layer_.displaySyncEnabled = (vsync == GHOST_kVSyncModeOff) ? NO : YES;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable EDR support. This is done by:
|
||||
* 1. Using a floating point render target, so that values outside 0..1 can be used
|
||||
* 2. Informing the OS that we are EDR aware, and intend to use values outside 0..1
|
||||
* 3. Setting the extended sRGB color space so that the OS knows how to interpret the
|
||||
* values.
|
||||
*/
|
||||
metal_layer_.wantsExtendedDynamicRangeContent = YES;
|
||||
metal_layer_.pixelFormat = METAL_FRAMEBUFFERPIXEL_FORMAT_EDR;
|
||||
const CFStringRef name = kCGColorSpaceExtendedSRGB;
|
||||
CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(name);
|
||||
metal_layer_.colorspace = colorspace;
|
||||
CGColorSpaceRelease(colorspace);
|
||||
|
||||
metalInit();
|
||||
}
|
||||
else {
|
||||
ghost_fatal_error_dialog(
|
||||
"[ERROR] Failed to create Metal device for offscreen GHOST Context.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize swap-interval. */
|
||||
mtl_SwapInterval = 60;
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_ContextMTL::~GHOST_ContextMTL()
|
||||
{
|
||||
/* Multiple threads can release their own context at the same time. */
|
||||
static std::mutex mutex;
|
||||
std::scoped_lock lock(mutex);
|
||||
|
||||
metalFree();
|
||||
|
||||
if (owns_metal_device_) {
|
||||
if (metal_layer_) {
|
||||
[metal_layer_ release];
|
||||
metal_layer_ = nil;
|
||||
}
|
||||
}
|
||||
assert(s_sharedCount);
|
||||
|
||||
s_sharedCount--;
|
||||
[s_sharedMetalCommandQueue release];
|
||||
if (s_sharedCount == 0) {
|
||||
s_sharedMetalCommandQueue = nil;
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextMTL::swapBufferRelease()
|
||||
{
|
||||
if (metal_view_) {
|
||||
metalSwapBuffers();
|
||||
}
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextMTL::setSwapInterval(int interval)
|
||||
{
|
||||
mtl_SwapInterval = interval;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextMTL::getSwapInterval(int &interval_out)
|
||||
{
|
||||
interval_out = mtl_SwapInterval;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextMTL::activateDrawingContext()
|
||||
{
|
||||
active_context_ = this;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextMTL::releaseDrawingContext()
|
||||
{
|
||||
active_context_ = nullptr;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
unsigned int GHOST_ContextMTL::getDefaultFramebuffer()
|
||||
{
|
||||
/* NOTE(Metal): This is not valid. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextMTL::updateDrawingContext()
|
||||
{
|
||||
if (metal_view_) {
|
||||
metalUpdateFramebuffer();
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
id<MTLTexture> GHOST_ContextMTL::metalOverlayTexture()
|
||||
{
|
||||
/* Increment Swap-chain - Only needed if context is requesting a new texture */
|
||||
current_swapchain_index = (current_swapchain_index + 1) % METAL_SWAPCHAIN_SIZE;
|
||||
|
||||
/* Ensure backing texture is ready for current swapchain index */
|
||||
updateDrawingContext();
|
||||
|
||||
/* Return texture. */
|
||||
return default_framebuffer_metal_texture_[current_swapchain_index].texture;
|
||||
}
|
||||
|
||||
MTLCommandQueue *GHOST_ContextMTL::metalCommandQueue()
|
||||
{
|
||||
return s_sharedMetalCommandQueue;
|
||||
}
|
||||
MTLDevice *GHOST_ContextMTL::metalDevice()
|
||||
{
|
||||
id<MTLDevice> device = metal_layer_.device;
|
||||
return (MTLDevice *)device;
|
||||
}
|
||||
|
||||
void GHOST_ContextMTL::metalRegisterPresentCallback(void (*callback)(
|
||||
MTLRenderPassDescriptor *, id<MTLRenderPipelineState>, id<MTLTexture>, id<CAMetalDrawable>))
|
||||
{
|
||||
this->contextPresentCallback = callback;
|
||||
}
|
||||
|
||||
void GHOST_ContextMTL::metalRegisterXrBlitCallback(
|
||||
void (*callback)(id<MTLTexture>, int, int, int, int))
|
||||
{
|
||||
this->xrBlitCallback = callback;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextMTL::initializeDrawingContext()
|
||||
{
|
||||
@autoreleasepool {
|
||||
if (metal_view_) {
|
||||
metalInitFramebuffer();
|
||||
}
|
||||
}
|
||||
active_context_ = this;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextMTL::releaseNativeHandles()
|
||||
{
|
||||
metal_view_ = nil;
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
void GHOST_ContextMTL::metalInit()
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = metal_layer_.device;
|
||||
|
||||
/* Create a command queue for blit/present operation.
|
||||
* NOTE: All context should share a single command queue
|
||||
* to ensure correct ordering of work submitted from multiple contexts. */
|
||||
if (s_sharedMetalCommandQueue == nil) {
|
||||
s_sharedMetalCommandQueue = (MTLCommandQueue *)[device
|
||||
newCommandQueueWithMaxCommandBufferCount:GHOST_ContextMTL::max_command_buffer_count];
|
||||
}
|
||||
/* Ensure active GHOSTContext retains a reference to the shared context. */
|
||||
[s_sharedMetalCommandQueue retain];
|
||||
s_sharedCount++;
|
||||
|
||||
/* Create shaders for blit operation. */
|
||||
NSString *source = @R"msl(
|
||||
using namespace metal;
|
||||
|
||||
struct Vertex {
|
||||
float4 position [[position]];
|
||||
float2 texCoord [[attribute(0)]];
|
||||
};
|
||||
|
||||
vertex Vertex vertex_shader(uint v_id [[vertex_id]]) {
|
||||
Vertex vtx;
|
||||
|
||||
vtx.position.x = float(v_id & 1) * 4.0 - 1.0;
|
||||
vtx.position.y = float(v_id >> 1) * 4.0 - 1.0;
|
||||
vtx.position.z = 0.0;
|
||||
vtx.position.w = 1.0;
|
||||
|
||||
vtx.texCoord = vtx.position.xy * 0.5 + 0.5;
|
||||
|
||||
return vtx;
|
||||
}
|
||||
|
||||
constexpr sampler s {};
|
||||
|
||||
fragment float4 fragment_shader(Vertex v [[stage_in]],
|
||||
texture2d<float> t [[texture(0)]]) {
|
||||
|
||||
/* Final blit should ensure alpha is 1.0. This resolves
|
||||
* rendering artifacts for blitting of final back-buffer.
|
||||
* Also clamp the RGB values to avoid artifacts. */
|
||||
float4 out_tex = t.sample(s, v.texCoord);
|
||||
out_tex.rgb = min(out_tex.rgb, 16384.0);
|
||||
out_tex.a = 1.0;
|
||||
return out_tex;
|
||||
}
|
||||
)msl";
|
||||
|
||||
MTLCompileOptions *options = [[[MTLCompileOptions alloc] init] autorelease];
|
||||
options.languageVersion = MTLLanguageVersion1_1;
|
||||
|
||||
NSError *error = nil;
|
||||
id<MTLLibrary> library = [device newLibraryWithSource:source options:options error:&error];
|
||||
if (error) {
|
||||
ghost_fatal_error_dialog(
|
||||
"GHOST_ContextMTL::metalInit: newLibraryWithSource:options:error: failed!");
|
||||
}
|
||||
|
||||
/* Create a render pipeline for blit operation. */
|
||||
MTLRenderPipelineDescriptor *desc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease];
|
||||
|
||||
desc.fragmentFunction = [library newFunctionWithName:@"fragment_shader"];
|
||||
desc.vertexFunction = [library newFunctionWithName:@"vertex_shader"];
|
||||
[desc.colorAttachments objectAtIndexedSubscript:0].pixelFormat =
|
||||
METAL_FRAMEBUFFERPIXEL_FORMAT_EDR;
|
||||
|
||||
/* Ensure library is released. */
|
||||
[library autorelease];
|
||||
|
||||
metal_render_pipeline_ = (MTLRenderPipelineState *)[device
|
||||
newRenderPipelineStateWithDescriptor:desc
|
||||
error:&error];
|
||||
if (error) {
|
||||
ghost_fatal_error_dialog(
|
||||
"GHOST_ContextMTL::metalInit: newRenderPipelineStateWithDescriptor:error: failed!");
|
||||
}
|
||||
|
||||
[desc.fragmentFunction release];
|
||||
[desc.vertexFunction release];
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_ContextMTL::metalFree()
|
||||
{
|
||||
if (metal_render_pipeline_) {
|
||||
[metal_render_pipeline_ release];
|
||||
metal_render_pipeline_ = nil;
|
||||
}
|
||||
|
||||
for (int i = 0; i < METAL_SWAPCHAIN_SIZE; i++) {
|
||||
if (default_framebuffer_metal_texture_[i].texture) {
|
||||
[default_framebuffer_metal_texture_[i].texture release];
|
||||
default_framebuffer_metal_texture_[i].texture = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_ContextMTL::metalInitFramebuffer()
|
||||
{
|
||||
updateDrawingContext();
|
||||
}
|
||||
|
||||
void GHOST_ContextMTL::metalUpdateFramebuffer()
|
||||
{
|
||||
@autoreleasepool {
|
||||
const NSSize drawableSize = metal_layer_.drawableSize;
|
||||
const size_t width = size_t(drawableSize.width);
|
||||
const size_t height = size_t(drawableSize.height);
|
||||
|
||||
if (default_framebuffer_metal_texture_[current_swapchain_index].texture &&
|
||||
default_framebuffer_metal_texture_[current_swapchain_index].texture.width == width &&
|
||||
default_framebuffer_metal_texture_[current_swapchain_index].texture.height == height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Free old texture */
|
||||
[default_framebuffer_metal_texture_[current_swapchain_index].texture release];
|
||||
|
||||
id<MTLDevice> device = metal_layer_.device;
|
||||
MTLTextureDescriptor *overlayDesc = [MTLTextureDescriptor
|
||||
texture2DDescriptorWithPixelFormat:METAL_FRAMEBUFFERPIXEL_FORMAT_EDR
|
||||
width:width
|
||||
height:height
|
||||
mipmapped:NO];
|
||||
overlayDesc.storageMode = MTLStorageModePrivate;
|
||||
overlayDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
|
||||
|
||||
id<MTLTexture> overlayTex = [device newTextureWithDescriptor:overlayDesc];
|
||||
if (!overlayTex) {
|
||||
ghost_fatal_error_dialog(
|
||||
"GHOST_ContextMTL::metalUpdateFramebuffer: failed to create Metal overlay texture!");
|
||||
}
|
||||
else {
|
||||
overlayTex.label = [NSString
|
||||
stringWithFormat:@"Metal Overlay for GHOST Context %p", this]; //@"";
|
||||
}
|
||||
|
||||
default_framebuffer_metal_texture_[current_swapchain_index].texture = overlayTex;
|
||||
|
||||
/* Clear texture on create */
|
||||
id<MTLCommandBuffer> cmdBuffer = [s_sharedMetalCommandQueue commandBuffer];
|
||||
MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
{
|
||||
auto attachment = [passDescriptor.colorAttachments objectAtIndexedSubscript:0];
|
||||
attachment.texture = default_framebuffer_metal_texture_[current_swapchain_index].texture;
|
||||
attachment.loadAction = MTLLoadActionClear;
|
||||
attachment.clearColor = MTLClearColorMake(0.294, 0.294, 0.294, 1.000);
|
||||
attachment.storeAction = MTLStoreActionStore;
|
||||
}
|
||||
{
|
||||
id<MTLRenderCommandEncoder> enc = [cmdBuffer
|
||||
renderCommandEncoderWithDescriptor:passDescriptor];
|
||||
[enc endEncoding];
|
||||
}
|
||||
[cmdBuffer commit];
|
||||
|
||||
metal_layer_.drawableSize = CGSizeMake(CGFloat(width), CGFloat(height));
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_ContextMTL::metalSwapBuffers()
|
||||
{
|
||||
@autoreleasepool {
|
||||
updateDrawingContext();
|
||||
|
||||
id<CAMetalDrawable> drawable = [metal_layer_ nextDrawable];
|
||||
if (!drawable) {
|
||||
return;
|
||||
}
|
||||
|
||||
MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
{
|
||||
auto attachment = [passDescriptor.colorAttachments objectAtIndexedSubscript:0];
|
||||
attachment.texture = drawable.texture;
|
||||
attachment.loadAction = MTLLoadActionClear;
|
||||
attachment.clearColor = MTLClearColorMake(1.0, 0.294, 0.294, 1.000);
|
||||
attachment.storeAction = MTLStoreActionStore;
|
||||
}
|
||||
|
||||
assert(contextPresentCallback);
|
||||
assert(default_framebuffer_metal_texture_[current_swapchain_index].texture != nil);
|
||||
(*contextPresentCallback)(passDescriptor,
|
||||
(id<MTLRenderPipelineState>)metal_render_pipeline_,
|
||||
default_framebuffer_metal_texture_[current_swapchain_index].texture,
|
||||
drawable);
|
||||
}
|
||||
}
|
||||
57
blender-5.2.0/intern/ghost/intern/GHOST_ContextNone.cc
Normal file
57
blender-5.2.0/intern/ghost/intern/GHOST_ContextNone.cc
Normal file
@@ -0,0 +1,57 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* Definition of GHOST_ContextNone class.
|
||||
*/
|
||||
|
||||
#include "GHOST_ContextNone.hh"
|
||||
|
||||
GHOST_TSuccess GHOST_ContextNone::swapBufferRelease()
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextNone::activateDrawingContext()
|
||||
{
|
||||
active_context_ = this;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextNone::releaseDrawingContext()
|
||||
{
|
||||
active_context_ = nullptr;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextNone::updateDrawingContext()
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextNone::initializeDrawingContext()
|
||||
{
|
||||
active_context_ = this;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextNone::releaseNativeHandles()
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextNone::setSwapInterval(int interval)
|
||||
{
|
||||
swap_interval_ = interval;
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextNone::getSwapInterval(int &interval_out)
|
||||
{
|
||||
interval_out = swap_interval_;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
76
blender-5.2.0/intern/ghost/intern/GHOST_ContextNone.hh
Normal file
76
blender-5.2.0/intern/ghost/intern/GHOST_ContextNone.hh
Normal file
@@ -0,0 +1,76 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* Declaration of GHOST_Context class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Context.hh"
|
||||
|
||||
class GHOST_ContextNone : public GHOST_Context {
|
||||
public:
|
||||
GHOST_ContextNone(const GHOST_ContextParams &context_params) : GHOST_Context(context_params) {}
|
||||
|
||||
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
|
||||
GHOST_TSuccess swapBufferAcquire() override
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy function
|
||||
* \return Always succeeds
|
||||
*/
|
||||
GHOST_TSuccess swapBufferRelease() override;
|
||||
|
||||
/**
|
||||
* Dummy function
|
||||
* \return Always succeeds.
|
||||
*/
|
||||
GHOST_TSuccess activateDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Dummy function
|
||||
* \return Always succeeds.
|
||||
*/
|
||||
GHOST_TSuccess releaseDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Dummy function
|
||||
* \return Always succeeds.
|
||||
*/
|
||||
GHOST_TSuccess updateDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Dummy function
|
||||
* \return Always succeeds.
|
||||
*/
|
||||
GHOST_TSuccess initializeDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Dummy function
|
||||
* \return Always succeeds.
|
||||
*/
|
||||
GHOST_TSuccess releaseNativeHandles() override;
|
||||
|
||||
/**
|
||||
* Dummy function
|
||||
* \return Always succeeds.
|
||||
*/
|
||||
GHOST_TSuccess setSwapInterval(int interval) override;
|
||||
|
||||
/**
|
||||
* Dummy function
|
||||
* \param interval_out: Gets whatever was set by #setSwapInterval.
|
||||
* \return Always succeeds.
|
||||
*/
|
||||
GHOST_TSuccess getSwapInterval(int &interval_out) override;
|
||||
|
||||
private:
|
||||
int swap_interval_ = 1;
|
||||
};
|
||||
176
blender-5.2.0/intern/ghost/intern/GHOST_ContextSDL.cc
Normal file
176
blender-5.2.0/intern/ghost/intern/GHOST_ContextSDL.cc
Normal file
@@ -0,0 +1,176 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* Definition of GHOST_ContextSDL class.
|
||||
*/
|
||||
|
||||
#include "GHOST_ContextSDL.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
SDL_GLContext GHOST_ContextSDL::s_sharedContext = nullptr;
|
||||
int GHOST_ContextSDL::s_sharedCount = 0;
|
||||
|
||||
GHOST_ContextSDL::GHOST_ContextSDL(const GHOST_ContextParams &context_params,
|
||||
SDL_Window *window,
|
||||
int contextProfileMask,
|
||||
int contextMajorVersion,
|
||||
int contextMinorVersion,
|
||||
int contextFlags,
|
||||
int contextResetNotificationStrategy)
|
||||
: GHOST_Context(context_params),
|
||||
window_(window),
|
||||
hidden_window_(nullptr),
|
||||
context_profile_mask_(contextProfileMask),
|
||||
context_major_version_(contextMajorVersion),
|
||||
context_minor_version_(contextMinorVersion),
|
||||
context_flags_(contextFlags),
|
||||
context_reset_notification_strategy_(contextResetNotificationStrategy),
|
||||
context_(nullptr)
|
||||
{
|
||||
// assert(window_ != nullptr);
|
||||
}
|
||||
|
||||
GHOST_ContextSDL::~GHOST_ContextSDL()
|
||||
{
|
||||
if (context_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (window_ != nullptr && context_ == SDL_GL_GetCurrentContext()) {
|
||||
SDL_GL_MakeCurrent(window_, nullptr);
|
||||
}
|
||||
if (context_ != s_sharedContext || s_sharedCount == 1) {
|
||||
assert(s_sharedCount > 0);
|
||||
|
||||
s_sharedCount--;
|
||||
|
||||
if (s_sharedCount == 0) {
|
||||
s_sharedContext = nullptr;
|
||||
}
|
||||
SDL_GL_DestroyContext(context_);
|
||||
}
|
||||
|
||||
if (hidden_window_ != nullptr) {
|
||||
SDL_DestroyWindow(hidden_window_);
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextSDL::swapBufferRelease()
|
||||
{
|
||||
SDL_GL_SwapWindow(window_);
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextSDL::activateDrawingContext()
|
||||
{
|
||||
if (context_ == nullptr) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
active_context_ = this;
|
||||
return SDL_GL_MakeCurrent(window_, context_) ? GHOST_kSuccess : GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextSDL::releaseDrawingContext()
|
||||
{
|
||||
if (context_ == nullptr) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
active_context_ = nullptr;
|
||||
/* Untested, may not work. */
|
||||
return SDL_GL_MakeCurrent(nullptr, nullptr) ? GHOST_kSuccess : GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextSDL::initializeDrawingContext()
|
||||
{
|
||||
const bool needAlpha = false;
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, context_profile_mask_);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, context_major_version_);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, context_minor_version_);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, context_flags_);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
|
||||
if (needAlpha) {
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
||||
}
|
||||
|
||||
if (context_params_.is_stereo_visual) {
|
||||
SDL_GL_SetAttribute(SDL_GL_STEREO, 1);
|
||||
}
|
||||
|
||||
if (window_ == nullptr) {
|
||||
hidden_window_ = SDL_CreateWindow("Offscreen Context Windows",
|
||||
1,
|
||||
1,
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS |
|
||||
SDL_WINDOW_HIDDEN);
|
||||
|
||||
window_ = hidden_window_;
|
||||
}
|
||||
|
||||
context_ = SDL_GL_CreateContext(window_);
|
||||
|
||||
GHOST_TSuccess success;
|
||||
|
||||
if (context_ != nullptr) {
|
||||
if (!s_sharedContext) {
|
||||
s_sharedContext = context_;
|
||||
}
|
||||
s_sharedCount++;
|
||||
|
||||
success = SDL_GL_MakeCurrent(window_, context_) ? GHOST_kSuccess : GHOST_kFailure;
|
||||
|
||||
{
|
||||
const GHOST_TVSyncModes vsync = getVSync();
|
||||
if (vsync != GHOST_kVSyncModeUnset) {
|
||||
setSwapInterval(int(vsync));
|
||||
}
|
||||
}
|
||||
|
||||
initClearGL();
|
||||
SDL_GL_SwapWindow(window_);
|
||||
|
||||
active_context_ = this;
|
||||
success = GHOST_kSuccess;
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextSDL::releaseNativeHandles()
|
||||
{
|
||||
window_ = nullptr;
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextSDL::setSwapInterval(int interval)
|
||||
{
|
||||
if (!SDL_GL_SetSwapInterval(interval)) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextSDL::getSwapInterval(int &interval_out)
|
||||
{
|
||||
if (!SDL_GL_GetSwapInterval(&interval_out)) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
111
blender-5.2.0/intern/ghost/intern/GHOST_ContextSDL.hh
Normal file
111
blender-5.2.0/intern/ghost/intern/GHOST_ContextSDL.hh
Normal file
@@ -0,0 +1,111 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Context.hh"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#ifndef GHOST_OPENGL_SDL_CONTEXT_FLAGS
|
||||
# ifdef WITH_GPU_DEBUG
|
||||
# define GHOST_OPENGL_SDL_CONTEXT_FLAGS SDL_GL_CONTEXT_DEBUG_FLAG
|
||||
# else
|
||||
# define GHOST_OPENGL_SDL_CONTEXT_FLAGS 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY
|
||||
# define GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY 0
|
||||
#endif
|
||||
|
||||
class GHOST_ContextSDL : public GHOST_Context {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_ContextSDL(const GHOST_ContextParams &context_params,
|
||||
SDL_Window *window,
|
||||
int contextProfileMask,
|
||||
int contextMajorVersion,
|
||||
int contextMinorVersion,
|
||||
int contextFlags,
|
||||
int contextResetNotificationStrategy);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_ContextSDL() override;
|
||||
|
||||
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
|
||||
GHOST_TSuccess swapBufferAcquire() override
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps front and back buffers of a window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess swapBufferRelease() override;
|
||||
|
||||
/**
|
||||
* Activates the drawing context of this window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess activateDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Release the drawing context of the calling thread.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess releaseDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Call immediately after new to initialize. If this fails then immediately delete the object.
|
||||
* \return Indication as to whether initialization has succeeded.
|
||||
*/
|
||||
GHOST_TSuccess initializeDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Removes references to native handles from this context and then returns
|
||||
* \return GHOST_kSuccess if it is OK for the parent to release the handles and
|
||||
* GHOST_kFailure if releasing the handles will interfere with sharing
|
||||
*/
|
||||
GHOST_TSuccess releaseNativeHandles() override;
|
||||
|
||||
/**
|
||||
* Sets the swap interval for #swapBuffers.
|
||||
* \param interval: The swap interval to use.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess setSwapInterval(int interval) override;
|
||||
|
||||
/**
|
||||
* Gets the current swap interval for #swapBuffers.
|
||||
* \param interval_out: Variable to store the swap interval if it can be read.
|
||||
* \return Whether the swap interval can be read.
|
||||
*/
|
||||
GHOST_TSuccess getSwapInterval(int &interval_out) override;
|
||||
|
||||
private:
|
||||
SDL_Window *window_;
|
||||
SDL_Window *hidden_window_;
|
||||
|
||||
const int context_profile_mask_;
|
||||
const int context_major_version_;
|
||||
const int context_minor_version_;
|
||||
const int context_flags_;
|
||||
const int context_reset_notification_strategy_;
|
||||
|
||||
SDL_GLContext context_; /* sdl_glcontext_ */
|
||||
|
||||
/** The first created OpenGL context (for sharing display lists) */
|
||||
static SDL_GLContext s_sharedContext;
|
||||
static int s_sharedCount;
|
||||
};
|
||||
1914
blender-5.2.0/intern/ghost/intern/GHOST_ContextVK.cc
Normal file
1914
blender-5.2.0/intern/ghost/intern/GHOST_ContextVK.cc
Normal file
File diff suppressed because it is too large
Load Diff
294
blender-5.2.0/intern/ghost/intern/GHOST_ContextVK.hh
Normal file
294
blender-5.2.0/intern/ghost/intern/GHOST_ContextVK.hh
Normal file
@@ -0,0 +1,294 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef WITH_VULKAN_BACKEND
|
||||
# error "ContextVK requires WITH_VULKAN_BACKEND"
|
||||
#endif
|
||||
|
||||
#include "GHOST_Context.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include "GHOST_SystemWin32.hh"
|
||||
#elif defined(__APPLE__)
|
||||
# include "GHOST_SystemCocoa.hh"
|
||||
#else
|
||||
# ifdef WITH_GHOST_X11
|
||||
# include "GHOST_SystemX11.hh"
|
||||
# else
|
||||
# define Display void
|
||||
# define Window void *
|
||||
# endif
|
||||
# ifdef WITH_GHOST_WAYLAND
|
||||
# include "GHOST_SystemWayland.hh"
|
||||
# else
|
||||
# define wl_surface void
|
||||
# define wl_display void
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace blender {
|
||||
class StringRefNull;
|
||||
}
|
||||
|
||||
#ifndef GHOST_OPENGL_VK_CONTEXT_FLAGS
|
||||
/* leave as convenience define for the future */
|
||||
# define GHOST_OPENGL_VK_CONTEXT_FLAGS 0
|
||||
#endif
|
||||
|
||||
#ifndef GHOST_OPENGL_VK_RESET_NOTIFICATION_STRATEGY
|
||||
# define GHOST_OPENGL_VK_RESET_NOTIFICATION_STRATEGY 0
|
||||
#endif
|
||||
|
||||
enum GHOST_TVulkanPlatformType {
|
||||
GHOST_kVulkanPlatformHeadless = 0,
|
||||
#ifdef WITH_GHOST_X11
|
||||
GHOST_kVulkanPlatformX11 = 1,
|
||||
#endif
|
||||
#ifdef WITH_GHOST_WAYLAND
|
||||
GHOST_kVulkanPlatformWayland = 2,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct GHOST_ContextVK_WindowInfo {
|
||||
int size[2];
|
||||
};
|
||||
|
||||
struct GHOST_FrameDiscard {
|
||||
std::vector<VkSwapchainKHR> swapchains;
|
||||
std::vector<VkSemaphore> semaphores;
|
||||
|
||||
void destroy(VkDevice vk_device);
|
||||
};
|
||||
|
||||
struct GHOST_SwapchainImage {
|
||||
/** Swap-chain image (owned by the swapchain). */
|
||||
VkImage vk_image = VK_NULL_HANDLE;
|
||||
|
||||
/**
|
||||
* Semaphore for presenting; being signaled when the swap-chain image is ready to be presented.
|
||||
*/
|
||||
VkSemaphore present_semaphore = VK_NULL_HANDLE;
|
||||
|
||||
void destroy(VkDevice vk_device);
|
||||
};
|
||||
|
||||
struct GHOST_Frame {
|
||||
/**
|
||||
* Fence signaled when "previous" use of the frame has finished rendering. When signaled the
|
||||
* frame can acquire a new image and the semaphores can be reused.
|
||||
*/
|
||||
VkFence submission_fence = VK_NULL_HANDLE;
|
||||
/** Semaphore for acquiring; being signaled when the swap-chain image is ready to be updated. */
|
||||
VkSemaphore acquire_semaphore = VK_NULL_HANDLE;
|
||||
|
||||
GHOST_FrameDiscard discard_pile;
|
||||
|
||||
void destroy(VkDevice vk_device);
|
||||
};
|
||||
|
||||
class GHOST_ContextVK : public GHOST_Context {
|
||||
friend class GHOST_XrGraphicsBindingVulkan;
|
||||
friend class GHOST_XrGraphicsBindingVulkanD3D;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_ContextVK(const GHOST_ContextParams &context_params,
|
||||
#ifdef _WIN32
|
||||
HWND hwnd,
|
||||
#elif defined(__APPLE__)
|
||||
/* FIXME CAMetalLayer but have issue with linking. */
|
||||
void *metal_layer,
|
||||
#else
|
||||
GHOST_TVulkanPlatformType platform,
|
||||
/* X11 */
|
||||
Window window,
|
||||
Display *display,
|
||||
/* Wayland */
|
||||
wl_surface *wayland_surface,
|
||||
wl_display *wayland_display,
|
||||
const GHOST_ContextVK_WindowInfo *wayland_window_info,
|
||||
#endif
|
||||
int contextMajorVersion,
|
||||
int contextMinorVersion,
|
||||
const GHOST_GPUDevice &preferred_device,
|
||||
const GHOST_WindowHDRInfo *hdr_info_ = nullptr);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_ContextVK() override;
|
||||
|
||||
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
|
||||
GHOST_TSuccess swapBufferAcquire() override;
|
||||
/**
|
||||
* Swaps front and back buffers of a window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess swapBufferRelease() override;
|
||||
|
||||
/**
|
||||
* Activates the drawing context of this window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess activateDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Release the drawing context of the calling thread.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess releaseDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Call immediately after new to initialize. If this fails then immediately delete the object.
|
||||
* \return Indication as to whether initialization has succeeded.
|
||||
*/
|
||||
GHOST_TSuccess initializeDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Removes references to native handles from this context and then returns
|
||||
* \return GHOST_kSuccess if it is OK for the parent to release the handles and
|
||||
* GHOST_kFailure if releasing the handles will interfere with sharing
|
||||
*/
|
||||
GHOST_TSuccess releaseNativeHandles() override;
|
||||
|
||||
/**
|
||||
* Gets the Vulkan context related resource handles.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess getVulkanHandles(GHOST_VulkanHandles &r_handles) override;
|
||||
|
||||
GHOST_TSuccess getVulkanSwapChainFormat(GHOST_VulkanSwapChainData *r_swap_chain_data) override;
|
||||
|
||||
GHOST_TSuccess setVulkanSwapBuffersCallbacks(
|
||||
std::function<void(const GHOST_VulkanSwapChainData *, bool)> swap_buffer_draw_callback,
|
||||
std::function<void(void)> swap_buffer_acquired_callback,
|
||||
std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback,
|
||||
std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback)
|
||||
override;
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND
|
||||
/**
|
||||
* \brief Check if the active driver supports wayland color management.
|
||||
*
|
||||
* NVIDIA driver before 595 don't support wayland color management protocol as expected resulting
|
||||
* in to bright output.
|
||||
*/
|
||||
static GHOST_TSuccess supportsWaylandColorManagement();
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Sets the swap interval for `swapBuffers`.
|
||||
* \param interval: The swap interval to use.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess setSwapInterval(int /*interval*/) override
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current swap interval for swapBuffers.
|
||||
* \param interval_out: Variable to store the swap interval if it can be read.
|
||||
* \return Whether the swap interval can be read.
|
||||
*/
|
||||
GHOST_TSuccess getSwapInterval(int & /*interval_out*/) override
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns if the context is rendered upside down compared to OpenGL.
|
||||
*
|
||||
* Vulkan is always rendered upside down.
|
||||
*/
|
||||
bool isUpsideDown() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Is the given extension name enabled on instance level?
|
||||
*
|
||||
* \returns false, when extension isn't enabled on instance level or when no instance exists.
|
||||
* Will return true when instance exists and extension name has been enabled on the instance.
|
||||
*/
|
||||
static bool is_instance_extension_enabled(blender::StringRefNull extension_name);
|
||||
|
||||
/**
|
||||
* \brief Is the given extension name enabled on device level?
|
||||
*
|
||||
* \returns false, when extension isn't enabled on device level or when no instance or device
|
||||
* exists. Will return true when instance exists and extension name has been enabled on the
|
||||
* instance.
|
||||
*/
|
||||
static bool is_device_extension_enabled(blender::StringRefNull extension_name);
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
HWND hwnd_;
|
||||
#elif defined(__APPLE__)
|
||||
/* Is CAMetalLayer* */
|
||||
void *metal_layer_;
|
||||
#else /* Linux */
|
||||
GHOST_TVulkanPlatformType platform_;
|
||||
/* X11 */
|
||||
Display *display_;
|
||||
Window window_;
|
||||
/* Wayland */
|
||||
wl_surface *wayland_surface_;
|
||||
wl_display *wayland_display_;
|
||||
const GHOST_ContextVK_WindowInfo *wayland_window_info_;
|
||||
#endif
|
||||
|
||||
const int context_major_version_;
|
||||
const int context_minor_version_;
|
||||
const GHOST_GPUDevice preferred_device_;
|
||||
|
||||
/* Optional HDR info updated by window. */
|
||||
const GHOST_WindowHDRInfo *hdr_info_;
|
||||
|
||||
/* For display only. */
|
||||
VkSurfaceKHR surface_;
|
||||
VkSwapchainKHR swapchain_;
|
||||
std::vector<GHOST_SwapchainImage> swapchain_images_;
|
||||
std::vector<GHOST_Frame> frame_data_;
|
||||
uint64_t render_frame_;
|
||||
uint64_t image_count_;
|
||||
|
||||
VkExtent2D render_extent_;
|
||||
VkExtent2D render_extent_min_;
|
||||
VkSurfaceFormatKHR surface_format_;
|
||||
bool use_hdr_swapchain_;
|
||||
|
||||
std::optional<uint32_t> acquired_swapchain_image_index_;
|
||||
|
||||
std::function<void(const GHOST_VulkanSwapChainData *, bool)> swap_buffer_draw_callback_;
|
||||
std::function<void(void)> swap_buffer_acquired_callback_;
|
||||
std::function<void(GHOST_VulkanOpenXRData *)> openxr_acquire_framebuffer_image_callback_;
|
||||
std::function<void(GHOST_VulkanOpenXRData *)> openxr_release_framebuffer_image_callback_;
|
||||
|
||||
std::vector<VkFence> fence_pile_;
|
||||
std::map<VkSwapchainKHR, std::vector<VkFence>> present_fences_;
|
||||
|
||||
const char *getPlatformSpecificSurfaceExtension() const;
|
||||
GHOST_TSuccess recreateSwapchain(bool use_hdr_swapchain);
|
||||
GHOST_TSuccess initializeFrameData();
|
||||
GHOST_TSuccess destroySwapchain();
|
||||
|
||||
VkFence getFence();
|
||||
void setPresentFence(VkSwapchainKHR swapchain, VkFence fence);
|
||||
void destroySwapchainPresentFences(VkSwapchainKHR swapchain);
|
||||
};
|
||||
737
blender-5.2.0/intern/ghost/intern/GHOST_ContextWGL.cc
Normal file
737
blender-5.2.0/intern/ghost/intern/GHOST_ContextWGL.cc
Normal file
@@ -0,0 +1,737 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*
|
||||
* Definition of GHOST_ContextWGL class.
|
||||
*/
|
||||
|
||||
#include "GHOST_ContextWGL.hh"
|
||||
|
||||
#include <tchar.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
HGLRC GHOST_ContextWGL::s_sharedHGLRC = nullptr;
|
||||
int GHOST_ContextWGL::s_sharedCount = 0;
|
||||
std::list<GHOST_ContextWGL::OffscreenWindowHandle>
|
||||
GHOST_ContextWGL::s_sharedOffscreenWindowHandles;
|
||||
|
||||
/* Some third-generation Intel video-cards are constantly bring problems */
|
||||
static bool is_crappy_intel_card()
|
||||
{
|
||||
return strstr((const char *)glGetString(GL_VENDOR), "Intel") != nullptr;
|
||||
}
|
||||
|
||||
GHOST_ContextWGL::GHOST_ContextWGL(const GHOST_ContextParams &context_params,
|
||||
bool alphaBackground,
|
||||
HWND hWnd,
|
||||
HDC hDC,
|
||||
int contextProfileMask,
|
||||
int contextMajorVersion,
|
||||
int contextMinorVersion,
|
||||
int contextFlags,
|
||||
int contextResetNotificationStrategy)
|
||||
: GHOST_Context(context_params),
|
||||
h_wnd_(hWnd),
|
||||
h_DC_(hDC),
|
||||
context_profile_mask_(contextProfileMask),
|
||||
context_major_version_(contextMajorVersion),
|
||||
context_minor_version_(contextMinorVersion),
|
||||
context_flags_(contextFlags),
|
||||
alpha_background_(alphaBackground),
|
||||
context_reset_notification_strategy_(contextResetNotificationStrategy),
|
||||
h_GLRC_(nullptr),
|
||||
#ifndef NDEBUG
|
||||
dummy_vendor_(nullptr),
|
||||
dummy_renderer_(nullptr),
|
||||
dummy_version_(nullptr),
|
||||
#endif
|
||||
offscreen_window_handle_(nullptr)
|
||||
{
|
||||
if (h_wnd_ == nullptr) {
|
||||
/* For offscreen context, OpenGL needs a dummy window to create a context on windows.
|
||||
* Create or reuse an existing one. */
|
||||
for (OffscreenWindowHandle &handle : s_sharedOffscreenWindowHandles) {
|
||||
if (!handle.used) {
|
||||
offscreen_window_handle_ = &handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (offscreen_window_handle_ == nullptr) {
|
||||
offscreen_window_handle_ = &s_sharedOffscreenWindowHandles.emplace_back();
|
||||
}
|
||||
offscreen_window_handle_->used = true;
|
||||
h_wnd_ = offscreen_window_handle_->h_wnd;
|
||||
h_DC_ = offscreen_window_handle_->h_DC;
|
||||
}
|
||||
|
||||
assert(h_DC_ != nullptr);
|
||||
}
|
||||
|
||||
GHOST_ContextWGL::~GHOST_ContextWGL()
|
||||
{
|
||||
if (h_GLRC_ != nullptr) {
|
||||
if (h_GLRC_ == ::wglGetCurrentContext()) {
|
||||
WIN32_CHK(::wglMakeCurrent(nullptr, nullptr));
|
||||
}
|
||||
|
||||
if (h_GLRC_ != s_sharedHGLRC || s_sharedCount == 1) {
|
||||
assert(s_sharedCount > 0);
|
||||
|
||||
s_sharedCount--;
|
||||
|
||||
if (s_sharedCount == 0) {
|
||||
s_sharedHGLRC = nullptr;
|
||||
}
|
||||
|
||||
WIN32_CHK(::wglDeleteContext(h_GLRC_));
|
||||
}
|
||||
}
|
||||
|
||||
releaseNativeHandles();
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (dummy_renderer_) {
|
||||
free((void *)dummy_renderer_);
|
||||
free((void *)dummy_vendor_);
|
||||
free((void *)dummy_version_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextWGL::swapBufferRelease()
|
||||
{
|
||||
return WIN32_CHK(::SwapBuffers(h_DC_)) ? GHOST_kSuccess : GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextWGL::setSwapInterval(int interval)
|
||||
{
|
||||
if (epoxy_has_wgl_extension(h_DC_, "WGL_EXT_swap_control")) {
|
||||
return WIN32_CHK(::wglSwapIntervalEXT(interval)) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
|
||||
}
|
||||
else {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextWGL::getSwapInterval(int &interval_out)
|
||||
{
|
||||
if (epoxy_has_wgl_extension(h_DC_, "WGL_EXT_swap_control")) {
|
||||
interval_out = ::wglGetSwapIntervalEXT();
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
else {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextWGL::activateDrawingContext()
|
||||
{
|
||||
if (WIN32_CHK(::wglMakeCurrent(h_DC_, h_GLRC_))) {
|
||||
active_context_ = this;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
else {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextWGL::releaseDrawingContext()
|
||||
{
|
||||
/* Calling wglMakeCurrent(nullptr, nullptr) without an active context returns an error,
|
||||
* so we always pass the device context handle. */
|
||||
if (WIN32_CHK(::wglMakeCurrent(h_DC_, nullptr))) {
|
||||
active_context_ = nullptr;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
else {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ron Fosner's code for weighting pixel formats and forcing software.
|
||||
* See http://www.opengl.org/resources/faq/technical/weight.cpp
|
||||
*/
|
||||
static int weight_pixel_format(PIXELFORMATDESCRIPTOR &pfd, PIXELFORMATDESCRIPTOR &preferredPFD)
|
||||
{
|
||||
int weight = 0;
|
||||
|
||||
/* assume desktop color depth is 32 bits per pixel */
|
||||
|
||||
/* cull unusable pixel formats */
|
||||
/* if no formats can be found, can we determine why it was rejected? */
|
||||
if (!(pfd.dwFlags & PFD_SUPPORT_OPENGL) || !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ||
|
||||
!(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this. */
|
||||
!(pfd.iPixelType == PFD_TYPE_RGBA) ||
|
||||
(pfd.cColorBits > 32) || /* 64 bit formats disable AERO. */
|
||||
(pfd.dwFlags & PFD_GENERIC_FORMAT)) /* No software renderers. */
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
weight = 1; /* it's usable */
|
||||
|
||||
weight += pfd.cColorBits - 8;
|
||||
|
||||
if (preferredPFD.cAlphaBits > 0 && pfd.cAlphaBits > 0) {
|
||||
weight++;
|
||||
}
|
||||
|
||||
return weight;
|
||||
}
|
||||
|
||||
/*
|
||||
* A modification of Ron Fosner's replacement for ChoosePixelFormat
|
||||
* returns 0 on error, else returns the pixel format number to be used
|
||||
*/
|
||||
static int choose_pixel_format_legacy(HDC hDC, PIXELFORMATDESCRIPTOR &preferredPFD)
|
||||
{
|
||||
int iPixelFormat = 0;
|
||||
int weight = 0;
|
||||
|
||||
int iStereoPixelFormat = 0;
|
||||
int stereoWeight = 0;
|
||||
|
||||
/* choose a pixel format using the useless Windows function in case we come up empty handed */
|
||||
int iLastResortPixelFormat = ::ChoosePixelFormat(hDC, &preferredPFD);
|
||||
|
||||
WIN32_CHK(iLastResortPixelFormat != 0);
|
||||
|
||||
int lastPFD = ::DescribePixelFormat(hDC, 1, sizeof(PIXELFORMATDESCRIPTOR), nullptr);
|
||||
|
||||
WIN32_CHK(lastPFD != 0);
|
||||
|
||||
for (int i = 1; i <= lastPFD; i++) {
|
||||
PIXELFORMATDESCRIPTOR pfd;
|
||||
int check = ::DescribePixelFormat(hDC, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
|
||||
|
||||
WIN32_CHK(check == lastPFD);
|
||||
|
||||
int w = weight_pixel_format(pfd, preferredPFD);
|
||||
|
||||
if (w > weight) {
|
||||
weight = w;
|
||||
iPixelFormat = i;
|
||||
}
|
||||
|
||||
if (w > stereoWeight && (preferredPFD.dwFlags & pfd.dwFlags & PFD_STEREO)) {
|
||||
stereoWeight = w;
|
||||
iStereoPixelFormat = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* choose any available stereo format over a non-stereo format */
|
||||
if (iStereoPixelFormat != 0) {
|
||||
iPixelFormat = iStereoPixelFormat;
|
||||
}
|
||||
|
||||
if (iPixelFormat == 0) {
|
||||
fprintf(stderr, "Warning! Using result of ChoosePixelFormat.\n");
|
||||
iPixelFormat = iLastResortPixelFormat;
|
||||
}
|
||||
|
||||
return iPixelFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a window for the purpose of creating a temporary context to initialize WGL extensions.
|
||||
* There is no generic way to clone the lpParam parameter,
|
||||
* so the caller is responsible for cloning it themselves.
|
||||
*/
|
||||
static HWND clone_window(HWND hWnd, LPVOID lpParam)
|
||||
{
|
||||
int count;
|
||||
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
DWORD dwExStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
|
||||
WIN32_CHK(GetLastError() == NO_ERROR);
|
||||
|
||||
/* The maximum length for WNDCLASSEXW.lpszClassName is 256. */
|
||||
WCHAR lpClassName[257] = L"";
|
||||
count = GetClassNameW(hWnd, lpClassName, sizeof(lpClassName) / sizeof(WCHAR));
|
||||
WIN32_CHK(count != 0);
|
||||
|
||||
const int titleLength = GetWindowTextLengthW(hWnd);
|
||||
WIN32_CHK(titleLength != 0 || GetLastError() == NO_ERROR);
|
||||
|
||||
/* Allocate space for characters + terminating null. */
|
||||
std::wstring WindowName(static_cast<size_t>(titleLength) + 1, L'\0');
|
||||
count = GetWindowTextW(hWnd, WindowName.data(), static_cast<int>(WindowName.size()));
|
||||
WIN32_CHK(count >= 0);
|
||||
|
||||
WindowName.resize(static_cast<size_t>(count));
|
||||
|
||||
DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE);
|
||||
WIN32_CHK(GetLastError() == NO_ERROR);
|
||||
|
||||
RECT rect;
|
||||
GetWindowRect(hWnd, &rect);
|
||||
WIN32_CHK(GetLastError() == NO_ERROR);
|
||||
|
||||
HWND hWndParent = (HWND)GetWindowLongPtr(hWnd, GWLP_HWNDPARENT);
|
||||
WIN32_CHK(GetLastError() == NO_ERROR);
|
||||
|
||||
HMENU hMenu = GetMenu(hWnd);
|
||||
WIN32_CHK(GetLastError() == NO_ERROR);
|
||||
|
||||
HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE);
|
||||
WIN32_CHK(GetLastError() == NO_ERROR);
|
||||
|
||||
HWND hwndCloned = CreateWindowExW(dwExStyle,
|
||||
lpClassName,
|
||||
WindowName.c_str(),
|
||||
dwStyle,
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
hWndParent,
|
||||
hMenu,
|
||||
hInstance,
|
||||
lpParam);
|
||||
|
||||
WIN32_CHK(hwndCloned != nullptr);
|
||||
|
||||
return hwndCloned;
|
||||
}
|
||||
|
||||
static void makeAttribList(std::vector<int> &out, bool stereoVisual, bool needAlpha)
|
||||
{
|
||||
out.clear();
|
||||
out.reserve(30);
|
||||
|
||||
out.push_back(WGL_SUPPORT_OPENGL_ARB);
|
||||
out.push_back(GL_TRUE);
|
||||
|
||||
out.push_back(WGL_DRAW_TO_WINDOW_ARB);
|
||||
out.push_back(GL_TRUE);
|
||||
|
||||
out.push_back(WGL_DOUBLE_BUFFER_ARB);
|
||||
out.push_back(GL_TRUE);
|
||||
|
||||
out.push_back(WGL_ACCELERATION_ARB);
|
||||
out.push_back(WGL_FULL_ACCELERATION_ARB);
|
||||
|
||||
if (stereoVisual) {
|
||||
out.push_back(WGL_STEREO_ARB);
|
||||
out.push_back(GL_TRUE);
|
||||
}
|
||||
|
||||
out.push_back(WGL_PIXEL_TYPE_ARB);
|
||||
out.push_back(WGL_TYPE_RGBA_ARB);
|
||||
|
||||
out.push_back(WGL_COLOR_BITS_ARB);
|
||||
out.push_back(24);
|
||||
|
||||
if (needAlpha) {
|
||||
out.push_back(WGL_ALPHA_BITS_ARB);
|
||||
out.push_back(8);
|
||||
}
|
||||
|
||||
out.push_back(0);
|
||||
}
|
||||
|
||||
/* Temporary context used to create the actual context. We need ARB pixel format
|
||||
* and context extensions, which are only available within a context. */
|
||||
struct DummyContextWGL {
|
||||
HWND dummyHWND = nullptr;
|
||||
|
||||
HDC dummyHDC = nullptr;
|
||||
HGLRC dummyHGLRC = nullptr;
|
||||
|
||||
HDC prevHDC = nullptr;
|
||||
HGLRC prevHGLRC = nullptr;
|
||||
|
||||
int dummyPixelFormat = 0;
|
||||
|
||||
PIXELFORMATDESCRIPTOR preferredPFD;
|
||||
|
||||
bool has_WGL_ARB_pixel_format = false;
|
||||
bool has_WGL_ARB_create_context = false;
|
||||
bool has_WGL_ARB_create_context_profile = false;
|
||||
bool has_WGL_ARB_create_context_robustness = false;
|
||||
|
||||
DummyContextWGL(HDC hDC, HWND hWnd, bool stereoVisual, bool needAlpha)
|
||||
{
|
||||
preferredPFD = {
|
||||
sizeof(PIXELFORMATDESCRIPTOR), /* size */
|
||||
1, /* version */
|
||||
(DWORD)(PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW |
|
||||
PFD_DOUBLEBUFFER | /* support double-buffering */
|
||||
(stereoVisual ? PFD_STEREO : 0)), /* support stereo */
|
||||
PFD_TYPE_RGBA, /* color type */
|
||||
(BYTE)(needAlpha ? 32 : 24), /* preferred color depth */
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0, /* color bits (ignored) */
|
||||
(BYTE)(needAlpha ? 8 : 0), /* alpha buffer */
|
||||
0, /* alpha shift (ignored) */
|
||||
0, /* no accumulation buffer */
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0, /* Accumulation bits (ignored). */
|
||||
0, /* depth buffer */
|
||||
0, /* stencil buffer */
|
||||
0, /* no auxiliary buffers */
|
||||
PFD_MAIN_PLANE, /* main layer */
|
||||
0, /* reserved */
|
||||
0,
|
||||
0,
|
||||
0 /* layer, visible, and damage masks (ignored) */
|
||||
};
|
||||
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
prevHDC = ::wglGetCurrentDC();
|
||||
WIN32_CHK(GetLastError() == NO_ERROR);
|
||||
|
||||
prevHGLRC = ::wglGetCurrentContext();
|
||||
WIN32_CHK(GetLastError() == NO_ERROR);
|
||||
|
||||
dummyPixelFormat = choose_pixel_format_legacy(hDC, preferredPFD);
|
||||
|
||||
if (dummyPixelFormat == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
PIXELFORMATDESCRIPTOR chosenPFD;
|
||||
if (!WIN32_CHK(::DescribePixelFormat(
|
||||
hDC, dummyPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (hWnd) {
|
||||
dummyHWND = clone_window(hWnd, nullptr);
|
||||
|
||||
if (dummyHWND == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
dummyHDC = GetDC(dummyHWND);
|
||||
}
|
||||
|
||||
if (!WIN32_CHK(dummyHDC != nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!WIN32_CHK(::SetPixelFormat(dummyHDC, dummyPixelFormat, &chosenPFD))) {
|
||||
return;
|
||||
}
|
||||
|
||||
dummyHGLRC = ::wglCreateContext(dummyHDC);
|
||||
|
||||
if (!WIN32_CHK(dummyHGLRC != nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!WIN32_CHK(::wglMakeCurrent(dummyHDC, dummyHGLRC))) {
|
||||
return;
|
||||
}
|
||||
|
||||
has_WGL_ARB_pixel_format = epoxy_has_wgl_extension(hDC, "WGL_ARB_pixel_format");
|
||||
has_WGL_ARB_create_context = epoxy_has_wgl_extension(hDC, "WGL_ARB_create_context");
|
||||
has_WGL_ARB_create_context_profile = epoxy_has_wgl_extension(hDC,
|
||||
"WGL_ARB_create_context_profile");
|
||||
has_WGL_ARB_create_context_robustness = epoxy_has_wgl_extension(
|
||||
hDC, "WGL_ARB_create_context_robustness");
|
||||
}
|
||||
|
||||
~DummyContextWGL()
|
||||
{
|
||||
WIN32_CHK(::wglMakeCurrent(prevHDC, prevHGLRC));
|
||||
|
||||
if (dummyHGLRC != nullptr) {
|
||||
WIN32_CHK(::wglDeleteContext(dummyHGLRC));
|
||||
}
|
||||
|
||||
if (dummyHWND != nullptr) {
|
||||
if (dummyHDC != nullptr) {
|
||||
WIN32_CHK(::ReleaseDC(dummyHWND, dummyHDC));
|
||||
}
|
||||
|
||||
WIN32_CHK(::DestroyWindow(dummyHWND));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int GHOST_ContextWGL::_choose_pixel_format_arb_1(bool stereoVisual, bool needAlpha)
|
||||
{
|
||||
std::vector<int> iAttributes;
|
||||
|
||||
#define _MAX_PIXEL_FORMATS 32
|
||||
|
||||
int iPixelFormat = 0;
|
||||
int iPixelFormats[_MAX_PIXEL_FORMATS];
|
||||
|
||||
makeAttribList(iAttributes, stereoVisual, needAlpha);
|
||||
|
||||
uint nNumFormats;
|
||||
WIN32_CHK(wglChoosePixelFormatARB(
|
||||
h_DC_, &(iAttributes[0]), nullptr, _MAX_PIXEL_FORMATS, iPixelFormats, &nNumFormats));
|
||||
|
||||
if (nNumFormats > 0) {
|
||||
iPixelFormat = iPixelFormats[0];
|
||||
}
|
||||
|
||||
// check pixel format
|
||||
if (iPixelFormat != 0) {
|
||||
if (needAlpha) {
|
||||
int alphaBits, iQuery = WGL_ALPHA_BITS_ARB;
|
||||
wglGetPixelFormatAttribivARB(h_DC_, iPixelFormat, 0, 1, &iQuery, &alphaBits);
|
||||
if (alphaBits == 0) {
|
||||
fprintf(stderr, "Warning! Unable to find a frame buffer with alpha channel.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return iPixelFormat;
|
||||
}
|
||||
|
||||
int GHOST_ContextWGL::choose_pixel_format_arb(bool stereoVisual, bool needAlpha)
|
||||
{
|
||||
int iPixelFormat;
|
||||
|
||||
iPixelFormat = _choose_pixel_format_arb_1(stereoVisual, needAlpha);
|
||||
|
||||
if (iPixelFormat == 0 && stereoVisual) {
|
||||
fprintf(stderr, "Warning! Unable to find a stereo pixel format.\n");
|
||||
|
||||
iPixelFormat = _choose_pixel_format_arb_1(false, needAlpha);
|
||||
|
||||
context_params_.is_stereo_visual = false; /* Set context property to actual value. */
|
||||
}
|
||||
|
||||
return iPixelFormat;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
static void reportContextString(const char *name, const char *dummy, const char *context)
|
||||
{
|
||||
fprintf(stderr, "%s: %s\n", name, context);
|
||||
|
||||
if (dummy && strcmp(dummy, context) != 0) {
|
||||
fprintf(stderr, "Warning! Dummy %s: %s\n", name, dummy);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
GHOST_TSuccess GHOST_ContextWGL::initializeDrawingContext()
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
HGLRC prevHGLRC = ::wglGetCurrentContext();
|
||||
WIN32_CHK(GetLastError() == NO_ERROR);
|
||||
|
||||
HDC prevHDC = ::wglGetCurrentDC();
|
||||
WIN32_CHK(GetLastError() == NO_ERROR);
|
||||
|
||||
{
|
||||
const bool needAlpha = alpha_background_;
|
||||
DummyContextWGL dummy(h_DC_, h_wnd_, context_params_.is_stereo_visual, needAlpha);
|
||||
|
||||
if (!dummy.has_WGL_ARB_create_context || ::GetPixelFormat(h_DC_) == 0) {
|
||||
int iPixelFormat = 0;
|
||||
|
||||
if (dummy.has_WGL_ARB_pixel_format) {
|
||||
iPixelFormat = choose_pixel_format_arb(context_params_.is_stereo_visual, needAlpha);
|
||||
}
|
||||
|
||||
if (iPixelFormat == 0) {
|
||||
iPixelFormat = choose_pixel_format_legacy(h_DC_, dummy.preferredPFD);
|
||||
}
|
||||
|
||||
if (iPixelFormat == 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
PIXELFORMATDESCRIPTOR chosenPFD;
|
||||
int lastPFD = ::DescribePixelFormat(
|
||||
h_DC_, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD);
|
||||
|
||||
if (!WIN32_CHK(lastPFD != 0)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (needAlpha && chosenPFD.cAlphaBits == 0) {
|
||||
fprintf(stderr, "Warning! Unable to find a pixel format with an alpha channel.\n");
|
||||
}
|
||||
|
||||
if (!WIN32_CHK(::SetPixelFormat(h_DC_, iPixelFormat, &chosenPFD))) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (dummy.has_WGL_ARB_create_context) {
|
||||
int profileBitCore = context_profile_mask_ & WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
|
||||
int profileBitCompat = context_profile_mask_ & WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
|
||||
|
||||
if (!dummy.has_WGL_ARB_create_context_profile && profileBitCore) {
|
||||
fprintf(stderr, "Warning! OpenGL core profile not available.\n");
|
||||
}
|
||||
|
||||
if (!dummy.has_WGL_ARB_create_context_profile && profileBitCompat) {
|
||||
fprintf(stderr, "Warning! OpenGL compatibility profile not available.\n");
|
||||
}
|
||||
|
||||
int profileMask = 0;
|
||||
|
||||
if (dummy.has_WGL_ARB_create_context_profile && profileBitCore) {
|
||||
profileMask |= profileBitCore;
|
||||
}
|
||||
|
||||
if (dummy.has_WGL_ARB_create_context_profile && profileBitCompat) {
|
||||
profileMask |= profileBitCompat;
|
||||
}
|
||||
|
||||
if (profileMask != context_profile_mask_) {
|
||||
fprintf(stderr, "Warning! Ignoring untested OpenGL context profile mask bits.");
|
||||
}
|
||||
|
||||
std::vector<int> iAttributes;
|
||||
|
||||
if (profileMask) {
|
||||
iAttributes.push_back(WGL_CONTEXT_PROFILE_MASK_ARB);
|
||||
iAttributes.push_back(profileMask);
|
||||
}
|
||||
|
||||
if (context_major_version_ != 0) {
|
||||
iAttributes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB);
|
||||
iAttributes.push_back(context_major_version_);
|
||||
}
|
||||
|
||||
if (context_minor_version_ != 0) {
|
||||
iAttributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB);
|
||||
iAttributes.push_back(context_minor_version_);
|
||||
}
|
||||
|
||||
if (context_flags_ != 0) {
|
||||
iAttributes.push_back(WGL_CONTEXT_FLAGS_ARB);
|
||||
iAttributes.push_back(context_flags_);
|
||||
}
|
||||
|
||||
if (context_reset_notification_strategy_ != 0) {
|
||||
if (dummy.has_WGL_ARB_create_context_robustness) {
|
||||
iAttributes.push_back(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
|
||||
iAttributes.push_back(context_reset_notification_strategy_);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Warning! Cannot set the reset notification strategy.");
|
||||
}
|
||||
}
|
||||
|
||||
iAttributes.push_back(0);
|
||||
|
||||
h_GLRC_ = ::wglCreateContextAttribsARB(h_DC_, s_sharedHGLRC, &(iAttributes[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Silence warnings interpreted as errors by users when trying to get
|
||||
* a context with version higher than 3.3 Core. */
|
||||
{
|
||||
const bool silent = context_major_version_ > 3;
|
||||
if (!WIN32_CHK_SILENT(h_GLRC_ != nullptr, silent)) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
s_sharedCount++;
|
||||
|
||||
if (s_sharedHGLRC == nullptr) {
|
||||
s_sharedHGLRC = h_GLRC_;
|
||||
}
|
||||
|
||||
if (!WIN32_CHK(::wglMakeCurrent(h_DC_, h_GLRC_))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
{
|
||||
const GHOST_TVSyncModes vsync = getVSync();
|
||||
if (vsync != GHOST_kVSyncModeUnset) {
|
||||
setSwapInterval(int(vsync));
|
||||
}
|
||||
}
|
||||
|
||||
if (is_crappy_intel_card()) {
|
||||
/* Some Intel cards with context 4.1 or 4.2
|
||||
* don't have the point sprite enabled by default.
|
||||
*
|
||||
* However GL_POINT_SPRITE was removed in 3.2 and is now permanently ON.
|
||||
* Then use brute force. */
|
||||
glEnable(GL_POINT_SPRITE);
|
||||
}
|
||||
|
||||
initClearGL();
|
||||
::SwapBuffers(h_DC_);
|
||||
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
const char *vendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
|
||||
const char *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
|
||||
const char *version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
|
||||
|
||||
reportContextString("Vendor", dummy_vendor_, vendor);
|
||||
reportContextString("Renderer", dummy_renderer_, renderer);
|
||||
reportContextString("Version", dummy_version_, version);
|
||||
|
||||
fprintf(stderr, "Context Version: %d.%d\n", context_major_version_, context_minor_version_);
|
||||
}
|
||||
#endif
|
||||
|
||||
active_context_ = this;
|
||||
return GHOST_kSuccess;
|
||||
error:
|
||||
::wglMakeCurrent(prevHDC, prevHGLRC);
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ContextWGL::releaseNativeHandles()
|
||||
{
|
||||
GHOST_TSuccess success = h_GLRC_ != s_sharedHGLRC || s_sharedCount == 1 ? GHOST_kSuccess :
|
||||
GHOST_kFailure;
|
||||
|
||||
if (offscreen_window_handle_) {
|
||||
offscreen_window_handle_->used = false;
|
||||
offscreen_window_handle_ = nullptr;
|
||||
}
|
||||
|
||||
h_wnd_ = nullptr;
|
||||
h_DC_ = nullptr;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_ContextWGL::OffscreenWindowHandle::OffscreenWindowHandle()
|
||||
{
|
||||
h_wnd = CreateWindowA("STATIC",
|
||||
"BlenderGLEW",
|
||||
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
|
||||
0,
|
||||
0,
|
||||
64,
|
||||
64,
|
||||
nullptr,
|
||||
nullptr,
|
||||
GetModuleHandle(nullptr),
|
||||
nullptr);
|
||||
h_DC = GetDC(h_wnd);
|
||||
}
|
||||
|
||||
GHOST_ContextWGL::OffscreenWindowHandle::~OffscreenWindowHandle()
|
||||
{
|
||||
if (h_DC != nullptr) {
|
||||
WIN32_CHK(::ReleaseDC(h_wnd, h_DC));
|
||||
}
|
||||
if (h_wnd != nullptr) {
|
||||
WIN32_CHK(::DestroyWindow(h_wnd));
|
||||
}
|
||||
}
|
||||
133
blender-5.2.0/intern/ghost/intern/GHOST_ContextWGL.hh
Normal file
133
blender-5.2.0/intern/ghost/intern/GHOST_ContextWGL.hh
Normal file
@@ -0,0 +1,133 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Context.hh"
|
||||
|
||||
#include <epoxy/wgl.h>
|
||||
|
||||
#include <list>
|
||||
|
||||
#ifndef GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY
|
||||
# define GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY 0
|
||||
#endif
|
||||
|
||||
class GHOST_ContextWGL : public GHOST_Context {
|
||||
/* XR code needs low level graphics data to send to OpenXR. */
|
||||
friend class GHOST_XrGraphicsBindingOpenGL;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_ContextWGL(const GHOST_ContextParams &context_params,
|
||||
bool alphaBackground,
|
||||
HWND hWnd,
|
||||
HDC hDC,
|
||||
int contextProfileMask,
|
||||
int contextMajorVersion,
|
||||
int contextMinorVersion,
|
||||
int contextFlags,
|
||||
int contextResetNotificationStrategy);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_ContextWGL() override;
|
||||
|
||||
/** \copydoc #GHOST_IContext::swapBuffersAcquire */
|
||||
GHOST_TSuccess swapBufferAcquire() override
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps front and back buffers of a window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess swapBufferRelease() override;
|
||||
|
||||
/**
|
||||
* Activates the drawing context of this window.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess activateDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Release the drawing context of the calling thread.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess releaseDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Call immediately after new to initialize. If this fails then immediately delete the object.
|
||||
* \return Indication as to whether initialization has succeeded.
|
||||
*/
|
||||
GHOST_TSuccess initializeDrawingContext() override;
|
||||
|
||||
/**
|
||||
* Removes references to native handles from this context and then returns
|
||||
* \return GHOST_kSuccess if it is OK for the parent to release the handles and
|
||||
* GHOST_kFailure if releasing the handles will interfere with sharing
|
||||
*/
|
||||
GHOST_TSuccess releaseNativeHandles() override;
|
||||
|
||||
/**
|
||||
* Sets the swap interval for #swapBuffers.
|
||||
* \param interval: The swap interval to use.
|
||||
* \return A boolean success indicator.
|
||||
*/
|
||||
GHOST_TSuccess setSwapInterval(int interval) override;
|
||||
|
||||
/**
|
||||
* Gets the current swap interval for #swapBuffers.
|
||||
* \param interval_out: Variable to store the swap interval if it can be read.
|
||||
* \return Whether the swap interval can be read.
|
||||
*/
|
||||
GHOST_TSuccess getSwapInterval(int &interval_out) override;
|
||||
|
||||
private:
|
||||
int choose_pixel_format_arb(bool stereoVisual, bool needAlpha);
|
||||
int _choose_pixel_format_arb_1(bool stereoVisual, bool needAlpha);
|
||||
|
||||
HWND h_wnd_;
|
||||
HDC h_DC_;
|
||||
|
||||
const int context_profile_mask_;
|
||||
const int context_major_version_;
|
||||
const int context_minor_version_;
|
||||
const int context_flags_;
|
||||
const bool alpha_background_;
|
||||
const int context_reset_notification_strategy_;
|
||||
|
||||
HGLRC h_GLRC_;
|
||||
|
||||
#ifndef NDEBUG
|
||||
const char *dummy_vendor_;
|
||||
const char *dummy_renderer_;
|
||||
const char *dummy_version_;
|
||||
#endif
|
||||
|
||||
static HGLRC s_sharedHGLRC;
|
||||
static int s_sharedCount;
|
||||
|
||||
/* Offscreen window handles are cached to ensure they are created and freed on the
|
||||
* main thread, as required by the Windows API. */
|
||||
struct OffscreenWindowHandle {
|
||||
OffscreenWindowHandle();
|
||||
~OffscreenWindowHandle();
|
||||
|
||||
HWND h_wnd = nullptr;
|
||||
HDC h_DC = nullptr;
|
||||
bool used = false;
|
||||
};
|
||||
|
||||
static std::list<OffscreenWindowHandle> s_sharedOffscreenWindowHandles;
|
||||
OffscreenWindowHandle *offscreen_window_handle_;
|
||||
};
|
||||
80
blender-5.2.0/intern/ghost/intern/GHOST_Debug.hh
Normal file
80
blender-5.2.0/intern/ghost/intern/GHOST_Debug.hh
Normal file
@@ -0,0 +1,80 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Macro's used in GHOST debug target.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# ifdef _DEBUG
|
||||
/* Suppress STL-MSVC debug info warning. */
|
||||
# pragma warning(disable : 4786)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <cstdio> /* For `printf()`. */
|
||||
#include <iostream>
|
||||
|
||||
#if defined(WITH_GHOST_DEBUG)
|
||||
# define GHOST_PRINT(x) \
|
||||
{ \
|
||||
std::cout << x; \
|
||||
} \
|
||||
((void)0)
|
||||
# define GHOST_PRINTF(x, ...) \
|
||||
{ \
|
||||
printf(x, __VA_ARGS__); \
|
||||
} \
|
||||
((void)0)
|
||||
#else
|
||||
/* Expand even when `WITH_GHOST_DEBUG` is disabled to prevent expressions
|
||||
* becoming invalid even when the option is disable. */
|
||||
# define GHOST_PRINT(x) \
|
||||
{ \
|
||||
if (false) { \
|
||||
std::cout << x; \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
# define GHOST_PRINTF(x, ...) \
|
||||
{ \
|
||||
if (false) { \
|
||||
printf(x, __VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#endif /* `!defined(WITH_GHOST_DEBUG)` */
|
||||
|
||||
#ifndef NDEBUG
|
||||
# ifdef WITH_ASSERT_ABORT
|
||||
# include <cstdlib> /* For `abort()`. */
|
||||
# define GHOST_ASSERT(x, info) \
|
||||
{ \
|
||||
if (!(x)) { \
|
||||
fprintf(stderr, "GHOST_ASSERT failed: "); \
|
||||
fprintf(stderr, info); \
|
||||
fprintf(stderr, "\n"); \
|
||||
abort(); \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
/* Show the failure in non-release builds too. */
|
||||
# else
|
||||
# define GHOST_ASSERT(x, info) \
|
||||
{ \
|
||||
if (!(x)) { \
|
||||
GHOST_PRINT("GHOST_ASSERT failed: "); \
|
||||
GHOST_PRINT(info); \
|
||||
GHOST_PRINT("\n"); \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
# endif
|
||||
#else /* `!defined(NDEBUG)` */
|
||||
# define GHOST_ASSERT(x, info) ((void)0)
|
||||
#endif /* `!defined(NDEBUG)` */
|
||||
355
blender-5.2.0/intern/ghost/intern/GHOST_DropTargetWin32.cc
Normal file
355
blender-5.2.0/intern/ghost/intern/GHOST_DropTargetWin32.cc
Normal file
@@ -0,0 +1,355 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include "GHOST_DropTargetWin32.hh"
|
||||
#include "GHOST_Debug.hh"
|
||||
#include <shellapi.h>
|
||||
|
||||
#include "utf_winfunc.hh"
|
||||
#include "utfconv.hh"
|
||||
|
||||
#ifdef WITH_GHOST_DEBUG
|
||||
/* utility */
|
||||
void printLastError(void);
|
||||
#endif /* WITH_GHOST_DEBUG */
|
||||
|
||||
GHOST_DropTargetWin32::GHOST_DropTargetWin32(GHOST_WindowWin32 *window, GHOST_SystemWin32 *system)
|
||||
: window_(window), system_(system)
|
||||
{
|
||||
c_ref_ = 1;
|
||||
h_wnd_ = window->getHWND();
|
||||
dragged_object_type_ = GHOST_kDragnDropTypeUnknown;
|
||||
}
|
||||
|
||||
GHOST_DropTargetWin32::~GHOST_DropTargetWin32() {}
|
||||
|
||||
/*
|
||||
* IUnknown::QueryInterface
|
||||
*/
|
||||
HRESULT __stdcall GHOST_DropTargetWin32::QueryInterface(REFIID riid, void **ppv_obj)
|
||||
{
|
||||
|
||||
if (!ppv_obj) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
*ppv_obj = nullptr;
|
||||
|
||||
if (riid == IID_IUnknown || riid == IID_IDropTarget) {
|
||||
AddRef();
|
||||
*ppv_obj = (void *)this;
|
||||
return S_OK;
|
||||
}
|
||||
*ppv_obj = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
/*
|
||||
* IUnknown::AddRef
|
||||
*/
|
||||
|
||||
ULONG __stdcall GHOST_DropTargetWin32::AddRef(void)
|
||||
{
|
||||
return ::InterlockedIncrement(&c_ref_);
|
||||
}
|
||||
|
||||
/*
|
||||
* IUnknown::Release
|
||||
*/
|
||||
ULONG __stdcall GHOST_DropTargetWin32::Release(void)
|
||||
{
|
||||
ULONG refs = ::InterlockedDecrement(&c_ref_);
|
||||
|
||||
if (refs == 0) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return refs;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of IDropTarget::DragEnter
|
||||
*/
|
||||
HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *p_data_object,
|
||||
DWORD /*grf_key_state*/,
|
||||
POINTL pt,
|
||||
DWORD *pdw_effect)
|
||||
{
|
||||
/* We accept all drop by default. */
|
||||
window_->setAcceptDragOperation(true);
|
||||
*pdw_effect = DROPEFFECT_NONE;
|
||||
|
||||
dragged_object_type_ = getGhostType(p_data_object);
|
||||
system_->pushDragDropEvent(
|
||||
GHOST_kEventDraggingEntered, dragged_object_type_, window_, pt.x, pt.y, nullptr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of IDropTarget::DragOver
|
||||
*/
|
||||
HRESULT __stdcall GHOST_DropTargetWin32::DragOver(DWORD /*grf_key_state*/,
|
||||
POINTL pt,
|
||||
DWORD *pdw_effect)
|
||||
{
|
||||
if (window_->canAcceptDragOperation()) {
|
||||
*pdw_effect = allowedDropEffect(*pdw_effect);
|
||||
}
|
||||
else {
|
||||
*pdw_effect = DROPEFFECT_NONE;
|
||||
/* XXX Uncomment to test drop. Drop will not be called if `pdw_effect == DROPEFFECT_NONE`. */
|
||||
// *pdw_effect = DROPEFFECT_COPY;
|
||||
}
|
||||
system_->pushDragDropEvent(
|
||||
GHOST_kEventDraggingUpdated, dragged_object_type_, window_, pt.x, pt.y, nullptr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of IDropTarget::DragLeave
|
||||
*/
|
||||
HRESULT __stdcall GHOST_DropTargetWin32::DragLeave(void)
|
||||
{
|
||||
system_->pushDragDropEvent(
|
||||
GHOST_kEventDraggingExited, dragged_object_type_, window_, 0, 0, nullptr);
|
||||
dragged_object_type_ = GHOST_kDragnDropTypeUnknown;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Implementation of IDropTarget::Drop
|
||||
* This function will not be called if pdw_effect is set to DROPEFFECT_NONE in
|
||||
* the implementation of IDropTarget::DragOver
|
||||
*/
|
||||
HRESULT __stdcall GHOST_DropTargetWin32::Drop(IDataObject *p_data_object,
|
||||
DWORD /*grf_key_state*/,
|
||||
POINTL pt,
|
||||
DWORD *pdw_effect)
|
||||
{
|
||||
void *data = getGhostData(p_data_object);
|
||||
if (window_->canAcceptDragOperation()) {
|
||||
*pdw_effect = allowedDropEffect(*pdw_effect);
|
||||
}
|
||||
else {
|
||||
*pdw_effect = DROPEFFECT_NONE;
|
||||
}
|
||||
if (data) {
|
||||
system_->pushDragDropEvent(
|
||||
GHOST_kEventDraggingDropDone, dragged_object_type_, window_, pt.x, pt.y, data);
|
||||
}
|
||||
dragged_object_type_ = GHOST_kDragnDropTypeUnknown;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
DWORD GHOST_DropTargetWin32::allowedDropEffect(DWORD dw_allowed)
|
||||
{
|
||||
DWORD dw_effect = DROPEFFECT_NONE;
|
||||
if (dw_allowed & DROPEFFECT_COPY) {
|
||||
dw_effect = DROPEFFECT_COPY;
|
||||
}
|
||||
return dw_effect;
|
||||
}
|
||||
|
||||
GHOST_TDragnDropTypes GHOST_DropTargetWin32::getGhostType(IDataObject *p_data_object)
|
||||
{
|
||||
/* File-names. Prefer looking for the CF_HDROP data format first as certain file-manager
|
||||
* applications send CF_TEXT as well. See issue #135672. */
|
||||
FORMATETC fmtetc = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||||
if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
|
||||
return GHOST_kDragnDropTypeFilenames;
|
||||
}
|
||||
|
||||
/* Text
|
||||
* NOTE: Unlike the clipboard, Windows will not synthesize missing text formats for drag and
|
||||
* drop operations, so we need to check for both CF_UNICODETEXT and CF_TEXT.
|
||||
*/
|
||||
fmtetc.cfFormat = CF_UNICODETEXT;
|
||||
if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
|
||||
return GHOST_kDragnDropTypeString;
|
||||
}
|
||||
|
||||
fmtetc.cfFormat = CF_TEXT;
|
||||
if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
|
||||
return GHOST_kDragnDropTypeString;
|
||||
}
|
||||
|
||||
return GHOST_kDragnDropTypeUnknown;
|
||||
}
|
||||
|
||||
void *GHOST_DropTargetWin32::getGhostData(IDataObject *p_data_object)
|
||||
{
|
||||
GHOST_TDragnDropTypes type = getGhostType(p_data_object);
|
||||
switch (type) {
|
||||
case GHOST_kDragnDropTypeFilenames:
|
||||
return getDropDataAsFilenames(p_data_object);
|
||||
case GHOST_kDragnDropTypeString:
|
||||
return getDropDataAsString(p_data_object);
|
||||
case GHOST_kDragnDropTypeBitmap:
|
||||
// return getDropDataAsBitmap(p_data_object);
|
||||
break;
|
||||
default:
|
||||
#ifdef WITH_GHOST_DEBUG
|
||||
::printf("\nGHOST_kDragnDropTypeUnknown");
|
||||
#endif /* WITH_GHOST_DEBUG */
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *GHOST_DropTargetWin32::getDropDataAsFilenames(IDataObject *p_data_object)
|
||||
{
|
||||
GHOST_TStringArray *str_array = nullptr;
|
||||
FORMATETC fmtetc = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||||
|
||||
/* Check if data-object supplies the format we want.
|
||||
* Double checking here, first in #getGhostType. */
|
||||
if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
|
||||
STGMEDIUM stgmed;
|
||||
if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) {
|
||||
const HDROP hdrop = (HDROP)::GlobalLock(stgmed.hGlobal);
|
||||
|
||||
const uint totfiles = ::DragQueryFileW(hdrop, -1, nullptr, 0);
|
||||
if (totfiles) {
|
||||
str_array = (GHOST_TStringArray *)::malloc(sizeof(GHOST_TStringArray));
|
||||
str_array->count = 0;
|
||||
str_array->strings = (uint8_t **)::malloc(totfiles * sizeof(uint8_t *));
|
||||
|
||||
for (uint nfile = 0; nfile < totfiles; nfile++) {
|
||||
WCHAR fpath[MAX_PATH];
|
||||
if (::DragQueryFileW(hdrop, nfile, fpath, MAX_PATH) > 0) {
|
||||
char *temp_path;
|
||||
if (!(temp_path = alloc_utf_8_from_16(fpath, 0))) {
|
||||
/* Just ignore paths that could not be converted verbatim. */
|
||||
continue;
|
||||
}
|
||||
str_array->strings[str_array->count++] = (uint8_t *)temp_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Free up memory. */
|
||||
::GlobalUnlock(stgmed.hGlobal);
|
||||
::ReleaseStgMedium(&stgmed);
|
||||
}
|
||||
}
|
||||
return str_array;
|
||||
}
|
||||
|
||||
void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *p_data_object)
|
||||
{
|
||||
char *tmp_string;
|
||||
FORMATETC fmtetc = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
||||
STGMEDIUM stgmed;
|
||||
|
||||
/* Try unicode first.
|
||||
* Check if data-object supplies the format we want. */
|
||||
if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
|
||||
if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) {
|
||||
LPCWSTR wstr = (LPCWSTR)::GlobalLock(stgmed.hGlobal);
|
||||
|
||||
tmp_string = alloc_utf_8_from_16((wchar_t *)wstr, 0);
|
||||
|
||||
/* Free memory. */
|
||||
::GlobalUnlock(stgmed.hGlobal);
|
||||
::ReleaseStgMedium(&stgmed);
|
||||
|
||||
#ifdef WITH_GHOST_DEBUG
|
||||
if (tmp_string) {
|
||||
::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n",
|
||||
tmp_string);
|
||||
}
|
||||
#endif /* WITH_GHOST_DEBUG */
|
||||
return tmp_string;
|
||||
}
|
||||
}
|
||||
|
||||
fmtetc.cfFormat = CF_TEXT;
|
||||
|
||||
if (p_data_object->QueryGetData(&fmtetc) == S_OK) {
|
||||
if (p_data_object->GetData(&fmtetc, &stgmed) == S_OK) {
|
||||
char *str = (char *)::GlobalLock(stgmed.hGlobal);
|
||||
int str_size = ::strlen(str) + 1;
|
||||
|
||||
tmp_string = (char *)::malloc(str_size);
|
||||
if (tmp_string) {
|
||||
::memcpy(tmp_string, str, str_size);
|
||||
}
|
||||
/* Free memory. */
|
||||
::GlobalUnlock(stgmed.hGlobal);
|
||||
::ReleaseStgMedium(&stgmed);
|
||||
|
||||
return tmp_string;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char *&out)
|
||||
{
|
||||
int size;
|
||||
out = nullptr; /* caller should free if != nullptr */
|
||||
|
||||
/* Get the required size. */
|
||||
size = ::WideCharToMultiByte(CP_ACP, /* System Default Codepage */
|
||||
0x00000400, /* WC_NO_BEST_FIT_CHARS */
|
||||
in,
|
||||
-1, /* -1 null terminated, makes output null terminated too. */
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr);
|
||||
|
||||
if (!size) {
|
||||
#ifdef WITH_GHOST_DEBUG
|
||||
::printLastError();
|
||||
#endif /* WITH_GHOST_DEBUG */
|
||||
return 0;
|
||||
}
|
||||
|
||||
out = (char *)::malloc(size);
|
||||
if (!out) {
|
||||
::printf("\nmalloc failed!!!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = ::WideCharToMultiByte(CP_ACP, 0x00000400, in, -1, (LPSTR)out, size, nullptr, nullptr);
|
||||
|
||||
if (!size) {
|
||||
#ifdef WITH_GHOST_DEBUG
|
||||
::printLastError();
|
||||
#endif /* WITH_GHOST_DEBUG */
|
||||
::free(out);
|
||||
out = nullptr;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifdef WITH_GHOST_DEBUG
|
||||
void printLastError(void)
|
||||
{
|
||||
LPTSTR s;
|
||||
DWORD err;
|
||||
|
||||
err = GetLastError();
|
||||
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
nullptr,
|
||||
err,
|
||||
0,
|
||||
(LPTSTR)&s,
|
||||
0,
|
||||
nullptr))
|
||||
{
|
||||
printf("\nLastError: (%d) %s\n", int(err), s);
|
||||
LocalFree(s);
|
||||
}
|
||||
}
|
||||
#endif /* WITH_GHOST_DEBUG */
|
||||
137
blender-5.2.0/intern/ghost/intern/GHOST_DropTargetWin32.hh
Normal file
137
blender-5.2.0/intern/ghost/intern/GHOST_DropTargetWin32.hh
Normal file
@@ -0,0 +1,137 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_SystemWin32.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
#include "GHOST_WindowWin32.hh"
|
||||
#include <string.h>
|
||||
|
||||
class GHOST_DropTargetWin32 final : public IDropTarget {
|
||||
public:
|
||||
/* IUnknownd implementation.
|
||||
* Enables clients to get pointers to other interfaces on a given object
|
||||
* through the QueryInterface method, and manage the existence of the object
|
||||
* through the AddRef and Release methods. All other COM interfaces are
|
||||
* inherited, directly or indirectly, from IUnknown. Therefore, the three
|
||||
* methods in IUnknown are the first entries in the VTable for every interface.
|
||||
*/
|
||||
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv_obj);
|
||||
ULONG __stdcall AddRef();
|
||||
ULONG __stdcall Release();
|
||||
|
||||
/* IDropTarget implementation
|
||||
* + The IDropTarget interface is one of the interfaces you implement to
|
||||
* provide drag-and-drop operations in your application. It contains methods
|
||||
* used in any application that can be a target for data during a
|
||||
* drag-and-drop operation. A drop-target application is responsible for:
|
||||
*
|
||||
* - Determining the effect of the drop on the target application.
|
||||
* - Incorporating any valid dropped data when the drop occurs.
|
||||
* - Communicating target feedback to the source so the source application
|
||||
* can provide appropriate visual feedback such as setting the cursor.
|
||||
* - Implementing drag scrolling.
|
||||
* - Registering and revoking its application windows as drop targets.
|
||||
*
|
||||
* The IDropTarget interface contains methods that handle all these
|
||||
* responsibilities except registering and revoking the application window
|
||||
* as a drop target, for which you must call the RegisterDragDrop and the
|
||||
* RevokeDragDrop functions.
|
||||
*/
|
||||
|
||||
HRESULT __stdcall DragEnter(IDataObject *p_data_object,
|
||||
DWORD grf_key_state,
|
||||
POINTL pt,
|
||||
DWORD *pdw_effect);
|
||||
HRESULT __stdcall DragOver(DWORD grf_key_state, POINTL pt, DWORD *pdw_effect);
|
||||
HRESULT __stdcall DragLeave();
|
||||
HRESULT __stdcall Drop(IDataObject *p_data_object,
|
||||
DWORD grf_key_state,
|
||||
POINTL pt,
|
||||
DWORD *pdw_effect);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* With the modifier keys, we want to distinguish left and right keys.
|
||||
* Sometimes this is not possible (Windows ME for instance). Then, we want
|
||||
* events generated for both keys.
|
||||
* \param window: The window to register as drop target.
|
||||
* \param system: The associated system.
|
||||
*/
|
||||
GHOST_DropTargetWin32(GHOST_WindowWin32 *window, GHOST_SystemWin32 *system);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
* Do NOT destroy directly. Use Release() instead to make COM happy.
|
||||
*/
|
||||
~GHOST_DropTargetWin32();
|
||||
|
||||
private:
|
||||
/* Internal helper functions */
|
||||
|
||||
/**
|
||||
* Base the effect on those allowed by the drop-source.
|
||||
* \param dw_allowed: Drop sources allowed drop effect.
|
||||
* \return The allowed drop effect.
|
||||
*/
|
||||
DWORD allowedDropEffect(DWORD dw_allowed);
|
||||
|
||||
/**
|
||||
* Query DataObject for the data types it supports.
|
||||
* \param p_data_object: Pointer to the DataObject.
|
||||
* \return GHOST data type.
|
||||
*/
|
||||
GHOST_TDragnDropTypes getGhostType(IDataObject *p_data_object);
|
||||
|
||||
/**
|
||||
* Get data to pass in event.
|
||||
* It checks the type and calls specific functions for each type.
|
||||
* \param p_data_object: Pointer to the DataObject.
|
||||
* \return Pointer to data.
|
||||
*/
|
||||
void *getGhostData(IDataObject *p_data_object);
|
||||
|
||||
/**
|
||||
* Allocate data as file array to pass in event.
|
||||
* \param p_data_object: Pointer to the DataObject.
|
||||
* \return Pointer to data.
|
||||
*/
|
||||
void *getDropDataAsFilenames(IDataObject *p_data_object);
|
||||
|
||||
/**
|
||||
* Allocate data as string to pass in event.
|
||||
* \param p_data_object: Pointer to the DataObject.
|
||||
* \return Pointer to data.
|
||||
*/
|
||||
void *getDropDataAsString(IDataObject *p_data_object);
|
||||
|
||||
/**
|
||||
* Convert Unicode to ANSI, replacing uncomfortable chars with '?'.
|
||||
* The ANSI codepage is the system default codepage,
|
||||
* and can change from system to system.
|
||||
* \param in: LPCWSTR.
|
||||
* \param out: char *. Is set to nullptr on failure.
|
||||
* \return 0 on failure. Else the size of the string including '\0'.
|
||||
*/
|
||||
int WideCharToANSI(LPCWSTR in, char *&out);
|
||||
|
||||
/* Private member variables */
|
||||
/* COM reference count. */
|
||||
LONG c_ref_;
|
||||
/* Handle of the associated window. */
|
||||
HWND h_wnd_;
|
||||
/* The associated GHOST_WindowWin32. */
|
||||
GHOST_WindowWin32 *window_;
|
||||
/* The System. */
|
||||
GHOST_SystemWin32 *system_;
|
||||
/* Data type of the dragged object */
|
||||
GHOST_TDragnDropTypes dragged_object_type_;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_DropTargetWin32")
|
||||
};
|
||||
232
blender-5.2.0/intern/ghost/intern/GHOST_DropTargetX11.cc
Normal file
232
blender-5.2.0/intern/ghost/intern/GHOST_DropTargetX11.cc
Normal file
@@ -0,0 +1,232 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include "GHOST_DropTargetX11.hh"
|
||||
#include "GHOST_Debug.hh"
|
||||
#include "GHOST_PathUtils.hh"
|
||||
#include "GHOST_utildefines.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
bool GHOST_DropTargetX11::xdnd_initialized_ = false;
|
||||
DndClass GHOST_DropTargetX11::dnd_class_;
|
||||
Atom *GHOST_DropTargetX11::dnd_types_ = nullptr;
|
||||
Atom *GHOST_DropTargetX11::dnd_actions_ = nullptr;
|
||||
const char *GHOST_DropTargetX11::dnd_mime_types_[] = {
|
||||
"url/url", "text/uri-list", "text/plain", "application/octet-stream"};
|
||||
int GHOST_DropTargetX11::ref_counter_ = 0;
|
||||
|
||||
#define dndTypeURLID 0
|
||||
#define dndTypeURIListID 1
|
||||
#define dndTypePlainTextID 2
|
||||
#define dndTypeOctetStreamID 3
|
||||
|
||||
#define dndTypeURL dnd_types_[dndTypeURLID]
|
||||
#define dndTypeURIList dnd_types_[dndTypeURIListID]
|
||||
#define dndTypePlainText dnd_types_[dndTypePlainTextID]
|
||||
#define dndTypeOctetStream dnd_types_[dndTypeOctetStreamID]
|
||||
|
||||
void GHOST_DropTargetX11::Initialize()
|
||||
{
|
||||
Display *display = system_->getXDisplay();
|
||||
int dndTypesCount = ARRAY_SIZE(dnd_mime_types_);
|
||||
int counter;
|
||||
|
||||
xdnd_init(&dnd_class_, display);
|
||||
|
||||
dnd_types_ = new Atom[dndTypesCount + 1];
|
||||
XInternAtoms(display, (char **)dnd_mime_types_, dndTypesCount, 0, dnd_types_);
|
||||
dnd_types_[dndTypesCount] = 0;
|
||||
|
||||
dnd_actions_ = new Atom[8];
|
||||
counter = 0;
|
||||
|
||||
dnd_actions_[counter++] = dnd_class_.XdndActionCopy;
|
||||
dnd_actions_[counter++] = dnd_class_.XdndActionMove;
|
||||
|
||||
#if 0 /* Not supported yet */
|
||||
dndActions[counter++] = dnd->XdndActionLink;
|
||||
dndActions[counter++] = dnd->XdndActionAsk;
|
||||
dndActions[counter++] = dnd->XdndActionPrivate;
|
||||
dndActions[counter++] = dnd->XdndActionList;
|
||||
dndActions[counter++] = dnd->XdndActionDescription;
|
||||
#endif
|
||||
|
||||
dnd_actions_[counter++] = 0;
|
||||
}
|
||||
|
||||
void GHOST_DropTargetX11::Uninitialize()
|
||||
{
|
||||
xdnd_shut(&dnd_class_);
|
||||
|
||||
delete[] dnd_actions_;
|
||||
delete[] dnd_types_;
|
||||
}
|
||||
|
||||
GHOST_DropTargetX11::GHOST_DropTargetX11(GHOST_WindowX11 *window, GHOST_SystemX11 *system)
|
||||
: window_(window), system_(system)
|
||||
{
|
||||
if (!xdnd_initialized_) {
|
||||
Initialize();
|
||||
xdnd_initialized_ = true;
|
||||
GHOST_PRINT("XDND initialized\n");
|
||||
}
|
||||
|
||||
Window wnd = window->getXWindow();
|
||||
|
||||
xdnd_set_dnd_aware(&dnd_class_, wnd, nullptr);
|
||||
xdnd_set_type_list(&dnd_class_, wnd, dnd_types_);
|
||||
|
||||
dragged_object_type_ = GHOST_kDragnDropTypeUnknown;
|
||||
ref_counter_++;
|
||||
}
|
||||
|
||||
GHOST_DropTargetX11::~GHOST_DropTargetX11()
|
||||
{
|
||||
ref_counter_--;
|
||||
if (ref_counter_ == 0) {
|
||||
Uninitialize();
|
||||
xdnd_initialized_ = false;
|
||||
GHOST_PRINT("XDND uninitialized\n");
|
||||
}
|
||||
}
|
||||
|
||||
char *GHOST_DropTargetX11::FileUrlDecode(const char *fileUrl)
|
||||
{
|
||||
if (strncmp(fileUrl, "file://", 7) == 0) {
|
||||
const char *file = fileUrl + 7;
|
||||
return GHOST_URL_decode_alloc(file, strlen(file));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *GHOST_DropTargetX11::getURIListGhostData(const uchar *dropBuffer, int dropBufferSize)
|
||||
{
|
||||
GHOST_TStringArray *strArray = nullptr;
|
||||
int totPaths = 0, curLength = 0;
|
||||
|
||||
/* Count total number of file paths in buffer. */
|
||||
for (int i = 0; i <= dropBufferSize; i++) {
|
||||
if (ELEM(dropBuffer[i], 0, '\n', '\r')) {
|
||||
if (curLength) {
|
||||
totPaths++;
|
||||
curLength = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
curLength++;
|
||||
}
|
||||
}
|
||||
|
||||
strArray = (GHOST_TStringArray *)malloc(sizeof(GHOST_TStringArray));
|
||||
strArray->count = 0;
|
||||
strArray->strings = (uint8_t **)malloc(totPaths * sizeof(uint8_t *));
|
||||
|
||||
curLength = 0;
|
||||
for (int i = 0; i <= dropBufferSize; i++) {
|
||||
if (ELEM(dropBuffer[i], 0, '\n', '\r')) {
|
||||
if (curLength) {
|
||||
char *curPath = (char *)malloc(curLength + 1);
|
||||
char *decodedPath;
|
||||
|
||||
strncpy(curPath, (char *)dropBuffer + i - curLength, curLength);
|
||||
curPath[curLength] = 0;
|
||||
|
||||
decodedPath = FileUrlDecode(curPath);
|
||||
if (decodedPath) {
|
||||
strArray->strings[strArray->count] = (uint8_t *)decodedPath;
|
||||
strArray->count++;
|
||||
}
|
||||
|
||||
free(curPath);
|
||||
curLength = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
curLength++;
|
||||
}
|
||||
}
|
||||
|
||||
return strArray;
|
||||
}
|
||||
|
||||
void *GHOST_DropTargetX11::getGhostData(Atom dropType, const uchar *dropBuffer, int dropBufferSize)
|
||||
{
|
||||
void *data = nullptr;
|
||||
uchar *tmpBuffer = (uchar *)malloc(dropBufferSize + 1);
|
||||
bool needsFree = true;
|
||||
|
||||
/* Ensure nil-terminator. */
|
||||
memcpy(tmpBuffer, dropBuffer, dropBufferSize);
|
||||
tmpBuffer[dropBufferSize] = 0;
|
||||
|
||||
if (dropType == dndTypeURIList) {
|
||||
dragged_object_type_ = GHOST_kDragnDropTypeFilenames;
|
||||
data = getURIListGhostData(tmpBuffer, dropBufferSize);
|
||||
}
|
||||
else if (dropType == dndTypeURL) {
|
||||
/* need to be tested */
|
||||
char *decodedPath = FileUrlDecode((const char *)tmpBuffer);
|
||||
|
||||
if (decodedPath) {
|
||||
dragged_object_type_ = GHOST_kDragnDropTypeString;
|
||||
data = decodedPath;
|
||||
}
|
||||
}
|
||||
else if (ELEM(dropType, dndTypePlainText, dndTypeOctetStream)) {
|
||||
dragged_object_type_ = GHOST_kDragnDropTypeString;
|
||||
data = tmpBuffer;
|
||||
needsFree = false;
|
||||
}
|
||||
else {
|
||||
dragged_object_type_ = GHOST_kDragnDropTypeUnknown;
|
||||
}
|
||||
|
||||
if (needsFree) {
|
||||
free(tmpBuffer);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
bool GHOST_DropTargetX11::GHOST_HandleClientMessage(XEvent *event)
|
||||
{
|
||||
Atom dropType;
|
||||
uchar *dropBuffer;
|
||||
int dropBufferSize, dropX, dropY;
|
||||
|
||||
if (xdnd_get_drop(system_->getXDisplay(),
|
||||
event,
|
||||
dnd_types_,
|
||||
dnd_actions_,
|
||||
&dropBuffer,
|
||||
&dropBufferSize,
|
||||
&dropType,
|
||||
&dropX,
|
||||
&dropY))
|
||||
{
|
||||
void *data = getGhostData(dropType, dropBuffer, dropBufferSize);
|
||||
|
||||
if (data) {
|
||||
system_->pushDragDropEvent(
|
||||
GHOST_kEventDraggingDropDone, dragged_object_type_, window_, dropX, dropY, data);
|
||||
}
|
||||
|
||||
free(dropBuffer);
|
||||
|
||||
dragged_object_type_ = GHOST_kDragnDropTypeUnknown;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
102
blender-5.2.0/intern/ghost/intern/GHOST_DropTargetX11.hh
Normal file
102
blender-5.2.0/intern/ghost/intern/GHOST_DropTargetX11.hh
Normal file
@@ -0,0 +1,102 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_SystemX11.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
#include "GHOST_WindowX11.hh"
|
||||
|
||||
#include "xdnd.h"
|
||||
|
||||
class GHOST_DropTargetX11 {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param window: The window to register as drop target.
|
||||
* \param system: The associated system.
|
||||
*/
|
||||
GHOST_DropTargetX11(GHOST_WindowX11 *window, GHOST_SystemX11 *system);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~GHOST_DropTargetX11();
|
||||
|
||||
/**
|
||||
* Handler of ClientMessage X11 event
|
||||
*/
|
||||
bool GHOST_HandleClientMessage(XEvent *event);
|
||||
|
||||
/**
|
||||
* Get data to pass in event.
|
||||
* It checks the type and calls specific functions for each type.
|
||||
* \param dropType: Type of dropped entity.
|
||||
* \param dropBuffer: Buffer returned from source application.
|
||||
* \param dropBufferSize: Size of returned buffer.
|
||||
* \return Pointer to data.
|
||||
*/
|
||||
void *getGhostData(Atom dropType, const unsigned char *dropBuffer, int dropBufferSize);
|
||||
|
||||
private:
|
||||
/* Internal helper functions */
|
||||
|
||||
/**
|
||||
* Initialize XDND and all related X atoms
|
||||
*/
|
||||
void Initialize();
|
||||
|
||||
/**
|
||||
* Uninitialized XDND and all related X atoms
|
||||
*/
|
||||
void Uninitialize();
|
||||
|
||||
/**
|
||||
* Get data to be passed to event from text/URI-list mime type
|
||||
* \param dropBuffer: Buffer returned from source application.
|
||||
* \param dropBufferSize: Size of dropped buffer.
|
||||
* \return pointer to newly created GHOST data.
|
||||
*/
|
||||
void *getURIListGhostData(const unsigned char *dropBuffer, int dropBufferSize);
|
||||
|
||||
/**
|
||||
* Fully decode file URL (i.e. converts `file:///a%20b/test` to `/a b/test`)
|
||||
* \param fileUrl: - file path URL to be fully decoded.
|
||||
* \return decoded file path (result should be free-d).
|
||||
*/
|
||||
char *FileUrlDecode(const char *fileUrl);
|
||||
|
||||
/* The associated GHOST_WindowWin32. */
|
||||
GHOST_WindowX11 *window_;
|
||||
/* The System. */
|
||||
GHOST_SystemX11 *system_;
|
||||
|
||||
/* Data type of the dragged object */
|
||||
GHOST_TDragnDropTypes dragged_object_type_;
|
||||
|
||||
/* Is drag-and-drop stuff initialized. */
|
||||
static bool xdnd_initialized_;
|
||||
|
||||
/* Class holding internal stiff of `xdnd` library. */
|
||||
static DndClass dnd_class_;
|
||||
|
||||
/* List of supported types to be dragged into. */
|
||||
static Atom *dnd_types_;
|
||||
|
||||
/* List of supported drag-and-drop actions. */
|
||||
static Atom *dnd_actions_;
|
||||
|
||||
/* List of supported MIME types to be dragged into. */
|
||||
static const char *dnd_mime_types_[];
|
||||
|
||||
/* Counter of references to global #XDND structures. */
|
||||
static int ref_counter_;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_DropTargetX11")
|
||||
};
|
||||
63
blender-5.2.0/intern/ghost/intern/GHOST_Event.hh
Normal file
63
blender-5.2.0/intern/ghost/intern/GHOST_Event.hh
Normal file
@@ -0,0 +1,63 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_Event class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_IEvent.hh"
|
||||
|
||||
/**
|
||||
* Base class for events received the operating system.
|
||||
*/
|
||||
class GHOST_Event : public GHOST_IEvent {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param msec: The time this event was generated.
|
||||
* \param type: The type of this event.
|
||||
* \param window: The generating window (or nullptr if system event).
|
||||
*/
|
||||
GHOST_Event(uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window)
|
||||
: type_(type), time_(msec), window_(window)
|
||||
{
|
||||
}
|
||||
|
||||
/** \copydoc #GHOST_IEvent::getType */
|
||||
GHOST_TEventType getType() const override
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
/** \copydoc #GHOST_IEvent::getTime */
|
||||
uint64_t getTime() const override
|
||||
{
|
||||
return time_;
|
||||
}
|
||||
|
||||
/** \copydoc #GHOST_IEvent::getWindow */
|
||||
GHOST_IWindow *getWindow() const override
|
||||
{
|
||||
return window_;
|
||||
}
|
||||
|
||||
/** \copydoc #GHOST_IEvent::getData */
|
||||
GHOST_TEventDataPtr getData() const override
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Type of this event. */
|
||||
GHOST_TEventType type_;
|
||||
/** The time this event was generated. */
|
||||
uint64_t time_;
|
||||
/** Pointer to the generating window. */
|
||||
GHOST_IWindow *window_;
|
||||
/** Pointer to the event data. */
|
||||
GHOST_TEventDataPtr data_ = nullptr;
|
||||
};
|
||||
41
blender-5.2.0/intern/ghost/intern/GHOST_EventButton.hh
Normal file
41
blender-5.2.0/intern/ghost/intern/GHOST_EventButton.hh
Normal file
@@ -0,0 +1,41 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_EventButton class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Event.hh"
|
||||
#include "GHOST_Window.hh"
|
||||
|
||||
/**
|
||||
* Mouse button event.
|
||||
*/
|
||||
class GHOST_EventButton : public GHOST_Event {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param time: The time this event was generated.
|
||||
* \param type: The type of this event.
|
||||
* \param window: The window of this event.
|
||||
* \param button: The state of the buttons were at the time of the event.
|
||||
* \param tablet: The tablet data associated with this event.
|
||||
*/
|
||||
GHOST_EventButton(uint64_t time,
|
||||
GHOST_TEventType type,
|
||||
GHOST_IWindow *window,
|
||||
GHOST_TButton button,
|
||||
const GHOST_TabletData &tablet)
|
||||
: GHOST_Event(time, type, window), button_event_data_({button, tablet})
|
||||
{
|
||||
data_ = &button_event_data_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** The button event data. */
|
||||
GHOST_TEventButtonData button_event_data_;
|
||||
};
|
||||
41
blender-5.2.0/intern/ghost/intern/GHOST_EventCursor.hh
Normal file
41
blender-5.2.0/intern/ghost/intern/GHOST_EventCursor.hh
Normal file
@@ -0,0 +1,41 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_EventCursor class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Event.hh"
|
||||
|
||||
/**
|
||||
* Cursor event.
|
||||
*/
|
||||
class GHOST_EventCursor : public GHOST_Event {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param msec: The time this event was generated.
|
||||
* \param type: The type of this event.
|
||||
* \param x: The x-coordinate of the location the cursor was at the time of the event.
|
||||
* \param y: The y-coordinate of the location the cursor was at the time of the event.
|
||||
* \param tablet: The tablet data associated with this event.
|
||||
*/
|
||||
GHOST_EventCursor(uint64_t msec,
|
||||
GHOST_TEventType type,
|
||||
GHOST_IWindow *window,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
const GHOST_TabletData &tablet)
|
||||
: GHOST_Event(msec, type, window), cursor_event_data_({x, y, tablet})
|
||||
{
|
||||
data_ = &cursor_event_data_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** The x,y-coordinates of the cursor position. */
|
||||
GHOST_TEventCursorData cursor_event_data_;
|
||||
};
|
||||
109
blender-5.2.0/intern/ghost/intern/GHOST_EventDragnDrop.hh
Normal file
109
blender-5.2.0/intern/ghost/intern/GHOST_EventDragnDrop.hh
Normal file
@@ -0,0 +1,109 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Event.hh"
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
/**
|
||||
* Drag & drop event
|
||||
*
|
||||
* The dragging sequence is performed in four phases:
|
||||
*
|
||||
* - Start sequence (GHOST_kEventDraggingEntered) that tells
|
||||
* a drag & drop operation has started.
|
||||
* Already gives the object data type, and the entering mouse location
|
||||
*
|
||||
* - Update mouse position (GHOST_kEventDraggingUpdated) sent upon each mouse move until the
|
||||
* drag & drop operation stops, to give the updated mouse position.
|
||||
* Useful to highlight a potential destination, and update the status
|
||||
* (through GHOST_IWindow::setAcceptDragOperation) telling if the object can be dropped at the
|
||||
* current cursor position.
|
||||
*
|
||||
* - Abort drag & drop sequence (#GHOST_kEventDraggingExited)
|
||||
* sent when the user moved the mouse outside the window.
|
||||
*
|
||||
* - Send the dropped data (GHOST_kEventDraggingDropDone)
|
||||
*
|
||||
* - Outside of the normal sequence, dropped data can be sent (GHOST_kEventDraggingDropOnIcon).
|
||||
* This can happen when the user drops an object on the application icon.
|
||||
* (Also used in OSX to pass the filename of the document the user doubled-clicked in the finder)
|
||||
*
|
||||
* Note that the mouse positions are given in Blender coordinates (y=0 at bottom)
|
||||
*
|
||||
* Currently supported object types:
|
||||
* - UTF8 string.
|
||||
* - array of strings representing filenames (#GHOST_TStringArray).
|
||||
* - bitmap #ImBuf.
|
||||
*/
|
||||
class GHOST_EventDragnDrop : public GHOST_Event {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param time: The time this event was generated.
|
||||
* \param type: The type of this event.
|
||||
* \param dataType: The type of the drop candidate object.
|
||||
* \param window: The window where the event occurred.
|
||||
* \param x: The x-coordinate of the location the cursor was at the time of the event.
|
||||
* \param y: The y-coordinate of the location the cursor was at the time of the event.
|
||||
* \param data: The "content" dropped in the window.
|
||||
*/
|
||||
GHOST_EventDragnDrop(uint64_t time,
|
||||
GHOST_TEventType type,
|
||||
GHOST_TDragnDropTypes dataType,
|
||||
GHOST_IWindow *window,
|
||||
int x,
|
||||
int y,
|
||||
GHOST_TDragnDropDataPtr data)
|
||||
: GHOST_Event(time, type, window)
|
||||
{
|
||||
dragn_drop_event_data_.x = x;
|
||||
dragn_drop_event_data_.y = y;
|
||||
dragn_drop_event_data_.dataType = dataType;
|
||||
dragn_drop_event_data_.data = data;
|
||||
data_ = &dragn_drop_event_data_;
|
||||
}
|
||||
|
||||
~GHOST_EventDragnDrop() override
|
||||
{
|
||||
/* Free the dropped object data. */
|
||||
if (dragn_drop_event_data_.data == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dragn_drop_event_data_.dataType) {
|
||||
case GHOST_kDragnDropTypeBitmap:
|
||||
blender::IMB_freeImBuf((blender::ImBuf *)dragn_drop_event_data_.data);
|
||||
break;
|
||||
case GHOST_kDragnDropTypeFilenames: {
|
||||
GHOST_TStringArray *strArray = (GHOST_TStringArray *)dragn_drop_event_data_.data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < strArray->count; i++) {
|
||||
free(strArray->strings[i]);
|
||||
}
|
||||
|
||||
free(strArray->strings);
|
||||
free(strArray);
|
||||
break;
|
||||
}
|
||||
case GHOST_kDragnDropTypeString:
|
||||
free(dragn_drop_event_data_.data);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/** The x,y-coordinates of the cursor position. */
|
||||
GHOST_TEventDragnDropData dragn_drop_event_data_;
|
||||
};
|
||||
68
blender-5.2.0/intern/ghost/intern/GHOST_EventKey.hh
Normal file
68
blender-5.2.0/intern/ghost/intern/GHOST_EventKey.hh
Normal file
@@ -0,0 +1,68 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_EventKey class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "GHOST_Event.hh"
|
||||
|
||||
/**
|
||||
* Key event.
|
||||
*/
|
||||
class GHOST_EventKey : public GHOST_Event {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param msec: The time this event was generated.
|
||||
* \param type: The type of key event.
|
||||
* \param key: The key code of the key.
|
||||
* \param is_repeat: Enabled for key repeat events (only for press events).
|
||||
*/
|
||||
GHOST_EventKey(
|
||||
uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window, GHOST_TKey key, bool is_repeat)
|
||||
: GHOST_Event(msec, type, window)
|
||||
{
|
||||
key_event_data_.key = key;
|
||||
key_event_data_.utf8_buf[0] = '\0';
|
||||
key_event_data_.is_repeat = is_repeat;
|
||||
data_ = &key_event_data_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* \param msec: The time this event was generated.
|
||||
* \param type: The type of key event.
|
||||
* \param key: The key code of the key.
|
||||
* \param is_repeat: Enabled for key repeat events (only for press events).
|
||||
* \param utf8_buf: The text associated with this key event (only for press events).
|
||||
*/
|
||||
GHOST_EventKey(uint64_t msec,
|
||||
GHOST_TEventType type,
|
||||
GHOST_IWindow *window,
|
||||
GHOST_TKey key,
|
||||
bool is_repeat,
|
||||
const char utf8_buf[6])
|
||||
: GHOST_Event(msec, type, window)
|
||||
{
|
||||
key_event_data_.key = key;
|
||||
if (utf8_buf) {
|
||||
memcpy(key_event_data_.utf8_buf, utf8_buf, sizeof(key_event_data_.utf8_buf));
|
||||
}
|
||||
else {
|
||||
key_event_data_.utf8_buf[0] = '\0';
|
||||
}
|
||||
key_event_data_.is_repeat = is_repeat;
|
||||
data_ = &key_event_data_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** The key event data. */
|
||||
GHOST_TEventKeyData key_event_data_;
|
||||
};
|
||||
185
blender-5.2.0/intern/ghost/intern/GHOST_EventManager.cc
Normal file
185
blender-5.2.0/intern/ghost/intern/GHOST_EventManager.cc
Normal file
@@ -0,0 +1,185 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright (C) 2001 NaN Technologies B.V.
|
||||
*/
|
||||
|
||||
#include "GHOST_EventManager.hh"
|
||||
#include "GHOST_Debug.hh"
|
||||
#include <algorithm>
|
||||
|
||||
GHOST_EventManager::GHOST_EventManager() = default;
|
||||
|
||||
GHOST_EventManager::~GHOST_EventManager()
|
||||
{
|
||||
disposeEvents();
|
||||
|
||||
TConsumerVector::iterator iter = consumers_.begin();
|
||||
while (iter != consumers_.end()) {
|
||||
GHOST_IEventConsumer *consumer = *iter;
|
||||
delete consumer;
|
||||
iter = consumers_.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GHOST_EventManager::getNumEvents()
|
||||
{
|
||||
return uint32_t(events_.size());
|
||||
}
|
||||
|
||||
uint32_t GHOST_EventManager::getNumEvents(GHOST_TEventType type)
|
||||
{
|
||||
uint32_t numEvents = 0;
|
||||
TEventStack::iterator p;
|
||||
for (p = events_.begin(); p != events_.end(); ++p) {
|
||||
if ((*p)->getType() == type) {
|
||||
numEvents++;
|
||||
}
|
||||
}
|
||||
return numEvents;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_EventManager::pushEvent(std::unique_ptr<const GHOST_IEvent> event)
|
||||
{
|
||||
GHOST_TSuccess success;
|
||||
GHOST_ASSERT(event, "invalid event");
|
||||
if (events_.size() < events_.max_size()) {
|
||||
events_.push_front(std::move(event));
|
||||
success = GHOST_kSuccess;
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void GHOST_EventManager::dispatchEvent(const GHOST_IEvent *event)
|
||||
{
|
||||
TConsumerVector::iterator iter;
|
||||
|
||||
for (iter = consumers_.begin(); iter != consumers_.end(); ++iter) {
|
||||
(*iter)->processEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_EventManager::dispatchEvent()
|
||||
{
|
||||
std::unique_ptr<const GHOST_IEvent> event = std::move(events_.back());
|
||||
events_.pop_back();
|
||||
const GHOST_IEvent *event_ptr = event.get();
|
||||
handled_events_.push_back(std::move(event));
|
||||
|
||||
dispatchEvent(event_ptr);
|
||||
}
|
||||
|
||||
void GHOST_EventManager::dispatchEvents()
|
||||
{
|
||||
while (!events_.empty()) {
|
||||
dispatchEvent();
|
||||
}
|
||||
|
||||
disposeEvents();
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_EventManager::addConsumer(GHOST_IEventConsumer *consumer)
|
||||
{
|
||||
GHOST_TSuccess success;
|
||||
GHOST_ASSERT(consumer, "invalid consumer");
|
||||
|
||||
/* Check to see whether the consumer is already in our list. */
|
||||
TConsumerVector::const_iterator iter = std::find(consumers_.begin(), consumers_.end(), consumer);
|
||||
|
||||
if (iter == consumers_.end()) {
|
||||
/* Add the consumer. */
|
||||
consumers_.push_back(consumer);
|
||||
success = GHOST_kSuccess;
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_EventManager::removeConsumer(GHOST_IEventConsumer *consumer)
|
||||
{
|
||||
GHOST_TSuccess success;
|
||||
GHOST_ASSERT(consumer, "invalid consumer");
|
||||
|
||||
/* Check to see whether the consumer is in our list. */
|
||||
TConsumerVector::iterator iter = std::find(consumers_.begin(), consumers_.end(), consumer);
|
||||
|
||||
if (iter != consumers_.end()) {
|
||||
/* Remove the consumer. */
|
||||
consumers_.erase(iter);
|
||||
success = GHOST_kSuccess;
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void GHOST_EventManager::removeWindowEvents(const GHOST_IWindow *window)
|
||||
{
|
||||
TEventStack::iterator iter;
|
||||
iter = events_.begin();
|
||||
while (iter != events_.end()) {
|
||||
std::unique_ptr<const GHOST_IEvent> &event = *iter;
|
||||
if (event->getWindow() == window) {
|
||||
GHOST_PRINT("GHOST_EventManager::removeWindowEvents(): removing event\n");
|
||||
/*
|
||||
* Found an event for this window, remove it.
|
||||
* The iterator will become invalid.
|
||||
*/
|
||||
event.reset();
|
||||
events_.erase(iter);
|
||||
iter = events_.begin();
|
||||
}
|
||||
else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_EventManager::removeTypeEvents(GHOST_TEventType type, const GHOST_IWindow *window)
|
||||
{
|
||||
TEventStack::iterator iter;
|
||||
iter = events_.begin();
|
||||
while (iter != events_.end()) {
|
||||
std::unique_ptr<const GHOST_IEvent> &event = *iter;
|
||||
if ((event->getType() == type) && (!window || (event->getWindow() == window))) {
|
||||
GHOST_PRINT("GHOST_EventManager::removeTypeEvents(): removing event\n");
|
||||
/*
|
||||
* Found an event of this type for the window, remove it.
|
||||
* The iterator will become invalid.
|
||||
*/
|
||||
event.reset();
|
||||
events_.erase(iter);
|
||||
iter = events_.begin();
|
||||
}
|
||||
else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_EventManager::disposeEvents()
|
||||
{
|
||||
while (handled_events_.empty() == false) {
|
||||
GHOST_ASSERT(handled_events_[0], "invalid event");
|
||||
handled_events_[0].reset();
|
||||
handled_events_.pop_front();
|
||||
}
|
||||
|
||||
while (events_.empty() == false) {
|
||||
GHOST_ASSERT(events_[0], "invalid event");
|
||||
events_[0].reset();
|
||||
events_.pop_front();
|
||||
}
|
||||
}
|
||||
124
blender-5.2.0/intern/ghost/intern/GHOST_EventManager.hh
Normal file
124
blender-5.2.0/intern/ghost/intern/GHOST_EventManager.hh
Normal file
@@ -0,0 +1,124 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_EventManager class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
#include "GHOST_IEventConsumer.hh"
|
||||
|
||||
/**
|
||||
* Manages an event stack and a list of event consumers.
|
||||
* The stack works on a FIFO (First In First Out) basis.
|
||||
* Events are pushed on the front of the stack and retrieved from the back.
|
||||
* Ownership of the event is transferred to the event manager as soon as an event is pushed.
|
||||
* Ownership of the event is transferred from the event manager as soon as an event is popped.
|
||||
* Events can be dispatched to the event consumers.
|
||||
*/
|
||||
class GHOST_EventManager {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_EventManager();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_EventManager();
|
||||
|
||||
/**
|
||||
* Returns the number of events currently on the stack.
|
||||
* \return The number of events on the stack.
|
||||
*/
|
||||
uint32_t getNumEvents();
|
||||
|
||||
/**
|
||||
* Returns the number of events of a certain type currently on the stack.
|
||||
* \param type: The type of events to be counted.
|
||||
* \return The number of events on the stack of this type.
|
||||
*/
|
||||
uint32_t getNumEvents(GHOST_TEventType type);
|
||||
|
||||
/**
|
||||
* Pushes an event on the stack.
|
||||
* To dispatch it, call dispatchEvent() or dispatchEvents().
|
||||
* Do not delete the event!
|
||||
* \param event: The event to push on the stack.
|
||||
*/
|
||||
GHOST_TSuccess pushEvent(std::unique_ptr<const GHOST_IEvent> event);
|
||||
|
||||
/**
|
||||
* Dispatches the given event directly, bypassing the event stack.
|
||||
*/
|
||||
void dispatchEvent(const GHOST_IEvent *event);
|
||||
|
||||
/**
|
||||
* Dispatches the event at the back of the stack.
|
||||
* The event will be removed from the stack.
|
||||
*/
|
||||
void dispatchEvent();
|
||||
|
||||
/**
|
||||
* Dispatches all the events on the stack.
|
||||
* The event stack will be empty afterwards.
|
||||
*/
|
||||
void dispatchEvents();
|
||||
|
||||
/**
|
||||
* Adds a consumer to the list of event consumers.
|
||||
* \param consumer: The consumer added to the list.
|
||||
* \return Indication as to whether addition has succeeded.
|
||||
*/
|
||||
GHOST_TSuccess addConsumer(GHOST_IEventConsumer *consumer);
|
||||
|
||||
/**
|
||||
* Removes a consumer from the list of event consumers.
|
||||
* \param consumer: The consumer removed from the list.
|
||||
* \return Indication as to whether removal has succeeded.
|
||||
*/
|
||||
GHOST_TSuccess removeConsumer(GHOST_IEventConsumer *consumer);
|
||||
|
||||
/**
|
||||
* Removes all events for a window from the stack.
|
||||
* \param window: The window to remove events for.
|
||||
*/
|
||||
void removeWindowEvents(const GHOST_IWindow *window);
|
||||
|
||||
/**
|
||||
* Removes all events of a certain type from the stack.
|
||||
* The window parameter is optional.
|
||||
* If non-null, the routine will remove events only associated with that window.
|
||||
* \param type: The type of events to be removed.
|
||||
* \param window: The window to remove the events for.
|
||||
*/
|
||||
void removeTypeEvents(GHOST_TEventType type, const GHOST_IWindow *window = nullptr);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Removes all events from the stack.
|
||||
*/
|
||||
void disposeEvents();
|
||||
|
||||
/** A stack with events. */
|
||||
using TEventStack = std::deque<std::unique_ptr<const GHOST_IEvent>>;
|
||||
|
||||
/** The event stack. */
|
||||
TEventStack events_;
|
||||
TEventStack handled_events_;
|
||||
|
||||
/** A vector with event consumers. */
|
||||
using TConsumerVector = std::vector<GHOST_IEventConsumer *>;
|
||||
|
||||
/** The list with event consumers. */
|
||||
TConsumerVector consumers_;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_EventManager")
|
||||
};
|
||||
39
blender-5.2.0/intern/ghost/intern/GHOST_EventNDOF.hh
Normal file
39
blender-5.2.0/intern/ghost/intern/GHOST_EventNDOF.hh
Normal file
@@ -0,0 +1,39 @@
|
||||
/* SPDX-FileCopyrightText: 2002-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef WITH_INPUT_NDOF
|
||||
# error NDOF code included in non-NDOF-enabled build
|
||||
#endif
|
||||
|
||||
#include "GHOST_Event.hh"
|
||||
|
||||
class GHOST_EventNDOFMotion : public GHOST_Event {
|
||||
protected:
|
||||
GHOST_TEventNDOFMotionData axis_data_;
|
||||
|
||||
public:
|
||||
GHOST_EventNDOFMotion(uint64_t time, GHOST_IWindow *window)
|
||||
: GHOST_Event(time, GHOST_kEventNDOFMotion, window)
|
||||
{
|
||||
data_ = &axis_data_;
|
||||
}
|
||||
};
|
||||
|
||||
class GHOST_EventNDOFButton : public GHOST_Event {
|
||||
protected:
|
||||
GHOST_TEventNDOFButtonData button_data_;
|
||||
|
||||
public:
|
||||
GHOST_EventNDOFButton(uint64_t time, GHOST_IWindow *window)
|
||||
: GHOST_Event(time, GHOST_kEventNDOFButton, window)
|
||||
{
|
||||
data_ = &button_data_;
|
||||
}
|
||||
};
|
||||
374
blender-5.2.0/intern/ghost/intern/GHOST_EventPrinter.cc
Normal file
374
blender-5.2.0/intern/ghost/intern/GHOST_EventPrinter.cc
Normal file
@@ -0,0 +1,374 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_EventPrinter class.
|
||||
*/
|
||||
|
||||
#include "GHOST_EventPrinter.hh"
|
||||
#include "GHOST_Debug.hh"
|
||||
#include "GHOST_EventDragnDrop.hh"
|
||||
#include "GHOST_EventKey.hh"
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
/* For now only used with NDOF. */
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
static const char *getButtonActionString(const GHOST_TButtonAction action)
|
||||
{
|
||||
switch (action) {
|
||||
case GHOST_kPress:
|
||||
return "Press";
|
||||
case GHOST_kRelease:
|
||||
return "Release";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
#endif /* WITH_INPUT_NDOF */
|
||||
|
||||
bool GHOST_EventPrinter::processEvent(const GHOST_IEvent *event)
|
||||
{
|
||||
bool handled = false;
|
||||
|
||||
GHOST_ASSERT(event, "event==0");
|
||||
|
||||
if (event->getType() == GHOST_kEventWindowUpdate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GHOST_TEventDataPtr data = event->getData();
|
||||
|
||||
std::cout << "GHOST_EventPrinter::processEvent, time: " << int32_t(event->getTime())
|
||||
<< ", type: ";
|
||||
|
||||
#define CASE_TYPE(ty) \
|
||||
case ty: { \
|
||||
std::cout << #ty; \
|
||||
handled = true; \
|
||||
break; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
const GHOST_TEventType event_type = event->getType();
|
||||
switch (event_type) {
|
||||
case GHOST_kEventUnknown: {
|
||||
std::cout << "GHOST_kEventUnknown";
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventCursorMove: {
|
||||
const GHOST_TEventCursorData *cursorData = static_cast<const GHOST_TEventCursorData *>(data);
|
||||
std::cout << "GHOST_kEventCursorMove, (x,y): (" << cursorData->x << "," << cursorData->y
|
||||
<< ")";
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventButtonDown: {
|
||||
const GHOST_TEventButtonData *buttonData = static_cast<const GHOST_TEventButtonData *>(data);
|
||||
std::cout << "GHOST_kEventButtonDown, button: " << buttonData->button;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventButtonUp: {
|
||||
const GHOST_TEventButtonData *buttonData = static_cast<const GHOST_TEventButtonData *>(data);
|
||||
std::cout << "GHOST_kEventCursorButtonUp, button: " << buttonData->button;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventWheel: {
|
||||
const GHOST_TEventWheelData *wheelData = static_cast<const GHOST_TEventWheelData *>(data);
|
||||
std::cout << "GHOST_kEventWheel, axis: "
|
||||
<< (wheelData->axis == GHOST_kEventWheelAxisVertical ? "vertical" : "horizontal")
|
||||
<< ", value: " << wheelData->value;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
CASE_TYPE(GHOST_kEventTrackpad);
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
case GHOST_kEventNDOFMotion: {
|
||||
const GHOST_TEventNDOFMotionData *ndof_motion =
|
||||
static_cast<const GHOST_TEventNDOFMotionData *>(data);
|
||||
std::cout << "GHOST_kEventNDOFMotion: ";
|
||||
std::cout << std::fixed << std::setprecision(2) <<
|
||||
/* Translation. */
|
||||
"tx=" << ndof_motion->tx << " ty=" << ndof_motion->tx << " tz=" << ndof_motion->tz <<
|
||||
/* Rotation. */
|
||||
"rx=" << ndof_motion->rx << " ry=" << ndof_motion->ry << " rz=" << ndof_motion->rz;
|
||||
std::cout << std::fixed << std::setprecision(4) << " dt=" << ndof_motion->dt;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventNDOFButton: {
|
||||
const GHOST_TEventNDOFButtonData *ndof_button =
|
||||
static_cast<const GHOST_TEventNDOFButtonData *>(data);
|
||||
std::cout << "GHOST_kEventNDOFButton: " << getButtonActionString(ndof_button->action)
|
||||
<< " button=" << ndof_button->button;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
#endif /* WITH_INPUT_NDOF */
|
||||
|
||||
case GHOST_kEventKeyDown: {
|
||||
const GHOST_TEventKeyData *keyData = static_cast<const GHOST_TEventKeyData *>(data);
|
||||
std::cout << "GHOST_kEventKeyDown, key: " << getKeyString(keyData->key);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventKeyUp: {
|
||||
const GHOST_TEventKeyData *keyData = static_cast<const GHOST_TEventKeyData *>(data);
|
||||
std::cout << "GHOST_kEventKeyUp, key: " << getKeyString(keyData->key);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
CASE_TYPE(GHOST_kEventQuitRequest);
|
||||
CASE_TYPE(GHOST_kEventWindowClose);
|
||||
CASE_TYPE(GHOST_kEventWindowActivate);
|
||||
CASE_TYPE(GHOST_kEventWindowDeactivate);
|
||||
CASE_TYPE(GHOST_kEventWindowUpdate);
|
||||
CASE_TYPE(GHOST_kEventWindowUpdateDecor);
|
||||
CASE_TYPE(GHOST_kEventWindowSize);
|
||||
CASE_TYPE(GHOST_kEventWindowMove);
|
||||
CASE_TYPE(GHOST_kEventWindowDPIHintChanged);
|
||||
|
||||
case GHOST_kEventDraggingEntered: {
|
||||
const GHOST_TEventDragnDropData *dragnDropData =
|
||||
static_cast<const GHOST_TEventDragnDropData *>(data);
|
||||
std::cout << "GHOST_kEventDraggingEntered, dragged object type : "
|
||||
<< dragnDropData->dataType;
|
||||
std::cout << " mouse at x=" << dragnDropData->x << " y=" << dragnDropData->y;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventDraggingUpdated: {
|
||||
const GHOST_TEventDragnDropData *dragnDropData =
|
||||
static_cast<const GHOST_TEventDragnDropData *>(data);
|
||||
std::cout << "GHOST_kEventDraggingUpdated, dragged object type : "
|
||||
<< dragnDropData->dataType;
|
||||
std::cout << " mouse at x=" << dragnDropData->x << " y=" << dragnDropData->y;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventDraggingExited: {
|
||||
const GHOST_TEventDragnDropData *dragnDropData =
|
||||
static_cast<const GHOST_TEventDragnDropData *>(data);
|
||||
std::cout << "GHOST_kEventDraggingExited, dragged object type : " << dragnDropData->dataType;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventDraggingDropDone: {
|
||||
const GHOST_TEventDragnDropData *dragnDropData =
|
||||
static_cast<const GHOST_TEventDragnDropData *>(data);
|
||||
std::cout << "GHOST_kEventDraggingDropDone,";
|
||||
std::cout << " mouse at x=" << dragnDropData->x << " y=" << dragnDropData->y;
|
||||
switch (dragnDropData->dataType) {
|
||||
case GHOST_kDragnDropTypeString: {
|
||||
std::cout << " type : GHOST_kDragnDropTypeString,";
|
||||
std::cout << "\n String received = " << (char *)dragnDropData->data;
|
||||
break;
|
||||
}
|
||||
case GHOST_kDragnDropTypeFilenames: {
|
||||
GHOST_TStringArray *strArray = (GHOST_TStringArray *)dragnDropData->data;
|
||||
int i;
|
||||
std::cout << " type : GHOST_kDragnDropTypeFilenames,";
|
||||
std::cout << "\n Received " << strArray->count << " filename"
|
||||
<< (strArray->count > 1 ? "s:" : ":");
|
||||
for (i = 0; i < strArray->count; i++) {
|
||||
std::cout << "\n File[" << i << "] : " << strArray->strings[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventOpenMainFile: {
|
||||
if (data) {
|
||||
std::cout << "GHOST_kEventOpenMainFile for path: " << static_cast<const char *>(data);
|
||||
}
|
||||
else {
|
||||
std::cout << "GHOST_kEventOpenMainFile with no path specified!!";
|
||||
}
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
CASE_TYPE(GHOST_kEventNativeResolutionChange);
|
||||
|
||||
CASE_TYPE(GHOST_kEventImeCompositionStart);
|
||||
CASE_TYPE(GHOST_kEventImeComposition);
|
||||
CASE_TYPE(GHOST_kEventImeCompositionEnd);
|
||||
}
|
||||
|
||||
#undef CASE_TYPE
|
||||
|
||||
if ((handled == false) && event_type != GHOST_kEventUnknown) {
|
||||
std::cout << "not found";
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
std::cout.flush();
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
const char *GHOST_EventPrinter::getKeyString(const GHOST_TKey key) const
|
||||
{
|
||||
const char *tstr = nullptr;
|
||||
|
||||
#define CASE_KEY(k, v) \
|
||||
case k: { \
|
||||
tstr = v; \
|
||||
break; \
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
CASE_KEY(GHOST_kKeyBackSpace, "BackSpace");
|
||||
CASE_KEY(GHOST_kKeyTab, "Tab");
|
||||
CASE_KEY(GHOST_kKeyLinefeed, "Linefeed");
|
||||
CASE_KEY(GHOST_kKeyClear, "Clear");
|
||||
CASE_KEY(GHOST_kKeyEnter, "Enter");
|
||||
CASE_KEY(GHOST_kKeyEsc, "Esc");
|
||||
CASE_KEY(GHOST_kKeySpace, "Space");
|
||||
CASE_KEY(GHOST_kKeyQuote, "Quote");
|
||||
CASE_KEY(GHOST_kKeyBackslash, "\\");
|
||||
CASE_KEY(GHOST_kKeyAccentGrave, "`");
|
||||
CASE_KEY(GHOST_kKeyLeftShift, "LeftShift");
|
||||
CASE_KEY(GHOST_kKeyRightShift, "RightShift");
|
||||
CASE_KEY(GHOST_kKeyLeftControl, "LeftControl");
|
||||
CASE_KEY(GHOST_kKeyRightControl, "RightControl");
|
||||
CASE_KEY(GHOST_kKeyLeftAlt, "LeftAlt");
|
||||
CASE_KEY(GHOST_kKeyRightAlt, "RightAlt");
|
||||
CASE_KEY(GHOST_kKeyLeftOS, "LeftOS");
|
||||
CASE_KEY(GHOST_kKeyRightOS, "RightOS");
|
||||
CASE_KEY(GHOST_kKeyLeftHyper, "LeftHyper");
|
||||
CASE_KEY(GHOST_kKeyRightHyper, "RightHyper");
|
||||
CASE_KEY(GHOST_kKeyApp, "App");
|
||||
CASE_KEY(GHOST_kKeyGrLess, "GrLess");
|
||||
CASE_KEY(GHOST_kKeyCapsLock, "CapsLock");
|
||||
CASE_KEY(GHOST_kKeyNumLock, "NumLock");
|
||||
CASE_KEY(GHOST_kKeyScrollLock, "ScrollLock");
|
||||
CASE_KEY(GHOST_kKeyLeftArrow, "LeftArrow");
|
||||
CASE_KEY(GHOST_kKeyRightArrow, "RightArrow");
|
||||
CASE_KEY(GHOST_kKeyUpArrow, "UpArrow");
|
||||
CASE_KEY(GHOST_kKeyDownArrow, "DownArrow");
|
||||
CASE_KEY(GHOST_kKeyPrintScreen, "PrintScreen");
|
||||
CASE_KEY(GHOST_kKeyPause, "Pause");
|
||||
CASE_KEY(GHOST_kKeyInsert, "Insert");
|
||||
CASE_KEY(GHOST_kKeyDelete, "Delete");
|
||||
CASE_KEY(GHOST_kKeyHome, "Home");
|
||||
CASE_KEY(GHOST_kKeyEnd, "End");
|
||||
CASE_KEY(GHOST_kKeyUpPage, "UpPage");
|
||||
CASE_KEY(GHOST_kKeyDownPage, "DownPage");
|
||||
CASE_KEY(GHOST_kKeyNumpadPeriod, "NumpadPeriod");
|
||||
CASE_KEY(GHOST_kKeyNumpadEnter, "NumpadEnter");
|
||||
CASE_KEY(GHOST_kKeyNumpadPlus, "NumpadPlus");
|
||||
CASE_KEY(GHOST_kKeyNumpadMinus, "NumpadMinus");
|
||||
CASE_KEY(GHOST_kKeyNumpadAsterisk, "NumpadAsterisk");
|
||||
CASE_KEY(GHOST_kKeyNumpadSlash, "NumpadSlash");
|
||||
CASE_KEY(GHOST_kKeyMediaPlay, "MediaPlayPause");
|
||||
CASE_KEY(GHOST_kKeyMediaStop, "MediaStop");
|
||||
CASE_KEY(GHOST_kKeyMediaFirst, "MediaFirst");
|
||||
CASE_KEY(GHOST_kKeyMediaLast, "MediaLast");
|
||||
CASE_KEY(GHOST_kKeyNumpad0, "Numpad 0");
|
||||
CASE_KEY(GHOST_kKeyNumpad1, "Numpad 1");
|
||||
CASE_KEY(GHOST_kKeyNumpad2, "Numpad 2");
|
||||
CASE_KEY(GHOST_kKeyNumpad3, "Numpad 3");
|
||||
CASE_KEY(GHOST_kKeyNumpad4, "Numpad 4");
|
||||
CASE_KEY(GHOST_kKeyNumpad5, "Numpad 5");
|
||||
CASE_KEY(GHOST_kKeyNumpad6, "Numpad 6");
|
||||
CASE_KEY(GHOST_kKeyNumpad7, "Numpad 7");
|
||||
CASE_KEY(GHOST_kKeyNumpad8, "Numpad 8");
|
||||
CASE_KEY(GHOST_kKeyNumpad9, "Numpad 9");
|
||||
|
||||
CASE_KEY(GHOST_kKeyF1, "F1");
|
||||
CASE_KEY(GHOST_kKeyF2, "F2");
|
||||
CASE_KEY(GHOST_kKeyF3, "F3");
|
||||
CASE_KEY(GHOST_kKeyF4, "F4");
|
||||
CASE_KEY(GHOST_kKeyF5, "F5");
|
||||
CASE_KEY(GHOST_kKeyF6, "F6");
|
||||
CASE_KEY(GHOST_kKeyF7, "F7");
|
||||
CASE_KEY(GHOST_kKeyF8, "F8");
|
||||
CASE_KEY(GHOST_kKeyF9, "F9");
|
||||
CASE_KEY(GHOST_kKeyF10, "F10");
|
||||
CASE_KEY(GHOST_kKeyF11, "F11");
|
||||
CASE_KEY(GHOST_kKeyF12, "F12");
|
||||
CASE_KEY(GHOST_kKeyF13, "F13");
|
||||
CASE_KEY(GHOST_kKeyF14, "F14");
|
||||
CASE_KEY(GHOST_kKeyF15, "F15");
|
||||
CASE_KEY(GHOST_kKeyF16, "F16");
|
||||
CASE_KEY(GHOST_kKeyF17, "F17");
|
||||
CASE_KEY(GHOST_kKeyF18, "F18");
|
||||
CASE_KEY(GHOST_kKeyF19, "F19");
|
||||
CASE_KEY(GHOST_kKeyF20, "F20");
|
||||
CASE_KEY(GHOST_kKeyF21, "F21");
|
||||
CASE_KEY(GHOST_kKeyF22, "F22");
|
||||
CASE_KEY(GHOST_kKeyF23, "F23");
|
||||
CASE_KEY(GHOST_kKeyF24, "F24");
|
||||
|
||||
CASE_KEY(GHOST_kKeyUnknown, "Unknown");
|
||||
|
||||
CASE_KEY(GHOST_kKeyComma, ",");
|
||||
CASE_KEY(GHOST_kKeyMinus, "-");
|
||||
CASE_KEY(GHOST_kKeyPlus, "=");
|
||||
CASE_KEY(GHOST_kKeyPeriod, ".");
|
||||
CASE_KEY(GHOST_kKeySlash, "/");
|
||||
CASE_KEY(GHOST_kKey0, "0");
|
||||
CASE_KEY(GHOST_kKey1, "1");
|
||||
CASE_KEY(GHOST_kKey2, "2");
|
||||
CASE_KEY(GHOST_kKey3, "3");
|
||||
CASE_KEY(GHOST_kKey4, "4");
|
||||
CASE_KEY(GHOST_kKey5, "5");
|
||||
CASE_KEY(GHOST_kKey6, "6");
|
||||
CASE_KEY(GHOST_kKey7, "7");
|
||||
CASE_KEY(GHOST_kKey8, "8");
|
||||
CASE_KEY(GHOST_kKey9, "9");
|
||||
CASE_KEY(GHOST_kKeySemicolon, ";");
|
||||
CASE_KEY(GHOST_kKeyEqual, "=");
|
||||
CASE_KEY(GHOST_kKeyA, "A");
|
||||
CASE_KEY(GHOST_kKeyB, "B");
|
||||
CASE_KEY(GHOST_kKeyC, "C");
|
||||
CASE_KEY(GHOST_kKeyD, "D");
|
||||
CASE_KEY(GHOST_kKeyE, "E");
|
||||
CASE_KEY(GHOST_kKeyF, "F");
|
||||
CASE_KEY(GHOST_kKeyG, "G");
|
||||
CASE_KEY(GHOST_kKeyH, "H");
|
||||
CASE_KEY(GHOST_kKeyI, "I");
|
||||
CASE_KEY(GHOST_kKeyJ, "J");
|
||||
CASE_KEY(GHOST_kKeyK, "K");
|
||||
CASE_KEY(GHOST_kKeyL, "L");
|
||||
CASE_KEY(GHOST_kKeyM, "M");
|
||||
CASE_KEY(GHOST_kKeyN, "N");
|
||||
CASE_KEY(GHOST_kKeyO, "O");
|
||||
CASE_KEY(GHOST_kKeyP, "P");
|
||||
CASE_KEY(GHOST_kKeyQ, "Q");
|
||||
CASE_KEY(GHOST_kKeyR, "R");
|
||||
CASE_KEY(GHOST_kKeyS, "S");
|
||||
CASE_KEY(GHOST_kKeyT, "T");
|
||||
CASE_KEY(GHOST_kKeyU, "U");
|
||||
CASE_KEY(GHOST_kKeyV, "V");
|
||||
CASE_KEY(GHOST_kKeyW, "W");
|
||||
CASE_KEY(GHOST_kKeyX, "X");
|
||||
CASE_KEY(GHOST_kKeyY, "Y");
|
||||
CASE_KEY(GHOST_kKeyZ, "Z");
|
||||
CASE_KEY(GHOST_kKeyLeftBracket, "[");
|
||||
CASE_KEY(GHOST_kKeyRightBracket, "]");
|
||||
}
|
||||
|
||||
#undef CASE_KEY
|
||||
|
||||
/* Shouldn't happen (the value is not known to #GHOST_TKey). */
|
||||
if (tstr == nullptr) {
|
||||
tstr = "Invalid";
|
||||
}
|
||||
return tstr;
|
||||
}
|
||||
34
blender-5.2.0/intern/ghost/intern/GHOST_EventPrinter.hh
Normal file
34
blender-5.2.0/intern/ghost/intern/GHOST_EventPrinter.hh
Normal file
@@ -0,0 +1,34 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_EventPrinter class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_IEventConsumer.hh"
|
||||
|
||||
/**
|
||||
* An Event consumer that prints all the events to standard out.
|
||||
* Really useful when debugging.
|
||||
*/
|
||||
class GHOST_EventPrinter : public GHOST_IEventConsumer {
|
||||
public:
|
||||
/**
|
||||
* Prints all the events received to std out.
|
||||
* \param event: The event that can be handled or not.
|
||||
* \return Indication as to whether the event was handled.
|
||||
*/
|
||||
bool processEvent(const GHOST_IEvent *event) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Converts GHOST key code to a readable string.
|
||||
* \param key: The GHOST key code to convert.
|
||||
* \return The GHOST key code converted to a readable string.
|
||||
*/
|
||||
const char *getKeyString(GHOST_TKey key) const;
|
||||
};
|
||||
41
blender-5.2.0/intern/ghost/intern/GHOST_EventString.hh
Normal file
41
blender-5.2.0/intern/ghost/intern/GHOST_EventString.hh
Normal file
@@ -0,0 +1,41 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_EventString class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Event.hh"
|
||||
|
||||
/**
|
||||
* Generic class for events with string data
|
||||
*/
|
||||
class GHOST_EventString : public GHOST_Event {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param msec: The time this event was generated.
|
||||
* \param type: The type of this event.
|
||||
* \param window: The generating window (or nullptr if system event).
|
||||
* \param data_ptr: Pointer to the (un-formatted) data associated with the event.
|
||||
*/
|
||||
GHOST_EventString(uint64_t msec,
|
||||
GHOST_TEventType type,
|
||||
GHOST_IWindow *window,
|
||||
GHOST_TEventDataPtr data_ptr)
|
||||
: GHOST_Event(msec, type, window)
|
||||
{
|
||||
data_ = data_ptr;
|
||||
}
|
||||
|
||||
~GHOST_EventString() override
|
||||
{
|
||||
if (data_) {
|
||||
free((void *)data_);
|
||||
}
|
||||
}
|
||||
};
|
||||
49
blender-5.2.0/intern/ghost/intern/GHOST_EventTrackpad.hh
Normal file
49
blender-5.2.0/intern/ghost/intern/GHOST_EventTrackpad.hh
Normal file
@@ -0,0 +1,49 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_EventTrackpad class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Event.hh"
|
||||
|
||||
/**
|
||||
* Trackpad (scroll, magnify, rotate, ...) event.
|
||||
*/
|
||||
class GHOST_EventTrackpad : public GHOST_Event {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param msec: The time this event was generated.
|
||||
* \param window: The window of this event.
|
||||
* \param subtype: The subtype of the event.
|
||||
* \param x: The x-delta of the pan event.
|
||||
* \param y: The y-delta of the pan event.
|
||||
*/
|
||||
GHOST_EventTrackpad(uint64_t msec,
|
||||
GHOST_IWindow *window,
|
||||
GHOST_TTrackpadEventSubTypes subtype,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
int32_t deltaX,
|
||||
int32_t deltaY,
|
||||
bool isDirectionInverted)
|
||||
: GHOST_Event(msec, GHOST_kEventTrackpad, window)
|
||||
{
|
||||
trackpad_event_data_.subtype = subtype;
|
||||
trackpad_event_data_.x = x;
|
||||
trackpad_event_data_.y = y;
|
||||
trackpad_event_data_.deltaX = deltaX;
|
||||
trackpad_event_data_.deltaY = deltaY;
|
||||
trackpad_event_data_.isDirectionInverted = isDirectionInverted;
|
||||
data_ = &trackpad_event_data_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** The mouse pan data */
|
||||
GHOST_TEventTrackpadData trackpad_event_data_;
|
||||
};
|
||||
38
blender-5.2.0/intern/ghost/intern/GHOST_EventWheel.hh
Normal file
38
blender-5.2.0/intern/ghost/intern/GHOST_EventWheel.hh
Normal file
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_EventWheel class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Event.hh"
|
||||
|
||||
/**
|
||||
* Mouse wheel event.
|
||||
* The displacement of the mouse wheel is counted in ticks.
|
||||
* A positive value means the wheel is turned away from the user.
|
||||
*/
|
||||
class GHOST_EventWheel : public GHOST_Event {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param msec: The time this event was generated.
|
||||
* \param window: The window of this event.
|
||||
* \param axis: The axis of the mouse wheel.
|
||||
* \param value: The displacement of the mouse wheel.
|
||||
*/
|
||||
GHOST_EventWheel(uint64_t msec, GHOST_IWindow *window, GHOST_TEventWheelAxis axis, int32_t value)
|
||||
: GHOST_Event(msec, GHOST_kEventWheel, window)
|
||||
{
|
||||
wheel_event_data_.axis = axis;
|
||||
wheel_event_data_.value = value;
|
||||
data_ = &wheel_event_data_;
|
||||
}
|
||||
|
||||
protected:
|
||||
GHOST_TEventWheelData wheel_event_data_;
|
||||
};
|
||||
296
blender-5.2.0/intern/ghost/intern/GHOST_ISystem.cc
Normal file
296
blender-5.2.0/intern/ghost/intern/GHOST_ISystem.cc
Normal file
@@ -0,0 +1,296 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright (C) 2001 NaN Technologies B.V.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "GHOST_ISystem.hh"
|
||||
#include "GHOST_SystemHeadless.hh"
|
||||
|
||||
#if defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND)
|
||||
# include "GHOST_SystemWayland.hh"
|
||||
# include "GHOST_SystemX11.hh"
|
||||
#elif defined(WITH_GHOST_X11)
|
||||
# include "GHOST_SystemX11.hh"
|
||||
#elif defined(WITH_GHOST_WAYLAND)
|
||||
# include "GHOST_SystemWayland.hh"
|
||||
#elif defined(WITH_GHOST_SDL)
|
||||
# include "GHOST_SystemSDL.hh"
|
||||
#elif defined(WIN32)
|
||||
# include "GHOST_SystemWin32.hh"
|
||||
#elif defined(__APPLE__)
|
||||
# include "GHOST_SystemCocoa.hh"
|
||||
#endif
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
static CLG_LogRef LOG = {"ghost.system"};
|
||||
|
||||
GHOST_ISystem *GHOST_ISystem::system_ = nullptr;
|
||||
const char *GHOST_ISystem::system_backend_id_ = nullptr;
|
||||
|
||||
GHOST_TBacktraceFn GHOST_ISystem::backtrace_fn_ = nullptr;
|
||||
|
||||
bool GHOST_ISystem::use_window_frame_ = true;
|
||||
|
||||
GHOST_TSuccess GHOST_ISystem::createSystem(bool verbose, [[maybe_unused]] bool background)
|
||||
{
|
||||
|
||||
/* When GHOST fails to start, report the back-ends that were attempted.
|
||||
* A Verbose argument could be supported in printing isn't always desired. */
|
||||
struct GHOST_BackendInfo {
|
||||
const char *id = nullptr;
|
||||
/** The cause of the failure. */
|
||||
std::string failure_msg;
|
||||
};
|
||||
std::vector<GHOST_BackendInfo> backends_attempted;
|
||||
|
||||
GHOST_TSuccess success;
|
||||
if (!system_) {
|
||||
#if defined(WITH_HEADLESS)
|
||||
/* Pass. */
|
||||
#elif defined(WITH_GHOST_WAYLAND)
|
||||
# if defined(WITH_GHOST_WAYLAND_DYNLOAD)
|
||||
const bool has_wayland_libraries = ghost_wl_dynload_libraries_init();
|
||||
# else
|
||||
const bool has_wayland_libraries = true;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(WITH_HEADLESS)
|
||||
/* Pass. */
|
||||
#elif defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND)
|
||||
/* Special case, try Wayland, fall back to X11. */
|
||||
if (has_wayland_libraries) {
|
||||
backends_attempted.push_back({"WAYLAND"});
|
||||
try {
|
||||
CLOG_INFO(&LOG, "Create Wayland system");
|
||||
system_ = new GHOST_SystemWayland(background);
|
||||
}
|
||||
catch (const std::runtime_error &e) {
|
||||
if (verbose) {
|
||||
backends_attempted.back().failure_msg = e.what();
|
||||
}
|
||||
CLOG_INFO(&LOG, "Wayland system not created, falling back to X11");
|
||||
delete system_;
|
||||
system_ = nullptr;
|
||||
# ifdef WITH_GHOST_WAYLAND_DYNLOAD
|
||||
ghost_wl_dynload_libraries_exit();
|
||||
# endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
system_ = nullptr;
|
||||
}
|
||||
|
||||
if (!system_) {
|
||||
/* Try to fall back to X11. */
|
||||
backends_attempted.push_back({"X11"});
|
||||
try {
|
||||
CLOG_INFO(&LOG, "Create X11 system");
|
||||
system_ = new GHOST_SystemX11();
|
||||
}
|
||||
catch (const std::runtime_error &e) {
|
||||
if (verbose) {
|
||||
backends_attempted.back().failure_msg = e.what();
|
||||
}
|
||||
delete system_;
|
||||
system_ = nullptr;
|
||||
}
|
||||
}
|
||||
#elif defined(WITH_GHOST_X11)
|
||||
backends_attempted.push_back({"X11"});
|
||||
try {
|
||||
CLOG_INFO(&LOG, "Create X11 system");
|
||||
system_ = new GHOST_SystemX11();
|
||||
}
|
||||
catch (const std::runtime_error &e) {
|
||||
if (verbose) {
|
||||
backends_attempted.back().failure_msg = e.what();
|
||||
}
|
||||
delete system_;
|
||||
system_ = nullptr;
|
||||
}
|
||||
#elif defined(WITH_GHOST_WAYLAND)
|
||||
if (has_wayland_libraries) {
|
||||
backends_attempted.push_back({"WAYLAND"});
|
||||
try {
|
||||
CLOG_INFO(&LOG, "Create Wayland system");
|
||||
system_ = new GHOST_SystemWayland(background);
|
||||
}
|
||||
catch (const std::runtime_error &e) {
|
||||
if (verbose) {
|
||||
backends_attempted.back().failure_msg = e.what();
|
||||
}
|
||||
delete system_;
|
||||
system_ = nullptr;
|
||||
# ifdef WITH_GHOST_WAYLAND_DYNLOAD
|
||||
ghost_wl_dynload_libraries_exit();
|
||||
# endif
|
||||
}
|
||||
}
|
||||
else {
|
||||
system_ = nullptr;
|
||||
}
|
||||
#elif defined(WITH_GHOST_SDL)
|
||||
backends_attempted.push_back({"SDL"});
|
||||
try {
|
||||
CLOG_INFO(&LOG, "Create SDL system");
|
||||
system_ = new GHOST_SystemSDL();
|
||||
}
|
||||
catch (const std::runtime_error &e) {
|
||||
if (verbose) {
|
||||
backends_attempted.back().failure_msg = e.what();
|
||||
}
|
||||
delete system_;
|
||||
system_ = nullptr;
|
||||
}
|
||||
#elif defined(WIN32)
|
||||
backends_attempted.push_back({"WIN32"});
|
||||
CLOG_INFO(&LOG, "Create Windows system");
|
||||
system_ = new GHOST_SystemWin32();
|
||||
#elif defined(__APPLE__)
|
||||
backends_attempted.push_back({"COCOA"});
|
||||
CLOG_INFO(&LOG, "Create Cocoa system");
|
||||
system_ = new GHOST_SystemCocoa();
|
||||
#endif
|
||||
|
||||
if (system_) {
|
||||
system_backend_id_ = backends_attempted.back().id;
|
||||
}
|
||||
else if (verbose || CLOG_CHECK(&LOG, CLG_LEVEL_INFO)) {
|
||||
bool show_messages = false;
|
||||
std::string msg = "Failed to initialize display for back-end(s): [";
|
||||
for (int i = 0; i < backends_attempted.size(); i++) {
|
||||
const GHOST_BackendInfo &backend_item = backends_attempted[i];
|
||||
if (i != 0) {
|
||||
msg += ", ";
|
||||
}
|
||||
msg += "'" + std::string(backend_item.id) + "'";
|
||||
if (!backend_item.failure_msg.empty()) {
|
||||
show_messages = true;
|
||||
}
|
||||
}
|
||||
msg += "]\n";
|
||||
if (show_messages) {
|
||||
for (int i = 0; i < backends_attempted.size(); i++) {
|
||||
const GHOST_BackendInfo &backend_item = backends_attempted[i];
|
||||
msg += " '";
|
||||
msg += backend_item.id;
|
||||
msg += "': ";
|
||||
msg += backend_item.failure_msg.empty() ? "<unknown>" : backend_item.failure_msg.c_str();
|
||||
msg += "\n";
|
||||
}
|
||||
}
|
||||
CLOG_STR_INFO_NOCHECK(&LOG, msg.c_str());
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND
|
||||
# ifndef WITH_GHOST_X11
|
||||
/* Special case, Blender will eventually drop X11.
|
||||
* show a more user friendly warning while this is in the process
|
||||
* of being disabled by default. */
|
||||
|
||||
const char *wayland_display = getenv("WAYLAND_DISPLAY");
|
||||
if (!(wayland_display && wayland_display[0])) {
|
||||
const char *x11_display = getenv("DISPLAY");
|
||||
if (x11_display && x11_display[0]) {
|
||||
/* WAYLAND isn't available but X11 is.
|
||||
* Print a message to let the users know how to enable X11.
|
||||
* Since this is a non-standard option (currently - but used for "lite" builds),
|
||||
* the user is likely a developer who created their own build. */
|
||||
CLOG_STR_INFO_NOCHECK(
|
||||
&LOG,
|
||||
"Built without X11 support, enable \"WITH_GHOST_X11\" build-option to use X11.\n");
|
||||
}
|
||||
}
|
||||
# endif /* !WITH_GHOST_X11 */
|
||||
#endif /* WITH_GHOST_WAYLAND */
|
||||
}
|
||||
success = system_ != nullptr ? GHOST_kSuccess : GHOST_kFailure;
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
if (success) {
|
||||
success = system_->init();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ISystem::createSystemBackground()
|
||||
{
|
||||
GHOST_TSuccess success;
|
||||
if (!system_) {
|
||||
#if !defined(WITH_HEADLESS)
|
||||
/* Try to create a off-screen render surface with the graphical systems. */
|
||||
CLOG_INFO(&LOG, "Create background system");
|
||||
success = createSystem(false, true);
|
||||
if (success) {
|
||||
return success;
|
||||
}
|
||||
/* Try to fall back to headless mode if all else fails. */
|
||||
#endif
|
||||
CLOG_INFO(&LOG, "Create headless system");
|
||||
system_ = new GHOST_SystemHeadless();
|
||||
success = system_ != nullptr ? GHOST_kSuccess : GHOST_kFailure;
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
if (success) {
|
||||
success = system_->init();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ISystem::disposeSystem()
|
||||
{
|
||||
CLOG_DEBUG(&LOG, "Dispose system");
|
||||
GHOST_TSuccess success = GHOST_kSuccess;
|
||||
if (system_) {
|
||||
delete system_;
|
||||
system_ = nullptr;
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_ISystem *GHOST_ISystem::getSystem()
|
||||
{
|
||||
return system_;
|
||||
}
|
||||
|
||||
const char *GHOST_ISystem::getSystemBackend()
|
||||
{
|
||||
return system_backend_id_;
|
||||
}
|
||||
|
||||
GHOST_TBacktraceFn GHOST_ISystem::getBacktraceFn()
|
||||
{
|
||||
return GHOST_ISystem::backtrace_fn_;
|
||||
}
|
||||
|
||||
void GHOST_ISystem::setBacktraceFn(GHOST_TBacktraceFn backtrace_fn)
|
||||
{
|
||||
GHOST_ISystem::backtrace_fn_ = backtrace_fn;
|
||||
}
|
||||
|
||||
bool GHOST_ISystem::getUseWindowFrame()
|
||||
{
|
||||
return GHOST_ISystem::use_window_frame_;
|
||||
}
|
||||
|
||||
void GHOST_ISystem::setUseWindowFrame(bool use_window_frame)
|
||||
{
|
||||
GHOST_ISystem::use_window_frame_ = use_window_frame;
|
||||
}
|
||||
67
blender-5.2.0/intern/ghost/intern/GHOST_ISystemPaths.cc
Normal file
67
blender-5.2.0/intern/ghost/intern/GHOST_ISystemPaths.cc
Normal file
@@ -0,0 +1,67 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright (C) 2001 NaN Technologies B.V.
|
||||
*/
|
||||
|
||||
#include "GHOST_ISystemPaths.hh"
|
||||
|
||||
#ifdef WIN32
|
||||
# include "GHOST_SystemPathsWin32.hh"
|
||||
#else
|
||||
# ifdef __APPLE__
|
||||
# include "GHOST_SystemPathsCocoa.hh"
|
||||
# else
|
||||
# include "GHOST_SystemPathsUnix.hh"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
GHOST_ISystemPaths *GHOST_ISystemPaths::system_paths_ = nullptr;
|
||||
|
||||
GHOST_TSuccess GHOST_ISystemPaths::create()
|
||||
{
|
||||
GHOST_TSuccess success;
|
||||
if (!system_paths_) {
|
||||
#ifdef WIN32
|
||||
system_paths_ = new GHOST_SystemPathsWin32();
|
||||
#else
|
||||
# ifdef __APPLE__
|
||||
system_paths_ = new GHOST_SystemPathsCocoa();
|
||||
# else
|
||||
system_paths_ = new GHOST_SystemPathsUnix();
|
||||
# endif
|
||||
#endif
|
||||
success = system_paths_ != nullptr ? GHOST_kSuccess : GHOST_kFailure;
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_ISystemPaths::dispose()
|
||||
{
|
||||
GHOST_TSuccess success = GHOST_kSuccess;
|
||||
if (system_paths_) {
|
||||
delete system_paths_;
|
||||
system_paths_ = nullptr;
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_ISystemPaths *GHOST_ISystemPaths::get()
|
||||
{
|
||||
if (!system_paths_) {
|
||||
create();
|
||||
}
|
||||
return system_paths_;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
#include "GHOST_Xr_openxr_includes.hh"
|
||||
|
||||
class GHOST_IXrGraphicsBinding {
|
||||
public:
|
||||
union {
|
||||
#if defined(WITH_GHOST_X11)
|
||||
# if defined(WITH_OPENGL_BACKEND)
|
||||
XrGraphicsBindingEGLMNDX egl;
|
||||
XrGraphicsBindingOpenGLXlibKHR glx;
|
||||
# endif
|
||||
#endif
|
||||
#if defined(WIN32)
|
||||
# if defined(WITH_OPENGL_BACKEND)
|
||||
XrGraphicsBindingOpenGLWin32KHR wgl;
|
||||
# endif
|
||||
XrGraphicsBindingD3D11KHR d3d11;
|
||||
#endif
|
||||
#if defined(WITH_GHOST_WAYLAND)
|
||||
# if defined(WITH_OPENGL_BACKEND)
|
||||
XrGraphicsBindingOpenGLWaylandKHR wl;
|
||||
# endif
|
||||
#endif
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
XrGraphicsBindingVulkanKHR vk;
|
||||
#endif
|
||||
#ifdef WITH_METAL_BACKEND
|
||||
XrGraphicsBindingMetalKHR metal;
|
||||
#endif
|
||||
} oxr_binding;
|
||||
|
||||
virtual ~GHOST_IXrGraphicsBinding() = default;
|
||||
|
||||
/**
|
||||
* \brief load the functions for the graphics binding.
|
||||
*
|
||||
* A graphic binding uses an extension. These extensions provide graphics platform specific
|
||||
* functions. This function is called before #checkVersionRequirements allowing to preload the
|
||||
* function pointers.
|
||||
*
|
||||
* \param instance: XrInstance where to retrieve the functions from.
|
||||
*
|
||||
* \returns true when the extensions functions were successfully loaded, return false when
|
||||
* the extension functions could not be loaded or would not allow to use the binding.
|
||||
*
|
||||
* \note These functions should be stored as instance variable as they can differ between
|
||||
* XrSessions.
|
||||
*/
|
||||
virtual bool loadExtensionFunctions(XrInstance instance) = 0;
|
||||
|
||||
/**
|
||||
* Does __not__ require this object to be initialized (can be called prior to
|
||||
* #initFromGhostContext). It's actually meant to be called first.
|
||||
*
|
||||
* \param r_requirement_info: Return argument to retrieve an informal string on the requirements.
|
||||
* to be met. Useful for error/debug messages.
|
||||
*/
|
||||
virtual bool checkVersionRequirements(class GHOST_Context &ghost_ctx,
|
||||
XrInstance instance,
|
||||
XrSystemId system_id,
|
||||
std::string *r_requirement_info) const = 0;
|
||||
virtual void initFromGhostContext(class GHOST_Context &ghost_ctx,
|
||||
XrInstance instance,
|
||||
XrSystemId system_id) = 0;
|
||||
virtual std::optional<int64_t> chooseSwapchainFormat(const std::vector<int64_t> &runtime_formats,
|
||||
GHOST_TXrSwapchainFormat &r_format,
|
||||
bool &r_is_rgb_format) const = 0;
|
||||
virtual std::vector<XrSwapchainImageBaseHeader *> createSwapchainImages(
|
||||
uint32_t image_count) = 0;
|
||||
virtual void submitToSwapchainBegin() = 0;
|
||||
virtual void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image,
|
||||
const GHOST_XrDrawViewInfo &draw_info) = 0;
|
||||
virtual void submitToSwapchainEnd() = 0;
|
||||
virtual bool needsUpsideDownDrawing(GHOST_Context &ghost_ctx) const = 0;
|
||||
|
||||
protected:
|
||||
/* Use GHOST_XrGraphicsBindingCreateFromType! */
|
||||
GHOST_IXrGraphicsBinding() = default;
|
||||
};
|
||||
|
||||
std::unique_ptr<GHOST_IXrGraphicsBinding> GHOST_XrGraphicsBindingCreateFromType(
|
||||
GHOST_TXrGraphicsBinding type, GHOST_Context &context);
|
||||
1001
blender-5.2.0/intern/ghost/intern/GHOST_IconX11.hh
Normal file
1001
blender-5.2.0/intern/ghost/intern/GHOST_IconX11.hh
Normal file
File diff suppressed because it is too large
Load Diff
527
blender-5.2.0/intern/ghost/intern/GHOST_ImeWin32.cc
Normal file
527
blender-5.2.0/intern/ghost/intern/GHOST_ImeWin32.cc
Normal file
@@ -0,0 +1,527 @@
|
||||
/* SPDX-FileCopyrightText: 2010 The Chromium Authors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
|
||||
# include "GHOST_ImeWin32.hh"
|
||||
# include "GHOST_WindowWin32.hh"
|
||||
# include "utfconv.hh"
|
||||
|
||||
/* ISO_639-1 2-Letter Abbreviations. */
|
||||
# define IMELANG_ENGLISH "en"
|
||||
# define IMELANG_CHINESE "zh"
|
||||
# define IMELANG_JAPANESE "ja"
|
||||
# define IMELANG_KOREAN "ko"
|
||||
|
||||
GHOST_ImeWin32::GHOST_ImeWin32()
|
||||
: is_composing_(false),
|
||||
language_(IMELANG_ENGLISH),
|
||||
conversion_modes_(IME_CMODE_ALPHANUMERIC),
|
||||
sentence_mode_(IME_SMODE_NONE),
|
||||
system_caret_(false),
|
||||
caret_rect_(-1, -1, 0, 0),
|
||||
is_first(true),
|
||||
is_enable(true)
|
||||
{
|
||||
}
|
||||
|
||||
GHOST_ImeWin32::~GHOST_ImeWin32() {}
|
||||
|
||||
void GHOST_ImeWin32::UpdateInputLanguage()
|
||||
{
|
||||
/* Get the current input locale full name. */
|
||||
WCHAR locale[LOCALE_NAME_MAX_LENGTH];
|
||||
LCIDToLocaleName(
|
||||
MAKELCID(LOWORD(::GetKeyboardLayout(0)), SORT_DEFAULT), locale, LOCALE_NAME_MAX_LENGTH, 0);
|
||||
/* Get the 2-letter ISO-63901 abbreviation of the input locale name. */
|
||||
WCHAR language_u16[W32_ISO639_LEN];
|
||||
GetLocaleInfoEx(locale, LOCALE_SISO639LANGNAME, language_u16, W32_ISO639_LEN);
|
||||
/* Store this as a UTF8 string. */
|
||||
WideCharToMultiByte(
|
||||
CP_UTF8, 0, language_u16, W32_ISO639_LEN, language_, W32_ISO639_LEN, nullptr, nullptr);
|
||||
}
|
||||
|
||||
BOOL GHOST_ImeWin32::IsLanguage(const char name[W32_ISO639_LEN])
|
||||
{
|
||||
return (strcmp(name, language_) == 0);
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::UpdateConversionStatus(HWND window_handle)
|
||||
{
|
||||
HIMC imm_context = ::ImmGetContext(window_handle);
|
||||
if (imm_context) {
|
||||
if (::ImmGetOpenStatus(imm_context)) {
|
||||
::ImmGetConversionStatus(imm_context, &conversion_modes_, &sentence_mode_);
|
||||
}
|
||||
else {
|
||||
conversion_modes_ = IME_CMODE_ALPHANUMERIC;
|
||||
sentence_mode_ = IME_SMODE_NONE;
|
||||
}
|
||||
::ImmReleaseContext(window_handle, imm_context);
|
||||
}
|
||||
else {
|
||||
conversion_modes_ = IME_CMODE_ALPHANUMERIC;
|
||||
sentence_mode_ = IME_SMODE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
bool GHOST_ImeWin32::IsEnglishMode()
|
||||
{
|
||||
return (conversion_modes_ & IME_CMODE_NOCONVERSION) ||
|
||||
!(conversion_modes_ & (IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE));
|
||||
}
|
||||
|
||||
bool GHOST_ImeWin32::IsImeKeyEvent(char ascii, GHOST_TKey key)
|
||||
{
|
||||
if (!(IsEnglishMode())) {
|
||||
/* In Chinese, Japanese, Korean, all alpha keys are processed by IME. */
|
||||
if ((ascii >= 'A' && ascii <= 'Z') || (ascii >= 'a' && ascii <= 'z')) {
|
||||
return true;
|
||||
}
|
||||
if (IsLanguage(IMELANG_JAPANESE) && (ascii >= ' ' && ascii <= '~')) {
|
||||
return true;
|
||||
}
|
||||
if (IsLanguage(IMELANG_CHINESE)) {
|
||||
if (ascii && strchr("!\"$'(),.:;<>?[\\]^_`/", ascii) && !(key == GHOST_kKeyNumpadPeriod)) {
|
||||
return true;
|
||||
}
|
||||
if (conversion_modes_ & IME_CMODE_FULLSHAPE && (ascii >= '0' && ascii <= '9')) {
|
||||
/* When in Full Width mode the number keys are also converted. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::CreateImeWindow(HWND window_handle)
|
||||
{
|
||||
/**
|
||||
* When a user disables TSF (Text Service Framework) and CUAS (Cicero
|
||||
* Unaware Application Support), Chinese IMEs somehow ignore function calls
|
||||
* to ::ImmSetCandidateWindow(), i.e. they do not move their candidate
|
||||
* window to the position given as its parameters, and use the position
|
||||
* of the current system caret instead, i.e. it uses ::GetCaretPos() to
|
||||
* retrieve the position of their IME candidate window.
|
||||
* Therefore, we create a temporary system caret for Chinese IMEs and use
|
||||
* it during this input context.
|
||||
* Since some third-party Japanese IME also uses ::GetCaretPos() to determine
|
||||
* their window position, we also create a caret for Japanese IMEs.
|
||||
*/
|
||||
if (!system_caret_ && (IsLanguage(IMELANG_CHINESE) || IsLanguage(IMELANG_JAPANESE))) {
|
||||
system_caret_ = ::CreateCaret(window_handle, nullptr, 1, 1);
|
||||
}
|
||||
/* Restore the positions of the IME windows. */
|
||||
UpdateImeWindow(window_handle);
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::SetImeWindowStyle(
|
||||
HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam, BOOL *handled)
|
||||
{
|
||||
/**
|
||||
* To prevent the IMM (Input Method Manager) from displaying the IME
|
||||
* composition window, Update the styles of the IME windows and EXPLICITLY
|
||||
* call ::DefWindowProc() here.
|
||||
* NOTE(hbono): We can NEVER let WTL call ::DefWindowProc() when we update
|
||||
* the styles of IME windows because the 'lparam' variable is a local one
|
||||
* and all its updates disappear in returning from this function, i.e. WTL
|
||||
* does not call ::DefWindowProc() with our updated 'lparam' value but call
|
||||
* the function with its original value and over-writes our window styles.
|
||||
*/
|
||||
*handled = TRUE;
|
||||
lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
|
||||
::DefWindowProc(window_handle, message, wparam, lparam);
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::DestroyImeWindow(HWND /*window_handle*/)
|
||||
{
|
||||
/* Destroy the system caret if we have created for this IME input context. */
|
||||
if (system_caret_) {
|
||||
::DestroyCaret();
|
||||
system_caret_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::MoveImeWindow(HWND /*window_handle*/, HIMC imm_context)
|
||||
{
|
||||
int x = caret_rect_.l_;
|
||||
int y = caret_rect_.t_;
|
||||
const int kCaretMargin = 1;
|
||||
/**
|
||||
* As written in a comment in GHOST_ImeWin32::CreateImeWindow(),
|
||||
* Chinese IMEs ignore function calls to ::ImmSetCandidateWindow()
|
||||
* when a user disables TSF (Text Service Framework) and CUAS (Cicero
|
||||
* Unaware Application Support).
|
||||
* On the other hand, when a user enables TSF and CUAS, Chinese IMEs
|
||||
* ignore the position of the current system caret and uses the
|
||||
* parameters given to ::ImmSetCandidateWindow() with its 'dwStyle'
|
||||
* parameter CFS_CANDIDATEPOS.
|
||||
* Therefore, we do not only call ::ImmSetCandidateWindow() but also
|
||||
* set the positions of the temporary system caret if it exists.
|
||||
*/
|
||||
CANDIDATEFORM candidate_position = {0, CFS_CANDIDATEPOS, {x, y}, {0, 0, 0, 0}};
|
||||
::ImmSetCandidateWindow(imm_context, &candidate_position);
|
||||
if (system_caret_) {
|
||||
::SetCaretPos(x, y);
|
||||
}
|
||||
if (IsLanguage(IMELANG_KOREAN)) {
|
||||
/**
|
||||
* Chinese IMEs and Japanese IMEs require the upper-left corner of
|
||||
* the caret to move the position of their candidate windows.
|
||||
* On the other hand, Korean IMEs require the lower-left corner of the
|
||||
* caret to move their candidate windows.
|
||||
*/
|
||||
y += kCaretMargin;
|
||||
}
|
||||
/**
|
||||
* Japanese IMEs and Korean IMEs also use the rectangle given to
|
||||
* ::ImmSetCandidateWindow() with its 'dwStyle' parameter CFS_EXCLUDE
|
||||
* to move their candidate windows when a user disables TSF and CUAS.
|
||||
* Therefore, we also set this parameter here.
|
||||
*/
|
||||
CANDIDATEFORM exclude_rectangle = {
|
||||
0, CFS_EXCLUDE, {x, y}, {x, y, x + caret_rect_.getWidth(), y + caret_rect_.getHeight()}};
|
||||
::ImmSetCandidateWindow(imm_context, &exclude_rectangle);
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::UpdateImeWindow(HWND window_handle)
|
||||
{
|
||||
/* Just move the IME window attached to the given window. */
|
||||
if (caret_rect_.l_ >= 0 && caret_rect_.t_ >= 0) {
|
||||
HIMC imm_context = ::ImmGetContext(window_handle);
|
||||
if (imm_context) {
|
||||
MoveImeWindow(window_handle, imm_context);
|
||||
::ImmReleaseContext(window_handle, imm_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::CleanupComposition(HWND window_handle)
|
||||
{
|
||||
/**
|
||||
* Notify the IMM attached to the given window to complete the ongoing
|
||||
* composition, (this case happens when the given window is de-activated
|
||||
* while composing a text and re-activated), and reset the composition status.
|
||||
*/
|
||||
if (is_composing_) {
|
||||
HIMC imm_context = ::ImmGetContext(window_handle);
|
||||
if (imm_context) {
|
||||
::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
|
||||
::ImmReleaseContext(window_handle, imm_context);
|
||||
}
|
||||
ResetComposition(window_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::CheckFirst(HWND window_handle)
|
||||
{
|
||||
if (is_first) {
|
||||
this->EndIME(window_handle);
|
||||
is_first = false;
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::ResetComposition(HWND /*window_handle*/)
|
||||
{
|
||||
/* Currently, just reset the composition status. */
|
||||
is_composing_ = false;
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::CompleteComposition(HWND window_handle, HIMC imm_context)
|
||||
{
|
||||
/**
|
||||
* We have to confirm there is an ongoing composition before completing it.
|
||||
* This is for preventing some IMEs from getting confused while completing an
|
||||
* ongoing composition even if they do not have any ongoing compositions.
|
||||
*/
|
||||
if (is_composing_) {
|
||||
::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
|
||||
ResetComposition(window_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::GetCaret(HIMC imm_context, LPARAM lparam, ImeComposition *composition)
|
||||
{
|
||||
/**
|
||||
* This operation is optional and language-dependent because the caret
|
||||
* style is dependent on the language, e.g.:
|
||||
* * Korean IMEs: the caret is a blinking block,
|
||||
* (It contains only one hangul character);
|
||||
* * Chinese IMEs: the caret is a blinking line,
|
||||
* (i.e. they do not need to retrieve the target selection);
|
||||
* * Japanese IMEs: the caret is a selection (or underlined) block,
|
||||
* (which can contain one or more Japanese characters).
|
||||
*/
|
||||
int target_start = -1;
|
||||
int target_end = -1;
|
||||
if (IsLanguage(IMELANG_KOREAN)) {
|
||||
if (lparam & CS_NOMOVECARET) {
|
||||
target_start = 0;
|
||||
target_end = 1;
|
||||
}
|
||||
}
|
||||
else if (IsLanguage(IMELANG_CHINESE)) {
|
||||
int clause_size = ImmGetCompositionStringW(imm_context, GCS_COMPCLAUSE, nullptr, 0);
|
||||
if (clause_size) {
|
||||
static std::vector<ulong> clauses;
|
||||
clause_size = clause_size / sizeof(clauses[0]);
|
||||
clauses.resize(clause_size);
|
||||
ImmGetCompositionStringW(
|
||||
imm_context, GCS_COMPCLAUSE, &clauses[0], sizeof(clauses[0]) * clause_size);
|
||||
if (composition->cursor_position == composition->ime_string.size()) {
|
||||
target_start = clauses[clause_size - 2];
|
||||
target_end = clauses[clause_size - 1];
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < clause_size - 1; i++) {
|
||||
if (clauses[i] == composition->cursor_position) {
|
||||
target_start = clauses[i];
|
||||
target_end = clauses[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (composition->cursor_position != -1) {
|
||||
target_start = composition->cursor_position;
|
||||
target_end = composition->ime_string.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (IsLanguage(IMELANG_JAPANESE)) {
|
||||
/**
|
||||
* For Japanese IMEs, the robustest way to retrieve the caret
|
||||
* is scanning the attribute of the latest composition string and
|
||||
* retrieving the beginning and the end of the target clause, i.e.
|
||||
* a clause being converted.
|
||||
*/
|
||||
if (lparam & GCS_COMPATTR) {
|
||||
int attribute_size = ::ImmGetCompositionStringW(imm_context, GCS_COMPATTR, nullptr, 0);
|
||||
if (attribute_size > 0) {
|
||||
char *attribute_data = new char[attribute_size];
|
||||
if (attribute_data) {
|
||||
::ImmGetCompositionStringW(imm_context, GCS_COMPATTR, attribute_data, attribute_size);
|
||||
for (target_start = 0; target_start < attribute_size; ++target_start) {
|
||||
if (IsTargetAttribute(attribute_data[target_start])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (target_end = target_start; target_end < attribute_size; ++target_end) {
|
||||
if (!IsTargetAttribute(attribute_data[target_end])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (target_start == attribute_size) {
|
||||
/**
|
||||
* This composition clause does not contain any target clauses,
|
||||
* i.e. this clauses is an input clause.
|
||||
* We treat whole this clause as a target clause.
|
||||
*/
|
||||
target_end = target_start;
|
||||
target_start = 0;
|
||||
}
|
||||
if (target_start != -1 && target_start < attribute_size &&
|
||||
attribute_data[target_start] == ATTR_TARGET_NOTCONVERTED)
|
||||
{
|
||||
composition->cursor_position = target_start;
|
||||
}
|
||||
}
|
||||
delete[] attribute_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
composition->target_start = target_start;
|
||||
composition->target_end = target_end;
|
||||
}
|
||||
|
||||
bool GHOST_ImeWin32::GetString(HIMC imm_context,
|
||||
WPARAM lparam,
|
||||
int type,
|
||||
ImeComposition *composition)
|
||||
{
|
||||
bool result = false;
|
||||
if (lparam & type) {
|
||||
int string_size = ::ImmGetCompositionStringW(imm_context, type, nullptr, 0);
|
||||
if (string_size > 0) {
|
||||
int string_length = string_size / sizeof(wchar_t);
|
||||
wchar_t *string_data = new wchar_t[string_length + 1];
|
||||
string_data[string_length] = '\0';
|
||||
if (string_data) {
|
||||
/* Fill the given ImeComposition object. */
|
||||
::ImmGetCompositionStringW(imm_context, type, string_data, string_size);
|
||||
composition->string_type = type;
|
||||
composition->ime_string = string_data;
|
||||
result = true;
|
||||
}
|
||||
delete[] string_data;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GHOST_ImeWin32::GetResult(HWND window_handle, LPARAM lparam, ImeComposition *composition)
|
||||
{
|
||||
bool result = false;
|
||||
HIMC imm_context = ::ImmGetContext(window_handle);
|
||||
if (imm_context) {
|
||||
/* Copy the result string to the ImeComposition object. */
|
||||
result = GetString(imm_context, lparam, GCS_RESULTSTR, composition);
|
||||
/**
|
||||
* Reset all the other parameters because a result string does not
|
||||
* have composition attributes.
|
||||
*/
|
||||
composition->cursor_position = -1;
|
||||
composition->target_start = -1;
|
||||
composition->target_end = -1;
|
||||
::ImmReleaseContext(window_handle, imm_context);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GHOST_ImeWin32::GetComposition(HWND window_handle, LPARAM lparam, ImeComposition *composition)
|
||||
{
|
||||
bool result = false;
|
||||
HIMC imm_context = ::ImmGetContext(window_handle);
|
||||
if (imm_context) {
|
||||
/* Copy the composition string to the ImeComposition object. */
|
||||
result = GetString(imm_context, lparam, GCS_COMPSTR, composition);
|
||||
|
||||
/* Retrieve the cursor position in the IME composition. */
|
||||
int cursor_position = ::ImmGetCompositionStringW(imm_context, GCS_CURSORPOS, nullptr, 0);
|
||||
composition->cursor_position = cursor_position;
|
||||
composition->target_start = -1;
|
||||
composition->target_end = -1;
|
||||
|
||||
/* Retrieve the target selection and Update the ImeComposition object. */
|
||||
GetCaret(imm_context, lparam, composition);
|
||||
|
||||
/* Mark that there is an ongoing composition. */
|
||||
is_composing_ = true;
|
||||
|
||||
::ImmReleaseContext(window_handle, imm_context);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::EndIME(HWND window_handle)
|
||||
{
|
||||
/**
|
||||
* A renderer process have moved its input focus to a password input
|
||||
* when there is an ongoing composition, e.g. a user has clicked a
|
||||
* mouse button and selected a password input while composing a text.
|
||||
* For this case, we have to cancel the ongoing composition and
|
||||
* clean up the resources attached to this object BEFORE DISABLING THE IME.
|
||||
*/
|
||||
if (!is_enable) {
|
||||
return;
|
||||
}
|
||||
is_enable = false;
|
||||
if (is_composing_) {
|
||||
HIMC imm_context = ::ImmGetContext(window_handle);
|
||||
if (imm_context) {
|
||||
/* Cancel composition */
|
||||
::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
||||
::ImmNotifyIME(imm_context, NI_CLOSECANDIDATE, 0, 0);
|
||||
::ImmReleaseContext(window_handle, imm_context);
|
||||
}
|
||||
ResetComposition(window_handle);
|
||||
}
|
||||
::ImmAssociateContextEx(window_handle, nullptr, 0);
|
||||
eventImeData.composite.clear();
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::BeginIME(HWND window_handle, const GHOST_Rect &caret_rect, bool complete)
|
||||
{
|
||||
if (is_enable && complete) {
|
||||
return;
|
||||
}
|
||||
is_enable = true;
|
||||
/**
|
||||
* Load the default IME context.
|
||||
* NOTE(hbono)
|
||||
* IMM ignores this call if the IME context is loaded. Therefore, we do
|
||||
* not have to check whether or not the IME context is loaded.
|
||||
*/
|
||||
::ImmAssociateContextEx(window_handle, nullptr, IACE_DEFAULT);
|
||||
/* Complete the ongoing composition and move the IME windows. */
|
||||
HIMC imm_context = ::ImmGetContext(window_handle);
|
||||
if (imm_context) {
|
||||
if (complete) {
|
||||
/**
|
||||
* A renderer process have moved its input focus to another edit
|
||||
* control when there is an ongoing composition, e.g. a user has
|
||||
* clicked a mouse button and selected another edit control while
|
||||
* composing a text.
|
||||
* For this case, we have to complete the ongoing composition and
|
||||
* hide the IME windows BEFORE MOVING THEM.
|
||||
*/
|
||||
CompleteComposition(window_handle, imm_context);
|
||||
}
|
||||
/**
|
||||
* Save the caret position, and Update the position of the IME window.
|
||||
* This update is used for moving an IME window when a renderer process
|
||||
* resize/moves the input caret.
|
||||
*/
|
||||
if (caret_rect.l_ >= 0 && caret_rect.t_ >= 0) {
|
||||
caret_rect_ = caret_rect;
|
||||
MoveImeWindow(window_handle, imm_context);
|
||||
}
|
||||
::ImmReleaseContext(window_handle, imm_context);
|
||||
}
|
||||
}
|
||||
|
||||
static void convert_utf16_to_utf8_len(std::wstring s, int &len)
|
||||
{
|
||||
if (len >= 0 && len <= s.size()) {
|
||||
len = count_utf_8_from_16(s.substr(0, len).c_str()) - 1;
|
||||
}
|
||||
else {
|
||||
len = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t updateUtf8Buf(ImeComposition &info)
|
||||
{
|
||||
size_t len = count_utf_8_from_16(info.ime_string.c_str());
|
||||
info.utf8_buf.resize(len);
|
||||
conv_utf_16_to_8(info.ime_string.c_str(), &info.utf8_buf[0], len);
|
||||
convert_utf16_to_utf8_len(info.ime_string, info.cursor_position);
|
||||
convert_utf16_to_utf8_len(info.ime_string, info.target_start);
|
||||
convert_utf16_to_utf8_len(info.ime_string, info.target_end);
|
||||
return len - 1;
|
||||
}
|
||||
|
||||
void GHOST_ImeWin32::UpdateInfo(HWND window_handle)
|
||||
{
|
||||
int res = this->GetResult(window_handle, GCS_RESULTSTR, &resultInfo);
|
||||
int comp = this->GetComposition(window_handle, GCS_COMPSTR | GCS_COMPATTR, &compInfo);
|
||||
/* Convert wchar to UTF8. */
|
||||
if (res) {
|
||||
updateUtf8Buf(resultInfo);
|
||||
eventImeData.result = std::string(&resultInfo.utf8_buf[0]);
|
||||
}
|
||||
else {
|
||||
eventImeData.result = "";
|
||||
}
|
||||
if (comp) {
|
||||
updateUtf8Buf(compInfo);
|
||||
eventImeData.composite = std::string(&compInfo.utf8_buf[0]);
|
||||
eventImeData.cursor_position = compInfo.cursor_position;
|
||||
eventImeData.target_start = compInfo.target_start;
|
||||
eventImeData.target_end = compInfo.target_end;
|
||||
}
|
||||
else {
|
||||
eventImeData.composite = "";
|
||||
eventImeData.cursor_position = -1;
|
||||
eventImeData.target_start = -1;
|
||||
eventImeData.target_end = -1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* WITH_INPUT_IME */
|
||||
357
blender-5.2.0/intern/ghost/intern/GHOST_ImeWin32.hh
Normal file
357
blender-5.2.0/intern/ghost/intern/GHOST_ImeWin32.hh
Normal file
@@ -0,0 +1,357 @@
|
||||
/* SPDX-FileCopyrightText: 2010 The Chromium Authors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
|
||||
# include <windows.h>
|
||||
|
||||
# include <string>
|
||||
|
||||
# include "GHOST_Event.hh"
|
||||
# include "GHOST_Rect.hh"
|
||||
# include <vector>
|
||||
|
||||
/* MSDN LOCALE_SISO639LANGNAME states maximum length of 9, including terminating null. */
|
||||
# define W32_ISO639_LEN 9
|
||||
|
||||
class GHOST_EventIME : public GHOST_Event {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param msec: The time this event was generated.
|
||||
* \param type: The type of key event.
|
||||
* \param customdata: custom-data, typically #GHOST_TEventImeData.
|
||||
*/
|
||||
GHOST_EventIME(uint64_t msec,
|
||||
GHOST_TEventType type,
|
||||
GHOST_IWindow *window,
|
||||
const void *customdata)
|
||||
: GHOST_Event(msec, type, window)
|
||||
{
|
||||
this->data_ = customdata;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This header file defines a struct and a class used for encapsulating IMM32
|
||||
* APIs, controls IMEs attached to a window, and enables the 'on-the-spot'
|
||||
* input without deep knowledge about the APIs, i.e. knowledge about the
|
||||
* language-specific and IME-specific behaviors.
|
||||
* The following items enumerates the simplest steps for an (window)
|
||||
* application to control its IMEs with the struct and the class defined
|
||||
* this file.
|
||||
* 1. Add an instance of the GHOST_ImeWin32 class to its window class.
|
||||
* (The GHOST_ImeWin32 class needs a window handle.)
|
||||
* 2. Add messages handlers listed in the following subsections, follow the
|
||||
* instructions written in each subsection, and use the GHOST_ImeWin32 class.
|
||||
* 2.1. WM_IME_SETCONTEXT (0x0281)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::CreateImeWindow();
|
||||
* - GHOST_ImeWin32::CleanupComposition(), and;
|
||||
* - GHOST_ImeWin32::SetImeWindowStyle().
|
||||
* An application MUST prevent from calling ::DefWindowProc().
|
||||
* 2.2. WM_IME_STARTCOMPOSITION (0x010D)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::CreateImeWindow(), and;
|
||||
* - GHOST_ImeWin32::ResetComposition().
|
||||
* An application MUST prevent from calling ::DefWindowProc().
|
||||
* 2.3. WM_IME_COMPOSITION (0x010F)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::UpdateImeWindow();
|
||||
* - GHOST_ImeWin32::GetResult();
|
||||
* - GHOST_ImeWin32::GetComposition(), and;
|
||||
* - GHOST_ImeWin32::ResetComposition() (optional).
|
||||
* An application MUST prevent from calling ::DefWindowProc().
|
||||
* 2.4. WM_IME_ENDCOMPOSITION (0x010E)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::ResetComposition(), and;
|
||||
* - GHOST_ImeWin32::DestroyImeWindow().
|
||||
* An application CAN call ::DefWindowProc().
|
||||
* 2.5. WM_INPUTLANGCHANGE (0x0051)
|
||||
* Call the functions listed below:
|
||||
* - GHOST_ImeWin32::UpdateInputLanguage().
|
||||
* An application CAN call ::DefWindowProc().
|
||||
*/
|
||||
|
||||
/* This struct represents the status of an ongoing composition. */
|
||||
struct ImeComposition {
|
||||
/* Represents the cursor position in the IME composition. */
|
||||
int cursor_position;
|
||||
|
||||
/* Represents the position of the beginning of the selection */
|
||||
int target_start;
|
||||
|
||||
/* Represents the position of the end of the selection */
|
||||
int target_end;
|
||||
|
||||
/**
|
||||
* Represents the type of the string in the 'ime_string' parameter.
|
||||
* Its possible values and description are listed below:
|
||||
* Value Description
|
||||
* 0 The parameter is not used.
|
||||
* GCS_RESULTSTR The parameter represents a result string.
|
||||
* GCS_COMPSTR The parameter represents a composition string.
|
||||
*/
|
||||
int string_type;
|
||||
|
||||
/* Represents the string retrieved from IME (Input Method Editor) */
|
||||
std::wstring ime_string;
|
||||
std::vector<char> utf8_buf;
|
||||
std::vector<unsigned char> format;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class controls the IMM (Input Method Manager) through IMM32 APIs and
|
||||
* enables it to retrieve the string being controlled by the IMM. (I wrote
|
||||
* a note to describe the reason why I do not use 'IME' but 'IMM' below.)
|
||||
* NOTE(hbono):
|
||||
* Fortunately or unfortunately, TSF (Text Service Framework) and
|
||||
* CUAS (Cicero Unaware Application Support) allows IMM32 APIs for
|
||||
* retrieving not only the inputs from IMEs (Input Method Editors), used
|
||||
* only for inputting East-Asian language texts, but also the ones from
|
||||
* tablets (on Windows XP Tablet PC Edition and Windows Vista), voice
|
||||
* recognizers (e.g. ViaVoice and Microsoft Office), etc.
|
||||
* We can disable TSF and CUAS in Windows XP Tablet PC Edition. On the other
|
||||
* hand, we can NEVER disable either TSF or CUAS in Windows Vista, i.e.
|
||||
* THIS CLASS IS NOT ONLY USED ON THE INPUT CONTEXTS OF EAST-ASIAN
|
||||
* LANGUAGES BUT ALSO USED ON THE INPUT CONTEXTS OF ALL LANGUAGES.
|
||||
*/
|
||||
class GHOST_ImeWin32 {
|
||||
public:
|
||||
GHOST_ImeWin32();
|
||||
~GHOST_ImeWin32();
|
||||
|
||||
/* Retrieves whether or not there is an ongoing composition. */
|
||||
bool is_composing() const
|
||||
{
|
||||
return is_composing_;
|
||||
}
|
||||
|
||||
/* Retrieve the input language from Windows and store it. */
|
||||
void UpdateInputLanguage();
|
||||
|
||||
BOOL IsLanguage(const char name[W32_ISO639_LEN]);
|
||||
|
||||
/* Saves the current conversion status. */
|
||||
void UpdateConversionStatus(HWND window_handle);
|
||||
|
||||
/* Is the IME currently in conversion mode? */
|
||||
bool IsEnglishMode();
|
||||
|
||||
/* Checks a key whether IME has to do handling. */
|
||||
bool IsImeKeyEvent(char ascii, GHOST_TKey key);
|
||||
|
||||
/**
|
||||
* Create the IME windows, and allocate required resources for them.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void CreateImeWindow(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Update the style of the IME windows.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
* * message [in] (UINT)
|
||||
* * wparam [in] (WPARAM)
|
||||
* * lparam [in] (LPARAM)
|
||||
* Represent the windows message of the caller.
|
||||
* These parameters are used for verifying if this function is called
|
||||
* in a handler function for WM_IME_SETCONTEXT messages because this
|
||||
* function uses ::DefWindowProc() to update the style.
|
||||
* A caller just has to pass the input parameters for the handler
|
||||
* function without modifications.
|
||||
* * handled [out] (BOOL*)
|
||||
* Returns ::DefWindowProc() is really called in this function.
|
||||
* PLEASE DO NOT CALL ::DefWindowProc() IF THIS VALUE IS TRUE!
|
||||
* All the window styles set in this function are over-written when
|
||||
* calling ::DefWindowProc() after returning this function.
|
||||
*/
|
||||
void SetImeWindowStyle(
|
||||
HWND window_handle, UINT message, WPARAM wparam, LPARAM lparam, BOOL *handled);
|
||||
|
||||
/**
|
||||
* Destroy the IME windows and all the resources attached to them.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void DestroyImeWindow(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Update the position of the IME windows.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void UpdateImeWindow(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Clean up the all resources attached to the given GHOST_ImeWin32 object, and
|
||||
* reset its composition status.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void CleanupComposition(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Reset the composition status.
|
||||
* Cancel the ongoing composition if it exists.
|
||||
* NOTE(hbono): This method does not release the allocated resources.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void ResetComposition(HWND window_handle);
|
||||
|
||||
/**
|
||||
* Retrieve a composition result of the ongoing composition if it exists.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
* * lparam [in] (LPARAM)
|
||||
* Specifies the updated members of the ongoing composition, and must be
|
||||
* the same parameter of a WM_IME_COMPOSITION message handler.
|
||||
* This parameter is used for checking if the ongoing composition has
|
||||
* its result string,
|
||||
* * composition [out] (ImeComposition)
|
||||
* Represents the struct contains the composition result.
|
||||
* Return values
|
||||
* * true
|
||||
* The ongoing composition has a composition result.
|
||||
* * false
|
||||
* The ongoing composition does not have composition results.
|
||||
* Remarks
|
||||
* This function is designed for being called from WM_IME_COMPOSITION
|
||||
* message handlers.
|
||||
*/
|
||||
bool GetResult(HWND window_handle, LPARAM lparam, ImeComposition *composition);
|
||||
|
||||
/**
|
||||
* Retrieve the current composition status of the ongoing composition.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
* * lparam [in] (LPARAM)
|
||||
* Specifies the updated members of the ongoing composition, and must be
|
||||
* the same parameter of a WM_IME_COMPOSITION message handler.
|
||||
* This parameter is used for checking if the ongoing composition has
|
||||
* its result string,
|
||||
* * composition [out] (ImeComposition)
|
||||
* Represents the struct contains the composition status.
|
||||
* Return values
|
||||
* * true
|
||||
* The status of the ongoing composition is updated.
|
||||
* * false
|
||||
* The status of the ongoing composition is not updated.
|
||||
* Remarks
|
||||
* This function is designed for being called from WM_IME_COMPOSITION
|
||||
* message handlers.
|
||||
*/
|
||||
bool GetComposition(HWND window_handle, LPARAM lparam, ImeComposition *composition);
|
||||
|
||||
/**
|
||||
* Enable the IME attached to the given window, i.e. allows user-input
|
||||
* events to be dispatched to the IME.
|
||||
* In Chrome, this function is used when:
|
||||
* * a renderer process moves its input focus to another edit control, or;
|
||||
* * a renderer process moves the position of the focused edit control.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
* * caret_rect [in] (`const gfx::Rect&`)
|
||||
* Represent the rectangle of the input caret.
|
||||
* This rectangle is used for controlling the positions of IME windows.
|
||||
* * complete [in] (bool)
|
||||
* Represents whether or not to complete the ongoing composition.
|
||||
* + true
|
||||
* After finishing the ongoing composition and close its IME windows,
|
||||
* start another composition and display its IME windows to the given
|
||||
* position.
|
||||
* + false
|
||||
* Just move the IME windows of the ongoing composition to the given
|
||||
* position without finishing it.
|
||||
*/
|
||||
void BeginIME(HWND window_handle, const GHOST_Rect &caret_rect, bool complete);
|
||||
|
||||
/**
|
||||
* Disable the IME attached to the given window, i.e. prohibits any user-input
|
||||
* events from being dispatched to the IME.
|
||||
* In Chrome, this function is used when:
|
||||
* * a renderer process sets its input focus to a password input.
|
||||
* Parameters
|
||||
* * window_handle [in] (HWND)
|
||||
* Represents the window handle of the caller.
|
||||
*/
|
||||
void EndIME(HWND window_handle);
|
||||
|
||||
/** Update #resultInfo and #compInfo */
|
||||
void UpdateInfo(HWND window_handle);
|
||||
|
||||
/** Disable IME when start up. */
|
||||
void CheckFirst(HWND window_handle);
|
||||
|
||||
ImeComposition resultInfo, compInfo;
|
||||
GHOST_TEventImeData eventImeData;
|
||||
|
||||
protected:
|
||||
/* Determines whether or not the given attribute represents a target (a.k.a. a selection). */
|
||||
bool IsTargetAttribute(char attribute) const
|
||||
{
|
||||
return (attribute == ATTR_TARGET_CONVERTED || attribute == ATTR_TARGET_NOTCONVERTED);
|
||||
}
|
||||
|
||||
/* Retrieve the target area. */
|
||||
void GetCaret(HIMC imm_context, LPARAM lparam, ImeComposition *composition);
|
||||
|
||||
/* Update the position of the IME windows. */
|
||||
void MoveImeWindow(HWND window_handle, HIMC imm_context);
|
||||
|
||||
/* Complete the ongoing composition if it exists. */
|
||||
void CompleteComposition(HWND window_handle, HIMC imm_context);
|
||||
|
||||
/* Retrieve a string from the IMM. */
|
||||
bool GetString(HIMC imm_context, WPARAM lparam, int type, ImeComposition *composition);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Represents whether or not there is an ongoing composition in a browser
|
||||
* process, i.e. whether or not a browser process is composing a text.
|
||||
*/
|
||||
bool is_composing_;
|
||||
|
||||
/* Abbreviated ISO 639-1 name of the input language, such as "en" for English. */
|
||||
char language_[W32_ISO639_LEN];
|
||||
|
||||
/* Current Conversion Mode Values. Retrieved with ImmGetConversionStatus. */
|
||||
DWORD conversion_modes_;
|
||||
|
||||
/* Current Sentence Mode. Retrieved with ImmGetConversionStatus. */
|
||||
DWORD sentence_mode_;
|
||||
|
||||
/**
|
||||
* Represents whether or not the current input context has created a system
|
||||
* caret to set the position of its IME candidate window.
|
||||
* * true: it creates a system caret.
|
||||
* * false: it does not create a system caret.
|
||||
*/
|
||||
bool system_caret_;
|
||||
|
||||
/* The rectangle of the input caret retrieved from a renderer process. */
|
||||
GHOST_Rect caret_rect_;
|
||||
|
||||
/* used for disable ime when start up */
|
||||
bool is_first, is_enable;
|
||||
};
|
||||
|
||||
#endif /* WITH_INPUT_IME */
|
||||
155
blender-5.2.0/intern/ghost/intern/GHOST_ModifierKeys.cc
Normal file
155
blender-5.2.0/intern/ghost/intern/GHOST_ModifierKeys.cc
Normal file
@@ -0,0 +1,155 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright (C) 2001 NaN Technologies B.V.
|
||||
*/
|
||||
|
||||
#include "GHOST_ModifierKeys.hh"
|
||||
#include "GHOST_Debug.hh"
|
||||
|
||||
GHOST_ModifierKeys::GHOST_ModifierKeys()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
GHOST_ModifierKeys::~GHOST_ModifierKeys() = default;
|
||||
|
||||
GHOST_TKey GHOST_ModifierKeys::getModifierKeyCode(GHOST_TModifierKey mask)
|
||||
{
|
||||
GHOST_TKey key;
|
||||
switch (mask) {
|
||||
case GHOST_kModifierKeyLeftShift:
|
||||
key = GHOST_kKeyLeftShift;
|
||||
break;
|
||||
case GHOST_kModifierKeyRightShift:
|
||||
key = GHOST_kKeyRightShift;
|
||||
break;
|
||||
case GHOST_kModifierKeyLeftAlt:
|
||||
key = GHOST_kKeyLeftAlt;
|
||||
break;
|
||||
case GHOST_kModifierKeyRightAlt:
|
||||
key = GHOST_kKeyRightAlt;
|
||||
break;
|
||||
case GHOST_kModifierKeyLeftControl:
|
||||
key = GHOST_kKeyLeftControl;
|
||||
break;
|
||||
case GHOST_kModifierKeyRightControl:
|
||||
key = GHOST_kKeyRightControl;
|
||||
break;
|
||||
case GHOST_kModifierKeyLeftOS:
|
||||
key = GHOST_kKeyLeftOS;
|
||||
break;
|
||||
case GHOST_kModifierKeyRightOS:
|
||||
key = GHOST_kKeyRightOS;
|
||||
break;
|
||||
case GHOST_kModifierKeyLeftHyper:
|
||||
key = GHOST_kKeyLeftHyper;
|
||||
break;
|
||||
case GHOST_kModifierKeyRightHyper:
|
||||
key = GHOST_kKeyRightHyper;
|
||||
break;
|
||||
default:
|
||||
/* Should not happen. */
|
||||
GHOST_ASSERT(0, "Invalid key!");
|
||||
key = GHOST_kKeyUnknown;
|
||||
break;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
bool GHOST_ModifierKeys::get(GHOST_TModifierKey mask) const
|
||||
{
|
||||
switch (mask) {
|
||||
case GHOST_kModifierKeyLeftShift:
|
||||
return left_shift_;
|
||||
case GHOST_kModifierKeyRightShift:
|
||||
return right_shift_;
|
||||
case GHOST_kModifierKeyLeftAlt:
|
||||
return left_alt_;
|
||||
case GHOST_kModifierKeyRightAlt:
|
||||
return right_alt_;
|
||||
case GHOST_kModifierKeyLeftControl:
|
||||
return left_control_;
|
||||
case GHOST_kModifierKeyRightControl:
|
||||
return right_control_;
|
||||
case GHOST_kModifierKeyLeftOS:
|
||||
return left_os_;
|
||||
case GHOST_kModifierKeyRightOS:
|
||||
return right_os_;
|
||||
case GHOST_kModifierKeyLeftHyper:
|
||||
return left_hyper_;
|
||||
case GHOST_kModifierKeyRightHyper:
|
||||
return right_hyper_;
|
||||
default:
|
||||
GHOST_ASSERT(0, "Invalid key!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_ModifierKeys::set(GHOST_TModifierKey mask, bool down)
|
||||
{
|
||||
switch (mask) {
|
||||
case GHOST_kModifierKeyLeftShift:
|
||||
left_shift_ = down;
|
||||
break;
|
||||
case GHOST_kModifierKeyRightShift:
|
||||
right_shift_ = down;
|
||||
break;
|
||||
case GHOST_kModifierKeyLeftAlt:
|
||||
left_alt_ = down;
|
||||
break;
|
||||
case GHOST_kModifierKeyRightAlt:
|
||||
right_alt_ = down;
|
||||
break;
|
||||
case GHOST_kModifierKeyLeftControl:
|
||||
left_control_ = down;
|
||||
break;
|
||||
case GHOST_kModifierKeyRightControl:
|
||||
right_control_ = down;
|
||||
break;
|
||||
case GHOST_kModifierKeyLeftOS:
|
||||
left_os_ = down;
|
||||
break;
|
||||
case GHOST_kModifierKeyRightOS:
|
||||
right_os_ = down;
|
||||
break;
|
||||
case GHOST_kModifierKeyLeftHyper:
|
||||
left_hyper_ = down;
|
||||
break;
|
||||
case GHOST_kModifierKeyRightHyper:
|
||||
right_hyper_ = down;
|
||||
break;
|
||||
default:
|
||||
GHOST_ASSERT(0, "Invalid key!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_ModifierKeys::clear()
|
||||
{
|
||||
left_shift_ = false;
|
||||
right_shift_ = false;
|
||||
left_alt_ = false;
|
||||
right_alt_ = false;
|
||||
left_control_ = false;
|
||||
right_control_ = false;
|
||||
left_os_ = false;
|
||||
right_os_ = false;
|
||||
left_hyper_ = false;
|
||||
right_hyper_ = false;
|
||||
}
|
||||
|
||||
bool GHOST_ModifierKeys::equals(const GHOST_ModifierKeys &keys) const
|
||||
{
|
||||
return (left_shift_ == keys.left_shift_) && (right_shift_ == keys.right_shift_) &&
|
||||
(left_alt_ == keys.left_alt_) && (right_alt_ == keys.right_alt_) &&
|
||||
(left_control_ == keys.left_control_) && (right_control_ == keys.right_control_) &&
|
||||
(left_os_ == keys.left_os_) && (right_os_ == keys.right_os_) &&
|
||||
(left_hyper_ == keys.left_hyper_) && (right_hyper_ == keys.right_hyper_);
|
||||
}
|
||||
76
blender-5.2.0/intern/ghost/intern/GHOST_ModifierKeys.hh
Normal file
76
blender-5.2.0/intern/ghost/intern/GHOST_ModifierKeys.hh
Normal file
@@ -0,0 +1,76 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_ModifierKeys struct.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
/**
|
||||
* Stores the state of modifier keys.
|
||||
* Discriminates between left and right modifier keys.
|
||||
*/
|
||||
struct GHOST_ModifierKeys {
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_ModifierKeys();
|
||||
|
||||
~GHOST_ModifierKeys();
|
||||
|
||||
/**
|
||||
* Returns the modifier key's key code from a modifier key mask.
|
||||
* \param mask: The mask of the modifier key.
|
||||
* \return The modifier key's key code.
|
||||
*/
|
||||
static GHOST_TKey getModifierKeyCode(GHOST_TModifierKey mask);
|
||||
|
||||
/**
|
||||
* Returns the state of a single modifier key.
|
||||
* \param mask: Key state to return.
|
||||
* \return The state of the key (pressed == true).
|
||||
*/
|
||||
bool get(GHOST_TModifierKey mask) const;
|
||||
|
||||
/**
|
||||
* Updates the state of a single modifier key.
|
||||
* \param mask: Key state to update.
|
||||
* \param down: The new state of the key.
|
||||
*/
|
||||
void set(GHOST_TModifierKey mask, bool down);
|
||||
|
||||
/**
|
||||
* Sets the state of all modifier keys to up.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Determines whether to modifier key states are equal.
|
||||
* \param keys: The modifier key state to compare to.
|
||||
* \return Indication of equality.
|
||||
*/
|
||||
bool equals(const GHOST_ModifierKeys &keys) const;
|
||||
|
||||
/** Bit-field that stores the appropriate key state. */
|
||||
uint8_t left_shift_ : 1;
|
||||
/** Bit-field that stores the appropriate key state. */
|
||||
uint8_t right_shift_ : 1;
|
||||
/** Bit-field that stores the appropriate key state. */
|
||||
uint8_t left_alt_ : 1;
|
||||
/** Bit-field that stores the appropriate key state. */
|
||||
uint8_t right_alt_ : 1;
|
||||
/** Bit-field that stores the appropriate key state. */
|
||||
uint8_t left_control_ : 1;
|
||||
/** Bit-field that stores the appropriate key state. */
|
||||
uint8_t right_control_ : 1;
|
||||
/** Bit-field that stores the appropriate key state. */
|
||||
uint8_t left_os_ : 1;
|
||||
uint8_t right_os_ : 1;
|
||||
uint8_t left_hyper_ : 1;
|
||||
uint8_t right_hyper_ : 1;
|
||||
};
|
||||
775
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManager.cc
Normal file
775
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManager.cc
Normal file
@@ -0,0 +1,775 @@
|
||||
/* SPDX-FileCopyrightText: 2007-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "GHOST_NDOFManager.hh"
|
||||
#include "GHOST_Debug.hh"
|
||||
#include "GHOST_EventKey.hh"
|
||||
#include "GHOST_EventNDOF.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
#include "GHOST_WindowManager.hh"
|
||||
#include "GHOST_utildefines.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstring> /* For memory functions. */
|
||||
#include <map>
|
||||
|
||||
static CLG_LogRef LOG = {"ghost.ndof"};
|
||||
|
||||
/**
|
||||
* 3Dconnexion keyboards and keypads use specific keys that have no standard equivalent.
|
||||
* These could be supported as generic "custom" keys, see !124155 review for details.
|
||||
*/
|
||||
// #define USE_3DCONNEXION_NONSTANDARD_KEYS
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name NDOF Enum Strings
|
||||
* \{ */
|
||||
|
||||
/* Printable values for #GHOST_TProgress enum (keep aligned). */
|
||||
static const char *ndof_progress_string[] = {
|
||||
"not started",
|
||||
"starting",
|
||||
"in progress",
|
||||
"finishing",
|
||||
"finished",
|
||||
};
|
||||
|
||||
/* Printable values for #NDOF_ButtonT enum (keep aligned) */
|
||||
#define MAP_ENTRY(button) {GHOST_##button, #button}
|
||||
static const std::map<GHOST_NDOF_ButtonT, const char *> ndof_button_names = {
|
||||
/* Disable wrapping, it makes it difficult to read. */
|
||||
/* clang-format off */
|
||||
MAP_ENTRY(NDOF_BUTTON_INVALID),
|
||||
MAP_ENTRY(NDOF_BUTTON_MENU),
|
||||
MAP_ENTRY(NDOF_BUTTON_FIT),
|
||||
MAP_ENTRY(NDOF_BUTTON_TOP),
|
||||
MAP_ENTRY(NDOF_BUTTON_LEFT),
|
||||
MAP_ENTRY(NDOF_BUTTON_RIGHT),
|
||||
MAP_ENTRY(NDOF_BUTTON_FRONT),
|
||||
MAP_ENTRY(NDOF_BUTTON_BOTTOM),
|
||||
MAP_ENTRY(NDOF_BUTTON_BACK),
|
||||
MAP_ENTRY(NDOF_BUTTON_ROLL_CW),
|
||||
MAP_ENTRY(NDOF_BUTTON_ROLL_CCW),
|
||||
MAP_ENTRY(NDOF_BUTTON_SPIN_CW),
|
||||
MAP_ENTRY(NDOF_BUTTON_SPIN_CCW),
|
||||
MAP_ENTRY(NDOF_BUTTON_TILT_CW),
|
||||
MAP_ENTRY(NDOF_BUTTON_TILT_CCW),
|
||||
MAP_ENTRY(NDOF_BUTTON_ISO1),
|
||||
MAP_ENTRY(NDOF_BUTTON_ISO2),
|
||||
MAP_ENTRY(NDOF_BUTTON_1),
|
||||
MAP_ENTRY(NDOF_BUTTON_2),
|
||||
MAP_ENTRY(NDOF_BUTTON_3),
|
||||
MAP_ENTRY(NDOF_BUTTON_4),
|
||||
MAP_ENTRY(NDOF_BUTTON_5),
|
||||
MAP_ENTRY(NDOF_BUTTON_6),
|
||||
MAP_ENTRY(NDOF_BUTTON_7),
|
||||
MAP_ENTRY(NDOF_BUTTON_8),
|
||||
MAP_ENTRY(NDOF_BUTTON_9),
|
||||
MAP_ENTRY(NDOF_BUTTON_10),
|
||||
MAP_ENTRY(NDOF_BUTTON_11),
|
||||
MAP_ENTRY(NDOF_BUTTON_12),
|
||||
MAP_ENTRY(NDOF_BUTTON_ESC),
|
||||
MAP_ENTRY(NDOF_BUTTON_ALT),
|
||||
MAP_ENTRY(NDOF_BUTTON_SHIFT),
|
||||
MAP_ENTRY(NDOF_BUTTON_CTRL),
|
||||
MAP_ENTRY(NDOF_BUTTON_ENTER),
|
||||
MAP_ENTRY(NDOF_BUTTON_DELETE),
|
||||
MAP_ENTRY(NDOF_BUTTON_TAB),
|
||||
MAP_ENTRY(NDOF_BUTTON_SPACE),
|
||||
MAP_ENTRY(NDOF_BUTTON_ROTATE),
|
||||
MAP_ENTRY(NDOF_BUTTON_PANZOOM),
|
||||
MAP_ENTRY(NDOF_BUTTON_DOMINANT),
|
||||
MAP_ENTRY(NDOF_BUTTON_PLUS),
|
||||
MAP_ENTRY(NDOF_BUTTON_MINUS),
|
||||
MAP_ENTRY(NDOF_BUTTON_V1),
|
||||
MAP_ENTRY(NDOF_BUTTON_V2),
|
||||
MAP_ENTRY(NDOF_BUTTON_V3),
|
||||
MAP_ENTRY(NDOF_BUTTON_SAVE_V1),
|
||||
MAP_ENTRY(NDOF_BUTTON_SAVE_V2),
|
||||
MAP_ENTRY(NDOF_BUTTON_SAVE_V3),
|
||||
MAP_ENTRY(NDOF_BUTTON_KBP_F1),
|
||||
MAP_ENTRY(NDOF_BUTTON_KBP_F2),
|
||||
MAP_ENTRY(NDOF_BUTTON_KBP_F3),
|
||||
MAP_ENTRY(NDOF_BUTTON_KBP_F4),
|
||||
MAP_ENTRY(NDOF_BUTTON_KBP_F5),
|
||||
MAP_ENTRY(NDOF_BUTTON_KBP_F6),
|
||||
MAP_ENTRY(NDOF_BUTTON_KBP_F7),
|
||||
MAP_ENTRY(NDOF_BUTTON_KBP_F8),
|
||||
MAP_ENTRY(NDOF_BUTTON_KBP_F9),
|
||||
MAP_ENTRY(NDOF_BUTTON_KBP_F10),
|
||||
MAP_ENTRY(NDOF_BUTTON_KBP_F11),
|
||||
MAP_ENTRY(NDOF_BUTTON_KBP_F12),
|
||||
MAP_ENTRY(NDOF_BUTTON_NP_F1),
|
||||
MAP_ENTRY(NDOF_BUTTON_NP_F2),
|
||||
MAP_ENTRY(NDOF_BUTTON_NP_F3),
|
||||
MAP_ENTRY(NDOF_BUTTON_NP_F4),
|
||||
/* clang-format on */
|
||||
};
|
||||
#undef MAP_ENTRY
|
||||
|
||||
static const char *ndof_device_names[] = {
|
||||
"UnknownDevice",
|
||||
"SpaceNavigator",
|
||||
"SpaceExplorer",
|
||||
"SpacePilotPro",
|
||||
"SpaceMousePro",
|
||||
"SpaceMouseWireless",
|
||||
"SpaceMouseProWireless",
|
||||
"SpaceMouseEnterprise",
|
||||
"SpacePilot",
|
||||
"Spaceball5000",
|
||||
"SpaceTraveler",
|
||||
"Keyboard Pro",
|
||||
"Numpad Pro",
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name NDOF Button Maps
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Shared by the some 3Dconnexion hardware
|
||||
* SpacePilotPro uses all of these
|
||||
* SpaceMouse Pro and SpaceNavigator use only some, based on button mask.
|
||||
*/
|
||||
static const GHOST_NDOF_ButtonT ndof_HID_map_Shared3Dx[] = {
|
||||
GHOST_NDOF_BUTTON_MENU, GHOST_NDOF_BUTTON_FIT, GHOST_NDOF_BUTTON_TOP,
|
||||
GHOST_NDOF_BUTTON_LEFT, GHOST_NDOF_BUTTON_RIGHT, GHOST_NDOF_BUTTON_FRONT,
|
||||
GHOST_NDOF_BUTTON_BOTTOM, GHOST_NDOF_BUTTON_BACK, GHOST_NDOF_BUTTON_ROLL_CW,
|
||||
GHOST_NDOF_BUTTON_ROLL_CCW, GHOST_NDOF_BUTTON_ISO1, GHOST_NDOF_BUTTON_ISO2,
|
||||
GHOST_NDOF_BUTTON_1, GHOST_NDOF_BUTTON_2, GHOST_NDOF_BUTTON_3,
|
||||
GHOST_NDOF_BUTTON_4, GHOST_NDOF_BUTTON_5, GHOST_NDOF_BUTTON_6,
|
||||
GHOST_NDOF_BUTTON_7, GHOST_NDOF_BUTTON_8, GHOST_NDOF_BUTTON_9,
|
||||
GHOST_NDOF_BUTTON_10, GHOST_NDOF_BUTTON_ESC, GHOST_NDOF_BUTTON_ALT,
|
||||
GHOST_NDOF_BUTTON_SHIFT, GHOST_NDOF_BUTTON_CTRL, GHOST_NDOF_BUTTON_ROTATE,
|
||||
GHOST_NDOF_BUTTON_PANZOOM, GHOST_NDOF_BUTTON_DOMINANT, GHOST_NDOF_BUTTON_PLUS,
|
||||
GHOST_NDOF_BUTTON_MINUS,
|
||||
};
|
||||
|
||||
static const GHOST_NDOF_ButtonT ndof_HID_map_SpaceExplorer[] = {
|
||||
GHOST_NDOF_BUTTON_1,
|
||||
GHOST_NDOF_BUTTON_2,
|
||||
GHOST_NDOF_BUTTON_TOP,
|
||||
GHOST_NDOF_BUTTON_LEFT,
|
||||
GHOST_NDOF_BUTTON_RIGHT,
|
||||
GHOST_NDOF_BUTTON_FRONT,
|
||||
GHOST_NDOF_BUTTON_ESC,
|
||||
GHOST_NDOF_BUTTON_ALT,
|
||||
GHOST_NDOF_BUTTON_SHIFT,
|
||||
GHOST_NDOF_BUTTON_CTRL,
|
||||
GHOST_NDOF_BUTTON_FIT,
|
||||
GHOST_NDOF_BUTTON_MENU,
|
||||
GHOST_NDOF_BUTTON_PLUS,
|
||||
GHOST_NDOF_BUTTON_MINUS,
|
||||
GHOST_NDOF_BUTTON_ROTATE,
|
||||
};
|
||||
|
||||
/* This is the older SpacePilot (sans Pro). */
|
||||
static const GHOST_NDOF_ButtonT ndof_HID_map_SpacePilot[] = {
|
||||
GHOST_NDOF_BUTTON_1, GHOST_NDOF_BUTTON_2,
|
||||
GHOST_NDOF_BUTTON_3, GHOST_NDOF_BUTTON_4,
|
||||
GHOST_NDOF_BUTTON_5, GHOST_NDOF_BUTTON_6,
|
||||
GHOST_NDOF_BUTTON_TOP, GHOST_NDOF_BUTTON_LEFT,
|
||||
GHOST_NDOF_BUTTON_RIGHT, GHOST_NDOF_BUTTON_FRONT,
|
||||
GHOST_NDOF_BUTTON_ESC, GHOST_NDOF_BUTTON_ALT,
|
||||
GHOST_NDOF_BUTTON_SHIFT, GHOST_NDOF_BUTTON_CTRL,
|
||||
GHOST_NDOF_BUTTON_FIT, GHOST_NDOF_BUTTON_MENU,
|
||||
GHOST_NDOF_BUTTON_PLUS, GHOST_NDOF_BUTTON_MINUS,
|
||||
GHOST_NDOF_BUTTON_DOMINANT, GHOST_NDOF_BUTTON_ROTATE,
|
||||
GHOST_NDOF_BUTTON_INVALID /* the CONFIG button -- what does it do? */
|
||||
};
|
||||
|
||||
static const GHOST_NDOF_ButtonT ndof_HID_map_Generic[] = {
|
||||
GHOST_NDOF_BUTTON_1,
|
||||
GHOST_NDOF_BUTTON_2,
|
||||
GHOST_NDOF_BUTTON_3,
|
||||
GHOST_NDOF_BUTTON_4,
|
||||
GHOST_NDOF_BUTTON_5,
|
||||
GHOST_NDOF_BUTTON_6,
|
||||
GHOST_NDOF_BUTTON_7,
|
||||
GHOST_NDOF_BUTTON_8,
|
||||
GHOST_NDOF_BUTTON_9,
|
||||
};
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name NDOF Manager Class
|
||||
* \{ */
|
||||
|
||||
static const int genericButtonCount = ARRAY_SIZE(ndof_HID_map_Generic);
|
||||
|
||||
GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System &sys)
|
||||
: system_(sys),
|
||||
device_type_(NDOF_UnknownDevice), /* Each platform has its own device detection code. */
|
||||
hid_map_button_num_(genericButtonCount),
|
||||
hid_map_button_mask_(0),
|
||||
hid_map_(ndof_HID_map_Generic),
|
||||
button_depressed_(0),
|
||||
pressed_buttons_cache_(),
|
||||
pressed_long_buttons_cache_(),
|
||||
motion_time_(0),
|
||||
motion_time_prev_(0),
|
||||
motion_state_(GHOST_kNotStarted),
|
||||
motion_event_pending_(false),
|
||||
motion_dead_zone_(0.0f)
|
||||
{
|
||||
/* To avoid the rare situation where one triple is updated and
|
||||
* the other is not, initialize them both here: */
|
||||
memset(translation_, 0, sizeof(translation_));
|
||||
memset(rotation_, 0, sizeof(rotation_));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name NDOF Device Setup
|
||||
* \{ */
|
||||
|
||||
bool GHOST_NDOFManager::setDevice(ushort vendor_id, ushort product_id)
|
||||
{
|
||||
/* Call this function until it returns true
|
||||
* it's a good idea to stop calling it after that, as it will "forget"
|
||||
* whichever device it already found. */
|
||||
|
||||
/* Default to safe generic behavior for "unknown" devices
|
||||
* unidentified devices will emit motion events like normal
|
||||
* rogue buttons do nothing by default, but can be customized by the user. */
|
||||
|
||||
device_type_ = NDOF_UnknownDevice;
|
||||
hid_map_ = ndof_HID_map_Generic;
|
||||
hid_map_button_num_ = genericButtonCount;
|
||||
hid_map_button_mask_ = 0;
|
||||
|
||||
/* "mystery device" owners can help build a HID_map for their hardware
|
||||
* A few users have already contributed information about several older devices
|
||||
* that I don't have access to. Thanks! */
|
||||
|
||||
switch (vendor_id) {
|
||||
case 0x046D: /* Logitech (3Dconnexion was a subsidiary). */
|
||||
switch (product_id) {
|
||||
/* -- current devices -- */
|
||||
case 0xC626: /* Full-size SpaceNavigator. */
|
||||
case 0xC628: /* The "for Notebooks" one. */
|
||||
{
|
||||
device_type_ = NDOF_SpaceNavigator;
|
||||
hid_map_button_num_ = 2;
|
||||
hid_map_ = ndof_HID_map_Shared3Dx;
|
||||
break;
|
||||
}
|
||||
case 0xC627: {
|
||||
device_type_ = NDOF_SpaceExplorer;
|
||||
hid_map_button_num_ = 15;
|
||||
hid_map_ = ndof_HID_map_SpaceExplorer;
|
||||
break;
|
||||
}
|
||||
case 0xC629: {
|
||||
device_type_ = NDOF_SpacePilotPro;
|
||||
hid_map_button_num_ = 31;
|
||||
hid_map_ = ndof_HID_map_Shared3Dx;
|
||||
break;
|
||||
}
|
||||
case 0xC62B: {
|
||||
device_type_ = NDOF_SpaceMousePro;
|
||||
hid_map_button_num_ = 27; /* 15 physical buttons, but HID codes range from 0 to 26. */
|
||||
hid_map_button_mask_ = 0x07C0F137;
|
||||
hid_map_ = ndof_HID_map_Shared3Dx;
|
||||
break;
|
||||
}
|
||||
|
||||
/* -- older devices -- */
|
||||
case 0xC625: {
|
||||
device_type_ = NDOF_SpacePilot;
|
||||
hid_map_button_num_ = 21;
|
||||
hid_map_ = ndof_HID_map_SpacePilot;
|
||||
break;
|
||||
}
|
||||
case 0xC621: {
|
||||
device_type_ = NDOF_Spaceball5000;
|
||||
hid_map_button_num_ = 12;
|
||||
break;
|
||||
}
|
||||
case 0xC623: {
|
||||
device_type_ = NDOF_SpaceTraveler;
|
||||
hid_map_button_num_ = 8;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
CLOG_INFO(&LOG, "Unknown Logitech product %04hx", product_id);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x256F: /* 3Dconnexion. */
|
||||
switch (product_id) {
|
||||
case 0xC62E: /* SpaceMouse Wireless (cabled). */
|
||||
case 0xC62F: /* SpaceMouse Wireless Receiver. */
|
||||
case 0xC658: /* Wireless (3DConnexion Universal Wireless Receiver in WIN32), see #82412. */
|
||||
{
|
||||
device_type_ = NDOF_SpaceMouseWireless;
|
||||
hid_map_button_num_ = 2;
|
||||
hid_map_ = ndof_HID_map_Shared3Dx;
|
||||
break;
|
||||
}
|
||||
case 0xC631: /* SpaceMouse Pro Wireless (cabled). */
|
||||
case 0xC632: /* SpaceMouse Pro Wireless Receiver. */
|
||||
case 0xC638: /* SpaceMouse Pro Wireless BT (cabled), see #116393.
|
||||
* 3Dconnexion docs describe this as "Wireless BT", but it is cabled. */
|
||||
case 0xC652: /* Universal Receiver. */
|
||||
{
|
||||
device_type_ = NDOF_SpaceMouseProWireless;
|
||||
hid_map_button_num_ = 27; /* 15 physical buttons, but HID codes range from 0 to 26. */
|
||||
hid_map_button_mask_ = 0x07C0F137;
|
||||
hid_map_ = ndof_HID_map_Shared3Dx;
|
||||
break;
|
||||
}
|
||||
case 0xC633: /* Newer devices don't need to use button mappings. */
|
||||
{
|
||||
device_type_ = NDOF_SpaceMouseEnterprise;
|
||||
break;
|
||||
}
|
||||
case 0xC664:
|
||||
case 0xC668: {
|
||||
device_type_ = NDOF_KeyboardPro;
|
||||
break;
|
||||
}
|
||||
case 0xC665: {
|
||||
device_type_ = NDOF_NumpadPro;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
CLOG_INFO(&LOG, "Unknown 3Dconnexion product %04hx", product_id);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CLOG_INFO(&LOG, "Unknown device %04hx:%04hx", vendor_id, product_id);
|
||||
}
|
||||
|
||||
if (device_type_ != NDOF_UnknownDevice) {
|
||||
CLOG_INFO(&LOG, "Using %s", ndof_device_names[device_type_]);
|
||||
}
|
||||
|
||||
if (hid_map_button_mask_ == 0) {
|
||||
hid_map_button_mask_ = int(~(UINT_MAX << hid_map_button_num_));
|
||||
}
|
||||
|
||||
CLOG_DEBUG(&LOG, "Device %d buttons -> hex:%X", hid_map_button_num_, uint(hid_map_button_mask_));
|
||||
|
||||
return device_type_ != NDOF_UnknownDevice;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name NDOF Update State
|
||||
* \{ */
|
||||
|
||||
void GHOST_NDOFManager::updateTranslation(const int t[3], uint64_t time)
|
||||
{
|
||||
memcpy(translation_, t, sizeof(translation_));
|
||||
motion_time_ = time;
|
||||
motion_event_pending_ = true;
|
||||
}
|
||||
|
||||
void GHOST_NDOFManager::updateRotation(const int r[3], uint64_t time)
|
||||
{
|
||||
memcpy(rotation_, r, sizeof(rotation_));
|
||||
motion_time_ = time;
|
||||
motion_event_pending_ = true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name NDOF Buttons
|
||||
* \{ */
|
||||
|
||||
static GHOST_TKey ghost_map_keyboard_from_ndof_button(const GHOST_NDOF_ButtonT button)
|
||||
{
|
||||
switch (button) {
|
||||
case GHOST_NDOF_BUTTON_ESC: {
|
||||
return GHOST_kKeyEsc;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_ENTER: {
|
||||
return GHOST_kKeyEnter;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_DELETE: {
|
||||
return GHOST_kKeyDelete;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_TAB: {
|
||||
return GHOST_kKeyTab;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_SPACE: {
|
||||
return GHOST_kKeySpace;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_ALT: {
|
||||
return GHOST_kKeyLeftAlt;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_SHIFT: {
|
||||
return GHOST_kKeyLeftShift;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_CTRL: {
|
||||
return GHOST_kKeyLeftControl;
|
||||
}
|
||||
|
||||
#ifdef USE_3DCONNEXION_NONSTANDARD_KEYS
|
||||
case GHOST_NDOF_BUTTON_NP_F1:
|
||||
case GHOST_NDOF_BUTTON_KBP_F1: {
|
||||
return GHOST_kKeyF1;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_NP_F2:
|
||||
case GHOST_NDOF_BUTTON_KBP_F2: {
|
||||
return GHOST_kKeyF2;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_NP_F3:
|
||||
case GHOST_NDOF_BUTTON_KBP_F3: {
|
||||
return GHOST_kKeyF3;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_NP_F4:
|
||||
case GHOST_NDOF_BUTTON_KBP_F4: {
|
||||
return GHOST_kKeyF4;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_KBP_F5: {
|
||||
return GHOST_kKeyF5;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_KBP_F6: {
|
||||
return GHOST_kKeyF6;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_KBP_F7: {
|
||||
return GHOST_kKeyF7;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_KBP_F8: {
|
||||
return GHOST_kKeyF8;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_KBP_F9: {
|
||||
return GHOST_kKeyF9;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_KBP_F10: {
|
||||
return GHOST_kKeyF10;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_KBP_F11: {
|
||||
return GHOST_kKeyF11;
|
||||
}
|
||||
case GHOST_NDOF_BUTTON_KBP_F12: {
|
||||
return GHOST_kKeyF12;
|
||||
}
|
||||
#endif /* !USE_3DCONNEXION_NONSTANDARD_KEYS */
|
||||
|
||||
default: {
|
||||
return GHOST_kKeyUnknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_NDOFManager::sendButtonEvent(GHOST_NDOF_ButtonT button,
|
||||
bool press,
|
||||
uint64_t time,
|
||||
GHOST_IWindow *window)
|
||||
{
|
||||
GHOST_ASSERT(button > GHOST_NDOF_BUTTON_NONE && button < GHOST_NDOF_BUTTON_USER,
|
||||
"rogue button trying to escape GHOST_NDOF manager");
|
||||
|
||||
auto event = std::make_unique<GHOST_EventNDOFButton>(time, window);
|
||||
GHOST_TEventNDOFButtonData *data = (GHOST_TEventNDOFButtonData *)event->getData();
|
||||
|
||||
data->action = press ? GHOST_kPress : GHOST_kRelease;
|
||||
data->button = button;
|
||||
|
||||
system_.pushEvent(std::move(event));
|
||||
}
|
||||
|
||||
void GHOST_NDOFManager::sendKeyEvent(GHOST_TKey key,
|
||||
bool press,
|
||||
uint64_t time,
|
||||
GHOST_IWindow *window)
|
||||
{
|
||||
GHOST_TEventType type = press ? GHOST_kEventKeyDown : GHOST_kEventKeyUp;
|
||||
auto event = std::make_unique<GHOST_EventKey>(time, type, window, key, false);
|
||||
|
||||
system_.pushEvent(std::move(event));
|
||||
}
|
||||
|
||||
void GHOST_NDOFManager::updateButton(GHOST_NDOF_ButtonT button, bool press, uint64_t time)
|
||||
{
|
||||
if (button == GHOST_NDOF_BUTTON_INVALID) {
|
||||
CLOG_DEBUG(
|
||||
&LOG, "Update button=%d, press=%d (mapped to none, ignoring!)", int(button), int(press));
|
||||
return;
|
||||
}
|
||||
|
||||
CLOG_DEBUG(&LOG,
|
||||
"Update button=%d, press=%d, name=%s",
|
||||
button,
|
||||
int(press),
|
||||
ndof_button_names.at(button));
|
||||
|
||||
#ifndef USE_3DCONNEXION_NONSTANDARD_KEYS
|
||||
if (((button >= GHOST_NDOF_BUTTON_KBP_F1) && (button <= GHOST_NDOF_BUTTON_KBP_F12)) ||
|
||||
((button >= GHOST_NDOF_BUTTON_NP_F1) && (button <= GHOST_NDOF_BUTTON_NP_F4)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif /* !USE_3DCONNEXION_NONSTANDARD_KEYS */
|
||||
|
||||
GHOST_IWindow *window = system_.getWindowManager()->getActiveWindow();
|
||||
|
||||
/* Delivery will fail, so don't bother sending. */
|
||||
if (window != nullptr) {
|
||||
const GHOST_TKey key = ghost_map_keyboard_from_ndof_button(button);
|
||||
if (key != GHOST_kKeyUnknown) {
|
||||
sendKeyEvent(key, press, time, window);
|
||||
}
|
||||
else {
|
||||
sendButtonEvent(button, press, time, window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_NDOFManager::updateButtonRAW(int button_number, bool press, uint64_t time)
|
||||
{
|
||||
GHOST_NDOF_ButtonT button;
|
||||
|
||||
/* For bit-mask devices button mapping isn't unified, therefore check the button map. */
|
||||
if (std::find(bitmask_devices_.begin(), bitmask_devices_.end(), device_type_) !=
|
||||
bitmask_devices_.end())
|
||||
{
|
||||
if (button_number >= hid_map_button_num_) {
|
||||
CLOG_DEBUG(
|
||||
&LOG, "Update button=%d, press=%d (out of range, ignoring!)", button_number, int(press));
|
||||
return;
|
||||
}
|
||||
button = hid_map_[button_number];
|
||||
}
|
||||
else {
|
||||
button = static_cast<GHOST_NDOF_ButtonT>(button_number);
|
||||
}
|
||||
|
||||
GHOST_NDOFManager::updateButton(button, press, time);
|
||||
}
|
||||
|
||||
void GHOST_NDOFManager::updateButtonsBitmask(int button_bits, uint64_t time)
|
||||
{
|
||||
/* Some devices send two data packets: bitmask and number array.
|
||||
* In this case, packet has to be ignored if it came from such a device. */
|
||||
if (std::find(bitmask_devices_.begin(), bitmask_devices_.end(), device_type_) ==
|
||||
bitmask_devices_.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
button_bits &= hid_map_button_mask_; /* Discard any "garbage" bits. */
|
||||
|
||||
int diff = button_depressed_ ^ button_bits;
|
||||
|
||||
for (int button_number = 0; button_number < hid_map_button_num_; ++button_number) {
|
||||
int mask = 1 << button_number;
|
||||
|
||||
if (diff & mask) {
|
||||
bool press = button_bits & mask;
|
||||
|
||||
if (press) {
|
||||
button_depressed_ |= mask; /* Set this button's bit. */
|
||||
}
|
||||
else {
|
||||
button_depressed_ &= ~mask; /* Clear this button's bit. */
|
||||
}
|
||||
|
||||
/* Bitmask devices don't have unified keymaps, so button numbers needs to be looked up in the
|
||||
* map. */
|
||||
const GHOST_NDOF_ButtonT button = hid_map_[button_number];
|
||||
updateButton(button, press, time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_NDOFManager::updateButtonsArray(NDOF_Button_Array buttons,
|
||||
uint64_t time,
|
||||
NDOF_Button_Type type)
|
||||
{
|
||||
NDOF_Button_Array &cache = (type == NDOF_Button_Type::LongButton) ? pressed_long_buttons_cache_ :
|
||||
pressed_buttons_cache_;
|
||||
|
||||
/* Find released buttons */
|
||||
for (const auto &cached_button : cache) {
|
||||
bool found = false;
|
||||
for (const auto &button : buttons) {
|
||||
if (button == cached_button) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
updateButton(cached_button, false, time);
|
||||
}
|
||||
}
|
||||
|
||||
/* Find pressed buttons */
|
||||
for (const auto &button : buttons) {
|
||||
bool found = false;
|
||||
for (const auto &cached_button : cache) {
|
||||
if (button == cached_button) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
updateButton(button, true, time);
|
||||
}
|
||||
}
|
||||
cache = buttons;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name NDOF Motion
|
||||
* \{ */
|
||||
|
||||
void GHOST_NDOFManager::setDeadZone(float dz)
|
||||
{
|
||||
/* Negative values don't make sense, so clamp at zero. */
|
||||
dz = std::max(dz, 0.0f);
|
||||
motion_dead_zone_ = dz;
|
||||
|
||||
/* Warn the rogue user/developer about high dead-zone, but allow it. */
|
||||
CLOG_INFO(&LOG, "Dead zone set to %.2f%s", dz, (dz > 0.5f) ? " (unexpectedly high)" : "");
|
||||
}
|
||||
|
||||
static bool atHomePosition(const GHOST_TEventNDOFMotionData *ndof)
|
||||
{
|
||||
#define HOME(foo) (ndof->foo == 0.0f)
|
||||
return HOME(tx) && HOME(ty) && HOME(tz) && HOME(rx) && HOME(ry) && HOME(rz);
|
||||
#undef HOME
|
||||
}
|
||||
|
||||
static bool nearHomePosition(const GHOST_TEventNDOFMotionData *ndof, float threshold)
|
||||
{
|
||||
if (threshold == 0.0f) {
|
||||
return atHomePosition(ndof);
|
||||
}
|
||||
#define HOME(foo) (fabsf(ndof->foo) < threshold)
|
||||
return HOME(tx) && HOME(ty) && HOME(tz) && HOME(rx) && HOME(ry) && HOME(rz);
|
||||
#undef HOME
|
||||
}
|
||||
|
||||
bool GHOST_NDOFManager::sendMotionEvent()
|
||||
{
|
||||
if (!motion_event_pending_) {
|
||||
if (motion_state_ != GHOST_kNotStarted) {
|
||||
/* Detect window de-activation and change the `motion_state_` even when no motion is pending.
|
||||
* Without this check it's possible the window is de-activated before the NDOF
|
||||
* motion callbacks have run, while the `motion_state_` is active.
|
||||
* In this case, activating the window again would create an event
|
||||
* with a large time-delta, see: #134733. */
|
||||
if (system_.getWindowManager()->getActiveWindow() == nullptr) {
|
||||
/* Avoid large `dt` times when changing windows. */
|
||||
motion_state_ = GHOST_kNotStarted;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
motion_event_pending_ = false; /* Any pending motion is handled right now. */
|
||||
|
||||
GHOST_IWindow *window = system_.getWindowManager()->getActiveWindow();
|
||||
|
||||
/* Delivery will fail, so don't bother sending. */
|
||||
if (window == nullptr) {
|
||||
/* Avoid large `dt` times when changing windows. */
|
||||
motion_state_ = GHOST_kNotStarted;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto event = std::make_unique<GHOST_EventNDOFMotion>(motion_time_, window);
|
||||
GHOST_TEventNDOFMotionData *data = (GHOST_TEventNDOFMotionData *)event->getData();
|
||||
|
||||
/* Scale axis values here to normalize them to around +/- 1
|
||||
* they are scaled again for overall sensitivity in the WM based on user preferences. */
|
||||
|
||||
const float scale = 1.0f / 350.0f; /* 3Dconnexion devices send +/- 350 usually */
|
||||
|
||||
data->tx = scale * translation_[0];
|
||||
data->ty = scale * translation_[1];
|
||||
data->tz = scale * translation_[2];
|
||||
|
||||
data->rx = scale * rotation_[0];
|
||||
data->ry = scale * rotation_[1];
|
||||
data->rz = scale * rotation_[2];
|
||||
data->dt = 0.001f * (motion_time_ - motion_time_prev_); /* In seconds. */
|
||||
motion_time_prev_ = motion_time_;
|
||||
|
||||
bool weHaveMotion = !nearHomePosition(data, motion_dead_zone_);
|
||||
|
||||
/* Determine what kind of motion event to send `(Starting, InProgress, Finishing)`
|
||||
* and where that leaves this NDOF manager `(NotStarted, InProgress, Finished)`. */
|
||||
switch (motion_state_) {
|
||||
case GHOST_kNotStarted:
|
||||
case GHOST_kFinished: {
|
||||
if (weHaveMotion) {
|
||||
data->progress = GHOST_kStarting;
|
||||
motion_state_ = GHOST_kInProgress;
|
||||
/* Previous motion time will be ancient, so just make up a reasonable time delta. */
|
||||
data->dt = NDOF_TIME_DELTA_STARTING;
|
||||
}
|
||||
else {
|
||||
/* Send no event and keep current state. */
|
||||
CLOG_DEBUG(&LOG, "Motion ignored");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GHOST_kInProgress: {
|
||||
if (weHaveMotion) {
|
||||
data->progress = GHOST_kInProgress;
|
||||
/* Remain 'InProgress'. */
|
||||
}
|
||||
else {
|
||||
data->progress = GHOST_kFinishing;
|
||||
motion_state_ = GHOST_kFinished;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/* Will always be one of the above. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
CLOG_DEBUG(&LOG,
|
||||
"Motion sent, T=(%.2f,%.2f,%.2f), R=(%.2f,%.2f,%.2f) dt=%.3f, status=%s",
|
||||
data->tx,
|
||||
data->ty,
|
||||
data->tz,
|
||||
data->rx,
|
||||
data->ry,
|
||||
data->rz,
|
||||
data->dt,
|
||||
ndof_progress_string[data->progress]);
|
||||
#else
|
||||
/* Raw values, may be useful for debugging. */
|
||||
CLOG_DEBUG(&LOG,
|
||||
"Motion sent, T=(%d,%d,%d) R=(%d,%d,%d) status=%s",
|
||||
translation_[0],
|
||||
translation_[1],
|
||||
translation_[2],
|
||||
rotation_[0],
|
||||
rotation_[1],
|
||||
rotation_[2],
|
||||
ndof_progress_string[data->progress]);
|
||||
#endif
|
||||
system_.pushEvent(std::move(event));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
142
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManager.hh
Normal file
142
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManager.hh
Normal file
@@ -0,0 +1,142 @@
|
||||
/* SPDX-FileCopyrightText: 2002-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef WITH_INPUT_NDOF
|
||||
# error NDOF code included in non-NDOF-enabled build
|
||||
#endif
|
||||
|
||||
#include "GHOST_System.hh"
|
||||
|
||||
#include <array>
|
||||
|
||||
enum NDOF_DeviceT {
|
||||
NDOF_UnknownDevice = 0,
|
||||
|
||||
/* Current devices. */
|
||||
NDOF_SpaceNavigator,
|
||||
NDOF_SpaceExplorer,
|
||||
NDOF_SpacePilotPro,
|
||||
NDOF_SpaceMousePro,
|
||||
NDOF_SpaceMouseWireless,
|
||||
NDOF_SpaceMouseProWireless,
|
||||
NDOF_SpaceMouseEnterprise,
|
||||
NDOF_KeyboardPro,
|
||||
NDOF_NumpadPro,
|
||||
|
||||
/* Older devices. */
|
||||
NDOF_SpacePilot,
|
||||
NDOF_Spaceball5000,
|
||||
NDOF_SpaceTraveler
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The dummy time delta to use for starting events.
|
||||
* 1/80th of a second (12.5ms).
|
||||
*/
|
||||
#define NDOF_TIME_DELTA_STARTING 0.0125f
|
||||
|
||||
using NDOF_Button_Array = std::array<GHOST_NDOF_ButtonT, 6>;
|
||||
|
||||
enum NDOF_Button_Type { ShortButton, LongButton };
|
||||
|
||||
class GHOST_NDOFManager {
|
||||
public:
|
||||
GHOST_NDOFManager(GHOST_System &);
|
||||
virtual ~GHOST_NDOFManager() = default;
|
||||
|
||||
/**
|
||||
* Whether multi-axis functionality is available (via the OS or driver)
|
||||
* does not imply that a device is plugged in or being used.
|
||||
*/
|
||||
virtual bool available() = 0;
|
||||
|
||||
/**
|
||||
* Each platform's device detection should call this
|
||||
* use standard USB/HID identifiers.
|
||||
*/
|
||||
bool setDevice(unsigned short vendor_id, unsigned short product_id);
|
||||
|
||||
/**
|
||||
* Filter out small/accidental/un-calibrated motions by
|
||||
* setting up a "dead zone" around home position
|
||||
* set to 0 to disable
|
||||
* 0.1 is a safe and reasonable value.
|
||||
*/
|
||||
void setDeadZone(float);
|
||||
|
||||
/**
|
||||
* The latest raw axis data from the device.
|
||||
*
|
||||
* \note axis data should be in blender view coordinates
|
||||
* - +X is to the right.
|
||||
* - +Y is up.
|
||||
* - +Z is out of the screen.
|
||||
* - for rotations, look from origin to each +axis.
|
||||
* - rotations are + when CCW, - when CW.
|
||||
* Each platform is responsible for getting axis data into this form
|
||||
* these values should not be scaled (just shuffled or flipped).
|
||||
*/
|
||||
void updateTranslation(const int t[3], uint64_t time);
|
||||
void updateRotation(const int r[3], uint64_t time);
|
||||
|
||||
/**
|
||||
* The latest raw button data from the device
|
||||
* use HID button encoding (not #NDOF_ButtonT).
|
||||
*/
|
||||
void updateButtonRAW(int button_number, bool press, uint64_t time);
|
||||
/**
|
||||
* Add the button event which has already been mapped to #GHOST_NDOF_ButtonT.
|
||||
*/
|
||||
void updateButton(GHOST_NDOF_ButtonT button, bool press, uint64_t time);
|
||||
void updateButtonsBitmask(int button_bits, uint64_t time);
|
||||
void updateButtonsArray(NDOF_Button_Array buttons, uint64_t time, NDOF_Button_Type type);
|
||||
/* #NDOFButton events are sent immediately */
|
||||
|
||||
/**
|
||||
* Processes and sends most recent raw data as an #NDOFMotion event
|
||||
* returns whether an event was sent.
|
||||
*/
|
||||
bool sendMotionEvent();
|
||||
|
||||
protected:
|
||||
GHOST_System &system_;
|
||||
|
||||
private:
|
||||
void sendButtonEvent(GHOST_NDOF_ButtonT, bool press, uint64_t time, GHOST_IWindow *);
|
||||
void sendKeyEvent(GHOST_TKey, bool press, uint64_t time, GHOST_IWindow *);
|
||||
|
||||
NDOF_DeviceT device_type_;
|
||||
int hid_map_button_num_;
|
||||
int hid_map_button_mask_;
|
||||
const GHOST_NDOF_ButtonT *hid_map_;
|
||||
|
||||
int translation_[3];
|
||||
int rotation_[3];
|
||||
|
||||
int button_depressed_; /* Bit field. */
|
||||
NDOF_Button_Array pressed_buttons_cache_;
|
||||
NDOF_Button_Array pressed_long_buttons_cache_;
|
||||
|
||||
uint64_t motion_time_; /* In milliseconds. */
|
||||
uint64_t motion_time_prev_; /* Time of most recent motion event sent. */
|
||||
|
||||
GHOST_TProgress motion_state_;
|
||||
bool motion_event_pending_;
|
||||
float motion_dead_zone_; /* Discard motion with each component < this. */
|
||||
|
||||
inline static std::array<NDOF_DeviceT, 9> bitmask_devices_ = {
|
||||
NDOF_SpaceNavigator,
|
||||
NDOF_SpaceExplorer,
|
||||
NDOF_SpacePilotPro,
|
||||
NDOF_SpaceMousePro,
|
||||
NDOF_SpaceMouseWireless,
|
||||
NDOF_SpaceMouseProWireless,
|
||||
NDOF_SpacePilot,
|
||||
NDOF_Spaceball5000,
|
||||
NDOF_SpaceTraveler,
|
||||
};
|
||||
};
|
||||
18
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManagerCocoa.hh
Normal file
18
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManagerCocoa.hh
Normal file
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2002-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_NDOFManager.hh"
|
||||
|
||||
// Event capture is handled within the NDOF manager on Macintosh,
|
||||
// so there's no need for SystemCocoa to look for them.
|
||||
|
||||
class GHOST_NDOFManagerCocoa : public GHOST_NDOFManager {
|
||||
public:
|
||||
GHOST_NDOFManagerCocoa(GHOST_System &);
|
||||
~GHOST_NDOFManagerCocoa() override;
|
||||
|
||||
bool available() override;
|
||||
};
|
||||
281
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManagerCocoa.mm
Normal file
281
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManagerCocoa.mm
Normal file
@@ -0,0 +1,281 @@
|
||||
/* SPDX-FileCopyrightText: 2010-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "GHOST_NDOFManagerCocoa.hh"
|
||||
|
||||
#include "GHOST_SystemCocoa.hh"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#define DEBUG_NDOF_DRIVER false
|
||||
|
||||
#if DEBUG_NDOF_DRIVER
|
||||
# include <cstdio>
|
||||
#endif
|
||||
|
||||
/* Static callback functions need to talk to these objects: */
|
||||
static GHOST_SystemCocoa *ghost_system = nullptr;
|
||||
static GHOST_NDOFManager *ndof_manager = nullptr;
|
||||
|
||||
static uint16_t clientID = 0;
|
||||
|
||||
static bool driver_loaded = false;
|
||||
|
||||
/* 3DxMacCore version >= minimal_version is considered "new".
|
||||
* It was firstly introduced in 3DxWare v10.8.4 r3716 and can process
|
||||
* (not yet documented in SDK manual) kConnexionCmdAppEvent events. */
|
||||
static NSString *new_driver_minimal_version = @"1.3.4.473";
|
||||
|
||||
/* Replicate just enough of the 3Dx API for our uses, not everything the driver provides. */
|
||||
|
||||
#define kConnexionClientModeTakeOver 1
|
||||
#define kConnexionMaskAll 0x3fff
|
||||
#define kConnexionMaskAxis 0x3f00
|
||||
#define kConnexionMaskNoButtons 0x0
|
||||
#define kConnexionMaskAllButtons 0xffffffff
|
||||
#define kConnexionCmdHandleButtons 2
|
||||
#define kConnexionCmdHandleAxis 3
|
||||
#define kConnexionCmdAppSpecific 10
|
||||
#define kConnexionCmdAppEvent 11
|
||||
#define kConnexionMsgDeviceState '3dSR'
|
||||
#define kConnexionCtlGetDeviceID '3did'
|
||||
|
||||
#pragma pack(push, 2) /* Just this struct. */
|
||||
struct ConnexionDeviceState {
|
||||
uint16_t version;
|
||||
uint16_t client;
|
||||
uint16_t command;
|
||||
int16_t param;
|
||||
int32_t value;
|
||||
uint64_t time;
|
||||
uint8_t report[8];
|
||||
uint16_t appEventPressed;
|
||||
int16_t axis[6]; /* TX, TY, TZ, RX, RY, RZ. */
|
||||
uint16_t address;
|
||||
uint32_t buttons;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
/* Callback functions: */
|
||||
typedef void (*AddedHandler)(uint32_t);
|
||||
typedef void (*RemovedHandler)(uint32_t);
|
||||
typedef void (*MessageHandler)(uint32_t, uint32_t msg_type, void *msg_arg);
|
||||
|
||||
/* Driver functions: */
|
||||
typedef int16_t (*SetConnexionHandlers_ptr)(MessageHandler, AddedHandler, RemovedHandler, bool);
|
||||
typedef void (*CleanupConnexionHandlers_ptr)();
|
||||
typedef uint16_t (*RegisterConnexionClient_ptr)(uint32_t signature,
|
||||
const char *name,
|
||||
uint16_t mode,
|
||||
uint32_t mask);
|
||||
typedef void (*SetConnexionClientButtonMask_ptr)(uint16_t clientID, uint32_t buttonMask);
|
||||
typedef void (*UnregisterConnexionClient_ptr)(uint16_t clientID);
|
||||
typedef int16_t (*ConnexionClientControl_ptr)(uint16_t clientID,
|
||||
uint32_t message,
|
||||
int32_t param,
|
||||
int32_t *result);
|
||||
|
||||
#define DECLARE_FUNC(name) name##_ptr name = nullptr
|
||||
|
||||
DECLARE_FUNC(SetConnexionHandlers);
|
||||
DECLARE_FUNC(CleanupConnexionHandlers);
|
||||
DECLARE_FUNC(RegisterConnexionClient);
|
||||
DECLARE_FUNC(SetConnexionClientButtonMask);
|
||||
DECLARE_FUNC(UnregisterConnexionClient);
|
||||
DECLARE_FUNC(ConnexionClientControl);
|
||||
|
||||
static void *load_func(void *module, const char *func_name)
|
||||
{
|
||||
void *func = dlsym(module, func_name);
|
||||
|
||||
#if DEBUG_NDOF_DRIVER
|
||||
if (func) {
|
||||
printf("'%s' loaded :D\n", func_name);
|
||||
}
|
||||
else {
|
||||
printf("<!> %s\n", dlerror());
|
||||
}
|
||||
#endif
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
#define LOAD_FUNC(name) name = (name##_ptr)load_func(module, #name)
|
||||
|
||||
static void *module; /* Handle to the whole driver. */
|
||||
|
||||
static bool load_driver_functions()
|
||||
{
|
||||
if (driver_loaded) {
|
||||
return true;
|
||||
}
|
||||
|
||||
module = dlopen("/Library/Frameworks/3DconnexionClient.framework/3DconnexionClient",
|
||||
RTLD_LAZY | RTLD_LOCAL);
|
||||
|
||||
if (module) {
|
||||
LOAD_FUNC(SetConnexionHandlers);
|
||||
|
||||
if (SetConnexionHandlers != nullptr) {
|
||||
driver_loaded = true;
|
||||
}
|
||||
|
||||
if (driver_loaded) {
|
||||
LOAD_FUNC(CleanupConnexionHandlers);
|
||||
LOAD_FUNC(RegisterConnexionClient);
|
||||
LOAD_FUNC(SetConnexionClientButtonMask);
|
||||
LOAD_FUNC(UnregisterConnexionClient);
|
||||
LOAD_FUNC(ConnexionClientControl);
|
||||
}
|
||||
}
|
||||
#if DEBUG_NDOF_DRIVER
|
||||
else {
|
||||
printf("<!> %s\n", dlerror());
|
||||
}
|
||||
|
||||
printf("loaded: %s\n", driver_loaded ? "YES" : "NO");
|
||||
#endif
|
||||
|
||||
return driver_loaded;
|
||||
}
|
||||
|
||||
static void unload_driver()
|
||||
{
|
||||
dlclose(module);
|
||||
}
|
||||
|
||||
static void DeviceAdded(uint32_t /*unused*/)
|
||||
{
|
||||
#if DEBUG_NDOF_DRIVER
|
||||
printf("ndof: device added\n");
|
||||
#endif
|
||||
|
||||
/* Determine exactly which device is plugged in. */
|
||||
int32_t result;
|
||||
ConnexionClientControl(clientID, kConnexionCtlGetDeviceID, 0, &result);
|
||||
const int16_t vendorID = result >> 16;
|
||||
const int16_t productID = result & 0xffff;
|
||||
|
||||
ndof_manager->setDevice(vendorID, productID);
|
||||
}
|
||||
|
||||
static void DeviceRemoved(uint32_t /*unused*/)
|
||||
{
|
||||
#if DEBUG_NDOF_DRIVER
|
||||
printf("ndof: device removed\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void DeviceEvent(uint32_t /*unused*/, uint32_t msg_type, void *msg_arg)
|
||||
{
|
||||
if (msg_type == kConnexionMsgDeviceState) {
|
||||
ConnexionDeviceState *s = (ConnexionDeviceState *)msg_arg;
|
||||
|
||||
/* Device state is broadcast to all clients; only react if sent to us. */
|
||||
if (s->client == clientID) {
|
||||
/* TODO: is s->time compatible with GHOST timestamps? if so use that instead. */
|
||||
const uint64_t now = ghost_system->getMilliSeconds();
|
||||
|
||||
switch (s->command) {
|
||||
case kConnexionCmdHandleAxis: {
|
||||
/* convert to blender view coordinates. */
|
||||
const int t[3] = {s->axis[0], -(s->axis[2]), s->axis[1]};
|
||||
const int r[3] = {-(s->axis[3]), s->axis[5], -(s->axis[4])};
|
||||
|
||||
ndof_manager->updateTranslation(t, now);
|
||||
ndof_manager->updateRotation(r, now);
|
||||
|
||||
ghost_system->notifyExternalEventProcessed();
|
||||
break;
|
||||
}
|
||||
case kConnexionCmdHandleButtons: {
|
||||
const int button_bits = s->buttons;
|
||||
#ifdef DEBUG_NDOF_BUTTONS
|
||||
printf("button bits: 0x%08x\n", button_bits);
|
||||
#endif
|
||||
ndof_manager->updateButtonsBitmask(button_bits, now);
|
||||
ghost_system->notifyExternalEventProcessed();
|
||||
break;
|
||||
}
|
||||
case kConnexionCmdAppEvent: {
|
||||
const int button_number = s->value;
|
||||
const bool pressed = s->appEventPressed;
|
||||
#ifdef DEBUG_NDOF_BUTTONS
|
||||
printf("button number: %d, pressed: %d\n", button_number, pressed);
|
||||
#endif
|
||||
ndof_manager->updateButton(GHOST_NDOF_ButtonT(button_number), pressed, now);
|
||||
ghost_system->notifyExternalEventProcessed();
|
||||
break;
|
||||
}
|
||||
#if DEBUG_NDOF_DRIVER
|
||||
case kConnexionCmdAppSpecific:
|
||||
printf("ndof: app-specific command, param = %hd, value = %d\n", s->param, s->value);
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("ndof: mystery device command %d\n", s->command);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_NDOFManagerCocoa::GHOST_NDOFManagerCocoa(GHOST_System &sys) : GHOST_NDOFManager(sys)
|
||||
{
|
||||
if (load_driver_functions()) {
|
||||
/* Give static functions something to talk to: */
|
||||
ghost_system = dynamic_cast<GHOST_SystemCocoa *>(&sys);
|
||||
ndof_manager = this;
|
||||
|
||||
const uint16_t error = SetConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved, true);
|
||||
|
||||
if (error) {
|
||||
#if DEBUG_NDOF_DRIVER
|
||||
printf("ndof: error %d while setting up handlers\n", error);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
const NSDictionary *dictInfos =
|
||||
[NSBundle bundleWithPath:@"/Library/Frameworks/3DconnexionClient.framework"]
|
||||
.infoDictionary;
|
||||
NSString *strVersion = [dictInfos objectForKey:(NSString *)kCFBundleVersionKey];
|
||||
const auto compare = [strVersion compare:new_driver_minimal_version];
|
||||
const bool has_new_driver = compare != NSOrderedAscending;
|
||||
|
||||
/* New driver makes use of kConnexionCmdAppEvent events, which require to have all buttons
|
||||
* unmasked. Basically, this means that driver consumes all NDOF device input and then sends
|
||||
* appropriate app events based on its configuration instead of forwarding raw data to the
|
||||
* application. When using an older driver, the old solution with all buttons forwarded
|
||||
* (masked) is preferred. */
|
||||
const uint32_t client_mask = has_new_driver ? kConnexionMaskAxis : kConnexionMaskAll;
|
||||
const uint32_t button_mask = has_new_driver ? kConnexionMaskNoButtons :
|
||||
kConnexionMaskAllButtons;
|
||||
|
||||
/* Pascal string *and* a four-letter constant. How old-school. */
|
||||
clientID = RegisterConnexionClient(
|
||||
'blnd', "\007blender", kConnexionClientModeTakeOver, client_mask);
|
||||
|
||||
SetConnexionClientButtonMask(clientID, button_mask);
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_NDOFManagerCocoa::~GHOST_NDOFManagerCocoa()
|
||||
{
|
||||
if (driver_loaded) {
|
||||
UnregisterConnexionClient(clientID);
|
||||
CleanupConnexionHandlers();
|
||||
unload_driver();
|
||||
|
||||
ghost_system = nullptr;
|
||||
ndof_manager = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool GHOST_NDOFManagerCocoa::available()
|
||||
{
|
||||
return driver_loaded;
|
||||
}
|
||||
161
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManagerUnix.cc
Normal file
161
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManagerUnix.cc
Normal file
@@ -0,0 +1,161 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "GHOST_NDOFManagerUnix.hh"
|
||||
#include "GHOST_System.hh"
|
||||
|
||||
/* Logging, use `ghost.ndof.unix.*` prefix. */
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib> /* For `getenv`. */
|
||||
#include <spnav.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const char *spnav_sock_path_default = "/var/run/spnav.sock";
|
||||
|
||||
static const char *spnav_sock_path_get()
|
||||
{
|
||||
/* The `SPNAV_SOCKET` environment variable convention
|
||||
* comes from LIBSPNAV 1.2, see: `src/spnav.c`. */
|
||||
const char *env_path = getenv("SPNAV_SOCKET");
|
||||
return env_path ? env_path : spnav_sock_path_default;
|
||||
}
|
||||
|
||||
static CLG_LogRef LOG = {"ghost.ndof"};
|
||||
|
||||
GHOST_NDOFManagerUnix::GHOST_NDOFManagerUnix(GHOST_System &sys)
|
||||
: GHOST_NDOFManager(sys), available_(false)
|
||||
{
|
||||
const char *spnav_sock_path = spnav_sock_path_get();
|
||||
if (access(spnav_sock_path, F_OK) != 0) {
|
||||
CLOG_DEBUG(&LOG, "'spacenavd' not found at \"%s\"", spnav_sock_path);
|
||||
}
|
||||
else if (spnav_open() != -1) {
|
||||
CLOG_DEBUG(&LOG, "'spacenavd' found at\"%s\"", spnav_sock_path);
|
||||
available_ = true;
|
||||
|
||||
/* Only used for logging as of LIBSPNAV 1.2. */
|
||||
spnav_client_name("blender");
|
||||
|
||||
/* determine exactly which device (if any) is plugged in */
|
||||
|
||||
#define MAX_LINE_LENGTH 100
|
||||
|
||||
/* look for USB devices with Logitech or 3Dconnexion's vendor ID */
|
||||
FILE *command_output = popen("lsusb | grep '046d:\\|256f:'", "r");
|
||||
if (command_output) {
|
||||
char line[MAX_LINE_LENGTH] = {0};
|
||||
while (fgets(line, MAX_LINE_LENGTH, command_output)) {
|
||||
ushort vendor_id = 0, product_id = 0;
|
||||
if (sscanf(line, "Bus %*d Device %*d: ID %hx:%hx", &vendor_id, &product_id) == 2) {
|
||||
if (setDevice(vendor_id, product_id)) {
|
||||
break; /* stop looking once the first 3D mouse is found */
|
||||
}
|
||||
}
|
||||
}
|
||||
pclose(command_output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_NDOFManagerUnix::~GHOST_NDOFManagerUnix()
|
||||
{
|
||||
if (available_) {
|
||||
spnav_close();
|
||||
}
|
||||
}
|
||||
|
||||
bool GHOST_NDOFManagerUnix::available()
|
||||
{
|
||||
return available_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround for a problem where we don't enter the #GHOST_kFinished state,
|
||||
* this causes any proceeding event to have a very high `dt` (time delta),
|
||||
* many seconds for eg, causing the view to jump.
|
||||
*
|
||||
* this workaround expects continuous events, if we miss a motion event,
|
||||
* immediately send a dummy event with no motion to ensure the finished state is reached.
|
||||
*/
|
||||
#define USE_FINISH_GLITCH_WORKAROUND
|
||||
/* TODO: make this available on all platforms */
|
||||
|
||||
#ifdef USE_FINISH_GLITCH_WORKAROUND
|
||||
static bool motion_test_prev = false;
|
||||
static uint64_t motion_test_prev_time = 0;
|
||||
/* NOTE(ideasman42): It's important not to generate zero-motion event immediately
|
||||
* because the SPNAV API may not have had a chance to generate events since the last
|
||||
* call to this function.
|
||||
* In my tests values between 50-100ms work well, opt for the larger value
|
||||
* to avoid false positives:
|
||||
* - Releasing the device can cause subtle wobble generating start/finish events.
|
||||
* - Subtle motion can *sometimes* cause start/finish events too.
|
||||
*
|
||||
* Furthermore, waiting a 10/th of a second for the "finish" event is fast enough
|
||||
* that any motion occurring in a shorter time-span can be considered "continuous". */
|
||||
# define MOTION_TEST_IDLE_MS 100
|
||||
#endif /* USE_FINISH_GLITCH_WORKAROUND */
|
||||
|
||||
bool GHOST_NDOFManagerUnix::processEvents()
|
||||
{
|
||||
bool anyProcessed = false;
|
||||
|
||||
if (available_) {
|
||||
spnav_event e;
|
||||
|
||||
#ifdef USE_FINISH_GLITCH_WORKAROUND
|
||||
bool motion_test = false;
|
||||
#endif
|
||||
|
||||
while (spnav_poll_event(&e)) {
|
||||
switch (e.type) {
|
||||
case SPNAV_EVENT_MOTION: {
|
||||
/* convert to blender view coords */
|
||||
uint64_t now = system_.getMilliSeconds();
|
||||
const int t[3] = {int(e.motion.x), int(e.motion.y), int(-e.motion.z)};
|
||||
const int r[3] = {int(-e.motion.rx), int(-e.motion.ry), int(e.motion.rz)};
|
||||
|
||||
updateTranslation(t, now);
|
||||
updateRotation(r, now);
|
||||
#ifdef USE_FINISH_GLITCH_WORKAROUND
|
||||
motion_test = true;
|
||||
motion_test_prev_time = now;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case SPNAV_EVENT_BUTTON:
|
||||
uint64_t now = system_.getMilliSeconds();
|
||||
updateButtonRAW(e.button.bnum, e.button.press, now);
|
||||
break;
|
||||
}
|
||||
anyProcessed = true;
|
||||
}
|
||||
|
||||
#ifdef USE_FINISH_GLITCH_WORKAROUND
|
||||
if (motion_test_prev == true && motion_test == false) {
|
||||
const uint64_t now = system_.getMilliSeconds();
|
||||
GHOST_ASSERT(motion_test_prev_time <= now, "Invalid time offset");
|
||||
if ((now - motion_test_prev_time) < MOTION_TEST_IDLE_MS) {
|
||||
/* Re-run this check next time `processEvents` is called. */
|
||||
motion_test = true;
|
||||
}
|
||||
else {
|
||||
const int v[3] = {0, 0, 0};
|
||||
|
||||
updateTranslation(v, now);
|
||||
updateRotation(v, now);
|
||||
|
||||
anyProcessed = true;
|
||||
}
|
||||
}
|
||||
motion_test_prev = motion_test;
|
||||
#endif /* USE_FINISH_GLITCH_WORKAROUND */
|
||||
}
|
||||
|
||||
return anyProcessed;
|
||||
}
|
||||
|
||||
#undef USE_FINISH_GLITCH_WORKAROUND
|
||||
22
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManagerUnix.hh
Normal file
22
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManagerUnix.hh
Normal file
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2002-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_NDOFManager.hh"
|
||||
|
||||
/**
|
||||
* Event capture is handled within the NDOF manager on Linux,
|
||||
* so there's no need for SystemX11 to look for them.
|
||||
*/
|
||||
class GHOST_NDOFManagerUnix : public GHOST_NDOFManager {
|
||||
public:
|
||||
GHOST_NDOFManagerUnix(GHOST_System &);
|
||||
~GHOST_NDOFManagerUnix() override;
|
||||
bool available() override;
|
||||
bool processEvents();
|
||||
|
||||
private:
|
||||
bool available_;
|
||||
};
|
||||
18
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManagerWin32.cc
Normal file
18
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManagerWin32.cc
Normal file
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2002-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "GHOST_NDOFManagerWin32.hh"
|
||||
|
||||
GHOST_NDOFManagerWin32::GHOST_NDOFManagerWin32(GHOST_System &sys) : GHOST_NDOFManager(sys)
|
||||
{
|
||||
/* pass */
|
||||
}
|
||||
|
||||
/* Whether multi-axis functionality is available (via the OS or driver)
|
||||
* does not imply that a device is plugged in or being used. */
|
||||
bool GHOST_NDOFManagerWin32::available()
|
||||
{
|
||||
/* Always available since RawInput is built into Windows. */
|
||||
return true;
|
||||
}
|
||||
13
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManagerWin32.hh
Normal file
13
blender-5.2.0/intern/ghost/intern/GHOST_NDOFManagerWin32.hh
Normal file
@@ -0,0 +1,13 @@
|
||||
/* SPDX-FileCopyrightText: 2002-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_NDOFManager.hh"
|
||||
|
||||
class GHOST_NDOFManagerWin32 : public GHOST_NDOFManager {
|
||||
public:
|
||||
GHOST_NDOFManagerWin32(GHOST_System &);
|
||||
bool available() override;
|
||||
};
|
||||
104
blender-5.2.0/intern/ghost/intern/GHOST_PathUtils.cc
Normal file
104
blender-5.2.0/intern/ghost/intern/GHOST_PathUtils.cc
Normal file
@@ -0,0 +1,104 @@
|
||||
/* SPDX-FileCopyrightText: 2010 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "GHOST_Debug.hh"
|
||||
#include "GHOST_PathUtils.hh"
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
/* Based on: https://stackoverflow.com/a/2766963/432509 */
|
||||
|
||||
enum DecodeState_e {
|
||||
/** Searching for an ampersand to convert. */
|
||||
STATE_SEARCH = 0,
|
||||
/** Convert the two proceeding characters from hex. */
|
||||
STATE_CONVERTING
|
||||
};
|
||||
|
||||
void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src, const int buf_src_len)
|
||||
{
|
||||
GHOST_ASSERT(strnlen(buf_src, buf_src_len) == buf_src_len, "Incorrect length");
|
||||
|
||||
DecodeState_e state = STATE_SEARCH;
|
||||
uint ascii_character;
|
||||
|
||||
memset(buf_dst, 0, buf_dst_size);
|
||||
|
||||
for (uint i = 0; i < buf_src_len; i++) {
|
||||
switch (state) {
|
||||
case STATE_SEARCH: {
|
||||
if (buf_src[i] != '%') {
|
||||
strncat(buf_dst, &buf_src[i], 1);
|
||||
assert(int(strlen(buf_dst)) < buf_dst_size);
|
||||
break;
|
||||
}
|
||||
|
||||
/* We are now converting. */
|
||||
state = STATE_CONVERTING;
|
||||
break;
|
||||
}
|
||||
case STATE_CONVERTING: {
|
||||
/* Create a buffer to hold the hex. For example, if `%20`,
|
||||
* this buffer would hold 20 (in ASCII). */
|
||||
char temp_num_buf[3];
|
||||
|
||||
/* Conversion complete (i.e. don't convert again next iteration). */
|
||||
state = STATE_SEARCH;
|
||||
|
||||
/* Ensure both characters are hexadecimal. */
|
||||
bool both_digits = true;
|
||||
for (int j = 0; j < 2; j++) {
|
||||
/* `isxdigit` serves to early null terminate the string too. */
|
||||
const char hex_char = buf_src[i + j];
|
||||
if (!isxdigit(hex_char)) {
|
||||
both_digits = false;
|
||||
break;
|
||||
}
|
||||
temp_num_buf[j] = hex_char;
|
||||
}
|
||||
if (!both_digits) {
|
||||
break;
|
||||
}
|
||||
/* Convert two hexadecimal characters into one character. */
|
||||
temp_num_buf[2] = '\0';
|
||||
sscanf(temp_num_buf, "%x", &ascii_character);
|
||||
|
||||
/* Ensure we aren't going to overflow. */
|
||||
assert(int(strlen(buf_dst)) < buf_dst_size);
|
||||
|
||||
/* Concatenate this character onto the output. */
|
||||
strncat(buf_dst, (char *)&ascii_character, 1);
|
||||
|
||||
/* Skip the next character. */
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *GHOST_URL_decode_alloc(const char *buf_src, const int buf_src_len)
|
||||
{
|
||||
/* Assume one character of encoded URL can be expanded to 4 chars max. */
|
||||
const size_t decoded_size_max = 4 * buf_src_len + 1;
|
||||
char *buf_dst = (char *)malloc(decoded_size_max);
|
||||
GHOST_URL_decode(buf_dst, decoded_size_max, buf_src, buf_src_len);
|
||||
const size_t decoded_size = strlen(buf_dst) + 1;
|
||||
if (decoded_size != decoded_size_max) {
|
||||
char *buf_dst_trim = (char *)malloc(decoded_size);
|
||||
memcpy(buf_dst_trim, buf_dst, decoded_size);
|
||||
free(buf_dst);
|
||||
buf_dst = buf_dst_trim;
|
||||
}
|
||||
return buf_dst;
|
||||
}
|
||||
26
blender-5.2.0/intern/ghost/intern/GHOST_PathUtils.hh
Normal file
26
blender-5.2.0/intern/ghost/intern/GHOST_PathUtils.hh
Normal file
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2010 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Decode URL (i.e. converts `file:///a%20b/test` to `file:///a b/test`)
|
||||
*
|
||||
* \param buf_dst: Buffer for decoded URL.
|
||||
* \param buf_dst_size: Size of output buffer.
|
||||
* \param buf_src: Input encoded buffer to be decoded.
|
||||
* \param buf_src_len: The length of `buf_src` to use.
|
||||
*/
|
||||
void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src, int buf_src_len);
|
||||
/**
|
||||
* A version of #GHOST_URL_decode that allocates the string & returns it.
|
||||
*
|
||||
* \param buf_src: Input encoded buffer to be decoded.
|
||||
* \return The decoded output buffer.
|
||||
*/
|
||||
char *GHOST_URL_decode_alloc(const char *buf_src, int buf_src_len);
|
||||
112
blender-5.2.0/intern/ghost/intern/GHOST_Rect.cc
Normal file
112
blender-5.2.0/intern/ghost/intern/GHOST_Rect.cc
Normal file
@@ -0,0 +1,112 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include "GHOST_Rect.hh"
|
||||
|
||||
void GHOST_Rect::inset(int32_t i)
|
||||
{
|
||||
if (i > 0) {
|
||||
/* Grow the rectangle. */
|
||||
l_ -= i;
|
||||
r_ += i;
|
||||
t_ -= i;
|
||||
b_ += i;
|
||||
}
|
||||
else if (i < 0) {
|
||||
/* Shrink the rectangle, check for insets larger than half the size. */
|
||||
int32_t i2 = i * 2;
|
||||
if (getWidth() > i2) {
|
||||
l_ += i;
|
||||
r_ -= i;
|
||||
}
|
||||
else {
|
||||
l_ = l_ + ((r_ - l_) / 2);
|
||||
r_ = l_;
|
||||
}
|
||||
if (getHeight() > i2) {
|
||||
t_ += i;
|
||||
b_ -= i;
|
||||
}
|
||||
else {
|
||||
t_ = t_ + ((b_ - t_) / 2);
|
||||
b_ = t_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TVisibility GHOST_Rect::getVisibility(GHOST_Rect &r) const
|
||||
{
|
||||
bool lt = isInside(r.l_, r.t_);
|
||||
bool rt = isInside(r.r_, r.t_);
|
||||
bool lb = isInside(r.l_, r.b_);
|
||||
bool rb = isInside(r.r_, r.b_);
|
||||
GHOST_TVisibility v;
|
||||
if (lt && rt && lb && rb) {
|
||||
/* All points inside, rectangle is inside this. */
|
||||
v = GHOST_kFullyVisible;
|
||||
}
|
||||
else if (!(lt || rt || lb || rb)) {
|
||||
/* None of the points inside.
|
||||
* Check to see whether the rectangle is larger than this one. */
|
||||
if ((r.l_ < l_) && (r.t_ < t_) && (r.r_ > r_) && (r.b_ > b_)) {
|
||||
v = GHOST_kPartiallyVisible;
|
||||
}
|
||||
else {
|
||||
v = GHOST_kNotVisible;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Some of the points inside, rectangle is partially inside. */
|
||||
v = GHOST_kPartiallyVisible;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void GHOST_Rect::setCenter(int32_t cx, int32_t cy)
|
||||
{
|
||||
int32_t offset = cx - (l_ + (r_ - l_) / 2);
|
||||
l_ += offset;
|
||||
r_ += offset;
|
||||
offset = cy - (t_ + (b_ - t_) / 2);
|
||||
t_ += offset;
|
||||
b_ += offset;
|
||||
}
|
||||
|
||||
void GHOST_Rect::setCenter(int32_t cx, int32_t cy, int32_t w, int32_t h)
|
||||
{
|
||||
long w_2, h_2;
|
||||
|
||||
w_2 = w >> 1;
|
||||
h_2 = h >> 1;
|
||||
l_ = cx - w_2;
|
||||
t_ = cy - h_2;
|
||||
r_ = l_ + w;
|
||||
b_ = t_ + h;
|
||||
}
|
||||
|
||||
bool GHOST_Rect::clip(GHOST_Rect &r) const
|
||||
{
|
||||
bool clipped = false;
|
||||
if (r.l_ < l_) {
|
||||
r.l_ = l_;
|
||||
clipped = true;
|
||||
}
|
||||
if (r.t_ < t_) {
|
||||
r.t_ = t_;
|
||||
clipped = true;
|
||||
}
|
||||
if (r.r_ > r_) {
|
||||
r.r_ = r_;
|
||||
clipped = true;
|
||||
}
|
||||
if (r.b_ > b_) {
|
||||
r.b_ = b_;
|
||||
clipped = true;
|
||||
}
|
||||
return clipped;
|
||||
}
|
||||
370
blender-5.2.0/intern/ghost/intern/GHOST_System.cc
Normal file
370
blender-5.2.0/intern/ghost/intern/GHOST_System.cc
Normal file
@@ -0,0 +1,370 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include "GHOST_System.hh"
|
||||
|
||||
#include "GHOST_EventManager.hh"
|
||||
#include "GHOST_TimerManager.hh"
|
||||
#include "GHOST_TimerTask.hh"
|
||||
#include "GHOST_WindowManager.hh"
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
# include "GHOST_NDOFManager.hh"
|
||||
#endif
|
||||
|
||||
GHOST_System::GHOST_System()
|
||||
: native_pixel_(false),
|
||||
window_focus_(true),
|
||||
auto_focus_(true),
|
||||
window_csd_params_({nullptr}),
|
||||
window_csd_layout_({0}),
|
||||
timer_manager_(nullptr),
|
||||
window_manager_(nullptr),
|
||||
event_manager_(nullptr),
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
ndof_manager_(nullptr),
|
||||
#endif
|
||||
multitouch_gestures_(true),
|
||||
tablet_api_(GHOST_kTabletAutomatic),
|
||||
is_debug_enabled_(false)
|
||||
{
|
||||
}
|
||||
|
||||
GHOST_System::~GHOST_System()
|
||||
{
|
||||
exit();
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::hasClipboardImage() const
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
uint *GHOST_System::getClipboardImage(int * /*r_width*/, int * /*r_height*/) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::putClipboardImage(uint * /*rgba*/,
|
||||
int /*width*/,
|
||||
int /*height*/) const
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
void GHOST_System::setWindowCSD(const GHOST_CSD_Params ¶ms)
|
||||
{
|
||||
window_csd_params_ = params;
|
||||
}
|
||||
|
||||
const GHOST_CSD_Params &GHOST_System::getWindowCSD() const
|
||||
{
|
||||
return window_csd_params_;
|
||||
}
|
||||
|
||||
const GHOST_CSD_Layout &GHOST_System::getWindowCSD_Layout() const
|
||||
{
|
||||
return window_csd_layout_;
|
||||
}
|
||||
void GHOST_System::setWindowCSD_Layout(const GHOST_CSD_Layout &layout)
|
||||
{
|
||||
window_csd_layout_ = layout;
|
||||
}
|
||||
|
||||
void GHOST_System::setIconGenerator(const GHOST_IconGenerator *icon_generator)
|
||||
{
|
||||
icon_generator_ = icon_generator;
|
||||
}
|
||||
|
||||
GHOST_ITimerTask *GHOST_System::installTimer(uint64_t delay,
|
||||
uint64_t interval,
|
||||
GHOST_TimerProcPtr timer_proc,
|
||||
GHOST_TUserDataPtr user_data)
|
||||
{
|
||||
uint64_t millis = getMilliSeconds();
|
||||
GHOST_TimerTask *timer = new GHOST_TimerTask(millis + delay, interval, timer_proc, user_data);
|
||||
if (timer) {
|
||||
if (timer_manager_->addTimer(timer) == GHOST_kSuccess) {
|
||||
/* Check to see whether we need to fire the timer right away. */
|
||||
timer_manager_->fireTimers(millis);
|
||||
}
|
||||
else {
|
||||
delete timer;
|
||||
timer = nullptr;
|
||||
}
|
||||
}
|
||||
return timer;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::removeTimer(GHOST_ITimerTask *timerTask)
|
||||
{
|
||||
GHOST_TSuccess success = GHOST_kFailure;
|
||||
if (timerTask) {
|
||||
success = timer_manager_->removeTimer((GHOST_TimerTask *)timerTask);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::disposeWindow(GHOST_IWindow *window)
|
||||
{
|
||||
GHOST_TSuccess success;
|
||||
|
||||
/*
|
||||
* Remove all pending events for the window.
|
||||
*/
|
||||
if (window_manager_->getWindowFound(window)) {
|
||||
event_manager_->removeWindowEvents(window);
|
||||
success = window_manager_->removeWindow(window);
|
||||
if (success) {
|
||||
delete window;
|
||||
}
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool GHOST_System::validWindow(GHOST_IWindow *window)
|
||||
{
|
||||
return window_manager_->getWindowFound(window);
|
||||
}
|
||||
|
||||
GHOST_IWindow *GHOST_System::getWindowUnderCursor(int32_t x, int32_t y)
|
||||
{
|
||||
/* TODO: This solution should follow the order of the activated windows (Z-order).
|
||||
* It is imperfect but usable in most cases. Ideally each platform should provide
|
||||
* a custom version of this function that properly considers z-order. */
|
||||
|
||||
std::vector<GHOST_IWindow *> windows = window_manager_->getWindows();
|
||||
std::vector<GHOST_IWindow *>::reverse_iterator iwindow_iter;
|
||||
|
||||
/* Search through the windows in reverse order because in most cases
|
||||
* the window that is on top was created after those that are below it. */
|
||||
|
||||
for (iwindow_iter = windows.rbegin(); iwindow_iter != windows.rend(); ++iwindow_iter) {
|
||||
|
||||
GHOST_IWindow *win = *iwindow_iter;
|
||||
|
||||
if (win->getState() == GHOST_kWindowStateMinimized) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GHOST_Rect bounds;
|
||||
win->getClientBounds(bounds);
|
||||
if (bounds.isInside(x, y)) {
|
||||
return win;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GHOST_System::dispatchEvents()
|
||||
{
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
/* NDOF Motion event is sent only once per dispatch, so do it now: */
|
||||
if (ndof_manager_) {
|
||||
ndof_manager_->sendMotionEvent();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (event_manager_) {
|
||||
event_manager_->dispatchEvents();
|
||||
}
|
||||
|
||||
timer_manager_->fireTimers(getMilliSeconds());
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::addEventConsumer(GHOST_IEventConsumer *consumer)
|
||||
{
|
||||
GHOST_TSuccess success;
|
||||
if (event_manager_) {
|
||||
success = event_manager_->addConsumer(consumer);
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::removeEventConsumer(GHOST_IEventConsumer *consumer)
|
||||
{
|
||||
GHOST_TSuccess success;
|
||||
if (event_manager_) {
|
||||
success = event_manager_->removeConsumer(consumer);
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::pushEvent(std::unique_ptr<const GHOST_IEvent> event)
|
||||
{
|
||||
GHOST_TSuccess success;
|
||||
if (event_manager_) {
|
||||
success = event_manager_->pushEvent(std::move(event));
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::getCursorPositionClientRelative(const GHOST_IWindow *window,
|
||||
int32_t &x,
|
||||
int32_t &y) const
|
||||
{
|
||||
/* Sub-classes that can implement this directly should do so. */
|
||||
int32_t screen_x, screen_y;
|
||||
GHOST_TSuccess success = getCursorPosition(screen_x, screen_y);
|
||||
if (success == GHOST_kSuccess) {
|
||||
window->screenToClient(screen_x, screen_y, x, y);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::setCursorPositionClientRelative(GHOST_IWindow *window,
|
||||
int32_t x,
|
||||
int32_t y)
|
||||
{
|
||||
/* Sub-classes that can implement this directly should do so. */
|
||||
int32_t screen_x, screen_y;
|
||||
window->clientToScreen(x, y, screen_x, screen_y);
|
||||
return setCursorPosition(screen_x, screen_y);
|
||||
}
|
||||
|
||||
uint32_t GHOST_System::getCursorPreferredLogicalSize() const
|
||||
{
|
||||
return uint32_t(24);
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKey mask, bool &is_down) const
|
||||
{
|
||||
is_down = false;
|
||||
|
||||
GHOST_ModifierKeys keys;
|
||||
/* Get the state of all modifier keys. */
|
||||
GHOST_TSuccess success = getModifierKeys(keys);
|
||||
if (success) {
|
||||
/* Isolate the state of the key requested. */
|
||||
is_down = keys.get(mask);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::getButtonState(GHOST_TButton mask, bool &is_down) const
|
||||
{
|
||||
is_down = false;
|
||||
|
||||
GHOST_Buttons buttons;
|
||||
/* Get the state of all mouse buttons. */
|
||||
GHOST_TSuccess success = getButtons(buttons);
|
||||
if (success) {
|
||||
/* Isolate the state of the mouse button requested. */
|
||||
is_down = buttons.get(mask);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void GHOST_System::setMultitouchGestures(const bool use)
|
||||
{
|
||||
multitouch_gestures_ = use;
|
||||
}
|
||||
|
||||
void GHOST_System::setTabletAPI(GHOST_TTabletAPI api)
|
||||
{
|
||||
tablet_api_ = api;
|
||||
}
|
||||
|
||||
GHOST_TTabletAPI GHOST_System::getTabletAPI()
|
||||
{
|
||||
return tablet_api_;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::getPixelAtCursor(float /*r_color*/[3]) const
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
void GHOST_System::setNDOFDeadZone(float deadzone)
|
||||
{
|
||||
if (this->ndof_manager_) {
|
||||
this->ndof_manager_->setDeadZone(deadzone);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
GHOST_TSuccess GHOST_System::init()
|
||||
{
|
||||
timer_manager_ = new GHOST_TimerManager();
|
||||
window_manager_ = new GHOST_WindowManager();
|
||||
event_manager_ = new GHOST_EventManager();
|
||||
|
||||
#ifdef WITH_GHOST_DEBUG
|
||||
if (event_manager_) {
|
||||
event_printer_ = new GHOST_EventPrinter();
|
||||
event_manager_->addConsumer(event_printer_);
|
||||
}
|
||||
#endif /* WITH_GHOST_DEBUG */
|
||||
|
||||
if (timer_manager_ && window_manager_ && event_manager_) {
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_System::exit()
|
||||
{
|
||||
/** WARNING: exit() may run more than once, since it may need to be called from a derived class
|
||||
* destructor. Take it into account when modifying this function. */
|
||||
|
||||
delete window_manager_;
|
||||
window_manager_ = nullptr;
|
||||
|
||||
delete timer_manager_;
|
||||
timer_manager_ = nullptr;
|
||||
|
||||
delete event_manager_;
|
||||
event_manager_ = nullptr;
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
delete ndof_manager_;
|
||||
ndof_manager_ = nullptr;
|
||||
#endif
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
bool GHOST_System::useNativePixel()
|
||||
{
|
||||
native_pixel_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GHOST_System::useWindowFocus(const bool use_focus)
|
||||
{
|
||||
window_focus_ = use_focus;
|
||||
}
|
||||
|
||||
void GHOST_System::setAutoFocus(const bool auto_focus)
|
||||
{
|
||||
auto_focus_ = auto_focus;
|
||||
}
|
||||
|
||||
void GHOST_System::initDebug(GHOST_Debug debug)
|
||||
{
|
||||
is_debug_enabled_ = debug.flags & GHOST_kDebugDefault;
|
||||
}
|
||||
|
||||
bool GHOST_System::isDebugEnabled()
|
||||
{
|
||||
return is_debug_enabled_;
|
||||
}
|
||||
326
blender-5.2.0/intern/ghost/intern/GHOST_System.hh
Normal file
326
blender-5.2.0/intern/ghost/intern/GHOST_System.hh
Normal file
@@ -0,0 +1,326 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_System class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_ISystem.hh"
|
||||
|
||||
#include "GHOST_Buttons.hh"
|
||||
#include "GHOST_Debug.hh"
|
||||
#include "GHOST_EventManager.hh"
|
||||
#include "GHOST_ModifierKeys.hh"
|
||||
#ifdef WITH_GHOST_DEBUG
|
||||
# include "GHOST_EventPrinter.hh"
|
||||
#endif // WITH_GHOST_DEBUG
|
||||
|
||||
class GHOST_Event;
|
||||
class GHOST_TimerManager;
|
||||
class GHOST_Window;
|
||||
class GHOST_WindowManager;
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
class GHOST_NDOFManager;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implementation of platform independent functionality of the GHOST_ISystem
|
||||
* interface.
|
||||
* GHOST_System is an abstract class because not all methods of GHOST_ISystem
|
||||
* are implemented.
|
||||
* \see GHOST_ISystem.
|
||||
*/
|
||||
class GHOST_System : public GHOST_ISystem {
|
||||
protected:
|
||||
/**
|
||||
* Constructor.
|
||||
* Protected default constructor to force use of static createSystem member.
|
||||
*/
|
||||
GHOST_System();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
* Protected default constructor to force use of static dispose member.
|
||||
*/
|
||||
~GHOST_System() override;
|
||||
|
||||
public:
|
||||
/***************************************************************************************
|
||||
* Time(r) functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/** \copydoc #GHOST_ISystem::installTimer */
|
||||
GHOST_ITimerTask *installTimer(uint64_t delay,
|
||||
uint64_t interval,
|
||||
GHOST_TimerProcPtr timer_proc,
|
||||
GHOST_TUserDataPtr user_data = nullptr) override;
|
||||
/** \copydoc #GHOST_ISystem::removeTimer */
|
||||
GHOST_TSuccess removeTimer(GHOST_ITimerTask *timerTask) override;
|
||||
|
||||
/***************************************************************************************
|
||||
* Display/window management functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/** \copydoc #GHOST_ISystem::disposeWindow */
|
||||
GHOST_TSuccess disposeWindow(GHOST_IWindow *window) override;
|
||||
|
||||
/** \copydoc #GHOST_ISystem::createOffscreenContext */
|
||||
GHOST_IContext *createOffscreenContext(GHOST_GPUSettings gpu_settings) override = 0;
|
||||
|
||||
/** \copydoc #GHOST_ISystem::validWindow */
|
||||
bool validWindow(GHOST_IWindow *window) override;
|
||||
|
||||
/** \copydoc #GHOST_ISystem::useNativePixel */
|
||||
bool useNativePixel() override;
|
||||
bool native_pixel_;
|
||||
|
||||
/** \copydoc #GHOST_ISystem::useWindowFocus */
|
||||
void useWindowFocus(const bool use_focus) override;
|
||||
|
||||
bool window_focus_;
|
||||
|
||||
/** \copydoc #GHOST_ISystem::setAutoFocus */
|
||||
void setAutoFocus(const bool auto_focus) override;
|
||||
bool auto_focus_;
|
||||
|
||||
/** \copydoc #GHOST_ISystem::getWindowUnderCursor */
|
||||
GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y) override;
|
||||
GHOST_CSD_Params window_csd_params_;
|
||||
GHOST_CSD_Layout window_csd_layout_;
|
||||
|
||||
/***************************************************************************************
|
||||
* Event management functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Inherited from GHOST_ISystem but left pure virtual
|
||||
*
|
||||
* <pre>
|
||||
* virtual bool processEvents(bool waitForEvent) = 0;
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
/** \copydoc #GHOST_ISystem::dispatchEvents */
|
||||
void dispatchEvents() override;
|
||||
/** \copydoc #GHOST_ISystem::addEventConsumer */
|
||||
GHOST_TSuccess addEventConsumer(GHOST_IEventConsumer *consumer) override;
|
||||
/** \copydoc #GHOST_ISystem::removeEventConsumer */
|
||||
GHOST_TSuccess removeEventConsumer(GHOST_IEventConsumer *consumer) override;
|
||||
|
||||
/***************************************************************************************
|
||||
* Cursor management functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/* Client relative functions use a default implementation
|
||||
* that converts from screen-coordinates to client coordinates.
|
||||
* Implementations may override. */
|
||||
|
||||
/** \copydoc #GHOST_ISystem::getCursorPositionClientRelative */
|
||||
GHOST_TSuccess getCursorPositionClientRelative(const GHOST_IWindow *window,
|
||||
int32_t &x,
|
||||
int32_t &y) const override;
|
||||
/** \copydoc #GHOST_ISystem::setCursorPositionClientRelative */
|
||||
GHOST_TSuccess setCursorPositionClientRelative(GHOST_IWindow *window,
|
||||
int32_t x,
|
||||
int32_t y) override;
|
||||
|
||||
/** \copydoc #GHOST_ISystem::getCursorPreferredLogicalSize */
|
||||
uint32_t getCursorPreferredLogicalSize() const override;
|
||||
|
||||
/**
|
||||
* Inherited from GHOST_ISystem but left pure virtual
|
||||
* <pre>
|
||||
* GHOST_TSuccess getCursorPosition(int32_t& x, int32_t& y) const = 0;
|
||||
* GHOST_TSuccess setCursorPosition(int32_t x, int32_t y)
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
/***************************************************************************************
|
||||
* Access to mouse button and keyboard states.
|
||||
***************************************************************************************/
|
||||
|
||||
/** \copydoc #GHOST_ISystem::getModifierKeyState */
|
||||
GHOST_TSuccess getModifierKeyState(GHOST_TModifierKey mask, bool &is_down) const override;
|
||||
|
||||
/** \copydoc #GHOST_ISystem::getButtonState */
|
||||
GHOST_TSuccess getButtonState(GHOST_TButton mask, bool &is_down) const override;
|
||||
|
||||
/** \copydoc #GHOST_ISystem::setMultitouchGestures */
|
||||
void setMultitouchGestures(const bool use) override;
|
||||
|
||||
/** \copydoc #GHOST_ISystem::setTabletAPI */
|
||||
void setTabletAPI(GHOST_TTabletAPI api) override;
|
||||
GHOST_TTabletAPI getTabletAPI();
|
||||
|
||||
/** \copydoc #GHOST_ISystem::getPixelAtCursor */
|
||||
GHOST_TSuccess getPixelAtCursor(float r_color[3]) const override;
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
/***************************************************************************************
|
||||
* Access to 3D mouse.
|
||||
***************************************************************************************/
|
||||
|
||||
/** \copydoc #GHOST_ISystem::setNDOFDeadZone */
|
||||
void setNDOFDeadZone(float deadzone) override;
|
||||
#endif
|
||||
|
||||
/***************************************************************************************
|
||||
* Access to the Clipboard
|
||||
***************************************************************************************/
|
||||
|
||||
/** \copydoc #GHOST_ISystem::getClipboard */
|
||||
char *getClipboard(bool selection) const override = 0;
|
||||
/** \copydoc #GHOST_ISystem::putClipboard */
|
||||
void putClipboard(const char *buffer, bool selection) const override = 0;
|
||||
/** \copydoc #GHOST_ISystem::hasClipboardImage */
|
||||
GHOST_TSuccess hasClipboardImage() const override;
|
||||
/** \copydoc #GHOST_ISystem::getClipboardImage */
|
||||
uint *getClipboardImage(int *r_width, int *r_height) const override;
|
||||
/** \copydoc #GHOST_ISystem::putClipboardImage */
|
||||
GHOST_TSuccess putClipboardImage(uint *rgba, int width, int height) const override;
|
||||
|
||||
/***************************************************************************************
|
||||
* Window "Client Side Decorations" (CSD)
|
||||
***************************************************************************************/
|
||||
|
||||
void setWindowCSD(const GHOST_CSD_Params ¶ms) override;
|
||||
const GHOST_CSD_Params &getWindowCSD() const;
|
||||
void setWindowCSD_Layout(const GHOST_CSD_Layout &csd_layout);
|
||||
const GHOST_CSD_Layout &getWindowCSD_Layout() const override;
|
||||
|
||||
/***************************************************************************************
|
||||
* Window Icon Generator.
|
||||
***************************************************************************************/
|
||||
|
||||
/** \copydoc #GHOST_ISystem::setIconGenerator */
|
||||
void setIconGenerator(const GHOST_IconGenerator *icon_generator) override;
|
||||
const GHOST_IconGenerator *icon_generator_ = nullptr;
|
||||
|
||||
/** \copydoc #GHOST_ISystem::showMessageBox */
|
||||
GHOST_TSuccess showMessageBox(const char * /*title*/,
|
||||
const char * /*message*/,
|
||||
const char * /*help_label*/,
|
||||
const char * /*continue_label*/,
|
||||
const char * /*link*/,
|
||||
GHOST_DialogOptions /*dialog_options*/) const override
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
};
|
||||
|
||||
/***************************************************************************************
|
||||
* Other (internal) functionality.
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Pushes an event on the stack.
|
||||
* To dispatch it, call dispatchEvent() or dispatchEvents().
|
||||
* Do not delete the event!
|
||||
* \param event: The event to push on the stack.
|
||||
*/
|
||||
GHOST_TSuccess pushEvent(std::unique_ptr<const GHOST_IEvent> event);
|
||||
|
||||
/**
|
||||
* \return The timer manager.
|
||||
*/
|
||||
inline GHOST_TimerManager *getTimerManager() const;
|
||||
|
||||
/**
|
||||
* \return A pointer to our event manager.
|
||||
*/
|
||||
inline GHOST_EventManager *getEventManager() const;
|
||||
|
||||
/**
|
||||
* \return A pointer to our window manager.
|
||||
*/
|
||||
inline GHOST_WindowManager *getWindowManager() const;
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
/**
|
||||
* \return A pointer to our n-degree of freedom manager.
|
||||
*/
|
||||
inline GHOST_NDOFManager *getNDOFManager() const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns the state of all modifier keys.
|
||||
* \param keys: The state of all modifier keys (true == pressed).
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const = 0;
|
||||
|
||||
/**
|
||||
* Returns the state of the mouse buttons (outside the message queue).
|
||||
* \param buttons: The state of the buttons.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
virtual GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const = 0;
|
||||
|
||||
/***************************************************************************************
|
||||
* Debugging
|
||||
***************************************************************************************/
|
||||
|
||||
/** \copydoc #GHOST_ISystem::initDebug */
|
||||
void initDebug(GHOST_Debug debug) override;
|
||||
|
||||
/** \copydoc #GHOST_ISystem::isDebugEnabled */
|
||||
bool isDebugEnabled() override;
|
||||
|
||||
protected:
|
||||
/** \copydoc #GHOST_ISystem::init */
|
||||
GHOST_TSuccess init() override;
|
||||
/** \copydoc #GHOST_ISystem::exit */
|
||||
GHOST_TSuccess exit() override;
|
||||
|
||||
/** The timer manager. */
|
||||
GHOST_TimerManager *timer_manager_;
|
||||
|
||||
/** The window manager. */
|
||||
GHOST_WindowManager *window_manager_;
|
||||
|
||||
/** The event manager. */
|
||||
GHOST_EventManager *event_manager_;
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
/** The N-degree of freedom device manager */
|
||||
GHOST_NDOFManager *ndof_manager_;
|
||||
#endif
|
||||
|
||||
#ifdef WITH_GHOST_DEBUG
|
||||
/** Prints all the events. */
|
||||
GHOST_EventPrinter *event_printer_;
|
||||
#endif // WITH_GHOST_DEBUG
|
||||
|
||||
/** Use multi-touch gestures. */
|
||||
bool multitouch_gestures_;
|
||||
|
||||
/** Which tablet API to use. */
|
||||
GHOST_TTabletAPI tablet_api_;
|
||||
|
||||
bool is_debug_enabled_;
|
||||
};
|
||||
|
||||
inline GHOST_TimerManager *GHOST_System::getTimerManager() const
|
||||
{
|
||||
return timer_manager_;
|
||||
}
|
||||
|
||||
inline GHOST_EventManager *GHOST_System::getEventManager() const
|
||||
{
|
||||
return event_manager_;
|
||||
}
|
||||
|
||||
inline GHOST_WindowManager *GHOST_System::getWindowManager() const
|
||||
{
|
||||
return window_manager_;
|
||||
}
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
inline GHOST_NDOFManager *GHOST_System::getNDOFManager() const
|
||||
{
|
||||
return ndof_manager_;
|
||||
}
|
||||
#endif
|
||||
363
blender-5.2.0/intern/ghost/intern/GHOST_SystemCocoa.hh
Normal file
363
blender-5.2.0/intern/ghost/intern/GHOST_SystemCocoa.hh
Normal file
@@ -0,0 +1,363 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_SystemCocoa class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __APPLE__
|
||||
# error Apple OSX only!
|
||||
#endif // __APPLE__
|
||||
|
||||
// #define __CARBONSOUND__
|
||||
|
||||
#include "GHOST_System.hh"
|
||||
|
||||
class GHOST_EventCursor;
|
||||
class GHOST_EventKey;
|
||||
class GHOST_EventWindow;
|
||||
class GHOST_WindowCocoa;
|
||||
|
||||
class GHOST_SystemCocoa : public GHOST_System {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_SystemCocoa();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_SystemCocoa() override;
|
||||
|
||||
/***************************************************************************************
|
||||
* Time(r) functionality.
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns the system time.
|
||||
* Returns the number of milliseconds since the start of the system process.
|
||||
* Based on ANSI clock() routine.
|
||||
* \return The number of milliseconds.
|
||||
*/
|
||||
uint64_t getMilliSeconds() const override;
|
||||
|
||||
/***************************************************************************************
|
||||
* Display/window management functionality.
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns the number of displays on this system.
|
||||
* \return The number of displays.
|
||||
*/
|
||||
uint8_t getNumDisplays() const override;
|
||||
|
||||
/**
|
||||
* Returns the dimensions of the main display on this system.
|
||||
* \return The dimension of the main display.
|
||||
*/
|
||||
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override;
|
||||
|
||||
/** Returns the combine dimensions of all monitors.
|
||||
* \return The dimension of the workspace.
|
||||
*/
|
||||
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const override;
|
||||
|
||||
/**
|
||||
* Create a new window.
|
||||
* The new window is added to the list of windows managed.
|
||||
* Never explicitly delete the window, use #disposeWindow() instead.
|
||||
* \param title: The name of the window.
|
||||
* (displayed in the title bar of the window if the OS supports it).
|
||||
* \param left: The coordinate of the left edge of the window.
|
||||
* \param top: The coordinate of the top edge of the window.
|
||||
* \param width: The width the window.
|
||||
* \param height: The height the window.
|
||||
* \param state: The state of the window when opened.
|
||||
* \param gpu_settings: Misc GPU settings.
|
||||
* \param exclusive: Use to show the window on top and ignore others (used full-screen).
|
||||
* \param parent_window: Parent (embedder) window.
|
||||
* \return The new window (or 0 if creation failed).
|
||||
*/
|
||||
GHOST_IWindow *createWindow(const char *title,
|
||||
int32_t left,
|
||||
int32_t top,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
GHOST_TWindowState state,
|
||||
GHOST_GPUSettings gpu_settings,
|
||||
const bool exclusive = false,
|
||||
const bool is_dialog = false,
|
||||
const GHOST_IWindow *parent_window = nullptr) override;
|
||||
|
||||
/**
|
||||
* Create a new off-screen context.
|
||||
* Never explicitly delete the context, use #disposeContext() instead.
|
||||
* \return The new context (or 0 if creation failed).
|
||||
*/
|
||||
GHOST_IContext *createOffscreenContext(GHOST_GPUSettings gpu_settings) override;
|
||||
|
||||
/**
|
||||
* Dispose of a context.
|
||||
* \param context: Pointer to the context to be disposed.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess disposeContext(GHOST_IContext *context) override;
|
||||
|
||||
/**
|
||||
* Get the Window under the cursor.
|
||||
* \param x: The x-coordinate of the cursor.
|
||||
* \param y: The y-coordinate of the cursor.
|
||||
* \return The window under the cursor or nullptr if none.
|
||||
*/
|
||||
GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y) override;
|
||||
|
||||
/***************************************************************************************
|
||||
* Event management functionality.
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Gets events from the system and stores them in the queue.
|
||||
* \param waitForEvent: Flag to wait for an event (or return immediately).
|
||||
* \return Indication of the presence of events.
|
||||
*/
|
||||
bool processEvents(bool waitForEvent) override;
|
||||
|
||||
/**
|
||||
* Handle User request to quit, from Menu bar Quit, and Command+Q
|
||||
* Display alert panel if changes performed since last save
|
||||
*/
|
||||
void handleQuitRequest();
|
||||
|
||||
/**
|
||||
* Handle Cocoa openFile event
|
||||
* Display confirmation request panel if changes performed since last save
|
||||
*/
|
||||
bool handleOpenDocumentRequest(void *filepathStr);
|
||||
|
||||
/**
|
||||
* Handles a drag & drop destination event. Called by GHOST_WindowCocoa window subclass.
|
||||
* \param eventType: The type of drag & drop event.
|
||||
* \param draggedObjectType: The type object concerned.
|
||||
* (currently array of file names, string, TIFF image).
|
||||
* \param window: The window on which the event occurred.
|
||||
* \param mouseX: x mouse coordinate (in cocoa base window coordinates).
|
||||
* \param mouseY: y mouse coordinate.
|
||||
* \return Indication whether the event was handled.
|
||||
*/
|
||||
GHOST_TSuccess handleDraggingEvent(GHOST_TEventType eventType,
|
||||
GHOST_TDragnDropTypes draggedObjectType,
|
||||
GHOST_WindowCocoa *window,
|
||||
int mouseX,
|
||||
int mouseY,
|
||||
void *data);
|
||||
|
||||
/***************************************************************************************
|
||||
* Cursor management functionality.
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns the current location of the cursor (location in screen coordinates)
|
||||
* \param x: The x-coordinate of the cursor.
|
||||
* \param y: The y-coordinate of the cursor.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override;
|
||||
|
||||
/**
|
||||
* \return the size of the cursor in logical pixels (before Hi-DPI scaling is applied).
|
||||
*
|
||||
* \warning On macOS the logical cursor size is *always* applied on top of hardware cursors,
|
||||
* this should only be used for software-cursor display.
|
||||
*/
|
||||
uint32_t getCursorPreferredLogicalSize() const override;
|
||||
|
||||
/**
|
||||
* Updates the location of the cursor (location in screen coordinates).
|
||||
* \param x: The x-coordinate of the cursor.
|
||||
* \param y: The y-coordinate of the cursor.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override;
|
||||
|
||||
/**
|
||||
* Get the color of the pixel at the current mouse cursor location
|
||||
* \param r_color: returned sRGB float colors
|
||||
* \return Success value (true == successful and supported by platform)
|
||||
*/
|
||||
GHOST_TSuccess getPixelAtCursor(float r_color[3]) const override;
|
||||
|
||||
/***************************************************************************************
|
||||
* Access to mouse button and keyboard states.
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns the state of all modifier keys.
|
||||
* \param keys: The state of all modifier keys (true == pressed).
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override;
|
||||
|
||||
/**
|
||||
* Returns the state of the mouse buttons (outside the message queue).
|
||||
* \param buttons: The state of the buttons.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override;
|
||||
|
||||
GHOST_TCapabilityFlag getCapabilities() const override;
|
||||
|
||||
/***************************************************************************************
|
||||
* Clipboard get / set.
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns Clipboard data
|
||||
* \param selection: Indicate which buffer to return.
|
||||
* \return Returns the selected buffer
|
||||
*/
|
||||
char *getClipboard(bool selection) const override;
|
||||
|
||||
/**
|
||||
* Puts buffer to system clipboard
|
||||
* \param buffer: The buffer to be copied.
|
||||
* \param selection: Indicates which buffer to copy too, only used on X11.
|
||||
*/
|
||||
void putClipboard(const char *buffer, bool selection) const override;
|
||||
|
||||
/**
|
||||
* Returns GHOST_kSuccess if the clipboard contains an image.
|
||||
*/
|
||||
GHOST_TSuccess hasClipboardImage() const override;
|
||||
|
||||
/**
|
||||
* Get image data from the Clipboard
|
||||
* \param r_width: the returned image width in pixels.
|
||||
* \param r_height: the returned image height in pixels.
|
||||
* \return pointer uint array in RGBA byte order. Caller must free.
|
||||
*/
|
||||
uint *getClipboardImage(int *r_width, int *r_height) const override;
|
||||
|
||||
/**
|
||||
* Put image data to the Clipboard
|
||||
* \param rgba: uint array in RGBA byte order.
|
||||
* \param width: the image width in pixels.
|
||||
* \param height: the image height in pixels.
|
||||
*/
|
||||
GHOST_TSuccess putClipboardImage(uint *rgba, int width, int height) const override;
|
||||
|
||||
/**
|
||||
* Handles a window event. Called by GHOST_WindowCocoa window delegate
|
||||
* \param eventType: The type of window event.
|
||||
* \param window: The window on which the event occurred.
|
||||
* \return Indication whether the event was handled.
|
||||
*/
|
||||
GHOST_TSuccess handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa *window);
|
||||
|
||||
/**
|
||||
* Handles the Cocoa event telling the application has become active (again)
|
||||
* \return Indication whether the event was handled.
|
||||
*/
|
||||
GHOST_TSuccess handleApplicationBecomeActiveEvent();
|
||||
|
||||
/**
|
||||
* \return True if any dialog window is open.
|
||||
*/
|
||||
bool hasDialogWindow();
|
||||
|
||||
/**
|
||||
* External objects should call this when they send an event outside processEvents.
|
||||
*/
|
||||
void notifyExternalEventProcessed();
|
||||
|
||||
/**
|
||||
* \see GHOST_ISystem
|
||||
*/
|
||||
bool setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a tablet event.
|
||||
* \param eventPtr: An #NSEvent pointer (cast to void* to enable compilation in standard C++).
|
||||
* \param eventType: The type of the event.
|
||||
* It needs to be passed separately as it can be either directly in the event type,
|
||||
* or as a sub-type if combined with a mouse button event.
|
||||
* \return Indication whether the event was handled.
|
||||
*/
|
||||
GHOST_TSuccess handleTabletEvent(void *eventPtr, short eventType);
|
||||
bool handleTabletEvent(void *eventPtr);
|
||||
|
||||
/**
|
||||
* Handles a mouse event.
|
||||
* \param eventPtr: An #NSEvent pointer (cast to `void *` to enable compilation in standard C++).
|
||||
* \return Indication whether the event was handled.
|
||||
*/
|
||||
GHOST_TSuccess handleMouseEvent(void *eventPtr);
|
||||
|
||||
/**
|
||||
* Handles a key event.
|
||||
* \param eventPtr: An #NSEvent pointer (cast to `void *` to enable compilation in standard C++).
|
||||
* \return Indication whether the event was handled.
|
||||
*/
|
||||
GHOST_TSuccess handleKeyEvent(void *eventPtr);
|
||||
|
||||
/**
|
||||
* Show a system message box
|
||||
* \param title: The title of the message box.
|
||||
* \param message: The message to display.
|
||||
* \param help_label: Help button label.
|
||||
* \param continue_label: Continue button label.
|
||||
* \param link: An optional hyperlink.
|
||||
* \param dialog_options: Options how to display the message.
|
||||
*/
|
||||
GHOST_TSuccess showMessageBox(const char *title,
|
||||
const char *message,
|
||||
const char *help_label,
|
||||
const char *continue_label,
|
||||
const char *link,
|
||||
GHOST_DialogOptions dialog_options) const override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Initializes the system.
|
||||
* For now, it just registers the window class (WNDCLASS).
|
||||
* \return A success value.
|
||||
*/
|
||||
GHOST_TSuccess init() override;
|
||||
|
||||
/**
|
||||
* Performs the actual cursor position update (location in screen coordinates).
|
||||
* \param x: The x-coordinate of the cursor.
|
||||
* \param y: The y-coordinate of the cursor.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess setMouseCursorPosition(int32_t x, int32_t y);
|
||||
|
||||
/** Event has been processed directly by Cocoa (or NDOF manager)
|
||||
* and has sent a ghost event to be dispatched */
|
||||
bool outside_loop_event_processed_;
|
||||
|
||||
/** Raised window is not yet known by the window manager,
|
||||
* so delay application become active event handling */
|
||||
bool need_delayed_application_become_active_event_processing_;
|
||||
|
||||
/** State of the modifiers. */
|
||||
uint32_t modifier_mask_;
|
||||
|
||||
/** Ignores window size messages (when window is dragged). */
|
||||
bool ignore_window_sized_messages_;
|
||||
|
||||
/** Temporarily ignore momentum scroll events */
|
||||
bool ignore_momentum_scroll_;
|
||||
/** Is the scroll wheel event generated by a multi-touch trackpad or mouse? */
|
||||
bool multi_touch_scroll_;
|
||||
/** To prevent multiple warp, we store the time of the last warp event
|
||||
* and ignore mouse moved events generated before that. */
|
||||
double last_warp_timestamp_;
|
||||
};
|
||||
2236
blender-5.2.0/intern/ghost/intern/GHOST_SystemCocoa.mm
Normal file
2236
blender-5.2.0/intern/ghost/intern/GHOST_SystemCocoa.mm
Normal file
File diff suppressed because it is too large
Load Diff
232
blender-5.2.0/intern/ghost/intern/GHOST_SystemHeadless.hh
Normal file
232
blender-5.2.0/intern/ghost/intern/GHOST_SystemHeadless.hh
Normal file
@@ -0,0 +1,232 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_SystemHeadless class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../GHOST_Types.hh"
|
||||
#include "GHOST_System.hh"
|
||||
#include "GHOST_WindowNULL.hh"
|
||||
|
||||
#if defined(WITH_OPENGL_BACKEND) && defined(__linux__)
|
||||
# include "GHOST_ContextEGL.hh"
|
||||
#endif
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
# include "GHOST_ContextVK.hh"
|
||||
#endif
|
||||
#include "GHOST_ContextNone.hh"
|
||||
|
||||
class GHOST_WindowNULL;
|
||||
|
||||
class GHOST_SystemHeadless : public GHOST_System {
|
||||
public:
|
||||
GHOST_SystemHeadless() : GHOST_System()
|
||||
{ /* nop */
|
||||
}
|
||||
~GHOST_SystemHeadless() override = default;
|
||||
|
||||
bool processEvents(bool /*waitForEvent*/) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys & /*keys*/) const override
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
GHOST_TSuccess getButtons(GHOST_Buttons & /*buttons*/) const override
|
||||
{
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
GHOST_TCapabilityFlag getCapabilities() const override
|
||||
{
|
||||
return GHOST_TCapabilityFlag(
|
||||
GHOST_CAPABILITY_FLAG_ALL &
|
||||
/* No windowing functionality supported.
|
||||
* In most cases this value doesn't matter for the headless backend.
|
||||
*
|
||||
* Nevertheless, don't advertise support.
|
||||
*
|
||||
* NOTE: order the following flags as they they're declared in the source. */
|
||||
~(
|
||||
/* Wrap. */
|
||||
GHOST_kCapabilityWindowPosition |
|
||||
/* Wrap. */
|
||||
GHOST_kCapabilityCursorWarp |
|
||||
/* Wrap. */
|
||||
GHOST_kCapabilityClipboardPrimary |
|
||||
/* Wrap. */
|
||||
GHOST_kCapabilityClipboardImage |
|
||||
/* Wrap. */
|
||||
GHOST_kCapabilityDesktopSample |
|
||||
/* Wrap. */
|
||||
GHOST_kCapabilityInputIME |
|
||||
/* Wrap. */
|
||||
GHOST_kCapabilityWindowDecorationStyles |
|
||||
/* Wrap. */
|
||||
GHOST_kCapabilityKeyboardHyperKey |
|
||||
/* Wrap. */
|
||||
GHOST_kCapabilityCursorRGBA |
|
||||
/* Wrap. */
|
||||
GHOST_kCapabilityCursorGenerator |
|
||||
/* Wrap. */
|
||||
GHOST_kCapabilityMultiMonitorPlacement |
|
||||
/* Wrap. */
|
||||
GHOST_kCapabilityWindowPath)
|
||||
|
||||
);
|
||||
}
|
||||
char *getClipboard(bool /*selection*/) const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
void putClipboard(const char * /*buffer*/, bool /*selection*/) const override
|
||||
{ /* nop */
|
||||
}
|
||||
uint64_t getMilliSeconds() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
uint8_t getNumDisplays() const override
|
||||
{
|
||||
return uint8_t(1);
|
||||
}
|
||||
GHOST_TSuccess getCursorPosition(int32_t & /*x*/, int32_t & /*y*/) const override
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
GHOST_TSuccess setCursorPosition(int32_t /*x*/, int32_t /*y*/) override
|
||||
{
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
void getMainDisplayDimensions(uint32_t & /*width*/, uint32_t & /*height*/) const override
|
||||
{ /* nop */
|
||||
}
|
||||
void getAllDisplayDimensions(uint32_t & /*width*/, uint32_t & /*height*/) const override
|
||||
{ /* nop */
|
||||
}
|
||||
GHOST_IContext *createOffscreenContext(GHOST_GPUSettings gpu_settings) override
|
||||
{
|
||||
const GHOST_ContextParams context_params_offscreen =
|
||||
GHOST_CONTEXT_PARAMS_FROM_GPU_SETTINGS_OFFSCREEN(gpu_settings);
|
||||
/* This may not be used depending on the build configuration. */
|
||||
(void)context_params_offscreen;
|
||||
|
||||
switch (gpu_settings.context_type) {
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
case GHOST_kDrawingContextTypeVulkan: {
|
||||
# ifdef _WIN32
|
||||
GHOST_Context *context = new GHOST_ContextVK(
|
||||
context_params_offscreen, (HWND)0, 1, 2, gpu_settings.preferred_device);
|
||||
# elif defined(__APPLE__)
|
||||
GHOST_Context *context = new GHOST_ContextVK(
|
||||
context_params_offscreen, nullptr, 1, 2, gpu_settings.preferred_device);
|
||||
# else
|
||||
GHOST_Context *context = new GHOST_ContextVK(context_params_offscreen,
|
||||
GHOST_kVulkanPlatformHeadless,
|
||||
0,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
1,
|
||||
2,
|
||||
gpu_settings.preferred_device);
|
||||
# endif
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
|
||||
delete context;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WITH_OPENGL_BACKEND) && defined(__linux__)
|
||||
case GHOST_kDrawingContextTypeOpenGL: {
|
||||
GHOST_Context *context;
|
||||
for (int minor = 6; minor >= 3; --minor) {
|
||||
context = new GHOST_ContextEGL((GHOST_System *)this,
|
||||
context_params_offscreen,
|
||||
EGLNativeWindowType(0),
|
||||
EGLNativeDisplayType(EGL_DEFAULT_DISPLAY),
|
||||
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
|
||||
4,
|
||||
minor,
|
||||
GHOST_OPENGL_EGL_CONTEXT_FLAGS,
|
||||
GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY,
|
||||
EGL_OPENGL_API);
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
delete context;
|
||||
context = nullptr;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
/* Unsupported backend. */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
GHOST_TSuccess disposeContext(GHOST_IContext *context) override
|
||||
{
|
||||
delete context;
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess init() override
|
||||
{
|
||||
GHOST_TSuccess success = GHOST_System::init();
|
||||
|
||||
if (success) {
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_IWindow *createWindow(const char *title,
|
||||
int32_t left,
|
||||
int32_t top,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
GHOST_TWindowState state,
|
||||
GHOST_GPUSettings gpu_settings,
|
||||
const bool /*exclusive*/,
|
||||
const bool /*is_dialog*/,
|
||||
const GHOST_IWindow *parent_window) override
|
||||
{
|
||||
const GHOST_ContextParams context_params = GHOST_CONTEXT_PARAMS_FROM_GPU_SETTINGS(
|
||||
gpu_settings);
|
||||
return new GHOST_WindowNULL(title,
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
state,
|
||||
parent_window,
|
||||
gpu_settings.context_type,
|
||||
context_params);
|
||||
}
|
||||
|
||||
GHOST_IWindow *getWindowUnderCursor(int32_t /*x*/, int32_t /*y*/) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
36
blender-5.2.0/intern/ghost/intern/GHOST_SystemPaths.hh
Normal file
36
blender-5.2.0/intern/ghost/intern/GHOST_SystemPaths.hh
Normal file
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2009 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_ISystemPaths.hh"
|
||||
|
||||
class GHOST_SystemPaths : public GHOST_ISystemPaths {
|
||||
protected:
|
||||
/**
|
||||
* Constructor.
|
||||
* Protected default constructor to force use of static createSystem member.
|
||||
*/
|
||||
GHOST_SystemPaths() {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
* Protected default constructor to force use of static dispose member.
|
||||
*/
|
||||
~GHOST_SystemPaths() override = default;
|
||||
|
||||
public:
|
||||
/** \copydoc #GHOST_SystemPaths::getSystemDir */
|
||||
const char *getSystemDir(int version, const char *versionstr) const override = 0;
|
||||
/** \copydoc #GHOST_SystemPaths::getUserDir */
|
||||
const char *getUserDir(int version, const char *versionstr) const override = 0;
|
||||
/** \copydoc #GHOST_SystemPaths::getBinaryDir */
|
||||
const char *getBinaryDir() const override = 0;
|
||||
/** \copydoc #GHOST_SystemPaths::addToSystemRecentFiles */
|
||||
void addToSystemRecentFiles(const char *filepath) const override = 0;
|
||||
};
|
||||
62
blender-5.2.0/intern/ghost/intern/GHOST_SystemPathsCocoa.hh
Normal file
62
blender-5.2.0/intern/ghost/intern/GHOST_SystemPathsCocoa.hh
Normal file
@@ -0,0 +1,62 @@
|
||||
/* SPDX-FileCopyrightText: 2010 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __APPLE__
|
||||
# error Apple OSX only!
|
||||
#endif // __APPLE__
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "GHOST_SystemPaths.hh"
|
||||
|
||||
class GHOST_SystemPathsCocoa : public GHOST_SystemPaths {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_SystemPathsCocoa() = default;
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_SystemPathsCocoa() override = default;
|
||||
|
||||
/**
|
||||
* Determine the base directory in which shared resources are located. It will first try to use
|
||||
* "unpack and run" path, then look for properly installed path, including versioning.
|
||||
* \return Unsigned char string pointing to system directory (eg `/usr/share/blender/`).
|
||||
*/
|
||||
const char *getSystemDir(int version, const char *versionstr) const override;
|
||||
|
||||
/**
|
||||
* Determine the base directory in which user configuration is stored, including versioning.
|
||||
* If needed, it will create the base directory.
|
||||
* \return Unsigned char string pointing to user directory (eg `~/.blender/`).
|
||||
*/
|
||||
const char *getUserDir(int version, const char *versionstr) const override;
|
||||
|
||||
/**
|
||||
* Determine a special ("well known") and easy to reach user directory.
|
||||
* \return If successfull, a string containing the user directory path (eg `~/Documents/`).
|
||||
*/
|
||||
std::optional<std::string> getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const override;
|
||||
|
||||
/**
|
||||
* Determine the directory of the current binary.
|
||||
* \return Unsigned char string pointing to the binary directory.
|
||||
*/
|
||||
const char *getBinaryDir() const override;
|
||||
|
||||
/**
|
||||
* Add the file to the operating system most recently used files
|
||||
*/
|
||||
void addToSystemRecentFiles(const char *filepath) const override;
|
||||
};
|
||||
134
blender-5.2.0/intern/ghost/intern/GHOST_SystemPathsCocoa.mm
Normal file
134
blender-5.2.0/intern/ghost/intern/GHOST_SystemPathsCocoa.mm
Normal file
@@ -0,0 +1,134 @@
|
||||
/* SPDX-FileCopyrightText: 2010 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#import <AppKit/NSDocumentController.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "GHOST_Debug.hh"
|
||||
#include "GHOST_SystemPathsCocoa.hh"
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Base directories retrieval.
|
||||
*/
|
||||
|
||||
static const char *GetApplicationSupportDir(const char *versionstr,
|
||||
const NSSearchPathDomainMask mask,
|
||||
char *tempPath,
|
||||
const std::size_t len_tempPath)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, mask, YES);
|
||||
|
||||
if (paths.count == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
NSString *basePath = [paths objectAtIndex:0];
|
||||
|
||||
snprintf(tempPath,
|
||||
len_tempPath,
|
||||
"%s/Blender/%s",
|
||||
[basePath cStringUsingEncoding:NSASCIIStringEncoding],
|
||||
versionstr);
|
||||
}
|
||||
return tempPath;
|
||||
}
|
||||
|
||||
const char *GHOST_SystemPathsCocoa::getSystemDir(int /* version */, const char *versionstr) const
|
||||
{
|
||||
static char tempPath[512] = "";
|
||||
return GetApplicationSupportDir(versionstr, NSLocalDomainMask, tempPath, sizeof(tempPath));
|
||||
}
|
||||
|
||||
const char *GHOST_SystemPathsCocoa::getUserDir(int /* version */, const char *versionstr) const
|
||||
{
|
||||
static char tempPath[512] = "";
|
||||
return GetApplicationSupportDir(versionstr, NSUserDomainMask, tempPath, sizeof(tempPath));
|
||||
}
|
||||
|
||||
std::optional<std::string> GHOST_SystemPathsCocoa::getUserSpecialDir(
|
||||
GHOST_TUserSpecialDirTypes type) const
|
||||
{
|
||||
char tempPath[512] = "";
|
||||
@autoreleasepool {
|
||||
NSSearchPathDirectory ns_directory;
|
||||
|
||||
switch (type) {
|
||||
case GHOST_kUserSpecialDirDesktop:
|
||||
ns_directory = NSDesktopDirectory;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirDocuments:
|
||||
ns_directory = NSDocumentDirectory;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirDownloads:
|
||||
ns_directory = NSDownloadsDirectory;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirMusic:
|
||||
ns_directory = NSMusicDirectory;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirPictures:
|
||||
ns_directory = NSPicturesDirectory;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirVideos:
|
||||
ns_directory = NSMoviesDirectory;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirCaches:
|
||||
ns_directory = NSCachesDirectory;
|
||||
break;
|
||||
default:
|
||||
GHOST_ASSERT(
|
||||
false,
|
||||
"GHOST_SystemPathsCocoa::getUserSpecialDir(): Invalid enum value for type parameter");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(ns_directory, NSUserDomainMask, YES);
|
||||
if (paths.count == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
NSString *basePath = [paths objectAtIndex:0];
|
||||
|
||||
const char *basePath_cstr = [basePath cStringUsingEncoding:NSASCIIStringEncoding];
|
||||
int basePath_len = strlen(basePath_cstr);
|
||||
|
||||
basePath_len = MIN(basePath_len, sizeof(tempPath) - 1);
|
||||
memcpy(tempPath, basePath_cstr, basePath_len);
|
||||
tempPath[basePath_len] = '\0';
|
||||
}
|
||||
if (!tempPath[0]) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return tempPath;
|
||||
}
|
||||
|
||||
const char *GHOST_SystemPathsCocoa::getBinaryDir() const
|
||||
{
|
||||
static char tempPath[512] = "";
|
||||
|
||||
@autoreleasepool {
|
||||
NSString *basePath = [[NSBundle mainBundle] bundlePath];
|
||||
|
||||
if (basePath == nil) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *basePath_cstr = [basePath cStringUsingEncoding:NSASCIIStringEncoding];
|
||||
int basePath_len = strlen(basePath_cstr);
|
||||
|
||||
basePath_len = MIN(basePath_len, sizeof(tempPath) - 1);
|
||||
memcpy(tempPath, basePath_cstr, basePath_len);
|
||||
tempPath[basePath_len] = '\0';
|
||||
}
|
||||
return tempPath;
|
||||
}
|
||||
|
||||
void GHOST_SystemPathsCocoa::addToSystemRecentFiles(const char *filepath) const
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSURL *file_url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:filepath]];
|
||||
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:file_url];
|
||||
}
|
||||
}
|
||||
197
blender-5.2.0/intern/ghost/intern/GHOST_SystemPathsUnix.cc
Normal file
197
blender-5.2.0/intern/ghost/intern/GHOST_SystemPathsUnix.cc
Normal file
@@ -0,0 +1,197 @@
|
||||
/* SPDX-FileCopyrightText: 2010 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
|
||||
#include "GHOST_SystemPathsUnix.hh"
|
||||
|
||||
#include "GHOST_Debug.hh"
|
||||
|
||||
/* For timing. */
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <pwd.h> /* For get home without use `getenv()`. */
|
||||
#include <string>
|
||||
|
||||
using std::string;
|
||||
|
||||
#ifdef PREFIX
|
||||
static const char *static_path = PREFIX "/share";
|
||||
#else
|
||||
static const char *static_path = nullptr;
|
||||
#endif
|
||||
|
||||
#if defined(PREFIX) && defined(BLENDER_INSTALL_LIBDIR)
|
||||
static const char *static_libs_path = PREFIX "/" BLENDER_INSTALL_LIBDIR;
|
||||
#else
|
||||
static const char *static_libs_path = nullptr;
|
||||
#endif
|
||||
|
||||
GHOST_SystemPathsUnix::GHOST_SystemPathsUnix() = default;
|
||||
|
||||
GHOST_SystemPathsUnix::~GHOST_SystemPathsUnix() = default;
|
||||
|
||||
const char *GHOST_SystemPathsUnix::getSystemDir(int /*version*/, const char *versionstr) const
|
||||
{
|
||||
/* no prefix assumes a portable build which only uses bundled scripts */
|
||||
if (static_path) {
|
||||
static string system_path = string(static_path) + "/blender/" + versionstr;
|
||||
return system_path.c_str();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *GHOST_SystemPathsUnix::getSystemLibsDir(int /*version*/, const char *versionstr) const
|
||||
{
|
||||
if (static_libs_path) {
|
||||
static string system_libs_path = string(static_libs_path) + "/blender/" + versionstr;
|
||||
return system_libs_path.c_str();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* See docstring & code-comments for #BLI_dir_home which matches this functionality.
|
||||
*/
|
||||
static const char *home_dir_get()
|
||||
{
|
||||
const char *home_dir = getenv("HOME");
|
||||
if (home_dir == nullptr) {
|
||||
if (const passwd *pwuser = getpwuid(getuid())) {
|
||||
home_dir = pwuser->pw_dir;
|
||||
}
|
||||
}
|
||||
return home_dir;
|
||||
}
|
||||
|
||||
const char *GHOST_SystemPathsUnix::getUserDir(int version, const char *versionstr) const
|
||||
{
|
||||
static string user_path;
|
||||
static int last_version = 0;
|
||||
|
||||
/* in blender 2.64, we migrate to XDG. to ensure the copy previous settings
|
||||
* operator works we give a different path depending on the requested version */
|
||||
if (version < 264) {
|
||||
if (user_path.empty() || last_version != version) {
|
||||
const char *home = home_dir_get();
|
||||
|
||||
last_version = version;
|
||||
|
||||
if (home) {
|
||||
user_path = string(home) + "/.blender/" + versionstr;
|
||||
}
|
||||
else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return user_path.c_str();
|
||||
}
|
||||
if (user_path.empty() || last_version != version) {
|
||||
const char *home = getenv("XDG_CONFIG_HOME");
|
||||
|
||||
last_version = version;
|
||||
|
||||
if (home) {
|
||||
user_path = string(home) + "/blender/" + versionstr;
|
||||
}
|
||||
else {
|
||||
home = home_dir_get();
|
||||
if (home) {
|
||||
user_path = string(home) + "/.config/blender/" + versionstr;
|
||||
}
|
||||
else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return user_path.c_str();
|
||||
}
|
||||
|
||||
std::optional<std::string> GHOST_SystemPathsUnix::getUserSpecialDir(
|
||||
GHOST_TUserSpecialDirTypes type) const
|
||||
{
|
||||
const char *type_str;
|
||||
|
||||
switch (type) {
|
||||
case GHOST_kUserSpecialDirDesktop:
|
||||
type_str = "DESKTOP";
|
||||
break;
|
||||
case GHOST_kUserSpecialDirDocuments:
|
||||
type_str = "DOCUMENTS";
|
||||
break;
|
||||
case GHOST_kUserSpecialDirDownloads:
|
||||
type_str = "DOWNLOAD";
|
||||
break;
|
||||
case GHOST_kUserSpecialDirMusic:
|
||||
type_str = "MUSIC";
|
||||
break;
|
||||
case GHOST_kUserSpecialDirPictures:
|
||||
type_str = "PICTURES";
|
||||
break;
|
||||
case GHOST_kUserSpecialDirVideos:
|
||||
type_str = "VIDEOS";
|
||||
break;
|
||||
case GHOST_kUserSpecialDirCaches: {
|
||||
const char *cache_dir = getenv("XDG_CACHE_HOME");
|
||||
if (cache_dir) {
|
||||
return cache_dir;
|
||||
}
|
||||
|
||||
/* If `XDG_CACHE_HOME` is not set, then `$HOME/.cache is used`. */
|
||||
const char *home_dir = home_dir_get();
|
||||
if (home_dir == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return string(home_dir) + "/.cache";
|
||||
}
|
||||
default:
|
||||
GHOST_ASSERT(
|
||||
false,
|
||||
"GHOST_SystemPathsUnix::getUserSpecialDir(): Invalid enum value for type parameter");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/* Pipe `stderr` to `/dev/null` to avoid error prints. We will fail gracefully still. */
|
||||
string command = string("xdg-user-dir ") + type_str + " 2> /dev/null";
|
||||
|
||||
FILE *fstream = popen(command.c_str(), "r");
|
||||
if (fstream == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::stringstream path_stream;
|
||||
while (!feof(fstream)) {
|
||||
char c = fgetc(fstream);
|
||||
/* `xdg-user-dir` ends the path with '\n'. */
|
||||
if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
path_stream << c;
|
||||
}
|
||||
if (pclose(fstream) == -1) {
|
||||
perror("GHOST_SystemPathsUnix::getUserSpecialDir failed at pclose()");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string path = path_stream.str();
|
||||
return path[0] ? std::optional(path) : std::nullopt;
|
||||
}
|
||||
|
||||
const char *GHOST_SystemPathsUnix::getBinaryDir() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GHOST_SystemPathsUnix::addToSystemRecentFiles(const char * /*filename*/) const
|
||||
{
|
||||
/* TODO: implement for X11 */
|
||||
}
|
||||
68
blender-5.2.0/intern/ghost/intern/GHOST_SystemPathsUnix.hh
Normal file
68
blender-5.2.0/intern/ghost/intern/GHOST_SystemPathsUnix.hh
Normal file
@@ -0,0 +1,68 @@
|
||||
/* SPDX-FileCopyrightText: 2010 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "../GHOST_Types.hh"
|
||||
#include "GHOST_SystemPaths.hh"
|
||||
|
||||
class GHOST_SystemPathsUnix : public GHOST_SystemPaths {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* this class should only be instantiated by GHOST_ISystem.
|
||||
*/
|
||||
GHOST_SystemPathsUnix();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_SystemPathsUnix() override;
|
||||
|
||||
/**
|
||||
* Determine the base directory in which shared resources are located. It will first try to use
|
||||
* "unpack and run" path, then look for properly installed path, including versioning.
|
||||
* \return Unsigned char string pointing to system directory (eg `/usr/share/blender/`).
|
||||
*/
|
||||
const char *getSystemDir(int version, const char *versionstr) const override;
|
||||
|
||||
/**
|
||||
* Determine the base directory for architecture-dependent shared libraries, mirroring
|
||||
* #getSystemDir under the install lib tree (eg `/usr/lib/blender/`).
|
||||
* \return Unsigned char string pointing to the system libraries directory or null
|
||||
* when the system-libraries path is not used.
|
||||
*/
|
||||
const char *getSystemLibsDir(int version, const char *versionstr) const override;
|
||||
|
||||
/**
|
||||
* Determine the base directory in which user configuration is stored, including versioning.
|
||||
* If needed, it will create the base directory.
|
||||
* \return Unsigned char string pointing to user directory (eg `~/.config/.blender/`).
|
||||
*/
|
||||
const char *getUserDir(int version, const char *versionstr) const override;
|
||||
|
||||
/**
|
||||
* Determine a special ("well known") and easy to reach user directory.
|
||||
* \return If successfull, a string containing the user directory path (eg `~/Documents/`).
|
||||
*/
|
||||
std::optional<std::string> getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const override;
|
||||
|
||||
/**
|
||||
* Determine the directory of the current binary.
|
||||
* \return Unsigned char string pointing to the binary directory.
|
||||
*/
|
||||
const char *getBinaryDir() const override;
|
||||
|
||||
/**
|
||||
* Add the file to the operating system most recently used files
|
||||
*/
|
||||
void addToSystemRecentFiles(const char *filepath) const override;
|
||||
};
|
||||
152
blender-5.2.0/intern/ghost/intern/GHOST_SystemPathsWin32.cc
Normal file
152
blender-5.2.0/intern/ghost/intern/GHOST_SystemPathsWin32.cc
Normal file
@@ -0,0 +1,152 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include "GHOST_SystemPathsWin32.hh"
|
||||
#include "GHOST_Debug.hh"
|
||||
|
||||
#ifndef _WIN32_IE
|
||||
# define _WIN32_IE 0x0501
|
||||
#endif
|
||||
#include "utfconv.hh"
|
||||
#include <shlobj.h>
|
||||
|
||||
GHOST_SystemPathsWin32::GHOST_SystemPathsWin32() {}
|
||||
|
||||
GHOST_SystemPathsWin32::~GHOST_SystemPathsWin32() {}
|
||||
|
||||
const char *GHOST_SystemPathsWin32::getSystemDir(int, const char *versionstr) const
|
||||
{
|
||||
const char *system_dir = nullptr;
|
||||
|
||||
/* 1 UTF16 might translate into 3 UTF8. 2 UTF16 translates into 4 UTF8. */
|
||||
static char knownpath[MAX_PATH * 3 + 128] = {0};
|
||||
PWSTR knownpath_16 = nullptr;
|
||||
|
||||
HRESULT hResult = SHGetKnownFolderPath(
|
||||
FOLDERID_ProgramData, KF_FLAG_DEFAULT, nullptr, &knownpath_16);
|
||||
|
||||
if (hResult == S_OK) {
|
||||
conv_utf_16_to_8(knownpath_16, knownpath, MAX_PATH * 3);
|
||||
strcat(knownpath, "\\Blender Foundation\\Blender\\");
|
||||
strcat(knownpath, versionstr);
|
||||
system_dir = knownpath;
|
||||
}
|
||||
|
||||
CoTaskMemFree(knownpath_16);
|
||||
return system_dir;
|
||||
}
|
||||
|
||||
const char *GHOST_SystemPathsWin32::getUserDir(int, const char *versionstr) const
|
||||
{
|
||||
const char *user_dir = nullptr;
|
||||
|
||||
static char knownpath[MAX_PATH * 3 + 128] = {0};
|
||||
PWSTR knownpath_16 = nullptr;
|
||||
|
||||
HRESULT hResult = SHGetKnownFolderPath(
|
||||
FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, nullptr, &knownpath_16);
|
||||
|
||||
if (hResult == S_OK) {
|
||||
conv_utf_16_to_8(knownpath_16, knownpath, MAX_PATH * 3);
|
||||
strcat(knownpath, "\\Blender Foundation\\Blender\\");
|
||||
strcat(knownpath, versionstr);
|
||||
user_dir = knownpath;
|
||||
}
|
||||
|
||||
CoTaskMemFree(knownpath_16);
|
||||
return user_dir;
|
||||
}
|
||||
|
||||
std::optional<std::string> GHOST_SystemPathsWin32::getUserSpecialDir(
|
||||
GHOST_TUserSpecialDirTypes type) const
|
||||
{
|
||||
const char *special_dir = nullptr;
|
||||
|
||||
GUID folderid;
|
||||
switch (type) {
|
||||
case GHOST_kUserSpecialDirDesktop:
|
||||
folderid = FOLDERID_Desktop;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirDocuments:
|
||||
folderid = FOLDERID_Documents;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirDownloads:
|
||||
folderid = FOLDERID_Downloads;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirMusic:
|
||||
folderid = FOLDERID_Music;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirPictures:
|
||||
folderid = FOLDERID_Pictures;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirVideos:
|
||||
folderid = FOLDERID_Videos;
|
||||
break;
|
||||
case GHOST_kUserSpecialDirCaches:
|
||||
folderid = FOLDERID_LocalAppData;
|
||||
break;
|
||||
default:
|
||||
GHOST_ASSERT(
|
||||
false,
|
||||
"GHOST_SystemPathsWin32::getUserSpecialDir(): Invalid enum value for type parameter");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static char knownpath[MAX_PATH * 3] = {0};
|
||||
PWSTR knownpath_16 = nullptr;
|
||||
HRESULT hResult = SHGetKnownFolderPath(folderid, KF_FLAG_DEFAULT, nullptr, &knownpath_16);
|
||||
|
||||
if (hResult == S_OK) {
|
||||
conv_utf_16_to_8(knownpath_16, knownpath, MAX_PATH * 3);
|
||||
special_dir = knownpath;
|
||||
}
|
||||
|
||||
if ((special_dir == nullptr) || (special_dir[0] == '\0')) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
CoTaskMemFree(knownpath_16);
|
||||
return special_dir;
|
||||
}
|
||||
|
||||
const char *GHOST_SystemPathsWin32::getBinaryDir() const
|
||||
{
|
||||
static char fullname[MAX_PATH * 3] = {0};
|
||||
wchar_t fullname_16[MAX_PATH * 3];
|
||||
|
||||
if (GetModuleFileNameW(0, fullname_16, MAX_PATH)) {
|
||||
conv_utf_16_to_8(fullname_16, fullname, MAX_PATH * 3);
|
||||
return fullname;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GHOST_SystemPathsWin32::addToSystemRecentFiles(const char *filepath) const
|
||||
{
|
||||
UTF16_ENCODE(filepath);
|
||||
UTF16_ENCODE(BLENDER_WIN_APPID);
|
||||
SHARDAPPIDINFO info;
|
||||
IShellItem *shell_item;
|
||||
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = SHCreateItemFromParsingName(filepath_16, nullptr, IID_PPV_ARGS(&shell_item));
|
||||
if (SUCCEEDED(hr)) {
|
||||
info.psi = shell_item;
|
||||
info.pszAppID = BLENDER_WIN_APPID_16;
|
||||
SHAddToRecentDocs(SHARD_APPIDINFO, &info);
|
||||
}
|
||||
|
||||
CoUninitialize();
|
||||
UTF16_UN_ENCODE(BLENDER_WIN_APPID);
|
||||
UTF16_UN_ENCODE(filepath);
|
||||
}
|
||||
68
blender-5.2.0/intern/ghost/intern/GHOST_SystemPathsWin32.hh
Normal file
68
blender-5.2.0/intern/ghost/intern/GHOST_SystemPathsWin32.hh
Normal file
@@ -0,0 +1,68 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#ifndef WIN32
|
||||
# error WIN32 only!
|
||||
#endif // WIN32
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "GHOST_SystemPaths.hh"
|
||||
|
||||
/**
|
||||
* WIN32 Implementation of GHOST_SystemPaths class.
|
||||
* \see GHOST_SystemPaths.
|
||||
*/
|
||||
class GHOST_SystemPathsWin32 : public GHOST_SystemPaths {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_SystemPathsWin32();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_SystemPathsWin32() override;
|
||||
|
||||
/**
|
||||
* Determine the base directory in which shared resources are located. It will first try to use
|
||||
* "unpack and run" path, then look for properly installed path, including versioning.
|
||||
* \return Unsigned char string pointing to system directory (eg `/usr/share/`).
|
||||
*/
|
||||
const char *getSystemDir(int version, const char *versionstr) const override;
|
||||
|
||||
/**
|
||||
* Determine the base directory in which user configuration is stored, including versioning.
|
||||
* If needed, it will create the base directory.
|
||||
* \return Unsigned char string pointing to user directory (eg `~/`).
|
||||
*/
|
||||
const char *getUserDir(int version, const char *versionstr) const override;
|
||||
|
||||
/**
|
||||
* Determine a special ("well known") and easy to reach user directory.
|
||||
* \return If successfull, a string containing the user directory path (eg `~/Documents/`).
|
||||
*/
|
||||
std::optional<std::string> getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const override;
|
||||
|
||||
/**
|
||||
* Determine the directory of the current binary.
|
||||
* \return Unsigned char string pointing to the binary directory.
|
||||
*/
|
||||
const char *getBinaryDir() const override;
|
||||
|
||||
/**
|
||||
* Add the file to the operating system most recently used files
|
||||
*/
|
||||
void addToSystemRecentFiles(const char *filepath) const override;
|
||||
};
|
||||
855
blender-5.2.0/intern/ghost/intern/GHOST_SystemSDL.cc
Normal file
855
blender-5.2.0/intern/ghost/intern/GHOST_SystemSDL.cc
Normal file
@@ -0,0 +1,855 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "GHOST_ContextSDL.hh"
|
||||
#include "GHOST_SystemSDL.hh"
|
||||
#include "GHOST_WindowSDL.hh"
|
||||
|
||||
#include "GHOST_WindowManager.hh"
|
||||
|
||||
#include "GHOST_EventButton.hh"
|
||||
#include "GHOST_EventCursor.hh"
|
||||
#include "GHOST_EventKey.hh"
|
||||
#include "GHOST_EventWheel.hh"
|
||||
|
||||
GHOST_SystemSDL::GHOST_SystemSDL() : GHOST_System()
|
||||
{
|
||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||
throw std::runtime_error(SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
||||
}
|
||||
|
||||
GHOST_SystemSDL::~GHOST_SystemSDL()
|
||||
{
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
GHOST_IWindow *GHOST_SystemSDL::createWindow(const char *title,
|
||||
int32_t left,
|
||||
int32_t top,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
GHOST_TWindowState state,
|
||||
GHOST_GPUSettings gpu_settings,
|
||||
const bool exclusive,
|
||||
const bool /*is_dialog*/,
|
||||
const GHOST_IWindow *parent_window)
|
||||
{
|
||||
GHOST_WindowSDL *window = nullptr;
|
||||
|
||||
const GHOST_ContextParams context_params = GHOST_CONTEXT_PARAMS_FROM_GPU_SETTINGS(gpu_settings);
|
||||
|
||||
window = new GHOST_WindowSDL(this,
|
||||
title,
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
state,
|
||||
gpu_settings.context_type,
|
||||
context_params,
|
||||
exclusive,
|
||||
parent_window);
|
||||
|
||||
if (window) {
|
||||
if (GHOST_kWindowStateFullScreen == state) {
|
||||
SDL_Window *sdl_win = window->getSDLWindow();
|
||||
SDL_SetWindowFullscreenMode(sdl_win, nullptr);
|
||||
SDL_ShowWindow(sdl_win);
|
||||
SDL_SetWindowFullscreen(sdl_win, true);
|
||||
}
|
||||
|
||||
if (window->getValid()) {
|
||||
window_manager_->addWindow(window);
|
||||
window_manager_->setActiveWindow(window);
|
||||
pushEvent(std::make_unique<GHOST_Event>(getMilliSeconds(), GHOST_kEventWindowSize, window));
|
||||
}
|
||||
else {
|
||||
delete window;
|
||||
window = nullptr;
|
||||
}
|
||||
}
|
||||
return window;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemSDL::init()
|
||||
{
|
||||
GHOST_TSuccess success = GHOST_System::init();
|
||||
|
||||
if (success) {
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dimensions of the main display on this system.
|
||||
* \return The dimension of the main display.
|
||||
*/
|
||||
void GHOST_SystemSDL::getAllDisplayDimensions(uint32_t &width, uint32_t &height) const
|
||||
{
|
||||
SDL_DisplayID display_id = SDL_GetPrimaryDisplay();
|
||||
const SDL_DisplayMode *mode = SDL_GetDesktopDisplayMode(display_id);
|
||||
if (mode == nullptr) {
|
||||
return;
|
||||
}
|
||||
width = mode->w;
|
||||
height = mode->h;
|
||||
}
|
||||
|
||||
void GHOST_SystemSDL::getMainDisplayDimensions(uint32_t &width, uint32_t &height) const
|
||||
{
|
||||
SDL_DisplayID display_id = SDL_GetPrimaryDisplay();
|
||||
const SDL_DisplayMode *mode = SDL_GetCurrentDisplayMode(display_id);
|
||||
if (mode == nullptr) {
|
||||
return;
|
||||
}
|
||||
width = mode->w;
|
||||
height = mode->h;
|
||||
}
|
||||
|
||||
uint8_t GHOST_SystemSDL::getNumDisplays() const
|
||||
{
|
||||
int count = 0;
|
||||
SDL_DisplayID *displays = SDL_GetDisplays(&count);
|
||||
SDL_free(displays);
|
||||
return uint8_t(count);
|
||||
}
|
||||
|
||||
GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GPUSettings gpu_settings)
|
||||
{
|
||||
const GHOST_ContextParams context_params_offscreen =
|
||||
GHOST_CONTEXT_PARAMS_FROM_GPU_SETTINGS_OFFSCREEN(gpu_settings);
|
||||
|
||||
switch (gpu_settings.context_type) {
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
case GHOST_kDrawingContextTypeOpenGL: {
|
||||
for (int minor = 6; minor >= 3; --minor) {
|
||||
GHOST_Context *context = new GHOST_ContextSDL(
|
||||
context_params_offscreen,
|
||||
nullptr,
|
||||
0, /* Profile bit. */
|
||||
4,
|
||||
minor,
|
||||
GHOST_OPENGL_SDL_CONTEXT_FLAGS,
|
||||
GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY);
|
||||
|
||||
if (context->initializeDrawingContext()) {
|
||||
return context;
|
||||
}
|
||||
delete context;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
/* Unsupported backend. */
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemSDL::disposeContext(GHOST_IContext *context)
|
||||
{
|
||||
delete context;
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemSDL::getModifierKeys(GHOST_ModifierKeys &keys) const
|
||||
{
|
||||
SDL_Keymod mod = SDL_GetModState();
|
||||
|
||||
keys.set(GHOST_kModifierKeyLeftShift, (mod & SDL_KMOD_LSHIFT) != 0);
|
||||
keys.set(GHOST_kModifierKeyRightShift, (mod & SDL_KMOD_RSHIFT) != 0);
|
||||
keys.set(GHOST_kModifierKeyLeftControl, (mod & SDL_KMOD_LCTRL) != 0);
|
||||
keys.set(GHOST_kModifierKeyRightControl, (mod & SDL_KMOD_RCTRL) != 0);
|
||||
keys.set(GHOST_kModifierKeyLeftAlt, (mod & SDL_KMOD_LALT) != 0);
|
||||
keys.set(GHOST_kModifierKeyRightAlt, (mod & SDL_KMOD_RALT) != 0);
|
||||
keys.set(GHOST_kModifierKeyLeftOS, (mod & SDL_KMOD_LGUI) != 0);
|
||||
keys.set(GHOST_kModifierKeyRightOS, (mod & SDL_KMOD_RGUI) != 0);
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
#define GXMAP(k, x, y) \
|
||||
case x: \
|
||||
k = y; \
|
||||
break
|
||||
|
||||
static GHOST_TKey convertSDLKey(SDL_Scancode key)
|
||||
{
|
||||
GHOST_TKey type;
|
||||
|
||||
if ((key >= SDL_SCANCODE_A) && (key <= SDL_SCANCODE_Z)) {
|
||||
type = GHOST_TKey(key - SDL_SCANCODE_A + int(GHOST_kKeyA));
|
||||
}
|
||||
else if ((key >= SDL_SCANCODE_1) && (key <= SDL_SCANCODE_0)) {
|
||||
type = (key == SDL_SCANCODE_0) ? GHOST_kKey0 :
|
||||
GHOST_TKey(key - SDL_SCANCODE_1 + int(GHOST_kKey1));
|
||||
}
|
||||
else if ((key >= SDL_SCANCODE_F1) && (key <= SDL_SCANCODE_F12)) {
|
||||
type = GHOST_TKey(key - SDL_SCANCODE_F1 + int(GHOST_kKeyF1));
|
||||
}
|
||||
else if ((key >= SDL_SCANCODE_F13) && (key <= SDL_SCANCODE_F24)) {
|
||||
type = GHOST_TKey(key - SDL_SCANCODE_F13 + int(GHOST_kKeyF13));
|
||||
}
|
||||
else {
|
||||
switch (key) {
|
||||
GXMAP(type, SDL_SCANCODE_BACKSPACE, GHOST_kKeyBackSpace);
|
||||
GXMAP(type, SDL_SCANCODE_TAB, GHOST_kKeyTab);
|
||||
GXMAP(type, SDL_SCANCODE_RETURN, GHOST_kKeyEnter);
|
||||
GXMAP(type, SDL_SCANCODE_ESCAPE, GHOST_kKeyEsc);
|
||||
GXMAP(type, SDL_SCANCODE_SPACE, GHOST_kKeySpace);
|
||||
|
||||
GXMAP(type, SDL_SCANCODE_SEMICOLON, GHOST_kKeySemicolon);
|
||||
GXMAP(type, SDL_SCANCODE_PERIOD, GHOST_kKeyPeriod);
|
||||
GXMAP(type, SDL_SCANCODE_COMMA, GHOST_kKeyComma);
|
||||
GXMAP(type, SDL_SCANCODE_APOSTROPHE, GHOST_kKeyQuote);
|
||||
GXMAP(type, SDL_SCANCODE_GRAVE, GHOST_kKeyAccentGrave);
|
||||
GXMAP(type, SDL_SCANCODE_MINUS, GHOST_kKeyMinus);
|
||||
GXMAP(type, SDL_SCANCODE_EQUALS, GHOST_kKeyEqual);
|
||||
|
||||
GXMAP(type, SDL_SCANCODE_SLASH, GHOST_kKeySlash);
|
||||
GXMAP(type, SDL_SCANCODE_BACKSLASH, GHOST_kKeyBackslash);
|
||||
GXMAP(type, SDL_SCANCODE_KP_EQUALS, GHOST_kKeyEqual);
|
||||
GXMAP(type, SDL_SCANCODE_LEFTBRACKET, GHOST_kKeyLeftBracket);
|
||||
GXMAP(type, SDL_SCANCODE_RIGHTBRACKET, GHOST_kKeyRightBracket);
|
||||
GXMAP(type, SDL_SCANCODE_PAUSE, GHOST_kKeyPause);
|
||||
|
||||
GXMAP(type, SDL_SCANCODE_LSHIFT, GHOST_kKeyLeftShift);
|
||||
GXMAP(type, SDL_SCANCODE_RSHIFT, GHOST_kKeyRightShift);
|
||||
GXMAP(type, SDL_SCANCODE_LCTRL, GHOST_kKeyLeftControl);
|
||||
GXMAP(type, SDL_SCANCODE_RCTRL, GHOST_kKeyRightControl);
|
||||
GXMAP(type, SDL_SCANCODE_LALT, GHOST_kKeyLeftAlt);
|
||||
GXMAP(type, SDL_SCANCODE_RALT, GHOST_kKeyRightAlt);
|
||||
GXMAP(type, SDL_SCANCODE_LGUI, GHOST_kKeyLeftOS);
|
||||
GXMAP(type, SDL_SCANCODE_RGUI, GHOST_kKeyRightOS);
|
||||
GXMAP(type, SDL_SCANCODE_APPLICATION, GHOST_kKeyApp);
|
||||
|
||||
GXMAP(type, SDL_SCANCODE_INSERT, GHOST_kKeyInsert);
|
||||
GXMAP(type, SDL_SCANCODE_DELETE, GHOST_kKeyDelete);
|
||||
GXMAP(type, SDL_SCANCODE_HOME, GHOST_kKeyHome);
|
||||
GXMAP(type, SDL_SCANCODE_END, GHOST_kKeyEnd);
|
||||
GXMAP(type, SDL_SCANCODE_PAGEUP, GHOST_kKeyUpPage);
|
||||
GXMAP(type, SDL_SCANCODE_PAGEDOWN, GHOST_kKeyDownPage);
|
||||
|
||||
GXMAP(type, SDL_SCANCODE_LEFT, GHOST_kKeyLeftArrow);
|
||||
GXMAP(type, SDL_SCANCODE_RIGHT, GHOST_kKeyRightArrow);
|
||||
GXMAP(type, SDL_SCANCODE_UP, GHOST_kKeyUpArrow);
|
||||
GXMAP(type, SDL_SCANCODE_DOWN, GHOST_kKeyDownArrow);
|
||||
|
||||
GXMAP(type, SDL_SCANCODE_CAPSLOCK, GHOST_kKeyCapsLock);
|
||||
GXMAP(type, SDL_SCANCODE_SCROLLLOCK, GHOST_kKeyScrollLock);
|
||||
GXMAP(type, SDL_SCANCODE_NUMLOCKCLEAR, GHOST_kKeyNumLock);
|
||||
GXMAP(type, SDL_SCANCODE_PRINTSCREEN, GHOST_kKeyPrintScreen);
|
||||
|
||||
/* keypad events */
|
||||
|
||||
/* NOTE: SDL defines a bunch of key-pad identifiers that aren't supported by GHOST,
|
||||
* such as #SDL_SCANCODE_KP_PERCENT, #SDL_SCANCODE_KP_XOR. */
|
||||
GXMAP(type, SDL_SCANCODE_KP_0, GHOST_kKeyNumpad0);
|
||||
GXMAP(type, SDL_SCANCODE_KP_1, GHOST_kKeyNumpad1);
|
||||
GXMAP(type, SDL_SCANCODE_KP_2, GHOST_kKeyNumpad2);
|
||||
GXMAP(type, SDL_SCANCODE_KP_3, GHOST_kKeyNumpad3);
|
||||
GXMAP(type, SDL_SCANCODE_KP_4, GHOST_kKeyNumpad4);
|
||||
GXMAP(type, SDL_SCANCODE_KP_5, GHOST_kKeyNumpad5);
|
||||
GXMAP(type, SDL_SCANCODE_KP_6, GHOST_kKeyNumpad6);
|
||||
GXMAP(type, SDL_SCANCODE_KP_7, GHOST_kKeyNumpad7);
|
||||
GXMAP(type, SDL_SCANCODE_KP_8, GHOST_kKeyNumpad8);
|
||||
GXMAP(type, SDL_SCANCODE_KP_9, GHOST_kKeyNumpad9);
|
||||
GXMAP(type, SDL_SCANCODE_KP_PERIOD, GHOST_kKeyNumpadPeriod);
|
||||
|
||||
GXMAP(type, SDL_SCANCODE_KP_ENTER, GHOST_kKeyNumpadEnter);
|
||||
GXMAP(type, SDL_SCANCODE_KP_PLUS, GHOST_kKeyNumpadPlus);
|
||||
GXMAP(type, SDL_SCANCODE_KP_MINUS, GHOST_kKeyNumpadMinus);
|
||||
GXMAP(type, SDL_SCANCODE_KP_MULTIPLY, GHOST_kKeyNumpadAsterisk);
|
||||
GXMAP(type, SDL_SCANCODE_KP_DIVIDE, GHOST_kKeyNumpadSlash);
|
||||
|
||||
/* Media keys in some keyboards and laptops with XFree86/XORG. */
|
||||
GXMAP(type, SDL_SCANCODE_MEDIA_PLAY, GHOST_kKeyMediaPlay);
|
||||
GXMAP(type, SDL_SCANCODE_MEDIA_STOP, GHOST_kKeyMediaStop);
|
||||
GXMAP(type, SDL_SCANCODE_MEDIA_PREVIOUS_TRACK, GHOST_kKeyMediaFirst);
|
||||
GXMAP(type, SDL_SCANCODE_MEDIA_NEXT_TRACK, GHOST_kKeyMediaLast);
|
||||
|
||||
/* International Keys. */
|
||||
|
||||
/* This key has multiple purposes,
|
||||
* however the only GHOST key that uses the scan-code is GrLess. */
|
||||
GXMAP(type, SDL_SCANCODE_NONUSBACKSLASH, GHOST_kKeyGrLess);
|
||||
|
||||
default:
|
||||
printf("Unknown\n");
|
||||
type = GHOST_kKeyUnknown;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
#undef GXMAP
|
||||
|
||||
static char convert_keyboard_event_to_ascii(const SDL_KeyboardEvent &sdl_sub_evt)
|
||||
{
|
||||
SDL_Keycode sym = sdl_sub_evt.key;
|
||||
if (sym > 127) {
|
||||
switch (sym) {
|
||||
case SDLK_KP_DIVIDE:
|
||||
sym = '/';
|
||||
break;
|
||||
case SDLK_KP_MULTIPLY:
|
||||
sym = '*';
|
||||
break;
|
||||
case SDLK_KP_MINUS:
|
||||
sym = '-';
|
||||
break;
|
||||
case SDLK_KP_PLUS:
|
||||
sym = '+';
|
||||
break;
|
||||
case SDLK_KP_1:
|
||||
sym = '1';
|
||||
break;
|
||||
case SDLK_KP_2:
|
||||
sym = '2';
|
||||
break;
|
||||
case SDLK_KP_3:
|
||||
sym = '3';
|
||||
break;
|
||||
case SDLK_KP_4:
|
||||
sym = '4';
|
||||
break;
|
||||
case SDLK_KP_5:
|
||||
sym = '5';
|
||||
break;
|
||||
case SDLK_KP_6:
|
||||
sym = '6';
|
||||
break;
|
||||
case SDLK_KP_7:
|
||||
sym = '7';
|
||||
break;
|
||||
case SDLK_KP_8:
|
||||
sym = '8';
|
||||
break;
|
||||
case SDLK_KP_9:
|
||||
sym = '9';
|
||||
break;
|
||||
case SDLK_KP_0:
|
||||
sym = '0';
|
||||
break;
|
||||
case SDLK_KP_PERIOD:
|
||||
sym = '.';
|
||||
break;
|
||||
default:
|
||||
sym = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sdl_sub_evt.mod & (SDL_KMOD_LSHIFT | SDL_KMOD_RSHIFT)) {
|
||||
/* Weak US keyboard assumptions. */
|
||||
if (sym >= 'a' && sym <= ('a' + 32)) {
|
||||
sym -= 32;
|
||||
}
|
||||
else {
|
||||
switch (sym) {
|
||||
case '`':
|
||||
sym = '~';
|
||||
break;
|
||||
case '1':
|
||||
sym = '!';
|
||||
break;
|
||||
case '2':
|
||||
sym = '@';
|
||||
break;
|
||||
case '3':
|
||||
sym = '#';
|
||||
break;
|
||||
case '4':
|
||||
sym = '$';
|
||||
break;
|
||||
case '5':
|
||||
sym = '%';
|
||||
break;
|
||||
case '6':
|
||||
sym = '^';
|
||||
break;
|
||||
case '7':
|
||||
sym = '&';
|
||||
break;
|
||||
case '8':
|
||||
sym = '*';
|
||||
break;
|
||||
case '9':
|
||||
sym = '(';
|
||||
break;
|
||||
case '0':
|
||||
sym = ')';
|
||||
break;
|
||||
case '-':
|
||||
sym = '_';
|
||||
break;
|
||||
case '=':
|
||||
sym = '+';
|
||||
break;
|
||||
case '[':
|
||||
sym = '{';
|
||||
break;
|
||||
case ']':
|
||||
sym = '}';
|
||||
break;
|
||||
case '\\':
|
||||
sym = '|';
|
||||
break;
|
||||
case ';':
|
||||
sym = ':';
|
||||
break;
|
||||
case '\'':
|
||||
sym = '"';
|
||||
break;
|
||||
case ',':
|
||||
sym = '<';
|
||||
break;
|
||||
case '.':
|
||||
sym = '>';
|
||||
break;
|
||||
case '/':
|
||||
sym = '?';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return char(sym);
|
||||
}
|
||||
|
||||
/**
|
||||
* Events don't always have valid windows,
|
||||
* but GHOST needs a window _always_. Fall back to the GL window.
|
||||
*/
|
||||
static SDL_Window *SDL_GetWindowFromID_fallback(SDL_WindowID id)
|
||||
{
|
||||
SDL_Window *sdl_win = SDL_GetWindowFromID(id);
|
||||
if (sdl_win == nullptr) {
|
||||
sdl_win = SDL_GL_GetCurrentWindow();
|
||||
}
|
||||
return sdl_win;
|
||||
}
|
||||
|
||||
void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
|
||||
{
|
||||
std::unique_ptr<GHOST_Event> g_event = nullptr;
|
||||
|
||||
switch (sdl_event->type) {
|
||||
case SDL_EVENT_WINDOW_EXPOSED:
|
||||
case SDL_EVENT_WINDOW_RESIZED:
|
||||
case SDL_EVENT_WINDOW_MOVED:
|
||||
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
||||
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED: {
|
||||
const SDL_WindowEvent &sdl_sub_evt = sdl_event->window;
|
||||
const uint64_t event_ms = SDL_NS_TO_MS(sdl_sub_evt.timestamp);
|
||||
GHOST_WindowSDL *window = findGhostWindow(
|
||||
SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
|
||||
/* Can be nullptr on close window. */
|
||||
|
||||
switch (sdl_event->type) {
|
||||
case SDL_EVENT_WINDOW_EXPOSED:
|
||||
g_event = std::make_unique<GHOST_Event>(event_ms, GHOST_kEventWindowUpdate, window);
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_RESIZED:
|
||||
g_event = std::make_unique<GHOST_Event>(event_ms, GHOST_kEventWindowSize, window);
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_MOVED:
|
||||
g_event = std::make_unique<GHOST_Event>(event_ms, GHOST_kEventWindowMove, window);
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
||||
g_event = std::make_unique<GHOST_Event>(event_ms, GHOST_kEventWindowActivate, window);
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
||||
g_event = std::make_unique<GHOST_Event>(event_ms, GHOST_kEventWindowDeactivate, window);
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
g_event = std::make_unique<GHOST_Event>(event_ms, GHOST_kEventWindowClose, window);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_EVENT_QUIT: {
|
||||
const SDL_QuitEvent &sdl_sub_evt = sdl_event->quit;
|
||||
const uint64_t event_ms = SDL_NS_TO_MS(sdl_sub_evt.timestamp);
|
||||
GHOST_IWindow *window = window_manager_->getActiveWindow();
|
||||
g_event = std::make_unique<GHOST_Event>(event_ms, GHOST_kEventQuitRequest, window);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_EVENT_MOUSE_MOTION: {
|
||||
const SDL_MouseMotionEvent &sdl_sub_evt = sdl_event->motion;
|
||||
const uint64_t event_ms = SDL_NS_TO_MS(sdl_sub_evt.timestamp);
|
||||
SDL_Window *sdl_win = SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID);
|
||||
GHOST_WindowSDL *window = findGhostWindow(sdl_win);
|
||||
assert(window != nullptr);
|
||||
|
||||
int x_win, y_win;
|
||||
SDL_GetWindowPosition(sdl_win, &x_win, &y_win);
|
||||
|
||||
int32_t x_root = int32_t(sdl_sub_evt.x) + x_win;
|
||||
int32_t y_root = int32_t(sdl_sub_evt.y) + y_win;
|
||||
|
||||
#if 0
|
||||
if (window->getCursorGrabMode() != GHOST_kGrabDisable &&
|
||||
window->getCursorGrabMode() != GHOST_kGrabNormal)
|
||||
{
|
||||
int32_t x_new = x_root;
|
||||
int32_t y_new = y_root;
|
||||
int32_t x_accum, y_accum;
|
||||
GHOST_Rect bounds;
|
||||
|
||||
/* fallback to window bounds */
|
||||
if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
|
||||
window->getClientBounds(bounds);
|
||||
}
|
||||
|
||||
/* Could also clamp to screen bounds wrap with a window outside the view will
|
||||
* fail at the moment. Use offset of 8 in case the window is at screen bounds. */
|
||||
bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis());
|
||||
window->getCursorGrabAccum(x_accum, y_accum);
|
||||
|
||||
/* Can't use #setCursorPosition because the mouse may have no focus! */
|
||||
if (x_new != x_root || y_new != y_root) {
|
||||
if (1 /* `xme.time > last_warp_` */) {
|
||||
/* when wrapping we don't need to add an event because the
|
||||
* #setCursorPosition call will cause a new event after */
|
||||
SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win); /* wrap */
|
||||
window->setCursorGrabAccum(x_accum + (x_root - x_new), y_accum + (y_root - y_new));
|
||||
// last_warp_ = lastEventTime(xme.time);
|
||||
}
|
||||
else {
|
||||
// setCursorPosition(x_new, y_new); /* wrap but don't accumulate */
|
||||
SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win);
|
||||
}
|
||||
|
||||
g_event = std::make_unique<GHOST_EventCursor>(
|
||||
event_ms, GHOST_kEventCursorMove, window, x_new, y_new, GHOST_TABLET_DATA_NONE);
|
||||
}
|
||||
else {
|
||||
g_event = std::make_unique<GHOST_EventCursor>(event_ms,
|
||||
GHOST_kEventCursorMove,
|
||||
window,
|
||||
x_root + x_accum,
|
||||
y_root + y_accum,
|
||||
GHOST_TABLET_DATA_NONE);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
g_event = std::make_unique<GHOST_EventCursor>(
|
||||
event_ms, GHOST_kEventCursorMove, window, x_root, y_root, GHOST_TABLET_DATA_NONE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
|
||||
const SDL_MouseButtonEvent &sdl_sub_evt = sdl_event->button;
|
||||
const uint64_t event_ms = SDL_NS_TO_MS(sdl_sub_evt.timestamp);
|
||||
GHOST_TButton gbmask = GHOST_kButtonMaskLeft;
|
||||
GHOST_TEventType type = sdl_sub_evt.down ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
|
||||
|
||||
GHOST_WindowSDL *window = findGhostWindow(
|
||||
SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
|
||||
assert(window != nullptr);
|
||||
|
||||
/* process rest of normal mouse buttons */
|
||||
if (sdl_sub_evt.button == SDL_BUTTON_LEFT) {
|
||||
gbmask = GHOST_kButtonMaskLeft;
|
||||
}
|
||||
else if (sdl_sub_evt.button == SDL_BUTTON_MIDDLE) {
|
||||
gbmask = GHOST_kButtonMaskMiddle;
|
||||
}
|
||||
else if (sdl_sub_evt.button == SDL_BUTTON_RIGHT) {
|
||||
gbmask = GHOST_kButtonMaskRight;
|
||||
/* these buttons are untested! */
|
||||
}
|
||||
else if (sdl_sub_evt.button == SDL_BUTTON_X1) {
|
||||
gbmask = GHOST_kButtonMaskButton4;
|
||||
}
|
||||
else if (sdl_sub_evt.button == SDL_BUTTON_X2) {
|
||||
gbmask = GHOST_kButtonMaskButton5;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
g_event = std::make_unique<GHOST_EventButton>(
|
||||
event_ms, type, window, gbmask, GHOST_TABLET_DATA_NONE);
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_WHEEL: {
|
||||
const SDL_MouseWheelEvent &sdl_sub_evt = sdl_event->wheel;
|
||||
const uint64_t event_ms = SDL_NS_TO_MS(sdl_sub_evt.timestamp);
|
||||
GHOST_WindowSDL *window = findGhostWindow(
|
||||
SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
|
||||
assert(window != nullptr);
|
||||
if (sdl_sub_evt.x != 0.0f) {
|
||||
g_event = std::make_unique<GHOST_EventWheel>(
|
||||
event_ms, window, GHOST_kEventWheelAxisHorizontal, int(sdl_sub_evt.x));
|
||||
}
|
||||
else if (sdl_sub_evt.y != 0.0f) {
|
||||
g_event = std::make_unique<GHOST_EventWheel>(
|
||||
event_ms, window, GHOST_kEventWheelAxisVertical, int(sdl_sub_evt.y));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP: {
|
||||
const SDL_KeyboardEvent &sdl_sub_evt = sdl_event->key;
|
||||
const uint64_t event_ms = SDL_NS_TO_MS(sdl_sub_evt.timestamp);
|
||||
GHOST_TEventType type = sdl_sub_evt.down ? GHOST_kEventKeyDown : GHOST_kEventKeyUp;
|
||||
const bool is_repeat = sdl_sub_evt.repeat != 0;
|
||||
|
||||
GHOST_WindowSDL *window = findGhostWindow(
|
||||
SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
|
||||
assert(window != nullptr);
|
||||
|
||||
GHOST_TKey gkey = convertSDLKey(sdl_sub_evt.scancode);
|
||||
/* NOTE: the `sdl_sub_evt.key` is truncated,
|
||||
* for unicode support ghost has to be modified. */
|
||||
|
||||
/* TODO(@ideasman42): support full unicode, SDL supports this but it needs to be
|
||||
* explicitly enabled via #SDL_StartTextInput which GHOST would have to wrap. */
|
||||
char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
|
||||
if (type == GHOST_kEventKeyDown) {
|
||||
utf8_buf[0] = convert_keyboard_event_to_ascii(sdl_sub_evt);
|
||||
}
|
||||
|
||||
g_event = std::make_unique<GHOST_EventKey>(
|
||||
event_ms, type, window, gkey, is_repeat, utf8_buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_event) {
|
||||
switch (g_event->getType()) {
|
||||
case GHOST_kEventWindowActivate: {
|
||||
window_manager_->setActiveWindow(g_event->getWindow());
|
||||
break;
|
||||
}
|
||||
case GHOST_kEventWindowDeactivate: {
|
||||
window_manager_->setWindowInactive(g_event->getWindow());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
pushEvent(std::move(g_event));
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemSDL::getCursorPosition(int32_t &x, int32_t &y) const
|
||||
{
|
||||
int x_win, y_win;
|
||||
SDL_Window *win = SDL_GetMouseFocus();
|
||||
SDL_GetWindowPosition(win, &x_win, &y_win);
|
||||
|
||||
float xf, yf;
|
||||
SDL_GetMouseState(&xf, &yf);
|
||||
x = int32_t(xf) + x_win;
|
||||
y = int32_t(yf) + y_win;
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemSDL::setCursorPosition(int32_t x, int32_t y)
|
||||
{
|
||||
int x_win, y_win;
|
||||
SDL_Window *win = SDL_GetMouseFocus();
|
||||
SDL_GetWindowPosition(win, &x_win, &y_win);
|
||||
|
||||
SDL_WarpMouseInWindow(win, x - x_win, y - y_win);
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
bool GHOST_SystemSDL::generateWindowExposeEvents()
|
||||
{
|
||||
std::vector<GHOST_WindowSDL *>::iterator w_start = dirty_windows_.begin();
|
||||
std::vector<GHOST_WindowSDL *>::const_iterator w_end = dirty_windows_.end();
|
||||
bool anyProcessed = false;
|
||||
|
||||
for (; w_start != w_end; ++w_start) {
|
||||
/* The caller doesn't have a time-stamp. */
|
||||
const uint64_t event_ms = getMilliSeconds();
|
||||
auto g_event = std::make_unique<GHOST_Event>(event_ms, GHOST_kEventWindowUpdate, *w_start);
|
||||
|
||||
(*w_start)->validate();
|
||||
|
||||
if (g_event) {
|
||||
// printf("Expose events pushed\n");
|
||||
pushEvent(std::move(g_event));
|
||||
anyProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
dirty_windows_.clear();
|
||||
return anyProcessed;
|
||||
}
|
||||
|
||||
bool GHOST_SystemSDL::processEvents(bool waitForEvent)
|
||||
{
|
||||
/* Get all the current events - translate them into
|
||||
* ghost events and call base class #pushEvent() method. */
|
||||
|
||||
bool anyProcessed = false;
|
||||
|
||||
do {
|
||||
GHOST_TimerManager *timerMgr = getTimerManager();
|
||||
|
||||
if (waitForEvent && dirty_windows_.empty() && !SDL_HasEvents(SDL_EVENT_FIRST, SDL_EVENT_LAST))
|
||||
{
|
||||
uint64_t next = timerMgr->nextFireTime();
|
||||
|
||||
if (next == GHOST_kFireTimeNever) {
|
||||
SDL_WaitEventTimeout(nullptr, -1);
|
||||
// SleepTillEvent(display_, -1);
|
||||
}
|
||||
else {
|
||||
int64_t maxSleep = next - getMilliSeconds();
|
||||
|
||||
if (maxSleep >= 0) {
|
||||
SDL_WaitEventTimeout(nullptr, next - getMilliSeconds());
|
||||
// SleepTillEvent(display_, next - getMilliSeconds()); /* X11. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (timerMgr->fireTimers(getMilliSeconds())) {
|
||||
anyProcessed = true;
|
||||
}
|
||||
|
||||
SDL_Event sdl_event;
|
||||
while (SDL_PollEvent(&sdl_event)) {
|
||||
processEvent(&sdl_event);
|
||||
anyProcessed = true;
|
||||
}
|
||||
|
||||
if (generateWindowExposeEvents()) {
|
||||
anyProcessed = true;
|
||||
}
|
||||
} while (waitForEvent && !anyProcessed);
|
||||
|
||||
return anyProcessed;
|
||||
}
|
||||
|
||||
GHOST_WindowSDL *GHOST_SystemSDL::findGhostWindow(SDL_Window *sdl_win)
|
||||
{
|
||||
if (sdl_win == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
/* It is not entirely safe to do this as the back-pointer may point
|
||||
* to a window that has recently been removed.
|
||||
* We should always check the window manager's list of windows
|
||||
* and only process events on these windows. */
|
||||
|
||||
const std::vector<GHOST_IWindow *> &win_vec = window_manager_->getWindows();
|
||||
|
||||
std::vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
|
||||
std::vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
|
||||
|
||||
for (; win_it != win_end; ++win_it) {
|
||||
GHOST_WindowSDL *window = static_cast<GHOST_WindowSDL *>(*win_it);
|
||||
if (window->getSDLWindow() == sdl_win) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GHOST_SystemSDL::addDirtyWindow(GHOST_WindowSDL *bad_wind)
|
||||
{
|
||||
GHOST_ASSERT((bad_wind != nullptr), "addDirtyWindow() nullptr ptr trapped (window)");
|
||||
|
||||
dirty_windows_.push_back(bad_wind);
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemSDL::getButtons(GHOST_Buttons &buttons) const
|
||||
{
|
||||
SDL_MouseButtonFlags state = SDL_GetMouseState(nullptr, nullptr);
|
||||
buttons.set(GHOST_kButtonMaskLeft, (state & SDL_BUTTON_LMASK) != 0);
|
||||
buttons.set(GHOST_kButtonMaskMiddle, (state & SDL_BUTTON_MMASK) != 0);
|
||||
buttons.set(GHOST_kButtonMaskRight, (state & SDL_BUTTON_RMASK) != 0);
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TCapabilityFlag GHOST_SystemSDL::getCapabilities() const
|
||||
{
|
||||
return GHOST_TCapabilityFlag(
|
||||
GHOST_CAPABILITY_FLAG_ALL &
|
||||
/* NOTE: order the following flags as they they're declared in the source. */
|
||||
~(
|
||||
/* This SDL back-end has not yet implemented image copy/paste. */
|
||||
GHOST_kCapabilityClipboardImage |
|
||||
/* This SDL back-end has not yet implemented color sampling the desktop. */
|
||||
GHOST_kCapabilityDesktopSample |
|
||||
/* No support yet for IME input methods. */
|
||||
GHOST_kCapabilityInputIME |
|
||||
/* No support for window decoration styles. */
|
||||
GHOST_kCapabilityWindowDecorationStyles |
|
||||
/* No support for precisely placing windows on multiple monitors. */
|
||||
GHOST_kCapabilityMultiMonitorPlacement |
|
||||
/* No support for a Hyper modifier key. */
|
||||
GHOST_kCapabilityKeyboardHyperKey |
|
||||
/* No support yet for RGBA mouse cursors. */
|
||||
GHOST_kCapabilityCursorRGBA |
|
||||
/* No support yet for dynamic cursor generation. */
|
||||
GHOST_kCapabilityCursorGenerator |
|
||||
/* No support for window path meta-data. */
|
||||
GHOST_kCapabilityWindowPath));
|
||||
}
|
||||
|
||||
char *GHOST_SystemSDL::getClipboard(bool selection) const
|
||||
{
|
||||
/* The clipboard must be freed with `SDL_free`, copy for the return value. */
|
||||
char *sdl_text = selection ? SDL_GetPrimarySelectionText() : SDL_GetClipboardText();
|
||||
if (sdl_text == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
char *result = strdup(sdl_text);
|
||||
SDL_free(sdl_text);
|
||||
return result;
|
||||
}
|
||||
|
||||
void GHOST_SystemSDL::putClipboard(const char *buffer, bool selection) const
|
||||
{
|
||||
if (selection) {
|
||||
SDL_SetPrimarySelectionText(buffer);
|
||||
}
|
||||
else {
|
||||
SDL_SetClipboardText(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t GHOST_SystemSDL::getMilliSeconds() const
|
||||
{
|
||||
return SDL_GetTicks();
|
||||
}
|
||||
85
blender-5.2.0/intern/ghost/intern/GHOST_SystemSDL.hh
Normal file
85
blender-5.2.0/intern/ghost/intern/GHOST_SystemSDL.hh
Normal file
@@ -0,0 +1,85 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_SystemSDL class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../GHOST_Types.hh"
|
||||
#include "GHOST_Event.hh"
|
||||
#include "GHOST_System.hh"
|
||||
#include "GHOST_TimerManager.hh"
|
||||
#include "GHOST_WindowSDL.hh"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
class GHOST_WindowSDL;
|
||||
|
||||
class GHOST_SystemSDL : public GHOST_System {
|
||||
public:
|
||||
void addDirtyWindow(GHOST_WindowSDL *bad_wind);
|
||||
|
||||
GHOST_SystemSDL();
|
||||
~GHOST_SystemSDL();
|
||||
|
||||
bool processEvents(bool waitForEvent) override;
|
||||
|
||||
bool setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override;
|
||||
|
||||
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override;
|
||||
|
||||
GHOST_TCapabilityFlag getCapabilities() const override;
|
||||
|
||||
char *getClipboard(bool selection) const override;
|
||||
|
||||
void putClipboard(const char *buffer, bool selection) const override;
|
||||
|
||||
uint64_t getMilliSeconds() const override;
|
||||
|
||||
uint8_t getNumDisplays() const override;
|
||||
|
||||
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override;
|
||||
|
||||
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override;
|
||||
|
||||
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const override;
|
||||
|
||||
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override;
|
||||
|
||||
GHOST_IContext *createOffscreenContext(GHOST_GPUSettings gpu_settings) override;
|
||||
|
||||
GHOST_TSuccess disposeContext(GHOST_IContext *context) override;
|
||||
|
||||
private:
|
||||
GHOST_TSuccess init() override;
|
||||
|
||||
GHOST_IWindow *createWindow(const char *title,
|
||||
int32_t left,
|
||||
int32_t top,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
GHOST_TWindowState state,
|
||||
GHOST_GPUSettings gpu_settings,
|
||||
const bool exclusive = false,
|
||||
const bool is_dialog = false,
|
||||
const GHOST_IWindow *parent_window = nullptr) override;
|
||||
|
||||
/* SDL specific */
|
||||
GHOST_WindowSDL *findGhostWindow(SDL_Window *sdl_win);
|
||||
|
||||
bool generateWindowExposeEvents();
|
||||
|
||||
void processEvent(SDL_Event *sdl_event);
|
||||
|
||||
/** The vector of windows that need to be updated. */
|
||||
std::vector<GHOST_WindowSDL *> dirty_windows_;
|
||||
};
|
||||
10770
blender-5.2.0/intern/ghost/intern/GHOST_SystemWayland.cc
Normal file
10770
blender-5.2.0/intern/ghost/intern/GHOST_SystemWayland.cc
Normal file
File diff suppressed because it is too large
Load Diff
383
blender-5.2.0/intern/ghost/intern/GHOST_SystemWayland.hh
Normal file
383
blender-5.2.0/intern/ghost/intern/GHOST_SystemWayland.hh
Normal file
@@ -0,0 +1,383 @@
|
||||
/* SPDX-FileCopyrightText: 2020-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_SystemWayland class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../GHOST_Types.hh"
|
||||
#include "GHOST_System.hh"
|
||||
#include "GHOST_WindowWayland.hh"
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_DYNLOAD
|
||||
# include <wayland_dynload_client.h>
|
||||
#endif
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
# include <atomic>
|
||||
# include <thread>
|
||||
#endif
|
||||
|
||||
class GHOST_WindowWayland;
|
||||
|
||||
bool ghost_wl_display_report_error_if_set(wl_display *display);
|
||||
|
||||
bool ghost_wl_output_own(const struct wl_output *wl_output);
|
||||
void ghost_wl_output_tag(struct wl_output *wl_output);
|
||||
struct GWL_Output *ghost_wl_output_user_data(struct wl_output *wl_output);
|
||||
|
||||
/**
|
||||
* Enter/exit handlers may be called with a null window surface (when the window just closed),
|
||||
* so add a version of the function that checks this.
|
||||
*
|
||||
* All of the functions could in fact however paranoid null checks make the expected
|
||||
* state difficult to reason about, so only use this in cases the surface may be null.
|
||||
*/
|
||||
bool ghost_wl_surface_own_with_null_check(const struct wl_surface *wl_surface);
|
||||
bool ghost_wl_surface_own(const struct wl_surface *wl_surface);
|
||||
void ghost_wl_surface_tag(struct wl_surface *wl_surface);
|
||||
GHOST_WindowWayland *ghost_wl_surface_user_data(struct wl_surface *wl_surface);
|
||||
|
||||
bool ghost_wl_surface_own_cursor_pointer(const struct wl_surface *wl_surface);
|
||||
void ghost_wl_surface_tag_cursor_pointer(struct wl_surface *wl_surface);
|
||||
|
||||
bool ghost_wl_surface_own_cursor_tablet(const struct wl_surface *wl_surface);
|
||||
void ghost_wl_surface_tag_cursor_tablet(struct wl_surface *wl_surface);
|
||||
|
||||
/* Scaling to: translates from WAYLAND into GHOST (viewport local) coordinates.
|
||||
* Scaling from: performs the reverse translation.
|
||||
*
|
||||
* Scaling "to" is used to map WAYLAND location cursor coordinates to GHOST coordinates.
|
||||
* Scaling "from" is used to clamp cursor coordinates in WAYLAND local coordinates. */
|
||||
|
||||
struct GWL_WindowScaleParams;
|
||||
wl_fixed_t gwl_window_scale_wl_fixed_to(const GWL_WindowScaleParams &scale_params,
|
||||
wl_fixed_t value);
|
||||
wl_fixed_t gwl_window_scale_wl_fixed_from(const GWL_WindowScaleParams &scale_params,
|
||||
wl_fixed_t value);
|
||||
|
||||
/* Avoid this where possible as scaling integers often incurs rounding errors.
|
||||
* Scale #wl_fixed_t where possible.
|
||||
*
|
||||
* In general scale by large values where this is less likely to be a problem. */
|
||||
|
||||
int gwl_window_scale_int_to(const GWL_WindowScaleParams &scale_params, int value);
|
||||
int gwl_window_scale_int_from(const GWL_WindowScaleParams &scale_params, int value);
|
||||
|
||||
/**
|
||||
* Scale a logical buffer size to physical pixels, returning an integer buffer scale.
|
||||
* The buffer scale is rounded up so `result / *r_buffer_scale == logical_size`.
|
||||
*/
|
||||
int gwl_window_scale_buffer_size_to(const GWL_WindowScaleParams &scale_params,
|
||||
int logical_size,
|
||||
int *r_buffer_scale);
|
||||
|
||||
#define FRACTIONAL_DENOMINATOR 120
|
||||
|
||||
/**
|
||||
* The current desktop (Gnome, KDE etc..).
|
||||
*
|
||||
* \note Use this as a last resort, ideally wayland integration would *not* depend on this.
|
||||
*/
|
||||
enum class GWL_CurrentDesktopType {
|
||||
Other = 0,
|
||||
Gnome,
|
||||
KDE,
|
||||
};
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_DYNLOAD
|
||||
/**
|
||||
* Return true when all required WAYLAND libraries are present.
|
||||
*/
|
||||
bool ghost_wl_dynload_libraries_init();
|
||||
void ghost_wl_dynload_libraries_exit();
|
||||
#endif
|
||||
|
||||
struct GWL_Output {
|
||||
|
||||
/** Wayland core types. */
|
||||
struct {
|
||||
wl_output *output = nullptr;
|
||||
} wl;
|
||||
|
||||
/** XDG native types. */
|
||||
struct {
|
||||
struct zxdg_output_v1 *output = nullptr;
|
||||
} xdg;
|
||||
|
||||
GHOST_SystemWayland *system = nullptr;
|
||||
|
||||
/**
|
||||
* Dimensions in pixels.
|
||||
*
|
||||
* \note Rotation (from the `transform` flag) has *not* been applied.
|
||||
* So a vertical monitor will still have a larger width.
|
||||
*/
|
||||
int32_t size_native[2] = {0, 0};
|
||||
/** Dimensions in millimeter. */
|
||||
int32_t size_mm[2] = {0, 0};
|
||||
|
||||
/**
|
||||
* Dimensions in logical points.
|
||||
*
|
||||
* \note A 2x Hi-DPI monitor with a `size_native` of 1600x1200
|
||||
* would have a `size_logical` of 800x600.
|
||||
*
|
||||
* \note Rotation (from the `transform` flag) *has* been applied.
|
||||
*/
|
||||
int32_t size_logical[2] = {0, 0};
|
||||
bool has_size_logical = false;
|
||||
|
||||
/** Monitor position in pixels. */
|
||||
int32_t position_logical[2] = {0, 0};
|
||||
bool has_position_logical = false;
|
||||
|
||||
int transform = 0;
|
||||
int scale = 1;
|
||||
/**
|
||||
* The integer `scale` value should be used in almost all cases,
|
||||
* as this is what is used for most API calls.
|
||||
* Only use fractional scaling to calculate the DPI.
|
||||
*
|
||||
* \note Use the same scale as #wp_fractional_scale_manager_v1
|
||||
* (avoid floating point arithmetic in general).
|
||||
*/
|
||||
int scale_fractional = (1 * FRACTIONAL_DENOMINATOR);
|
||||
bool has_scale_fractional = false;
|
||||
|
||||
std::string make;
|
||||
std::string model;
|
||||
};
|
||||
|
||||
class GHOST_SystemWayland : public GHOST_System {
|
||||
public:
|
||||
GHOST_SystemWayland(bool background);
|
||||
GHOST_SystemWayland() : GHOST_SystemWayland(true) {};
|
||||
|
||||
~GHOST_SystemWayland() override;
|
||||
|
||||
GHOST_TSuccess init() override;
|
||||
|
||||
bool processEvents(bool waitForEvent) override;
|
||||
|
||||
bool setConsoleWindowState(GHOST_TConsoleWindowState action) override;
|
||||
|
||||
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override;
|
||||
|
||||
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override;
|
||||
|
||||
char *getClipboard(bool selection) const override;
|
||||
|
||||
void putClipboard(const char *buffer, bool selection) const override;
|
||||
|
||||
/**
|
||||
* Returns GHOST_kSuccess if the clipboard contains an image.
|
||||
*/
|
||||
GHOST_TSuccess hasClipboardImage() const override;
|
||||
|
||||
/**
|
||||
* Get image data from the Clipboard
|
||||
* \param r_width: the returned image width in pixels.
|
||||
* \param r_height: the returned image height in pixels.
|
||||
* \return pointer uint array in RGBA byte order. Caller must free.
|
||||
*/
|
||||
uint *getClipboardImage(int *r_width, int *r_height) const override;
|
||||
|
||||
/**
|
||||
* Put image data to the Clipboard
|
||||
* \param rgba: uint array in RGBA byte order.
|
||||
* \param width: the image width in pixels.
|
||||
* \param height: the image height in pixels.
|
||||
*/
|
||||
GHOST_TSuccess putClipboardImage(uint *rgba, int width, int height) const override;
|
||||
|
||||
uint8_t getNumDisplays() const override;
|
||||
|
||||
uint64_t getMilliSeconds() const override;
|
||||
|
||||
GHOST_TSuccess getCursorPositionClientRelative(const GHOST_IWindow *window,
|
||||
int32_t &x,
|
||||
int32_t &y) const override;
|
||||
GHOST_TSuccess setCursorPositionClientRelative(GHOST_IWindow *window,
|
||||
int32_t x,
|
||||
int32_t y) override;
|
||||
|
||||
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override;
|
||||
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override;
|
||||
|
||||
uint32_t getCursorPreferredLogicalSize() const override;
|
||||
|
||||
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override;
|
||||
|
||||
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const override;
|
||||
|
||||
GHOST_IContext *createOffscreenContext(GHOST_GPUSettings gpu_settings) override;
|
||||
|
||||
GHOST_TSuccess disposeContext(GHOST_IContext *context) override;
|
||||
|
||||
GHOST_IWindow *createWindow(const char *title,
|
||||
int32_t left,
|
||||
int32_t top,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
GHOST_TWindowState state,
|
||||
GHOST_GPUSettings gpu_settings,
|
||||
const bool exclusive,
|
||||
const bool is_dialog,
|
||||
const GHOST_IWindow *parent_window) override;
|
||||
|
||||
GHOST_TCapabilityFlag getCapabilities() const override;
|
||||
|
||||
void setMultitouchGestures(const bool use) override;
|
||||
|
||||
/* WAYLAND utility functions (share window/system logic). */
|
||||
|
||||
GHOST_TSuccess cursor_shape_set(GHOST_TStandardCursor shape);
|
||||
|
||||
GHOST_TSuccess cursor_shape_check(GHOST_TStandardCursor cursor_shape);
|
||||
|
||||
GHOST_TSuccess cursor_shape_custom_set(const GHOST_CursorGenerator &cg);
|
||||
|
||||
GHOST_TSuccess cursor_bitmap_get(GHOST_CursorBitmapRef *bitmap);
|
||||
|
||||
GHOST_TSuccess cursor_visibility_set(bool visible);
|
||||
|
||||
bool cursor_grab_use_software_display_get(const GHOST_TGrabCursorMode mode);
|
||||
|
||||
/**
|
||||
* Return a separate WAYLAND local timer manager to #GHOST_System::getTimerManager
|
||||
* Manipulation & access must lock with #GHOST_WaylandSystem::server_mutex.
|
||||
*
|
||||
* See #GWL_Display::key_repeat_timer_manager docstring for details on why this is needed.
|
||||
*/
|
||||
GHOST_TimerManager *key_repeat_timer_manager();
|
||||
|
||||
void xdg_toplevel_icon_update(GHOST_WindowWayland *window, struct xdg_toplevel *toplevel);
|
||||
|
||||
/* WAYLAND direct-data access. */
|
||||
|
||||
struct wl_display *wl_display_get();
|
||||
struct wl_compositor *wl_compositor_get();
|
||||
struct zwp_primary_selection_device_manager_v1 *wp_primary_selection_manager_get();
|
||||
struct xdg_activation_v1 *xdg_activation_manager_get();
|
||||
struct zwp_pointer_gestures_v1 *wp_pointer_gestures_get();
|
||||
struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager_get();
|
||||
struct wp_viewporter *wp_viewporter_get();
|
||||
struct wp_color_manager_v1 *wp_color_manager_get();
|
||||
struct wl_event_queue *wp_color_manager_queue_get();
|
||||
|
||||
bool supports_color_manager_feature_windows_scrgb() const;
|
||||
bool supports_color_manager_extended_srgb_linear() const;
|
||||
|
||||
struct xdg_wm_base *xdg_decor_shell_get();
|
||||
struct zxdg_decoration_manager_v1 *xdg_decor_manager_get();
|
||||
/* End `xdg_decor`. */
|
||||
|
||||
const std::span<GWL_Output *const> outputs_get() const;
|
||||
|
||||
struct wl_shm *wl_shm_get() const;
|
||||
|
||||
void ime_begin(const GHOST_WindowWayland *win,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
int32_t w,
|
||||
int32_t h,
|
||||
bool completed) const;
|
||||
void ime_end(const GHOST_WindowWayland *win) const;
|
||||
|
||||
bool use_window_frame_get() const;
|
||||
bool use_window_frame_csd_get() const;
|
||||
#ifdef WITH_GHOST_CSD
|
||||
const GHOST_CSD_Layout &csd_layout_base_get() const;
|
||||
#endif
|
||||
|
||||
static const char *xdg_app_id_get();
|
||||
|
||||
/* WAYLAND utility functions. */
|
||||
|
||||
/**
|
||||
* Use this function instead of #GHOST_System::getMilliSeconds,
|
||||
* passing in the time-stamp from WAYLAND input to get the event
|
||||
* time-stamp with an offset applied to make it compatible with `getMilliSeconds`.
|
||||
*/
|
||||
uint64_t ms_from_input_time(const uint32_t timestamp_as_uint);
|
||||
|
||||
/**
|
||||
* Push an event, with support for calling from a thread.
|
||||
* NOTE: only needed for `USE_EVENT_BACKGROUND_THREAD`.
|
||||
*/
|
||||
GHOST_TSuccess pushEvent_maybe_pending(std::unique_ptr<const GHOST_IEvent> event);
|
||||
|
||||
/** Set this seat to be active. */
|
||||
void seat_active_set(const struct GWL_Seat *seat);
|
||||
|
||||
struct wl_seat *wl_seat_active_get_with_input_serial(uint32_t &serial);
|
||||
|
||||
/**
|
||||
* Clear all references to this output.
|
||||
*
|
||||
* \note The compositor should have already called the `wl_surface_listener.leave` callback,
|
||||
* however some compositors may not (see #103586).
|
||||
* So remove references to the output before it's destroyed to avoid crashing.
|
||||
*
|
||||
* \return true when any references were removed.
|
||||
*/
|
||||
bool output_unref(struct wl_output *wl_output);
|
||||
|
||||
void output_scale_update(GWL_Output *output);
|
||||
|
||||
/**
|
||||
* Clear all references to this surface to prevent accessing nullptr pointers.
|
||||
*
|
||||
* \return true when any references were removed.
|
||||
*/
|
||||
bool window_surface_unref(const wl_surface *wl_surface);
|
||||
|
||||
bool window_cursor_grab_set(const GHOST_TGrabCursorMode mode,
|
||||
const GHOST_TGrabCursorMode mode_current,
|
||||
int32_t init_grab_xy[2],
|
||||
const GHOST_Rect *wrap_bounds,
|
||||
GHOST_TAxisFlag wrap_axis,
|
||||
wl_surface *wl_surface,
|
||||
const struct GWL_WindowScaleParams &scale_params);
|
||||
|
||||
#ifdef USE_EVENT_BACKGROUND_THREAD
|
||||
/* NOTE: allocate mutex so `const` functions can lock the mutex. */
|
||||
|
||||
/** Lock to prevent #wl_display_dispatch / #wl_display_roundtrip / #wl_display_flush
|
||||
* from running at the same time. */
|
||||
std::mutex *server_mutex = nullptr;
|
||||
|
||||
/**
|
||||
* Threads must lock this before manipulating #GWL_Display::ghost_timer_manager.
|
||||
*
|
||||
* \note Using a separate lock to `server_mutex` is necessary because the
|
||||
* server lock is already held when calling `ghost_wl_display_event_pump`.
|
||||
* If manipulating the timer used the `server_mutex`, event pump can indirectly
|
||||
* handle key up/down events which would lock `server_mutex` causing a dead-lock.
|
||||
*/
|
||||
std::mutex *timer_mutex = nullptr;
|
||||
|
||||
std::thread::id main_thread_id;
|
||||
|
||||
std::atomic<bool> has_pending_actions_for_window = false;
|
||||
#endif
|
||||
|
||||
private:
|
||||
/**
|
||||
* Support freeing the internal data separately from the destructor
|
||||
* so it can be called when WAYLAND isn't running (immediately before raising an exception).
|
||||
*/
|
||||
void display_destroy_and_free_all();
|
||||
|
||||
struct GWL_Display *display_;
|
||||
};
|
||||
3000
blender-5.2.0/intern/ghost/intern/GHOST_SystemWin32.cc
Normal file
3000
blender-5.2.0/intern/ghost/intern/GHOST_SystemWin32.cc
Normal file
File diff suppressed because it is too large
Load Diff
513
blender-5.2.0/intern/ghost/intern/GHOST_SystemWin32.hh
Normal file
513
blender-5.2.0/intern/ghost/intern/GHOST_SystemWin32.hh
Normal file
@@ -0,0 +1,513 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_SystemWin32 class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef WIN32
|
||||
# error WIN32 only!
|
||||
#endif /* WIN32 */
|
||||
|
||||
#include <ole2.h> /* For drag-n-drop. */
|
||||
#include <windows.h>
|
||||
|
||||
#include "GHOST_System.hh"
|
||||
|
||||
class GHOST_EventButton;
|
||||
class GHOST_EventCursor;
|
||||
class GHOST_EventKey;
|
||||
class GHOST_EventWheel;
|
||||
class GHOST_EventWindow;
|
||||
class GHOST_EventDragnDrop;
|
||||
|
||||
class GHOST_ContextD3D;
|
||||
class GHOST_WindowWin32;
|
||||
|
||||
/**
|
||||
* WIN32 Implementation of GHOST_System class.
|
||||
* \see GHOST_System.
|
||||
*/
|
||||
class GHOST_SystemWin32 : public GHOST_System {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_SystemWin32();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_SystemWin32() override;
|
||||
|
||||
/***************************************************************************************
|
||||
** Time(r) functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* This method converts performance counter measurements into milliseconds since the start of the
|
||||
* Blender process.
|
||||
* \return The number of milliseconds since the start of the Blender process.
|
||||
*/
|
||||
uint64_t performanceCounterToMillis(__int64 perf_ticks) const;
|
||||
|
||||
/**
|
||||
* Returns the system time.
|
||||
* Returns the number of milliseconds since the start of the Blender process.
|
||||
* This overloaded method uses the high frequency timer if available.
|
||||
* \return The number of milliseconds.
|
||||
*/
|
||||
uint64_t getMilliSeconds() const override;
|
||||
|
||||
/***************************************************************************************
|
||||
** Display/window management functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns the number of displays on this system.
|
||||
* \return The number of displays.
|
||||
*/
|
||||
uint8_t getNumDisplays() const override;
|
||||
|
||||
/**
|
||||
* Returns the dimensions of the main display on this system.
|
||||
* \return The dimension of the main display.
|
||||
*/
|
||||
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override;
|
||||
|
||||
/**
|
||||
* Returns the dimensions of all displays on this system.
|
||||
* \return The dimension of the main display.
|
||||
*/
|
||||
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const override;
|
||||
|
||||
/**
|
||||
* Create a new window.
|
||||
* The new window is added to the list of windows managed.
|
||||
* Never explicitly delete the window, use #disposeWindow() instead.
|
||||
* \param title: The name of the window.
|
||||
* (displayed in the title bar of the window if the OS supports it).
|
||||
* \param left: The coordinate of the left edge of the window.
|
||||
* \param top: The coordinate of the top edge of the window.
|
||||
* \param width: The width the window.
|
||||
* \param height: The height the window.
|
||||
* \param state: The state of the window when opened.
|
||||
* \param gpu_settings: Misc GPU settings.
|
||||
* \param exclusive: Use to show the window on top and ignore others (used full-screen).
|
||||
* \param parent_window: Parent window.
|
||||
* \return The new window (or 0 if creation failed).
|
||||
*/
|
||||
GHOST_IWindow *createWindow(const char *title,
|
||||
int32_t left,
|
||||
int32_t top,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
GHOST_TWindowState state,
|
||||
GHOST_GPUSettings gpu_settings,
|
||||
const bool exclusive = false,
|
||||
const bool is_dialog = false,
|
||||
const GHOST_IWindow *parent_window = nullptr) override;
|
||||
|
||||
/**
|
||||
* Create a new off-screen context.
|
||||
* Never explicitly delete the window, use #disposeContext() instead.
|
||||
* \return The new context (or 0 if creation failed).
|
||||
*/
|
||||
GHOST_IContext *createOffscreenContext(GHOST_GPUSettings gpu_settings) override;
|
||||
|
||||
/**
|
||||
* Dispose of a context.
|
||||
* \param context: Pointer to the context to be disposed.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess disposeContext(GHOST_IContext *context) override;
|
||||
|
||||
/**
|
||||
* Create a new off-screen DirectX context.
|
||||
* Never explicitly delete the context, use #disposeContext() instead.
|
||||
* This is for GHOST internal, Win32 specific use, so it can be called statically.
|
||||
*
|
||||
* \return The new context (or 0 if creation failed).
|
||||
*/
|
||||
static GHOST_ContextD3D *createOffscreenContextD3D();
|
||||
|
||||
/**
|
||||
* Dispose of a DirectX context.
|
||||
* This is for GHOST internal, Win32 specific use, so it can be called statically.
|
||||
* \param context: Pointer to the context to be disposed.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
static GHOST_TSuccess disposeContextD3D(GHOST_ContextD3D *context);
|
||||
|
||||
/**
|
||||
* Get the Window under the mouse cursor. Location obtained from the OS.
|
||||
* \return The window under the cursor or nullptr if none.
|
||||
*/
|
||||
GHOST_IWindow *getWindowUnderCursor(int32_t /*x*/, int32_t /*y*/) override;
|
||||
|
||||
/***************************************************************************************
|
||||
** Event management functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Gets events from the system and stores them in the queue.
|
||||
* \param waitForEvent: Flag to wait for an event (or return immediately).
|
||||
* \return Indication of the presence of events.
|
||||
*/
|
||||
bool processEvents(bool waitForEvent) override;
|
||||
|
||||
/***************************************************************************************
|
||||
** Cursor management functionality
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns the current location of the cursor (location in screen coordinates)
|
||||
* \param x: The x-coordinate of the cursor.
|
||||
* \param y: The y-coordinate of the cursor.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override;
|
||||
|
||||
/**
|
||||
* Updates the location of the cursor (location in screen coordinates).
|
||||
* \param x: The x-coordinate of the cursor.
|
||||
* \param y: The y-coordinate of the cursor.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override;
|
||||
|
||||
/**
|
||||
* Get the color of the pixel at the current mouse cursor location
|
||||
* \param r_color: returned sRGB float colors
|
||||
* \return Success value (true == successful and supported by platform)
|
||||
*/
|
||||
GHOST_TSuccess getPixelAtCursor(float r_color[3]) const override;
|
||||
|
||||
uint32_t getCursorPreferredLogicalSize() const override;
|
||||
|
||||
/***************************************************************************************
|
||||
** Access to mouse button and keyboard states.
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Returns the state of all modifier keys.
|
||||
* \param keys: The state of all modifier keys (true == pressed).
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override;
|
||||
|
||||
/**
|
||||
* Returns the state of the mouse buttons (outside the message queue).
|
||||
* \param buttons: The state of the buttons.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override;
|
||||
|
||||
GHOST_TCapabilityFlag getCapabilities() const override;
|
||||
|
||||
/**
|
||||
* Returns unsigned char from CUT_BUFFER0
|
||||
* \param selection: Used by X11 only.
|
||||
* \return Returns the Clipboard.
|
||||
*/
|
||||
char *getClipboard(bool selection) const override;
|
||||
|
||||
/**
|
||||
* Puts buffer to system clipboard.
|
||||
* \param selection: Used by X11 only.
|
||||
* \return No return.
|
||||
*/
|
||||
void putClipboard(const char *buffer, bool selection) const override;
|
||||
|
||||
/**
|
||||
* Returns GHOST_kSuccess if the clipboard contains an image.
|
||||
*/
|
||||
GHOST_TSuccess hasClipboardImage() const override;
|
||||
|
||||
/**
|
||||
* Get image data from the Clipboard
|
||||
* \param r_width: the returned image width in pixels.
|
||||
* \param r_height: the returned image height in pixels.
|
||||
* \return pointer uint array in RGBA byte order. Caller must free.
|
||||
*/
|
||||
uint *getClipboardImage(int *r_width, int *r_height) const override;
|
||||
|
||||
/**
|
||||
* Put image data to the Clipboard
|
||||
* \param rgba: uint array in RGBA byte order.
|
||||
* \param width: the image width in pixels.
|
||||
* \param height: the image height in pixels.
|
||||
*/
|
||||
GHOST_TSuccess putClipboardImage(uint *rgba, int width, int height) const override;
|
||||
|
||||
/**
|
||||
* Show a system message box
|
||||
* \param title: The title of the message box.
|
||||
* \param message: The message to display.
|
||||
* \param help_label: Help button label.
|
||||
* \param continue_label: Continue button label.
|
||||
* \param link: An optional hyperlink.
|
||||
* \param dialog_options: Options how to display the message.
|
||||
*/
|
||||
GHOST_TSuccess showMessageBox(const char *title,
|
||||
const char *message,
|
||||
const char *help_label,
|
||||
const char *continue_label,
|
||||
const char *link,
|
||||
GHOST_DialogOptions dialog_options) const override;
|
||||
|
||||
/**
|
||||
* Creates a drag & drop event and pushes it immediately onto the event queue.
|
||||
* Called by #GHOST_DropTargetWin32 class.
|
||||
* \param eventType: The type of drag & drop event
|
||||
* \param draggedObjectType: The type object concerned
|
||||
* (currently array of file names, string, ?bitmap)
|
||||
* \param window: The window on which the event occurred
|
||||
* \param mouseX: x mouse coordinate (in window coordinates)
|
||||
* \param mouseY: y mouse coordinate
|
||||
* \return Indication whether the event was handled.
|
||||
*/
|
||||
static GHOST_TSuccess pushDragDropEvent(GHOST_TEventType eventType,
|
||||
GHOST_TDragnDropTypes draggedObjectType,
|
||||
GHOST_WindowWin32 *window,
|
||||
int mouseX,
|
||||
int mouseY,
|
||||
void *data);
|
||||
|
||||
/***************************************************************************************
|
||||
** Modify tablet API
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Set which tablet API to use.
|
||||
* \param api: Enum indicating which API to use.
|
||||
*/
|
||||
void setTabletAPI(GHOST_TTabletAPI api) override;
|
||||
|
||||
/***************************************************************************************
|
||||
** Debug Info
|
||||
***************************************************************************************/
|
||||
|
||||
/**
|
||||
* Specify which debug messages are to be shown.
|
||||
* \param debug: Flag for systems to debug.
|
||||
*/
|
||||
void initDebug(GHOST_Debug debug) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Initializes the system.
|
||||
* For now, it just registers the window class (WNDCLASS).
|
||||
* \return A success value.
|
||||
*/
|
||||
GHOST_TSuccess init() override;
|
||||
|
||||
/**
|
||||
* Closes the system down.
|
||||
* \return A success value.
|
||||
*/
|
||||
GHOST_TSuccess exit() override;
|
||||
|
||||
/**
|
||||
* Converts raw WIN32 key codes from the `wndproc` to GHOST keys.
|
||||
* \param vKey: The virtual key from #hardKey.
|
||||
* \param ScanCode: The ScanCode of pressed key (similar to PS/2 Set 1).
|
||||
* \param extend: Flag if key is not primly (left or right).
|
||||
* \return The GHOST key (GHOST_kKeyUnknown if no match).
|
||||
*/
|
||||
GHOST_TKey convertKey(short vKey, short ScanCode, short extend) const;
|
||||
|
||||
/**
|
||||
* Catches raw WIN32 key codes from WM_INPUT in the `wndproc`.
|
||||
* \param raw: RawInput structure with detailed info about the key event.
|
||||
* \param r_key_down: Set true when the key is pressed, otherwise false.
|
||||
* \return The GHOST key (GHOST_kKeyUnknown if no match).
|
||||
*/
|
||||
GHOST_TKey hardKey(RAWINPUT const &raw, bool *r_key_down);
|
||||
|
||||
/**
|
||||
* Creates mouse button event.
|
||||
* \param type: The type of event to create.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \param mask: The button mask of this event.
|
||||
* \return The event created.
|
||||
*/
|
||||
static std::unique_ptr<GHOST_EventButton> processButtonEvent(GHOST_TEventType type,
|
||||
GHOST_WindowWin32 *window,
|
||||
GHOST_TButton mask);
|
||||
|
||||
/**
|
||||
* Creates tablet events from Wintab events.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
*/
|
||||
static void processWintabEvent(GHOST_WindowWin32 *window);
|
||||
|
||||
/**
|
||||
* Creates tablet events from pointer events.
|
||||
* \param type: The type of pointer event.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \param wParam: The wParam from the `wndproc`.
|
||||
* \param lParam: The lParam from the `wndproc`.
|
||||
* \param eventhandled: True if the method handled the event.
|
||||
*/
|
||||
static void processPointerEvent(
|
||||
UINT type, GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam, bool &eventhandled);
|
||||
|
||||
/**
|
||||
* Creates cursor event.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \return The event created.
|
||||
*/
|
||||
static std::unique_ptr<GHOST_EventCursor> processCursorEvent(GHOST_WindowWin32 *window,
|
||||
const int32_t screen_co[2]);
|
||||
|
||||
/**
|
||||
* Handles a vertical mouse wheel event.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \param wParam: The wParam from the `wndproc`.
|
||||
* \param lParam: The lParam from the `wndproc`.
|
||||
*/
|
||||
static void processWheelEventVertical(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
/**
|
||||
* Handles a horizontal mouse wheel event.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \param wParam: The wParam from the `wndproc`.
|
||||
* \param lParam: The lParam from the `wndproc`.
|
||||
*/
|
||||
static void processWheelEventHorizontal(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
/**
|
||||
* Creates a key event and updates the key data stored locally (modifier_keys_).
|
||||
* In most cases this is a straightforward conversion of key codes.
|
||||
* For the modifier keys however, we want to distinguish left and right keys.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \param raw: RawInput structure with detailed info about the key event.
|
||||
*/
|
||||
static std::unique_ptr<GHOST_EventKey> processKeyEvent(GHOST_WindowWin32 *window,
|
||||
RAWINPUT const &raw);
|
||||
|
||||
/**
|
||||
* Process special keys `VK_OEM_*`, to see if current key layout
|
||||
* gives us anything special, like `!` on French AZERTY.
|
||||
* \param vKey: The virtual key from #hardKey.
|
||||
* \param scanCode: The ScanCode of pressed key (similar to PS/2 Set 1).
|
||||
*/
|
||||
GHOST_TKey processSpecialKey(short vKey, short scanCode) const;
|
||||
|
||||
/**
|
||||
* Creates a window size event.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \return The event created.
|
||||
*/
|
||||
static std::unique_ptr<GHOST_Event> processWindowSizeEvent(GHOST_WindowWin32 *window);
|
||||
|
||||
/**
|
||||
* Creates a window event.
|
||||
* \param type: The type of event to create.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \return The event created.
|
||||
*/
|
||||
static std::unique_ptr<GHOST_Event> processWindowEvent(GHOST_TEventType type,
|
||||
GHOST_WindowWin32 *window);
|
||||
|
||||
#ifdef WITH_INPUT_IME
|
||||
/**
|
||||
* Creates a IME event.
|
||||
* \param type: The type of event to create.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \param data: IME data.
|
||||
* \return The event created.
|
||||
*/
|
||||
static std::unique_ptr<GHOST_Event> processImeEvent(GHOST_TEventType type,
|
||||
GHOST_WindowWin32 *window,
|
||||
const GHOST_TEventImeData *data);
|
||||
#endif /* WITH_INPUT_IME */
|
||||
|
||||
/**
|
||||
* Handles minimum window size.
|
||||
* \param minmax: The MINMAXINFO structure.
|
||||
*/
|
||||
static void processMinMaxInfo(MINMAXINFO *minmax);
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
/**
|
||||
* Handles Motion and Button events from a SpaceNavigator or related device.
|
||||
* Instead of returning an event object, this function communicates directly
|
||||
* with the GHOST_NDOFManager.
|
||||
* \param raw: RawInput structure with detailed info about the NDOF event.
|
||||
* \return Whether an event was generated and sent.
|
||||
*/
|
||||
bool processNDOF(RAWINPUT const &raw);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Drives Direct Manipulation update.
|
||||
*/
|
||||
void driveTrackpad();
|
||||
|
||||
/**
|
||||
* Creates trackpad events for the active window.
|
||||
*/
|
||||
void processTrackpad();
|
||||
|
||||
/**
|
||||
* Check current key layout for AltGr
|
||||
*/
|
||||
inline void handleKeyboardChange();
|
||||
|
||||
/**
|
||||
* Windows call back routine for our window class.
|
||||
*/
|
||||
static LRESULT WINAPI s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
/**
|
||||
* Set the Console State
|
||||
* \param action: console state
|
||||
* \return current status (1 -visible, 0 - hidden)
|
||||
*/
|
||||
bool setConsoleWindowState(GHOST_TConsoleWindowState action) override;
|
||||
|
||||
/** State variable set at initialization. */
|
||||
bool has_performance_counter_;
|
||||
/** High frequency timer variable. */
|
||||
__int64 freq_;
|
||||
/** AltGr on current keyboard layout. */
|
||||
bool has_alt_gr_;
|
||||
/** Language identifier. */
|
||||
WORD lang_id_;
|
||||
/** Stores keyboard layout. */
|
||||
HKL keylayout_;
|
||||
|
||||
/** Console status. */
|
||||
bool console_status_;
|
||||
|
||||
/** Wheel delta accumulators. */
|
||||
int wheel_delta_accum_vertical_;
|
||||
int wheel_delta_accum_horizontal_;
|
||||
};
|
||||
|
||||
inline void GHOST_SystemWin32::handleKeyboardChange()
|
||||
{
|
||||
keylayout_ = GetKeyboardLayout(0); /* Get keylayout for current thread. */
|
||||
int i;
|
||||
SHORT s;
|
||||
|
||||
/* Save the language identifier. */
|
||||
lang_id_ = LOWORD(keylayout_);
|
||||
|
||||
for (has_alt_gr_ = false, i = 32; i < 256; ++i) {
|
||||
s = VkKeyScanEx((char)i, keylayout_);
|
||||
/* `s == -1` means no key that translates passed char code high byte contains shift state.
|
||||
* bit 2 Control pressed, bit 4 `Alt` pressed if both are pressed,
|
||||
* we have `AltGr` key-combination on key-layout. */
|
||||
if (s != -1 && (s & 0x600) == 0x600) {
|
||||
has_alt_gr_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
2848
blender-5.2.0/intern/ghost/intern/GHOST_SystemX11.cc
Normal file
2848
blender-5.2.0/intern/ghost/intern/GHOST_SystemX11.cc
Normal file
File diff suppressed because it is too large
Load Diff
394
blender-5.2.0/intern/ghost/intern/GHOST_SystemX11.hh
Normal file
394
blender-5.2.0/intern/ghost/intern/GHOST_SystemX11.hh
Normal file
@@ -0,0 +1,394 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_SystemX11 class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <X11/XKBlib.h> /* Allow detectable auto-repeat. */
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include "../GHOST_Types.hh"
|
||||
#include "GHOST_System.hh"
|
||||
|
||||
/* For tablets. */
|
||||
#ifdef WITH_X11_XINPUT
|
||||
# include <X11/extensions/XInput.h>
|
||||
|
||||
/* Disable XINPUT warp, currently not implemented by XORG for multi-head display.
|
||||
* (see comment in XSERVER `Xi/xiwarppointer.c` -> `FIXME: panoramix stuff is missing` ~ v1.13.4)
|
||||
* If this is supported we can add back XINPUT for warping (fixing #48901).
|
||||
* For now disable (see #50383). */
|
||||
// # define USE_X11_XINPUT_WARP
|
||||
#endif
|
||||
|
||||
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
|
||||
# define GHOST_X11_RES_NAME "Blender" /* res_name */
|
||||
# define GHOST_X11_RES_CLASS "Blender" /* res_class */
|
||||
#endif
|
||||
|
||||
/* generic error handlers */
|
||||
int GHOST_X11_ApplicationErrorHandler(Display *display, XErrorEvent *event);
|
||||
int GHOST_X11_ApplicationIOErrorHandler(Display *display);
|
||||
|
||||
#define GHOST_X11_ERROR_HANDLERS_OVERRIDE(var) \
|
||||
struct { \
|
||||
XErrorHandler handler; \
|
||||
XIOErrorHandler handler_io; \
|
||||
} var = { \
|
||||
XSetErrorHandler(GHOST_X11_ApplicationErrorHandler), \
|
||||
XSetIOErrorHandler(GHOST_X11_ApplicationIOErrorHandler), \
|
||||
}
|
||||
|
||||
#define GHOST_X11_ERROR_HANDLERS_RESTORE(var) \
|
||||
{ \
|
||||
(void)XSetErrorHandler(var.handler); \
|
||||
(void)XSetIOErrorHandler(var.handler_io); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
class GHOST_WindowX11;
|
||||
|
||||
/**
|
||||
* X11 Implementation of GHOST_System class.
|
||||
* \see GHOST_System.
|
||||
*/
|
||||
class GHOST_SystemX11 : public GHOST_System {
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
* this class should only be instantiated by GHOST_ISystem.
|
||||
*/
|
||||
|
||||
GHOST_SystemX11();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_SystemX11() override;
|
||||
|
||||
GHOST_TSuccess init() override;
|
||||
|
||||
/**
|
||||
* \section Interface Inherited from GHOST_ISystem
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the system time.
|
||||
* Returns the number of milliseconds since the start of the system process.
|
||||
* \return The number of milliseconds.
|
||||
*/
|
||||
uint64_t getMilliSeconds() const override;
|
||||
|
||||
/**
|
||||
* Returns the number of displays on this system.
|
||||
* \return The number of displays.
|
||||
*/
|
||||
uint8_t getNumDisplays() const override;
|
||||
|
||||
/**
|
||||
* Returns the dimensions of the main display on this system.
|
||||
* \return The dimension of the main display.
|
||||
*/
|
||||
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override;
|
||||
|
||||
/**
|
||||
* Returns the dimensions of all displays on this system.
|
||||
* \return The dimension of the main display.
|
||||
*/
|
||||
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const override;
|
||||
|
||||
/**
|
||||
* Create a new window.
|
||||
* The new window is added to the list of windows managed.
|
||||
* Never explicitly delete the window, use #disposeWindow() instead.
|
||||
* \param title: The name of the window.
|
||||
* (displayed in the title bar of the window if the OS supports it).
|
||||
* \param left: The coordinate of the left edge of the window.
|
||||
* \param top: The coordinate of the top edge of the window.
|
||||
* \param width: The width the window.
|
||||
* \param height: The height the window.
|
||||
* \param state: The state of the window when opened.
|
||||
* \param gpu_settings: Misc GPU settings.
|
||||
* \param exclusive: Use to show the window on top and ignore others (used full-screen).
|
||||
* \param parent_window: Parent (embedder) window.
|
||||
* \return The new window (or 0 if creation failed).
|
||||
*/
|
||||
GHOST_IWindow *createWindow(const char *title,
|
||||
int32_t left,
|
||||
int32_t top,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
GHOST_TWindowState state,
|
||||
GHOST_GPUSettings gpu_settings,
|
||||
const bool exclusive = false,
|
||||
const bool is_dialog = false,
|
||||
const GHOST_IWindow *parent_window = nullptr) override;
|
||||
|
||||
/**
|
||||
* Create a new off-screen context.
|
||||
* Never explicitly delete the context, use #disposeContext() instead.
|
||||
* \return The new context (or 0 if creation failed).
|
||||
*/
|
||||
GHOST_IContext *createOffscreenContext(GHOST_GPUSettings gpu_settings) override;
|
||||
|
||||
/**
|
||||
* Dispose of a context.
|
||||
* \param context: Pointer to the context to be disposed.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess disposeContext(GHOST_IContext *context) override;
|
||||
|
||||
/**
|
||||
* Retrieves events from the system and stores them in the queue.
|
||||
* \param waitForEvent: Flag to wait for an event (or return immediately).
|
||||
* \return Indication of the presence of events.
|
||||
*/
|
||||
bool processEvents(bool waitForEvent) override;
|
||||
|
||||
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override;
|
||||
|
||||
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override;
|
||||
|
||||
/**
|
||||
* Get the color of the pixel at the current mouse cursor location
|
||||
* \param r_color: returned sRGB float colors
|
||||
* \return Success value (true == successful and supported by platform)
|
||||
*/
|
||||
GHOST_TSuccess getPixelAtCursor(float r_color[3]) const override;
|
||||
|
||||
/**
|
||||
* Returns the state of all modifier keys.
|
||||
* \param keys: The state of all modifier keys (true == pressed).
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override;
|
||||
|
||||
/**
|
||||
* Returns the state of the mouse buttons (outside the message queue).
|
||||
* \param buttons: The state of the buttons.
|
||||
* \return Indication of success.
|
||||
*/
|
||||
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override;
|
||||
|
||||
GHOST_TCapabilityFlag getCapabilities() const override;
|
||||
|
||||
/**
|
||||
* Flag a window as dirty. This will
|
||||
* generate a GHOST window update event on a call to processEvents()
|
||||
*/
|
||||
|
||||
void addDirtyWindow(GHOST_WindowX11 *bad_wind);
|
||||
|
||||
/**
|
||||
* return a pointer to the X11 display structure
|
||||
*/
|
||||
|
||||
Display *getXDisplay()
|
||||
{
|
||||
return display_;
|
||||
}
|
||||
|
||||
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
|
||||
XIM getX11_XIM()
|
||||
{
|
||||
return xim_;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Use this function instead of #GHOST_System::getMilliSeconds,
|
||||
* passing in the time-stamp from X to input to get the event
|
||||
* time-stamp with an offset applied to make it compatible with `getMilliSeconds`.
|
||||
*/
|
||||
uint64_t ms_from_input_time(const Time timestamp) const;
|
||||
|
||||
/** Helped function for get data from the clipboard. */
|
||||
void getClipboard_xcout(const XEvent *evt,
|
||||
Atom sel,
|
||||
Atom target,
|
||||
unsigned char **txt,
|
||||
unsigned long *len,
|
||||
unsigned int *context) const;
|
||||
|
||||
/**
|
||||
* Returns unsigned char from CUT_BUFFER0
|
||||
* \param selection: Get selection, X11 only feature.
|
||||
* \return Returns the Clipboard indicated by Flag.
|
||||
*/
|
||||
char *getClipboard(bool selection) const override;
|
||||
|
||||
/**
|
||||
* Puts buffer to system clipboard
|
||||
* \param buffer: The buffer to copy to the clipboard.
|
||||
* \param selection: Set the selection into the clipboard, X11 only feature.
|
||||
*/
|
||||
void putClipboard(const char *buffer, bool selection) const override;
|
||||
|
||||
/**
|
||||
* Show a system message box
|
||||
* \param title: The title of the message box.
|
||||
* \param message: The message to display.
|
||||
* \param help_label: Help button label.
|
||||
* \param continue_label: Continue button label.
|
||||
* \param link: An optional hyperlink.
|
||||
* \param dialog_options: Options how to display the message.
|
||||
*/
|
||||
GHOST_TSuccess showMessageBox(const char *title,
|
||||
const char *message,
|
||||
const char *help_label,
|
||||
const char *continue_label,
|
||||
const char *link,
|
||||
GHOST_DialogOptions dialog_options) const override;
|
||||
#ifdef WITH_XDND
|
||||
/**
|
||||
* Creates a drag & drop event and pushes it immediately onto the event queue.
|
||||
* Called by #GHOST_DropTargetX11 class.
|
||||
* \param eventType: The type of drag & drop event.
|
||||
* \param draggedObjectType: The type object concerned.
|
||||
* (currently array of file names, string, ?bitmap)
|
||||
* \param window: The window on which the event occurred.
|
||||
* \param mouseX: x mouse coordinate (in window coordinates).
|
||||
* \param mouseY: y mouse coordinate.
|
||||
* \return Indication whether the event was handled.
|
||||
*/
|
||||
static GHOST_TSuccess pushDragDropEvent(GHOST_TEventType eventType,
|
||||
GHOST_TDragnDropTypes draggedObjectType,
|
||||
GHOST_IWindow *window,
|
||||
int mouseX,
|
||||
int mouseY,
|
||||
void *data);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \see GHOST_ISystem
|
||||
*/
|
||||
bool setConsoleWindowState(GHOST_TConsoleWindowState /*action*/) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WITH_X11_XINPUT
|
||||
typedef struct GHOST_TabletX11 {
|
||||
GHOST_TTabletMode mode;
|
||||
XDevice *Device;
|
||||
XID ID;
|
||||
|
||||
int MotionEvent;
|
||||
int ProxInEvent;
|
||||
int ProxOutEvent;
|
||||
int PressEvent;
|
||||
|
||||
int PressureLevels;
|
||||
int XtiltLevels, YtiltLevels;
|
||||
} GHOST_TabletX11;
|
||||
|
||||
std::vector<GHOST_TabletX11> &GetXTablets()
|
||||
{
|
||||
return xtablets_;
|
||||
}
|
||||
#endif // WITH_X11_XINPUT
|
||||
|
||||
struct {
|
||||
/**
|
||||
* Atom used for ICCCM, WM-spec and Motif.
|
||||
* We only need get this atom at the start, it's relative
|
||||
* to the display not the window and are public for every
|
||||
* window that need it.
|
||||
*/
|
||||
Atom WM_STATE;
|
||||
Atom WM_CHANGE_STATE;
|
||||
Atom _NET_WM_STATE;
|
||||
Atom _NET_WM_STATE_MAXIMIZED_HORZ;
|
||||
Atom _NET_WM_STATE_MAXIMIZED_VERT;
|
||||
Atom _NET_WM_STATE_FULLSCREEN;
|
||||
Atom _MOTIF_WM_HINTS;
|
||||
Atom WM_TAKE_FOCUS;
|
||||
Atom WM_PROTOCOLS;
|
||||
Atom WM_DELETE_WINDOW;
|
||||
|
||||
/* Atoms for Selection, copy & paste. */
|
||||
Atom TARGETS;
|
||||
Atom STRING;
|
||||
Atom C_STRING;
|
||||
Atom COMPOUND_TEXT;
|
||||
Atom TEXT;
|
||||
Atom CLIPBOARD;
|
||||
Atom PRIMARY;
|
||||
Atom XCLIP_OUT;
|
||||
Atom INCR;
|
||||
Atom UTF8_STRING;
|
||||
#ifdef WITH_X11_XINPUT
|
||||
Atom TABLET;
|
||||
#endif
|
||||
} atom_;
|
||||
|
||||
#ifdef WITH_X11_XINPUT
|
||||
XExtensionVersion xinput_version_;
|
||||
#endif
|
||||
|
||||
private:
|
||||
Display *display_;
|
||||
|
||||
/** Use for scan-code look-ups. */
|
||||
XkbDescRec *xkb_descr_;
|
||||
|
||||
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
|
||||
XIM xim_;
|
||||
#endif
|
||||
|
||||
#ifdef WITH_X11_XINPUT
|
||||
/* Tablet devices */
|
||||
std::vector<GHOST_TabletX11> xtablets_;
|
||||
#endif
|
||||
|
||||
/** The vector of windows that need to be updated. */
|
||||
std::vector<GHOST_WindowX11 *> dirty_windows_;
|
||||
|
||||
/** A vector of keyboard key masks. */
|
||||
char keyboard_vector_[32];
|
||||
|
||||
/**
|
||||
* To prevent multiple warp, we store the time of the last warp event
|
||||
* and stop accumulating all events generated before that.
|
||||
*/
|
||||
Time last_warp_x_;
|
||||
Time last_warp_y_;
|
||||
|
||||
/* Detect auto-repeat glitch. */
|
||||
unsigned int last_release_keycode_;
|
||||
Time last_release_time_;
|
||||
|
||||
#ifdef WITH_X11_XINPUT
|
||||
/** Last key press or release, to apply to XIM generated events. */
|
||||
Time last_key_time_;
|
||||
#endif
|
||||
|
||||
uint keycode_last_repeat_key_;
|
||||
|
||||
/**
|
||||
* Return the ghost window associated with the
|
||||
* X11 window xwind
|
||||
*/
|
||||
|
||||
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
|
||||
bool openX11_IM();
|
||||
#endif
|
||||
|
||||
#ifdef WITH_X11_XINPUT
|
||||
void clearXInputDevices();
|
||||
void refreshXInputDevices();
|
||||
#endif
|
||||
|
||||
GHOST_WindowX11 *findGhostWindow(Window xwind) const;
|
||||
|
||||
void processEvent(XEvent *xe);
|
||||
|
||||
Time lastEventTime(Time default_time);
|
||||
|
||||
bool generateWindowExposeEvents();
|
||||
};
|
||||
116
blender-5.2.0/intern/ghost/intern/GHOST_TaskbarWin32.hh
Normal file
116
blender-5.2.0/intern/ghost/intern/GHOST_TaskbarWin32.hh
Normal file
@@ -0,0 +1,116 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifndef WIN32
|
||||
# error WIN32 only!
|
||||
#endif // WIN32
|
||||
|
||||
#include <shlobj.h>
|
||||
#include <windows.h>
|
||||
|
||||
// ITaskbarList, ITaskbarList2 and ITaskbarList3 might be missing, present here in that case.
|
||||
// Note, ITaskbarList3 is supported only since Windows 7, though. Check for that is done in
|
||||
// GHOST_WindowWin32
|
||||
#ifndef __ITaskbarList_INTERFACE_DEFINED__
|
||||
# define __ITaskbarList_INTERFACE_DEFINED__
|
||||
extern "C" {
|
||||
const GUID CLSID_TaskbarList = {
|
||||
0x56FDF344, 0xFD6D, 0x11D0, {0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}};
|
||||
const GUID IID_ITaskbarList = {
|
||||
0x56FDF342, 0xFD6D, 0x11D0, {0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}};
|
||||
}
|
||||
|
||||
class ITaskbarList : public IUnknown {
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE HrInit(void) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE AddTab(HWND hwnd) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE DeleteTab(HWND hwnd) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE ActivateTab(HWND hwnd) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE SetActiveAlt(HWND hwnd) = 0;
|
||||
};
|
||||
#endif /* ITaskbarList */
|
||||
|
||||
#ifndef __ITaskbarList2_INTERFACE_DEFINED__
|
||||
# define __ITaskbarList2_INTERFACE_DEFINED__
|
||||
extern "C" {
|
||||
const GUID IID_ITaskbarList2 = {
|
||||
0x602D4995, 0xB13A, 0x429b, {0xA6, 0x6E, 0x19, 0x35, 0xE4, 0x4F, 0x43, 0x17}};
|
||||
}
|
||||
class ITaskbarList2 : public ITaskbarList {
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE MarkFullscreenWindow(HWND hwnd, BOOL fFullscreen) = 0;
|
||||
};
|
||||
#endif /* ITaskbarList2 */
|
||||
|
||||
#ifndef __ITaskbarList3_INTERFACE_DEFINED__
|
||||
# define __ITaskbarList3_INTERFACE_DEFINED__
|
||||
typedef enum THUMBBUTTONFLAGS {
|
||||
THBF_ENABLED = 0,
|
||||
THBF_DISABLED = 0x1,
|
||||
THBF_DISMISSONCLICK = 0x2,
|
||||
THBF_NOBACKGROUND = 0x4,
|
||||
THBF_HIDDEN = 0x8,
|
||||
THBF_NONINTERACTIVE = 0x10,
|
||||
} THUMBBUTTONFLAGS;
|
||||
|
||||
typedef enum THUMBBUTTONMASK {
|
||||
THB_BITMAP = 0x1,
|
||||
THB_ICON = 0x2,
|
||||
THB_TOOLTIP = 0x4,
|
||||
THB_FLAGS = 0x8,
|
||||
} THUMBBUTTONMASK;
|
||||
|
||||
typedef struct THUMBBUTTON {
|
||||
THUMBBUTTONMASK dwMask;
|
||||
UINT iId;
|
||||
UINT iBitmap;
|
||||
HICON hIcon;
|
||||
WCHAR szTip[260];
|
||||
THUMBBUTTONFLAGS dwFlags;
|
||||
} THUMBBUTTON;
|
||||
|
||||
typedef enum TBPFLAG {
|
||||
TBPF_NOPROGRESS = 0,
|
||||
TBPF_INDETERMINATE = 0x1,
|
||||
TBPF_NORMAL = 0x2,
|
||||
TBPF_ERROR = 0x4,
|
||||
TBPF_PAUSED = 0x8,
|
||||
} TBPFLAG;
|
||||
|
||||
# define THBN_CLICKED 0x1800
|
||||
|
||||
extern "C" {
|
||||
const GUID IID_ITaskList3 = {
|
||||
0xEA1AFB91, 0x9E28, 0x4B86, {0x90, 0xE9, 0x9E, 0x9F, 0x8A, 0x5E, 0xEF, 0xAF}};
|
||||
}
|
||||
|
||||
class ITaskbarList3 : public ITaskbarList2 {
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE SetProgressValue(HWND hwnd,
|
||||
ULONGLONG ullCompleted,
|
||||
ULONGLONG ullTotal) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE SetProgressState(HWND hwnd, TBPFLAG tbpFlags) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE RegisterTab(HWND hwndTab, HWND hwndMDI) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE UnregisterTab(HWND hwndTab) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE SetTabOrder(HWND hwndTab, HWND hwndInsertBefore) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE SetTabActive(HWND hwndTab, HWND hwndMDI, DWORD dwReserved) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE ThumbBarAddButtons(HWND hwnd,
|
||||
UINT cButtons,
|
||||
THUMBBUTTON *pButton) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE ThumbBarUpdateButtons(HWND hwnd,
|
||||
UINT cButtons,
|
||||
THUMBBUTTON *pButton) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE ThumbBarSetImageList(HWND hwnd, HIMAGELIST himl) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE SetOverlayIcon(HWND hwnd,
|
||||
HICON hIcon,
|
||||
LPCWSTR pszDescription) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE SetThumbnailTooltip(HWND hwnd, LPCWSTR pszTip) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE SetThumbnailClip(HWND hwnd, RECT *prcClip) = 0;
|
||||
};
|
||||
#endif /* ITaskbarList3 */
|
||||
123
blender-5.2.0/intern/ghost/intern/GHOST_TimerManager.cc
Normal file
123
blender-5.2.0/intern/ghost/intern/GHOST_TimerManager.cc
Normal file
@@ -0,0 +1,123 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright (C) 2001 NaN Technologies B.V.
|
||||
*/
|
||||
|
||||
#include "GHOST_TimerManager.hh"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "GHOST_TimerTask.hh"
|
||||
|
||||
GHOST_TimerManager::GHOST_TimerManager() = default;
|
||||
|
||||
GHOST_TimerManager::~GHOST_TimerManager()
|
||||
{
|
||||
disposeTimers();
|
||||
}
|
||||
|
||||
uint32_t GHOST_TimerManager::getNumTimers()
|
||||
{
|
||||
return uint32_t(timers_.size());
|
||||
}
|
||||
|
||||
bool GHOST_TimerManager::getTimerFound(GHOST_TimerTask *timer)
|
||||
{
|
||||
TTimerVector::const_iterator iter = std::find(timers_.begin(), timers_.end(), timer);
|
||||
return iter != timers_.end();
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_TimerManager::addTimer(GHOST_TimerTask *timer)
|
||||
{
|
||||
GHOST_TSuccess success;
|
||||
if (!getTimerFound(timer)) {
|
||||
/* Add the timer task. */
|
||||
timers_.push_back(timer);
|
||||
success = GHOST_kSuccess;
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_TimerManager::removeTimer(GHOST_TimerTask *timer)
|
||||
{
|
||||
GHOST_TSuccess success;
|
||||
TTimerVector::iterator iter = std::find(timers_.begin(), timers_.end(), timer);
|
||||
if (iter != timers_.end()) {
|
||||
/* Remove the timer task. */
|
||||
timers_.erase(iter);
|
||||
delete timer;
|
||||
success = GHOST_kSuccess;
|
||||
}
|
||||
else {
|
||||
success = GHOST_kFailure;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
uint64_t GHOST_TimerManager::nextFireTime()
|
||||
{
|
||||
uint64_t smallest = GHOST_kFireTimeNever;
|
||||
TTimerVector::iterator iter;
|
||||
|
||||
for (iter = timers_.begin(); iter != timers_.end(); ++iter) {
|
||||
const uint64_t next = (*iter)->getNext();
|
||||
smallest = std::min(next, smallest);
|
||||
}
|
||||
|
||||
return smallest;
|
||||
}
|
||||
|
||||
bool GHOST_TimerManager::fireTimers(uint64_t time)
|
||||
{
|
||||
TTimerVector::iterator iter;
|
||||
bool anyProcessed = false;
|
||||
|
||||
for (iter = timers_.begin(); iter != timers_.end(); ++iter) {
|
||||
if (fireTimer(time, *iter)) {
|
||||
anyProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return anyProcessed;
|
||||
}
|
||||
|
||||
bool GHOST_TimerManager::fireTimer(uint64_t time, GHOST_TimerTask *task)
|
||||
{
|
||||
uint64_t next = task->getNext();
|
||||
|
||||
/* Check if the timer should be fired. */
|
||||
if (time > next) {
|
||||
/* Fire the timer. */
|
||||
GHOST_TimerProcPtr timer_proc = task->getTimerProc();
|
||||
uint64_t start = task->getStart();
|
||||
timer_proc(task, time - start);
|
||||
|
||||
/* Update the time at which we will fire it again. */
|
||||
uint64_t interval = task->getInterval();
|
||||
uint64_t numCalls = (next - start) / interval;
|
||||
numCalls++;
|
||||
next = start + numCalls * interval;
|
||||
task->setNext(next);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GHOST_TimerManager::disposeTimers()
|
||||
{
|
||||
while (timers_.empty() == false) {
|
||||
delete timers_[0];
|
||||
timers_.erase(timers_.begin());
|
||||
}
|
||||
}
|
||||
96
blender-5.2.0/intern/ghost/intern/GHOST_TimerManager.hh
Normal file
96
blender-5.2.0/intern/ghost/intern/GHOST_TimerManager.hh
Normal file
@@ -0,0 +1,96 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_TimerManager class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
class GHOST_TimerTask;
|
||||
|
||||
/**
|
||||
* Manages a list of timer tasks.
|
||||
* Timer tasks added are owned by the manager.
|
||||
* Don't delete timer task objects.
|
||||
*/
|
||||
class GHOST_TimerManager {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
GHOST_TimerManager();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~GHOST_TimerManager();
|
||||
|
||||
/**
|
||||
* Returns the number of timer tasks.
|
||||
* \return The number of events on the stack.
|
||||
*/
|
||||
uint32_t getNumTimers();
|
||||
|
||||
/**
|
||||
* Returns whether this timer task ins in our list.
|
||||
* \return Indication of presence.
|
||||
*/
|
||||
bool getTimerFound(GHOST_TimerTask *timer);
|
||||
|
||||
/**
|
||||
* Adds a timer task to the list.
|
||||
* It is only added when it not already present in the list.
|
||||
* \param timer: The timer task added to the list.
|
||||
* \return Indication as to whether addition has succeeded.
|
||||
*/
|
||||
GHOST_TSuccess addTimer(GHOST_TimerTask *timer);
|
||||
|
||||
/**
|
||||
* Removes a timer task from the list.
|
||||
* It is only removed when it is found in the list.
|
||||
* \param timer: The timer task to be removed from the list.
|
||||
* \return Indication as to whether removal has succeeded.
|
||||
*/
|
||||
GHOST_TSuccess removeTimer(GHOST_TimerTask *timer);
|
||||
|
||||
/**
|
||||
* Finds the soonest time the next timer would fire.
|
||||
* \return The soonest time the next timer would fire,
|
||||
* or GHOST_kFireTimeNever if no timers exist.
|
||||
*/
|
||||
uint64_t nextFireTime();
|
||||
|
||||
/**
|
||||
* Checks all timer tasks to see if they are expired and fires them if needed.
|
||||
* \param time: The current time.
|
||||
* \return True if any timers were fired.
|
||||
*/
|
||||
bool fireTimers(uint64_t time);
|
||||
|
||||
/**
|
||||
* Checks this timer task to see if they are expired and fires them if needed.
|
||||
* \param time: The current time.
|
||||
* \param task: The timer task to check and optionally fire.
|
||||
* \return True if the timer fired.
|
||||
*/
|
||||
bool fireTimer(uint64_t time, GHOST_TimerTask *task);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Deletes all timers.
|
||||
*/
|
||||
void disposeTimers();
|
||||
|
||||
using TTimerVector = std::vector<GHOST_TimerTask *>;
|
||||
/** The list with event consumers. */
|
||||
TTimerVector timers_;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_TimerManager")
|
||||
};
|
||||
152
blender-5.2.0/intern/ghost/intern/GHOST_TimerTask.hh
Normal file
152
blender-5.2.0/intern/ghost/intern/GHOST_TimerTask.hh
Normal file
@@ -0,0 +1,152 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST_TimerTask class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GHOST_ITimerTask.hh"
|
||||
|
||||
/**
|
||||
* Implementation of a timer task.
|
||||
*/
|
||||
class GHOST_TimerTask : public GHOST_ITimerTask {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* \param start: The timer start time.
|
||||
* \param interval: The interval between calls to the #timer_proc.
|
||||
* \param timer_proc: The callback invoked when the interval expires.
|
||||
* \param user_data: The timer user data.
|
||||
*/
|
||||
GHOST_TimerTask(uint64_t start,
|
||||
uint64_t interval,
|
||||
GHOST_TimerProcPtr timer_proc,
|
||||
GHOST_TUserDataPtr user_data = nullptr)
|
||||
: start_(start),
|
||||
interval_(interval),
|
||||
next_(start),
|
||||
timer_proc_(timer_proc),
|
||||
user_data_(user_data)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timer start time.
|
||||
* \return The timer start time.
|
||||
*/
|
||||
uint64_t getStart() const
|
||||
{
|
||||
return start_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the timer start time.
|
||||
* \param start: The timer start time.
|
||||
*/
|
||||
void setStart(uint64_t start)
|
||||
{
|
||||
start_ = start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timer interval.
|
||||
* \return The timer interval.
|
||||
*/
|
||||
uint64_t getInterval() const
|
||||
{
|
||||
return interval_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the timer interval.
|
||||
* \param interval: The timer interval.
|
||||
*/
|
||||
void setInterval(uint64_t interval)
|
||||
{
|
||||
interval_ = interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time the timer_proc will be called.
|
||||
* \return The time the timer_proc will be called.
|
||||
*/
|
||||
uint64_t getNext() const
|
||||
{
|
||||
return next_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the time the timer_proc will be called.
|
||||
* \param next: The time the timer_proc will be called.
|
||||
*/
|
||||
void setNext(uint64_t next)
|
||||
{
|
||||
next_ = next;
|
||||
}
|
||||
|
||||
/** \copydoc #GHOST_ITimerTask::getTimerProc */
|
||||
GHOST_TimerProcPtr getTimerProc() const override
|
||||
{
|
||||
return timer_proc_;
|
||||
}
|
||||
|
||||
/** \copydoc #GHOST_ITimerTask::setTimerProc */
|
||||
void setTimerProc(const GHOST_TimerProcPtr timer_proc) override
|
||||
{
|
||||
timer_proc_ = timer_proc;
|
||||
}
|
||||
|
||||
/** \copydoc #GHOST_ITimerTask::getUserData */
|
||||
GHOST_TUserDataPtr getUserData() const override
|
||||
{
|
||||
return user_data_;
|
||||
}
|
||||
|
||||
/** \copydoc #GHOST_ITimerTask::setUserData */
|
||||
void setUserData(const GHOST_TUserDataPtr user_data) override
|
||||
{
|
||||
user_data_ = user_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the auxiliary storage room.
|
||||
* \return The auxiliary storage room.
|
||||
*/
|
||||
uint32_t getAuxData() const
|
||||
{
|
||||
return aux_data_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the auxiliary storage room.
|
||||
* \param auxData: The auxiliary storage room.
|
||||
*/
|
||||
void setAuxData(uint32_t auxData)
|
||||
{
|
||||
aux_data_ = auxData;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** The time the timer task was started. */
|
||||
uint64_t start_;
|
||||
|
||||
/** The interval between calls. */
|
||||
uint64_t interval_;
|
||||
|
||||
/** The time the timer_proc will be called. */
|
||||
uint64_t next_;
|
||||
|
||||
/** The callback invoked when the timer expires. */
|
||||
GHOST_TimerProcPtr timer_proc_;
|
||||
|
||||
/** The timer task user data. */
|
||||
GHOST_TUserDataPtr user_data_;
|
||||
|
||||
/** Auxiliary storage room. */
|
||||
uint32_t aux_data_ = 0;
|
||||
};
|
||||
353
blender-5.2.0/intern/ghost/intern/GHOST_TrackpadWin32.cc
Normal file
353
blender-5.2.0/intern/ghost/intern/GHOST_TrackpadWin32.cc
Normal file
@@ -0,0 +1,353 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "GHOST_Debug.hh"
|
||||
#include "GHOST_TrackpadWin32.hh"
|
||||
|
||||
GHOST_DirectManipulationHelper::GHOST_DirectManipulationHelper(
|
||||
HWND hWnd,
|
||||
Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager,
|
||||
Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager,
|
||||
Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport,
|
||||
Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
|
||||
directManipulationEventHandler,
|
||||
DWORD directManipulationViewportHandlerCookie,
|
||||
bool isScrollDirectionInverted)
|
||||
: h_wnd_(hWnd),
|
||||
scroll_direction_reg_key_(nullptr),
|
||||
scroll_direction_change_event_(nullptr),
|
||||
direct_manipulation_manager_(directManipulationManager),
|
||||
direct_manipulation_update_manager_(directManipulationUpdateManager),
|
||||
direct_manipulation_viewport_(directManipulationViewport),
|
||||
direct_manipulation_event_handler_(directManipulationEventHandler),
|
||||
direct_manipulation_viewport_handler_cookie_(directManipulationViewportHandlerCookie),
|
||||
is_scroll_direction_inverted_(isScrollDirectionInverted)
|
||||
{
|
||||
}
|
||||
|
||||
GHOST_DirectManipulationHelper *GHOST_DirectManipulationHelper::create(HWND hWnd, uint16_t dpi)
|
||||
{
|
||||
#define DM_CHECK_RESULT_AND_EXIT_EARLY(hr, failMessage) \
|
||||
{ \
|
||||
if (!SUCCEEDED(hr)) { \
|
||||
GHOST_PRINT(failMessage); \
|
||||
return nullptr; \
|
||||
} \
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager;
|
||||
HRESULT hr = ::CoCreateInstance(CLSID_DirectManipulationManager,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&directManipulationManager));
|
||||
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager create failed\n");
|
||||
|
||||
/* Since we want to use fake viewport, we need to send fake updates to UpdateManager. */
|
||||
Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager;
|
||||
hr = directManipulationManager->GetUpdateManager(IID_PPV_ARGS(&directManipulationUpdateManager));
|
||||
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Get UpdateManager failed\n");
|
||||
|
||||
Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport;
|
||||
hr = directManipulationManager->CreateViewport(
|
||||
nullptr, hWnd, IID_PPV_ARGS(&directManipulationViewport));
|
||||
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport create failed\n");
|
||||
|
||||
DIRECTMANIPULATION_CONFIGURATION configuration =
|
||||
DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
|
||||
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
|
||||
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
|
||||
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA |
|
||||
DIRECTMANIPULATION_CONFIGURATION_SCALING;
|
||||
|
||||
hr = directManipulationViewport->ActivateConfiguration(configuration);
|
||||
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ActivateConfiguration failed\n");
|
||||
|
||||
/* Since we are using fake viewport and only want to use Direct Manipulation for touchpad, we
|
||||
* need to use MANUALUPDATE option. */
|
||||
hr = directManipulationViewport->SetViewportOptions(
|
||||
DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE);
|
||||
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ViewportOptions failed\n");
|
||||
|
||||
/* We receive Direct Manipulation transform updates in IDirectManipulationViewportEventHandler
|
||||
* callbacks. */
|
||||
Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
|
||||
directManipulationEventHandler =
|
||||
Microsoft::WRL::Make<GHOST_DirectManipulationViewportEventHandler>(dpi);
|
||||
DWORD directManipulationViewportHandlerCookie;
|
||||
directManipulationViewport->AddEventHandler(
|
||||
hWnd, directManipulationEventHandler.Get(), &directManipulationViewportHandlerCookie);
|
||||
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport add EventHandler failed\n");
|
||||
|
||||
/* Set default rect for viewport before activating. */
|
||||
RECT rect = {0, 0, 10000, 10000};
|
||||
hr = directManipulationViewport->SetViewportRect(&rect);
|
||||
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set rect failed\n");
|
||||
|
||||
hr = directManipulationManager->Activate(hWnd);
|
||||
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager activate failed\n");
|
||||
|
||||
hr = directManipulationViewport->Enable();
|
||||
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport enable failed\n");
|
||||
|
||||
directManipulationEventHandler->resetViewport(directManipulationViewport.Get());
|
||||
|
||||
bool isScrollDirectionInverted = getScrollDirectionFromReg();
|
||||
|
||||
auto instance = new GHOST_DirectManipulationHelper(hWnd,
|
||||
directManipulationManager,
|
||||
directManipulationUpdateManager,
|
||||
directManipulationViewport,
|
||||
directManipulationEventHandler,
|
||||
directManipulationViewportHandlerCookie,
|
||||
isScrollDirectionInverted);
|
||||
|
||||
instance->registerScrollDirectionChangeListener();
|
||||
|
||||
return instance;
|
||||
|
||||
#undef DM_CHECK_RESULT_AND_EXIT_EARLY
|
||||
}
|
||||
|
||||
bool GHOST_DirectManipulationHelper::getScrollDirectionFromReg()
|
||||
{
|
||||
DWORD scrollDirectionRegValue = 0;
|
||||
DWORD pcbData = sizeof(scrollDirectionRegValue);
|
||||
HRESULT hr = HRESULT_FROM_WIN32(
|
||||
RegGetValueW(HKEY_CURRENT_USER,
|
||||
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\",
|
||||
L"ScrollDirection",
|
||||
RRF_RT_REG_DWORD,
|
||||
nullptr,
|
||||
&scrollDirectionRegValue,
|
||||
&pcbData));
|
||||
if (!SUCCEEDED(hr)) {
|
||||
GHOST_PRINT("Failed to get scroll direction from registry\n");
|
||||
/* This is the default value for scroll direction, not a failure/success indicator. */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Return false for natural scrolling direction (upward motion results in content scrolling
|
||||
* downward), and true for reversed direction. */
|
||||
return scrollDirectionRegValue == 0;
|
||||
}
|
||||
|
||||
void GHOST_DirectManipulationHelper::registerScrollDirectionChangeListener()
|
||||
{
|
||||
|
||||
if (!scroll_direction_reg_key_) {
|
||||
HRESULT hr = HRESULT_FROM_WIN32(
|
||||
RegOpenKeyExW(HKEY_CURRENT_USER,
|
||||
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\",
|
||||
0,
|
||||
KEY_NOTIFY,
|
||||
&scroll_direction_reg_key_));
|
||||
if (!SUCCEEDED(hr)) {
|
||||
GHOST_PRINT("Failed to open scroll direction registry key\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!scroll_direction_change_event_) {
|
||||
scroll_direction_change_event_ = CreateEventW(nullptr, true, false, nullptr);
|
||||
}
|
||||
else {
|
||||
ResetEvent(scroll_direction_change_event_);
|
||||
}
|
||||
HRESULT hr = HRESULT_FROM_WIN32(RegNotifyChangeKeyValue(scroll_direction_reg_key_,
|
||||
true,
|
||||
REG_NOTIFY_CHANGE_LAST_SET,
|
||||
scroll_direction_change_event_,
|
||||
true));
|
||||
if (!SUCCEEDED(hr)) {
|
||||
GHOST_PRINT("Failed to register scroll direction change listener\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_DirectManipulationHelper::onPointerHitTest(UINT32 pointerId)
|
||||
{
|
||||
[[maybe_unused]] HRESULT hr = direct_manipulation_viewport_->SetContact(pointerId);
|
||||
GHOST_ASSERT(SUCCEEDED(hr), "Viewport set contact failed\n");
|
||||
|
||||
if (WaitForSingleObject(scroll_direction_change_event_, 0) == WAIT_OBJECT_0) {
|
||||
is_scroll_direction_inverted_ = getScrollDirectionFromReg();
|
||||
registerScrollDirectionChangeListener();
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_DirectManipulationHelper::update()
|
||||
{
|
||||
if (direct_manipulation_event_handler_->dm_status == DIRECTMANIPULATION_RUNNING ||
|
||||
direct_manipulation_event_handler_->dm_status == DIRECTMANIPULATION_INERTIA)
|
||||
{
|
||||
[[maybe_unused]] HRESULT hr = direct_manipulation_update_manager_->Update(nullptr);
|
||||
GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationUpdateManager update failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_DirectManipulationHelper::setDPI(uint16_t dpi)
|
||||
{
|
||||
direct_manipulation_event_handler_->dpi = dpi;
|
||||
}
|
||||
|
||||
GHOST_TTrackpadInfo GHOST_DirectManipulationHelper::getTrackpadInfo()
|
||||
{
|
||||
GHOST_TTrackpadInfo result = direct_manipulation_event_handler_->accumulated_values;
|
||||
result.isScrollDirectionInverted = is_scroll_direction_inverted_;
|
||||
|
||||
direct_manipulation_event_handler_->accumulated_values = {0, 0, 0};
|
||||
return result;
|
||||
}
|
||||
|
||||
GHOST_DirectManipulationHelper::~GHOST_DirectManipulationHelper()
|
||||
{
|
||||
[[maybe_unused]] HRESULT hr;
|
||||
hr = direct_manipulation_viewport_->Stop();
|
||||
GHOST_ASSERT(SUCCEEDED(hr), "Viewport stop failed\n");
|
||||
|
||||
hr = direct_manipulation_viewport_->RemoveEventHandler(
|
||||
direct_manipulation_viewport_handler_cookie_);
|
||||
GHOST_ASSERT(SUCCEEDED(hr), "Viewport remove event handler failed\n");
|
||||
|
||||
hr = direct_manipulation_viewport_->Abandon();
|
||||
GHOST_ASSERT(SUCCEEDED(hr), "Viewport abandon failed\n");
|
||||
|
||||
hr = direct_manipulation_manager_->Deactivate(h_wnd_);
|
||||
GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationManager deactivate failed\n");
|
||||
|
||||
if (scroll_direction_change_event_) {
|
||||
CloseHandle(scroll_direction_change_event_);
|
||||
scroll_direction_change_event_ = nullptr;
|
||||
}
|
||||
if (scroll_direction_reg_key_) {
|
||||
RegCloseKey(scroll_direction_reg_key_);
|
||||
scroll_direction_reg_key_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_DirectManipulationViewportEventHandler::GHOST_DirectManipulationViewportEventHandler(
|
||||
uint16_t dpi)
|
||||
: accumulated_values({0, 0, 0}), dpi(dpi), dm_status(DIRECTMANIPULATION_BUILDING)
|
||||
{
|
||||
}
|
||||
|
||||
void GHOST_DirectManipulationViewportEventHandler::resetViewport(
|
||||
IDirectManipulationViewport *viewport)
|
||||
{
|
||||
if (gesture_state != GESTURE_NONE) {
|
||||
[[maybe_unused]] HRESULT hr = viewport->ZoomToRect(0.0f, 0.0f, 10000.0f, 10000.0f, FALSE);
|
||||
GHOST_ASSERT(SUCCEEDED(hr), "Viewport reset failed\n");
|
||||
}
|
||||
|
||||
gesture_state = GESTURE_NONE;
|
||||
|
||||
last_scale = PINCH_SCALE_FACTOR;
|
||||
last_x = 0.0f;
|
||||
last_y = 0.0f;
|
||||
}
|
||||
|
||||
HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportStatusChanged(
|
||||
IDirectManipulationViewport *viewport,
|
||||
DIRECTMANIPULATION_STATUS current,
|
||||
DIRECTMANIPULATION_STATUS previous)
|
||||
{
|
||||
dm_status = current;
|
||||
|
||||
if (current == previous) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (previous == DIRECTMANIPULATION_ENABLED || current == DIRECTMANIPULATION_READY ||
|
||||
(previous == DIRECTMANIPULATION_INERTIA && current != DIRECTMANIPULATION_INERTIA))
|
||||
{
|
||||
resetViewport(viewport);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportUpdated(
|
||||
IDirectManipulationViewport * /*viewport*/)
|
||||
{
|
||||
/* Nothing to do here. */
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT GHOST_DirectManipulationViewportEventHandler::OnContentUpdated(
|
||||
IDirectManipulationViewport * /*viewport*/, IDirectManipulationContent *content)
|
||||
{
|
||||
float transform[6];
|
||||
HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform));
|
||||
GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationContent get transform failed\n");
|
||||
|
||||
const float device_scale_factor = dpi / 96.0f;
|
||||
|
||||
const float scale = transform[0] * PINCH_SCALE_FACTOR;
|
||||
const float x = transform[4] / device_scale_factor;
|
||||
const float y = transform[5] / device_scale_factor;
|
||||
|
||||
const float EPS = 3e-5;
|
||||
|
||||
/* Ignore repeating or incorrect input. */
|
||||
if ((fabs(scale - last_scale) <= EPS && fabs(x - last_x) <= EPS && fabs(y - last_y) <= EPS) ||
|
||||
scale == 0.0f)
|
||||
{
|
||||
GHOST_PRINT("Ignoring touchpad input\n");
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Assume that every gesture is a pan in the beginning.
|
||||
* If it's a pinch, the gesture will be changed below. */
|
||||
if (gesture_state == GESTURE_NONE) {
|
||||
gesture_state = GESTURE_PAN;
|
||||
}
|
||||
|
||||
/* DM doesn't always immediately recognize pinch gestures,
|
||||
* so allow transition from pan to pinch. */
|
||||
if (gesture_state == GESTURE_PAN) {
|
||||
if (fabs(scale - PINCH_SCALE_FACTOR) > EPS) {
|
||||
gesture_state = GESTURE_PINCH;
|
||||
}
|
||||
}
|
||||
|
||||
/* This state machine is used here because:
|
||||
* 1. Pinch and pan gestures must be differentiated and cannot be processed at the same time
|
||||
* because XY transform values become nonsensical during pinch gesture.
|
||||
* 2. GHOST requires delta values for events while DM provides transformation matrix of the
|
||||
* current gesture.
|
||||
* 3. GHOST events accept integer values while DM values are non-integer.
|
||||
* Truncated fractional parts are accumulated and accounted for in following updates.
|
||||
*/
|
||||
switch (gesture_state) {
|
||||
case GESTURE_PINCH: {
|
||||
int32_t dscale = roundf(scale - last_scale);
|
||||
|
||||
last_scale += dscale;
|
||||
|
||||
accumulated_values.scale += dscale;
|
||||
break;
|
||||
}
|
||||
case GESTURE_PAN: {
|
||||
int32_t dx = roundf(x - last_x);
|
||||
int32_t dy = roundf(y - last_y);
|
||||
|
||||
last_x += dx;
|
||||
last_y += dy;
|
||||
|
||||
accumulated_values.x += dx;
|
||||
accumulated_values.y += dy;
|
||||
break;
|
||||
}
|
||||
case GESTURE_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
140
blender-5.2.0/intern/ghost/intern/GHOST_TrackpadWin32.hh
Normal file
140
blender-5.2.0/intern/ghost/intern/GHOST_TrackpadWin32.hh
Normal file
@@ -0,0 +1,140 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
* Declaration of GHOST DirectManipulation classes.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef WIN32
|
||||
# error WIN32 only!
|
||||
#endif // WIN32
|
||||
|
||||
#include "GHOST_Types.hh"
|
||||
|
||||
#include <directmanipulation.h>
|
||||
#include <wrl.h>
|
||||
|
||||
#define PINCH_SCALE_FACTOR 125.0f
|
||||
|
||||
typedef struct {
|
||||
int32_t x, y, scale;
|
||||
bool isScrollDirectionInverted;
|
||||
} GHOST_TTrackpadInfo;
|
||||
|
||||
class GHOST_DirectManipulationHelper;
|
||||
|
||||
class GHOST_DirectManipulationViewportEventHandler
|
||||
: public Microsoft::WRL::RuntimeClass<
|
||||
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>,
|
||||
Microsoft::WRL::Implements<
|
||||
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>,
|
||||
Microsoft::WRL::FtmBase,
|
||||
IDirectManipulationViewportEventHandler>> {
|
||||
public:
|
||||
GHOST_DirectManipulationViewportEventHandler(uint16_t dpi);
|
||||
|
||||
/**
|
||||
* Resets viewport and tracked touchpad state.
|
||||
*/
|
||||
void resetViewport(IDirectManipulationViewport *viewport);
|
||||
|
||||
/** DirectManipulation callbacks. */
|
||||
HRESULT STDMETHODCALLTYPE OnViewportStatusChanged(IDirectManipulationViewport *viewport,
|
||||
DIRECTMANIPULATION_STATUS current,
|
||||
DIRECTMANIPULATION_STATUS previous) override;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnViewportUpdated(IDirectManipulationViewport *viewport) override;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnContentUpdated(IDirectManipulationViewport *viewport,
|
||||
IDirectManipulationContent *content) override;
|
||||
|
||||
private:
|
||||
enum { GESTURE_NONE, GESTURE_PAN, GESTURE_PINCH } gesture_state;
|
||||
|
||||
int32_t last_x, last_y, last_scale;
|
||||
GHOST_TTrackpadInfo accumulated_values;
|
||||
uint16_t dpi;
|
||||
DIRECTMANIPULATION_STATUS dm_status;
|
||||
|
||||
friend class GHOST_DirectManipulationHelper;
|
||||
};
|
||||
|
||||
class GHOST_DirectManipulationHelper {
|
||||
public:
|
||||
/**
|
||||
* Creates a GHOST_DirectManipulationHelper for the provided window.
|
||||
* \param hWnd: The window receiving DirectManipulation events.
|
||||
* \param dpi: The current DPI.
|
||||
* \return Pointer to the new GHOST_DirectManipulationHelper if created, nullptr if there was an
|
||||
* error.
|
||||
*/
|
||||
static GHOST_DirectManipulationHelper *create(HWND hWnd, uint16_t dpi);
|
||||
|
||||
~GHOST_DirectManipulationHelper();
|
||||
|
||||
/**
|
||||
* Drives the DirectManipulation context.
|
||||
* DirectManipulation's intended use is to tie user input into DirectComposition's compositor
|
||||
* scaling and translating. We are not using DirectComposition and therefore must drive
|
||||
* DirectManipulation manually.
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Sets pointer in contact with the DirectManipulation context.
|
||||
* \param pointerId: ID of the pointer in contact.
|
||||
*/
|
||||
void onPointerHitTest(UINT32 pointerId);
|
||||
|
||||
/**
|
||||
* Updates DPI information for touchpad scaling.
|
||||
* \param dpi: The new DPI.
|
||||
*/
|
||||
void setDPI(uint16_t dpi);
|
||||
|
||||
/**
|
||||
* Retrieves trackpad input.
|
||||
* \return The accumulated trackpad translation and scale since last call.
|
||||
*/
|
||||
GHOST_TTrackpadInfo getTrackpadInfo();
|
||||
|
||||
private:
|
||||
GHOST_DirectManipulationHelper(
|
||||
HWND hWnd,
|
||||
Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager,
|
||||
Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager,
|
||||
Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport,
|
||||
Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
|
||||
directManipulationEventHandler,
|
||||
DWORD directManipulationViewportHandlerCookie,
|
||||
bool isScrollDirectionInverted);
|
||||
|
||||
/**
|
||||
* Retrieves the scroll direction from the registry.
|
||||
* \return True if scroll direction is inverted.
|
||||
*/
|
||||
static bool getScrollDirectionFromReg();
|
||||
|
||||
/**
|
||||
* Registers listener for registry scroll direction entry changes.
|
||||
*/
|
||||
void registerScrollDirectionChangeListener();
|
||||
|
||||
HWND h_wnd_;
|
||||
|
||||
HKEY scroll_direction_reg_key_;
|
||||
HANDLE scroll_direction_change_event_;
|
||||
|
||||
Microsoft::WRL::ComPtr<IDirectManipulationManager> direct_manipulation_manager_;
|
||||
Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> direct_manipulation_update_manager_;
|
||||
Microsoft::WRL::ComPtr<IDirectManipulationViewport> direct_manipulation_viewport_;
|
||||
Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
|
||||
direct_manipulation_event_handler_;
|
||||
DWORD direct_manipulation_viewport_handler_cookie_;
|
||||
|
||||
bool is_scroll_direction_inverted_;
|
||||
};
|
||||
33
blender-5.2.0/intern/ghost/intern/GHOST_Util.hh
Normal file
33
blender-5.2.0/intern/ghost/intern/GHOST_Util.hh
Normal file
@@ -0,0 +1,33 @@
|
||||
/* SPDX-FileCopyrightText: 2002-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
/**
|
||||
* RAII wrapper for typical C `void *` custom data.
|
||||
* Used for exception safe custom-data handling during constructor calls.
|
||||
*/
|
||||
struct GHOST_C_CustomDataWrapper {
|
||||
using FreeFn = std::function<void(void *)>;
|
||||
|
||||
void *custom_data_;
|
||||
FreeFn free_fn_;
|
||||
|
||||
GHOST_C_CustomDataWrapper(void *custom_data, FreeFn free_fn)
|
||||
: custom_data_(custom_data), free_fn_(free_fn)
|
||||
{
|
||||
}
|
||||
~GHOST_C_CustomDataWrapper()
|
||||
{
|
||||
if (free_fn_ != nullptr && custom_data_ != nullptr) {
|
||||
free_fn_(custom_data_);
|
||||
}
|
||||
}
|
||||
};
|
||||
20
blender-5.2.0/intern/ghost/intern/GHOST_WaylandUtils.hh
Normal file
20
blender-5.2.0/intern/ghost/intern/GHOST_WaylandUtils.hh
Normal file
@@ -0,0 +1,20 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#undef wl_array_for_each
|
||||
/**
|
||||
* This macro causes a warning for C++ code, define our own.
|
||||
* See: https://gitlab.freedesktop.org/wayland/wayland/-/issues/34
|
||||
*/
|
||||
#define WL_ARRAY_FOR_EACH(pos, array) \
|
||||
for (pos = (decltype(pos))((array)->data); \
|
||||
reinterpret_cast<const char *>(pos) < \
|
||||
(reinterpret_cast<const char *>((array)->data) + (array)->size); \
|
||||
(pos)++)
|
||||
293
blender-5.2.0/intern/ghost/intern/GHOST_Window.cc
Normal file
293
blender-5.2.0/intern/ghost/intern/GHOST_Window.cc
Normal file
@@ -0,0 +1,293 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup GHOST
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright (C) 2001 NaN Technologies B.V.
|
||||
*/
|
||||
|
||||
#include "GHOST_Window.hh"
|
||||
|
||||
#include "GHOST_ContextNone.hh"
|
||||
|
||||
#include "GHOST_utildefines.hh"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
GHOST_Window::GHOST_Window(uint32_t width,
|
||||
uint32_t height,
|
||||
GHOST_TWindowState state,
|
||||
const GHOST_ContextParams &context_params,
|
||||
const bool /*exclusive*/)
|
||||
: drawing_context_type_(GHOST_kDrawingContextTypeNone),
|
||||
user_data_(nullptr),
|
||||
cursor_visible_(true),
|
||||
cursor_grab_(GHOST_kGrabDisable),
|
||||
cursor_grab_axis_(GHOST_kAxisNone),
|
||||
cursor_grab_init_pos_{0, 0},
|
||||
cursor_grab_accum_pos_{0, 0},
|
||||
cursor_shape_(GHOST_kStandardCursorDefault),
|
||||
progress_bar_visible_(false),
|
||||
can_accept_drag_operation_(false),
|
||||
is_unsaved_changes_(false),
|
||||
window_decoration_style_flags_(GHOST_kDecorationNone),
|
||||
window_decoration_style_settings_(),
|
||||
want_context_params_(context_params),
|
||||
native_pixel_size_(1.0f),
|
||||
context_(nullptr)
|
||||
|
||||
{
|
||||
const GHOST_ContextParams context_params_none = GHOST_CONTEXT_PARAMS_NONE;
|
||||
context_ = new GHOST_ContextNone(context_params_none);
|
||||
|
||||
full_screen_ = state == GHOST_kWindowStateFullScreen;
|
||||
if (full_screen_) {
|
||||
full_screen_width_ = width;
|
||||
full_screen_height_ = height;
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_Window::~GHOST_Window()
|
||||
{
|
||||
delete context_;
|
||||
}
|
||||
|
||||
void *GHOST_Window::getOSWindow() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GHOST_TWindowDecorationStyleFlags GHOST_Window::getWindowDecorationStyleFlags()
|
||||
{
|
||||
return window_decoration_style_flags_;
|
||||
}
|
||||
|
||||
void GHOST_Window::setWindowDecorationStyleFlags(GHOST_TWindowDecorationStyleFlags style_flags)
|
||||
{
|
||||
window_decoration_style_flags_ = style_flags;
|
||||
}
|
||||
|
||||
void GHOST_Window::setWindowDecorationStyleSettings(
|
||||
GHOST_WindowDecorationStyleSettings decoration_settings)
|
||||
{
|
||||
window_decoration_style_settings_ = decoration_settings;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::setDrawingContextType(GHOST_TDrawingContextType type)
|
||||
{
|
||||
if (type != drawing_context_type_) {
|
||||
delete context_;
|
||||
context_ = nullptr;
|
||||
|
||||
if (type != GHOST_kDrawingContextTypeNone) {
|
||||
context_ = newDrawingContext(type);
|
||||
}
|
||||
if (context_ != nullptr) {
|
||||
drawing_context_type_ = type;
|
||||
}
|
||||
else {
|
||||
context_ = new GHOST_ContextNone(want_context_params_);
|
||||
drawing_context_type_ = GHOST_kDrawingContextTypeNone;
|
||||
}
|
||||
|
||||
return (type == drawing_context_type_) ? GHOST_kSuccess : GHOST_kFailure;
|
||||
}
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_IContext *GHOST_Window::getDrawingContext()
|
||||
{
|
||||
return context_;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::swapBufferAcquire()
|
||||
{
|
||||
return context_->swapBufferAcquire();
|
||||
}
|
||||
GHOST_TSuccess GHOST_Window::swapBufferRelease()
|
||||
{
|
||||
return context_->swapBufferRelease();
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::setSwapInterval(int interval)
|
||||
{
|
||||
return context_->setSwapInterval(interval);
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::getSwapInterval(int &interval_out)
|
||||
{
|
||||
return context_->getSwapInterval(interval_out);
|
||||
}
|
||||
|
||||
GHOST_Context *GHOST_Window::getContext()
|
||||
{
|
||||
return context_;
|
||||
}
|
||||
|
||||
uint GHOST_Window::getDefaultFramebuffer()
|
||||
{
|
||||
return (context_) ? context_->getDefaultFramebuffer() : 0;
|
||||
}
|
||||
|
||||
#ifdef WITH_VULKAN_BACKEND
|
||||
GHOST_TSuccess GHOST_Window::getVulkanSwapChainFormat(GHOST_VulkanSwapChainData *r_swap_chain_data)
|
||||
{
|
||||
return context_->getVulkanSwapChainFormat(r_swap_chain_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
GHOST_TSuccess GHOST_Window::activateDrawingContext()
|
||||
{
|
||||
return context_->activateDrawingContext();
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::updateDrawingContext()
|
||||
{
|
||||
return context_->updateDrawingContext();
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::releaseNativeHandles()
|
||||
{
|
||||
return context_->releaseNativeHandles();
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::setCursorVisibility(bool visible)
|
||||
{
|
||||
if (setWindowCursorVisibility(visible)) {
|
||||
cursor_visible_ = visible;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::setCursorGrab(GHOST_TGrabCursorMode mode,
|
||||
GHOST_TAxisFlag wrap_axis,
|
||||
GHOST_Rect *bounds,
|
||||
int32_t mouse_ungrab_xy[2])
|
||||
{
|
||||
if (cursor_grab_ == mode) {
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
/* Override with new location. */
|
||||
if (mouse_ungrab_xy) {
|
||||
assert(mode == GHOST_kGrabDisable);
|
||||
cursor_grab_init_pos_[0] = mouse_ungrab_xy[0];
|
||||
cursor_grab_init_pos_[1] = mouse_ungrab_xy[1];
|
||||
}
|
||||
|
||||
if (setWindowCursorGrab(mode)) {
|
||||
|
||||
if (mode == GHOST_kGrabDisable) {
|
||||
cursor_grab_bounds_.l_ = cursor_grab_bounds_.r_ = -1;
|
||||
}
|
||||
else if (bounds) {
|
||||
cursor_grab_bounds_ = *bounds;
|
||||
}
|
||||
else { /* if bounds not defined, use window */
|
||||
getClientBounds(cursor_grab_bounds_);
|
||||
}
|
||||
cursor_grab_ = mode;
|
||||
cursor_grab_axis_ = wrap_axis;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::getCursorGrabBounds(GHOST_Rect &bounds) const
|
||||
{
|
||||
if (!ELEM(cursor_grab_, GHOST_kGrabWrap, GHOST_kGrabHide)) {
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
bounds = cursor_grab_bounds_;
|
||||
return (bounds.l_ == -1 && bounds.r_ == -1) ? GHOST_kFailure : GHOST_kSuccess;
|
||||
}
|
||||
|
||||
void GHOST_Window::getCursorGrabState(GHOST_TGrabCursorMode &mode,
|
||||
GHOST_TAxisFlag &wrap_axis,
|
||||
GHOST_Rect &bounds,
|
||||
bool &use_software_cursor)
|
||||
{
|
||||
mode = cursor_grab_;
|
||||
if (cursor_grab_ == GHOST_kGrabWrap) {
|
||||
bounds = cursor_grab_bounds_;
|
||||
wrap_axis = cursor_grab_axis_;
|
||||
}
|
||||
else {
|
||||
bounds.l_ = -1;
|
||||
bounds.r_ = -1;
|
||||
bounds.t_ = -1;
|
||||
bounds.b_ = -1;
|
||||
wrap_axis = GHOST_kAxisNone;
|
||||
}
|
||||
use_software_cursor = (cursor_grab_ != GHOST_kGrabDisable) ? getCursorGrabUseSoftwareDisplay() :
|
||||
false;
|
||||
}
|
||||
|
||||
bool GHOST_Window::getCursorGrabUseSoftwareDisplay()
|
||||
{
|
||||
/* Sub-classes may override, by default don't use software cursor. */
|
||||
return false;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::setCursorShape(GHOST_TStandardCursor cursor_shape)
|
||||
{
|
||||
if (setWindowCursorShape(cursor_shape)) {
|
||||
cursor_shape_ = cursor_shape;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::setCustomCursorShape(const uint8_t *bitmap,
|
||||
const uint8_t *mask,
|
||||
const int size[2],
|
||||
const int hot_spot[2],
|
||||
bool can_invert_color)
|
||||
{
|
||||
if (setWindowCustomCursorShape(bitmap, mask, size, hot_spot, can_invert_color)) {
|
||||
cursor_shape_ = GHOST_kStandardCursorCustom;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::setCustomCursorGenerator(GHOST_CursorGenerator *cursor_generator)
|
||||
{
|
||||
if (setWindowCustomCursorGenerator(cursor_generator)) {
|
||||
cursor_shape_ = GHOST_kStandardCursorCustom;
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::getCursorBitmap(GHOST_CursorBitmapRef * /*bitmap*/)
|
||||
{
|
||||
/* Sub-classes may override. */
|
||||
return GHOST_kFailure;
|
||||
}
|
||||
|
||||
void GHOST_Window::setAcceptDragOperation(bool can_accept)
|
||||
{
|
||||
can_accept_drag_operation_ = can_accept;
|
||||
}
|
||||
|
||||
bool GHOST_Window::canAcceptDragOperation() const
|
||||
{
|
||||
return can_accept_drag_operation_;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_Window::setModifiedState(bool is_unsaved_changes)
|
||||
{
|
||||
is_unsaved_changes_ = is_unsaved_changes;
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
bool GHOST_Window::getModifiedState()
|
||||
{
|
||||
return is_unsaved_changes_;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user