cleanup and refactoring
This commit is contained in:
parent
2302158928
commit
76f6bf62a4
1285 changed files with 757994 additions and 8 deletions
540
raytracer/nvpro_core/nvp/NvFoundation.h
Normal file
540
raytracer/nvpro_core/nvp/NvFoundation.h
Normal file
|
|
@ -0,0 +1,540 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2018-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//--------------------------------------------------------------------
|
||||
/// @DOC_SKIP
|
||||
#ifndef NV_FOUNDATION_H
|
||||
#define NV_FOUNDATION_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifndef _INTPTR
|
||||
#define _INTPTR 0
|
||||
#endif
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning( disable : 4985 ) // 'symbol name': attributes not present on previous declaration
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include <float.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define NV_ASSERT(exp) (assert(exp))
|
||||
#define NV_ALWAYS_ASSERT() NV_ASSERT(0)
|
||||
|
||||
//***************************************
|
||||
// FILE: NvVersionNumber.h
|
||||
//***************************************
|
||||
|
||||
/*
|
||||
VersionNumbers: The combination of these
|
||||
numbers uniquely identifies the API, and should
|
||||
be incremented when the SDK API changes. This may
|
||||
include changes to file formats.
|
||||
|
||||
This header is included in the main SDK header files
|
||||
so that the entire SDK and everything that builds on it
|
||||
is completely rebuilt when this file changes. Thus,
|
||||
this file is not to include a frequently changing
|
||||
build number. See BuildNumber.h for that.
|
||||
|
||||
Each of these three values should stay below 255 because
|
||||
sometimes they are stored in a byte.
|
||||
*/
|
||||
|
||||
/** \addtogroup foundation
|
||||
@{
|
||||
*/
|
||||
|
||||
#define NV_FOUNDATION_VERSION_MAJOR 1
|
||||
#define NV_FOUNDATION_VERSION_MINOR 1
|
||||
#define NV_FOUNDATION_VERSION_BUGFIX 0
|
||||
|
||||
/**
|
||||
The constant NV_FOUNDATION_VERSION is used to confirm the version of the foundation headers.
|
||||
This is to ensure that the application is using the same header version as the library was built with.
|
||||
*/
|
||||
#define NV_FOUNDATION_VERSION ((NV_FOUNDATION_VERSION_MAJOR<<24) + (NV_FOUNDATION_VERSION_MINOR<<16) + (NV_FOUNDATION_VERSION_BUGFIX<<8) + 0)
|
||||
|
||||
//***************************************
|
||||
// FILE: NvPreprocessor.h
|
||||
//***************************************
|
||||
|
||||
/**
|
||||
List of preprocessor defines used to configure the SDK
|
||||
- NV_DEBUG: enable asserts (exactly one needs to be defined)
|
||||
- NV_CHECKED: enable run time checks, mostly unused or equiv. to NV_DEBUG
|
||||
- NV_SUPPORT_VISUAL_DEBUGGER: ...
|
||||
- AG_PERFMON: ... (Deprecated)
|
||||
*/
|
||||
|
||||
/**
|
||||
Compiler define
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
# define NV_VC
|
||||
# if _MSC_VER >= 1700
|
||||
# define NV_VC11
|
||||
# elif _MSC_VER >= 1600
|
||||
# define NV_VC10
|
||||
# elif _MSC_VER >= 1500
|
||||
# define NV_VC9
|
||||
# elif _MSC_VER >= 1400
|
||||
# define NV_VC8
|
||||
# elif _MSC_VER >= 1300
|
||||
# define NV_VC7
|
||||
# else
|
||||
# define NV_VC6
|
||||
# endif
|
||||
#elif defined(__ghs__)
|
||||
# define NV_GHS
|
||||
#elif __GNUC__ || __SNC__
|
||||
# define NV_GNUC
|
||||
#else
|
||||
# error "Unknown compiler"
|
||||
#endif
|
||||
|
||||
/**
|
||||
Platform define
|
||||
*/
|
||||
#ifdef NV_VC
|
||||
# ifdef XBOXONE
|
||||
# define NV_XBOXONE
|
||||
# define NV_X64
|
||||
# elif defined(_M_IX86)
|
||||
# define NV_X86
|
||||
# define NV_WINDOWS
|
||||
# elif defined(_M_X64)
|
||||
# define NV_X64
|
||||
# define NV_WINDOWS
|
||||
# elif defined(_M_PPC)
|
||||
# define NV_PPC
|
||||
# define NV_X360
|
||||
# define NV_VMX
|
||||
# elif defined(_M_ARM)
|
||||
# define NV_ARM
|
||||
# define NV_ARM_NEON
|
||||
# else
|
||||
# error "Unknown platform"
|
||||
# endif
|
||||
# if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_PARTITION_APP
|
||||
# define NV_WINMODERN
|
||||
# endif
|
||||
#elif defined NV_GNUC
|
||||
# ifdef __CELLOS_LV2__
|
||||
# define NV_PS3
|
||||
# define NV_VMX
|
||||
# elif defined(__arm__) || defined(__aarch64__)
|
||||
# define NV_ARM
|
||||
# if defined(__SNC__)
|
||||
# define NV_PSP2
|
||||
# endif
|
||||
# if defined(__ARM_NEON__)
|
||||
# define NV_ARM_NEON
|
||||
# endif
|
||||
# elif defined(__i386__)
|
||||
# define NV_X86
|
||||
# define NV_VMX
|
||||
# elif defined(__x86_64__)
|
||||
# ifdef __PS4__
|
||||
# define NV_PS4
|
||||
# define NV_X64
|
||||
# else
|
||||
# define NV_X64
|
||||
# endif
|
||||
# elif defined(__ppc__)
|
||||
# define NV_PPC
|
||||
# elif defined(__ppc64__)
|
||||
# define NV_PPC
|
||||
# define NV_PPC64
|
||||
# else
|
||||
# error "Unknown platform"
|
||||
# endif
|
||||
# if defined(ANDROID)
|
||||
# define NV_ANDROID
|
||||
# define NV_UNIX
|
||||
# elif defined(__linux__)
|
||||
# define NV_LINUX
|
||||
# define NV_UNIX
|
||||
# elif defined(__APPLE__)
|
||||
# define NV_APPLE
|
||||
# define NV_UNIX
|
||||
# if defined(__arm__)
|
||||
# define NV_APPLE_IOS
|
||||
# else
|
||||
# define NV_OSX
|
||||
# endif
|
||||
# elif defined(__CYGWIN__)
|
||||
# define NV_CYGWIN
|
||||
# define NV_LINUX
|
||||
# define NV_UNIX
|
||||
# endif
|
||||
#elif defined NV_GHS
|
||||
# define NV_WIIU
|
||||
#endif
|
||||
|
||||
/**
|
||||
DLL export macros
|
||||
*/
|
||||
#if !defined(NV_C_EXPORT)
|
||||
# if defined(NV_WINDOWS) || defined(NV_WINMODERN)
|
||||
# define NV_C_EXPORT extern "C"
|
||||
# else
|
||||
# define NV_C_EXPORT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
Calling convention
|
||||
*/
|
||||
#ifndef NV_CALL_CONV
|
||||
# if defined NV_WINDOWS
|
||||
# define NV_CALL_CONV __cdecl
|
||||
# else
|
||||
# define NV_CALL_CONV
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
Pack macros - disabled on SPU because they are not supported
|
||||
*/
|
||||
#if defined(NV_VC)
|
||||
# define NV_PUSH_PACK_DEFAULT __pragma( pack(push, 8) )
|
||||
# define NV_POP_PACK __pragma( pack(pop) )
|
||||
#elif (defined(NV_GNUC) && !defined(__SPU__)) || defined(NV_GHS)
|
||||
# define NV_PUSH_PACK_DEFAULT _Pragma("pack(push, 8)")
|
||||
# define NV_POP_PACK _Pragma("pack(pop)")
|
||||
#else
|
||||
# define NV_PUSH_PACK_DEFAULT
|
||||
# define NV_POP_PACK
|
||||
#endif
|
||||
|
||||
/**
|
||||
Inline macro
|
||||
*/
|
||||
#if defined(NV_WINDOWS) || defined(NV_X360) || defined(NV_WINMODERN) || defined(NV_XBOXONE)
|
||||
# define NV_INLINE inline
|
||||
# pragma inline_depth( 255 )
|
||||
#else
|
||||
# define NV_INLINE inline
|
||||
#endif
|
||||
|
||||
/**
|
||||
Force inline macro
|
||||
*/
|
||||
#if defined(NV_VC)
|
||||
#define NV_FORCE_INLINE __forceinline
|
||||
#elif defined(NV_LINUX) // Workaround; Fedora Core 3 do not agree with force inline and NvcPool
|
||||
#define NV_FORCE_INLINE inline
|
||||
#elif defined(NV_GNUC) || defined(NV_GHS)
|
||||
#define NV_FORCE_INLINE inline __attribute__((always_inline))
|
||||
#else
|
||||
#define NV_FORCE_INLINE inline
|
||||
#endif
|
||||
|
||||
/**
|
||||
Noinline macro
|
||||
*/
|
||||
#if defined NV_WINDOWS || defined NV_XBOXONE
|
||||
# define NV_NOINLINE __declspec(noinline)
|
||||
#elif defined(NV_GNUC) || defined(NV_GHS)
|
||||
# define NV_NOINLINE __attribute__ ((noinline))
|
||||
#else
|
||||
# define NV_NOINLINE
|
||||
#endif
|
||||
|
||||
|
||||
/*! restrict macro */
|
||||
#if defined(__CUDACC__)
|
||||
# define NV_RESTRICT __restrict__
|
||||
#elif (defined(NV_GNUC) || defined(NV_VC) || defined(NV_GHS)) && !defined(NV_PS4) // ps4 doesn't like restricted functions
|
||||
# define NV_RESTRICT __restrict
|
||||
#else
|
||||
# define NV_RESTRICT
|
||||
#endif
|
||||
|
||||
#if defined(NV_WINDOWS) || defined(NV_X360) || defined(NV_WINMODERN) || defined(NV_XBOXONE)
|
||||
#define NV_NOALIAS __declspec(noalias)
|
||||
#else
|
||||
#define NV_NOALIAS
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
Alignment macros
|
||||
|
||||
NV_ALIGN_PREFIX and NV_ALIGN_SUFFIX can be used for type alignment instead of aligning individual variables as follows:
|
||||
NV_ALIGN_PREFIX(16)
|
||||
struct A {
|
||||
...
|
||||
} NV_ALIGN_SUFFIX(16);
|
||||
This declaration style is parsed correctly by Visual Assist.
|
||||
|
||||
*/
|
||||
#ifndef NV_ALIGN
|
||||
#if defined(NV_VC)
|
||||
#define NV_ALIGN(alignment, decl) __declspec(align(alignment)) decl
|
||||
#define NV_ALIGN_PREFIX(alignment) __declspec(align(alignment))
|
||||
#define NV_ALIGN_SUFFIX(alignment)
|
||||
#elif defined(NV_GNUC) || defined(NV_GHS) || defined(NV_APPLE_IOS)
|
||||
#define NV_ALIGN(alignment, decl) decl __attribute__ ((aligned(alignment)))
|
||||
#define NV_ALIGN_PREFIX(alignment)
|
||||
#define NV_ALIGN_SUFFIX(alignment) __attribute__ ((aligned(alignment)))
|
||||
#else
|
||||
#define NV_ALIGN(alignment, decl)
|
||||
#define NV_ALIGN_PREFIX(alignment)
|
||||
#define NV_ALIGN_SUFFIX(alignment)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
Deprecated macro
|
||||
- To deprecate a function: Place NV_DEPRECATED at the start of the function header (leftmost word).
|
||||
- To deprecate a 'typdef', a 'struct' or a 'class': Place NV_DEPRECATED directly after the keywords ('typdef', 'struct', 'class').
|
||||
*/
|
||||
#if 0 // set to 1 to create warnings for deprecated functions
|
||||
# define NV_DEPRECATED __declspec(deprecated)
|
||||
#else
|
||||
# define NV_DEPRECATED
|
||||
#endif
|
||||
|
||||
// VC6 no '__FUNCTION__' workaround
|
||||
#if defined NV_VC6 && !defined __FUNCTION__
|
||||
# define __FUNCTION__ "Undefined"
|
||||
#endif
|
||||
|
||||
/**
|
||||
General defines
|
||||
*/
|
||||
|
||||
// static assert
|
||||
#define NV_COMPILE_TIME_ASSERT(exp) typedef char NvCompileTimeAssert_Dummy[(exp) ? 1 : -1]
|
||||
|
||||
#if defined(NV_GNUC)
|
||||
#define NV_OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
|
||||
#else
|
||||
#define NV_OFFSET_OF(X, Y) offsetof(X, Y)
|
||||
#endif
|
||||
|
||||
#define NV_SIZE_OF(Class, Member) sizeof(((Class*)0)->Member)
|
||||
|
||||
#define NV_ARRAY_SIZE(X) (sizeof((X))/sizeof((X)[0]))
|
||||
|
||||
#define NV_PAD_POW2(value, pad) (((value) + ((pad)-1)) & (~((pad)-1)))
|
||||
|
||||
// _DEBUG is provided only by MSVC, but not GCC.
|
||||
// NDEBUG is the canonical platform agnostic way to detect debug/release builds
|
||||
#if !defined(_DEBUG) && !defined(NDEBUG)
|
||||
#define _DEBUG 1
|
||||
#endif
|
||||
|
||||
// check that exactly one of NDEBUG and _DEBUG is defined
|
||||
#if !(defined NDEBUG ^ defined _DEBUG)
|
||||
#error "NDEBUG and _DEBUG are mutually exclusive"
|
||||
#endif
|
||||
|
||||
// make sure NV_CHECKED is defined in all _DEBUG configurations as well
|
||||
#if !defined(NV_CHECKED) && defined _DEBUG
|
||||
#define NV_CHECKED
|
||||
#endif
|
||||
|
||||
#ifdef __CUDACC__
|
||||
#define NV_CUDA_CALLABLE __host__ __device__
|
||||
#else
|
||||
#define NV_CUDA_CALLABLE
|
||||
#endif
|
||||
|
||||
// avoid unreferenced parameter warning (why not just disable it?)
|
||||
// PT: or why not just omit the parameter's name from the declaration????
|
||||
#if defined(__cplusplus__)
|
||||
template <class T> NV_CUDA_CALLABLE NV_INLINE void NV_UNUSED(T const&) {}
|
||||
#else
|
||||
# define NV_UNUSED(var) (void)(var)
|
||||
#endif
|
||||
|
||||
// Ensure that the application hasn't tweaked the pack value to less than 8, which would break
|
||||
// matching between the API headers and the binaries
|
||||
// This assert works on win32/win64/360/ps3, but may need further specialization on other platforms.
|
||||
// Some GCC compilers need the compiler flag -malign-double to be set.
|
||||
// Apparently the apple-clang-llvm compiler doesn't support malign-double.
|
||||
|
||||
typedef struct NvPackValidation { char _; long long a; } NvPackValidation;
|
||||
|
||||
#if !defined(NV_APPLE)
|
||||
NV_COMPILE_TIME_ASSERT(NV_OFFSET_OF(NvPackValidation, a) == 8);
|
||||
#endif
|
||||
|
||||
// use in a cpp file to suppress LNK4221
|
||||
#if defined(NV_VC)
|
||||
#define NV_DUMMY_SYMBOL namespace { char NvDummySymbol; }
|
||||
#else
|
||||
#define NV_DUMMY_SYMBOL
|
||||
#endif
|
||||
|
||||
#ifdef __SPU__
|
||||
#define NV_IS_SPU 1
|
||||
#else
|
||||
#define NV_IS_SPU 0
|
||||
#endif
|
||||
|
||||
#ifdef NV_X64
|
||||
#define NV_IS_X64 1
|
||||
#else
|
||||
#define NV_IS_X64 0
|
||||
#endif
|
||||
|
||||
#ifdef NV_WINDOWS
|
||||
#define NV_IS_WINDOWS 1
|
||||
#else
|
||||
#define NV_IS_WINDOWS 0
|
||||
#endif
|
||||
|
||||
#ifdef NV_X86
|
||||
#define NV_IS_X86 1
|
||||
#else
|
||||
#define NV_IS_X86 0
|
||||
#endif
|
||||
|
||||
#ifdef NV_X64
|
||||
#define NV_IS_X64 1
|
||||
#else
|
||||
#define NV_IS_X64 0
|
||||
#endif
|
||||
|
||||
#if defined(NV_X86) || defined(NV_X64)
|
||||
#define NV_IS_INTEL 1
|
||||
#else
|
||||
#define NV_IS_INTEL 0
|
||||
#endif
|
||||
|
||||
#ifdef NV_X360
|
||||
#define NV_IS_X360 1
|
||||
#else
|
||||
#define NV_IS_X360 0
|
||||
#endif
|
||||
|
||||
#ifdef NV_PS3
|
||||
#define NV_IS_PS3 1
|
||||
#else
|
||||
#define NV_IS_PS3 0
|
||||
#endif
|
||||
|
||||
#define NV_IS_PPU (NV_IS_PS3 && !NV_IS_SPU) // PS3 PPU
|
||||
|
||||
#ifdef NV_GNUC
|
||||
#define NV_WEAK_SYMBOL __attribute__((weak)) // this is to support SIMD constant merging in template specialization
|
||||
#else
|
||||
#define NV_WEAK_SYMBOL
|
||||
#endif
|
||||
|
||||
// Type ranges
|
||||
#define NV_MAX_I8 127 //maximum possible sbyte value, 0x7f
|
||||
#define NV_MIN_I8 (-128) //minimum possible sbyte value, 0x80
|
||||
#define NV_MAX_U8 255U //maximum possible ubyte value, 0xff
|
||||
#define NV_MIN_U8 0 //minimum possible ubyte value, 0x00
|
||||
#define NV_MAX_I16 32767 //maximum possible sword value, 0x7fff
|
||||
#define NV_MIN_I16 (-32768) //minimum possible sword value, 0x8000
|
||||
#define NV_MAX_U16 65535U //maximum possible uword value, 0xffff
|
||||
#define NV_MIN_U16 0 //minimum possible uword value, 0x0000
|
||||
#define NV_MAX_I32 2147483647 //maximum possible sdword value, 0x7fffffff
|
||||
#define NV_MIN_I32 (-2147483647 - 1) //minimum possible sdword value, 0x80000000
|
||||
#define NV_MAX_U32 4294967295U //maximum possible udword value, 0xffffffff
|
||||
#define NV_MIN_U32 0 //minimum possible udword value, 0x00000000
|
||||
#define NV_MAX_F32 3.4028234663852885981170418348452e+38F
|
||||
//maximum possible float value
|
||||
#define NV_MAX_F64 DBL_MAX //maximum possible double value
|
||||
|
||||
#define NV_EPS_F32 FLT_EPSILON //maximum relative error of float rounding
|
||||
#define NV_EPS_F64 DBL_EPSILON //maximum relative error of double rounding
|
||||
|
||||
#define NV_MAX_REAL NV_MAX_F32
|
||||
#define NV_EPS_REAL NV_EPS_F32
|
||||
#define NV_NORMALIZATION_EPSILON float(1e-20f)
|
||||
|
||||
/** enum for empty constructor tag*/
|
||||
enum NvEMPTY { NvEmpty };
|
||||
|
||||
/** Basic struct data type for float2 Vectors */
|
||||
typedef struct
|
||||
{
|
||||
float x,y;
|
||||
} NV_float2;
|
||||
|
||||
/** Basic struct data type for float3 Vectors */
|
||||
typedef struct
|
||||
{
|
||||
float x,y,z;
|
||||
} NV_float3;
|
||||
|
||||
/** Basic struct data type for float4 Vector or quaternion */
|
||||
typedef struct
|
||||
{
|
||||
float x,y,z,w;
|
||||
} NV_float4;
|
||||
|
||||
|
||||
/** Basic struct data type for 7 floats, typically used to represent a 'pose' comprised of a quaternion rotation (x,y,z,w) followed by a position (x,y,z) */
|
||||
typedef struct
|
||||
{
|
||||
NV_float4 q;
|
||||
NV_float3 p;
|
||||
} NV_float7;
|
||||
|
||||
|
||||
/** Basic struct data type for 9 floats, typically a 3x3 matrix */
|
||||
typedef struct
|
||||
{
|
||||
float transform[9];
|
||||
} NV_float9;
|
||||
|
||||
|
||||
/** Basic struct data type for 12 floats, typically a 3x4 matrix */
|
||||
typedef struct
|
||||
{
|
||||
float transform[12];
|
||||
} NV_float12;
|
||||
|
||||
|
||||
/** Basic struct data type for 16 floats, typically a 4x4 matrix */
|
||||
typedef struct
|
||||
{
|
||||
float transform[16];
|
||||
} NV_float16;
|
||||
|
||||
/** Basic struct data to for a 3d bounding box */
|
||||
typedef struct
|
||||
{
|
||||
float minimum[3];
|
||||
float maximum[3];
|
||||
} NV_bounds3;
|
||||
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
#endif // NV_FOUNDATION_H
|
||||
83
raytracer/nvpro_core/nvp/README.md
Normal file
83
raytracer/nvpro_core/nvp/README.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
## Table of Contents
|
||||
- [nvpfilesystem.hpp](#nvpfilesystemhpp)
|
||||
- [nvpsystem.hpp](#nvpsystemhpp)
|
||||
- [nvpwindow.hpp](#nvpwindowhpp)
|
||||
|
||||
## nvpfilesystem.hpp
|
||||
### class nvp::FileSystemMonitor
|
||||
|
||||
|
||||
Monitors files and/or directories for changes
|
||||
|
||||
This cross-platform wrapper does not create any threads, but is designed for
|
||||
it. checkEvents() will block until either an event is generated, or cancel()
|
||||
is called. See ModifiedFilesMonitor for an example.
|
||||
### class nvp::FSMRunner
|
||||
|
||||
|
||||
Adds a thread to nvp::FileSystemMonitor that repeatedly calls
|
||||
nvp::FileSystemMonitor::checkEvents().
|
||||
### class nvp::FSMCallbacks
|
||||
|
||||
|
||||
Utility class to get per-path callbacks.
|
||||
|
||||
Make sure PathSetCallback objects returned by add() do not outlive the FSMCallbacks.
|
||||
Be careful not to destroy a PathCallback during a callback.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
FSMCallbacks callbacks;
|
||||
|
||||
auto callbackFile1 = callbacks.add(std::vector<std::string>{"file1.txt"}, nvp::FileSystemMonitor::FSM_MODIFY,
|
||||
[this](nvp::FileSystemMonitor::EventData ev) {
|
||||
// Do something with file1.txt
|
||||
});
|
||||
|
||||
auto callbackFile2 = callbacks.add(std::vector<std::string>{"file2.txt"}, nvp::FileSystemMonitor::FSM_MODIFY,
|
||||
[this](nvp::FileSystemMonitor::EventData ev) {
|
||||
// Do something with file2.txt
|
||||
});
|
||||
|
||||
// When callbackFile1 goes out of scope, file1.txt stops being monitored
|
||||
callbackFile1.reset()
|
||||
```
|
||||
### class nvp::ModifiedFilesMonitor
|
||||
|
||||
|
||||
Monitors files and/or directories for changes.
|
||||
|
||||
Starts a thread at creation. Be careful to keep it in scope while events are needed.
|
||||
|
||||
This cross-platform wrapper does not create any threads, but is designed to be
|
||||
used with one. checkEvents() will block until either an event is generated, or
|
||||
cancel() is called.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
std::vector<std::string> dirs = {"shaders_bin"};
|
||||
nvp::FileSystemMonitor::Callback callback = [](nvp::FileSystemMonitor::EventData ev){
|
||||
g_reloadShaders = true;
|
||||
};
|
||||
auto fileMonitor = std::make_unique<nvp::ModifiedFilesMonitor>(dirs, callback);
|
||||
```
|
||||
|
||||
## nvpsystem.hpp
|
||||
### class NVPSystem
|
||||
|
||||
> NVPSystem is a utility class to handle some basic system
|
||||
functionality that all projects likely make use of.
|
||||
It does not require any window to be opened.
|
||||
Typical usage is calling init right after main and deinit
|
||||
in the end, or use the NVPSystem object for that.
|
||||
init
|
||||
- calls glfwInit and registers the error callback for it
|
||||
- sets up and log filename based on projectName via nvprintSetLogFileName
|
||||
|
||||
## nvpwindow.hpp
|
||||
### class NVPWindow
|
||||
|
||||
> base class for a window, to catch events
|
||||
Using and deriving of NVPWindow base-class is optional.
|
||||
However one must always make use of the NVPSystem
|
||||
That takes care of glfwInit/terminate as well.
|
||||
21
raytracer/nvpro_core/nvp/include_gl.h
Normal file
21
raytracer/nvpro_core/nvp/include_gl.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2018-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//--------------------------------------------------------------------
|
||||
/// @DOC_SKIP
|
||||
#include <nvgl/extensions_gl.hpp>
|
||||
862
raytracer/nvpro_core/nvp/linux_file_dialog.h
Normal file
862
raytracer/nvpro_core/nvp/linux_file_dialog.h
Normal file
|
|
@ -0,0 +1,862 @@
|
|||
//
|
||||
// Courtesy from Sam Hocevar <sam@hocevar.net>
|
||||
//
|
||||
|
||||
/// @DOC_SKIP (keyword to exclude this file from automatic README.md generation)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _POSIX_C_SOURCE
|
||||
# define _POSIX_C_SOURCE 2 // for popen()
|
||||
#endif
|
||||
#include <cstdio> // popen()
|
||||
#include <cstdlib> // std::getenv()
|
||||
#include <fcntl.h> // fcntl()
|
||||
#include <unistd.h> // read(), pipe(), dup2()
|
||||
#include <csignal> // ::kill, std::signal
|
||||
#include <sys/wait.h> // waitpid()
|
||||
|
||||
#include <string> // std::string
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <iostream> // std::ostream
|
||||
#include <map> // std::map
|
||||
#include <set> // std::set
|
||||
#include <regex> // std::regex
|
||||
#include <thread> // std::mutex, std::this_thread
|
||||
#include <chrono> // std::chrono
|
||||
|
||||
enum class button
|
||||
{
|
||||
cancel = -1,
|
||||
ok,
|
||||
yes,
|
||||
no,
|
||||
abort,
|
||||
retry,
|
||||
ignore,
|
||||
};
|
||||
|
||||
enum class choice
|
||||
{
|
||||
ok = 0,
|
||||
ok_cancel,
|
||||
yes_no,
|
||||
yes_no_cancel,
|
||||
retry_cancel,
|
||||
abort_retry_ignore,
|
||||
};
|
||||
|
||||
enum class icon
|
||||
{
|
||||
info = 0,
|
||||
warning,
|
||||
error,
|
||||
question,
|
||||
};
|
||||
|
||||
// Additional option flags for various dialog constructors
|
||||
enum class opt : uint8_t
|
||||
{
|
||||
none = 0,
|
||||
// For file open, allow multiselect.
|
||||
multiselect = 0x1,
|
||||
// For file save, force overwrite and disable the confirmation dialog.
|
||||
force_overwrite = 0x2,
|
||||
// For folder select, force path to be the provided argument instead
|
||||
// of the last opened directory, which is the Microsoft-recommended,
|
||||
// user-friendly behaviour.
|
||||
force_path = 0x4,
|
||||
};
|
||||
|
||||
inline opt operator |(opt a, opt b) { return opt(uint8_t(a) | uint8_t(b)); }
|
||||
inline bool operator &(opt a, opt b) { return bool(uint8_t(a) & uint8_t(b)); }
|
||||
|
||||
// The settings class, only exposing to the user a way to set verbose mode
|
||||
// and to force a rescan of installed desktop helpers (zenity, kdialog…).
|
||||
class settings
|
||||
{
|
||||
public:
|
||||
static bool available();
|
||||
|
||||
static void verbose(bool value);
|
||||
static void rescan();
|
||||
|
||||
protected:
|
||||
explicit settings(bool resync = false);
|
||||
|
||||
bool check_program(std::string const &program);
|
||||
|
||||
inline bool is_zenity() const;
|
||||
inline bool is_kdialog() const;
|
||||
|
||||
enum class flag
|
||||
{
|
||||
is_scanned = 0,
|
||||
is_verbose,
|
||||
|
||||
has_zenity,
|
||||
has_matedialog,
|
||||
has_qarma,
|
||||
has_kdialog,
|
||||
is_vista,
|
||||
|
||||
max_flag,
|
||||
};
|
||||
|
||||
// Static array of flags for internal state
|
||||
bool const &flags(flag in_flag) const;
|
||||
|
||||
// Non-const getter for the static array of flags
|
||||
bool &flags(flag in_flag);
|
||||
};
|
||||
|
||||
// Internal classes, not to be used by client applications
|
||||
namespace internal
|
||||
{
|
||||
|
||||
// Process wait timeout, in milliseconds
|
||||
static int const default_wait_timeout = 20;
|
||||
|
||||
class executor
|
||||
{
|
||||
friend class dialog;
|
||||
|
||||
public:
|
||||
// High level function to get the result of a command
|
||||
std::string result(int *exit_code = nullptr);
|
||||
|
||||
// High level function to abort
|
||||
bool kill();
|
||||
|
||||
void start_process(std::vector<std::string> const &command);
|
||||
|
||||
~executor();
|
||||
|
||||
protected:
|
||||
bool ready(int timeout = default_wait_timeout);
|
||||
void stop();
|
||||
|
||||
private:
|
||||
bool m_running = false;
|
||||
std::string m_stdout;
|
||||
int m_exit_code = -1;
|
||||
pid_t m_pid = 0;
|
||||
int m_fd = -1;
|
||||
};
|
||||
|
||||
class dialog : protected settings
|
||||
{
|
||||
public:
|
||||
bool ready(int timeout = default_wait_timeout) const;
|
||||
bool kill() const;
|
||||
|
||||
protected:
|
||||
explicit dialog();
|
||||
|
||||
std::vector<std::string> desktop_helper() const;
|
||||
static std::string buttons_to_name(choice _choice);
|
||||
static std::string get_icon_name(icon _icon);
|
||||
|
||||
std::string powershell_quote(std::string const &str) const;
|
||||
std::string shell_quote(std::string const &str) const;
|
||||
|
||||
// Keep handle to executing command
|
||||
std::shared_ptr<executor> m_async;
|
||||
};
|
||||
|
||||
class file_dialog : public dialog
|
||||
{
|
||||
protected:
|
||||
enum type
|
||||
{
|
||||
open,
|
||||
save,
|
||||
folder,
|
||||
};
|
||||
|
||||
file_dialog(type in_type,
|
||||
std::string const &title,
|
||||
std::string const &default_path = "",
|
||||
std::vector<std::string> const &filters = {},
|
||||
opt options = opt::none);
|
||||
|
||||
protected:
|
||||
std::string string_result();
|
||||
std::vector<std::string> vector_result();
|
||||
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
//
|
||||
// The notify widget
|
||||
//
|
||||
|
||||
class notify : public internal::dialog
|
||||
{
|
||||
public:
|
||||
notify(std::string const &title,
|
||||
std::string const &message,
|
||||
icon _icon = icon::info);
|
||||
};
|
||||
|
||||
//
|
||||
// The message widget
|
||||
//
|
||||
|
||||
class message : public internal::dialog
|
||||
{
|
||||
public:
|
||||
message(std::string const &title,
|
||||
std::string const &text,
|
||||
choice _choice = choice::ok_cancel,
|
||||
icon _icon = icon::info);
|
||||
|
||||
button result();
|
||||
|
||||
private:
|
||||
// Some extra logic to map the exit code to button number
|
||||
std::map<int, button> m_mappings;
|
||||
};
|
||||
|
||||
//
|
||||
// The open_file, save_file, and open_folder widgets
|
||||
//
|
||||
|
||||
class open_file : public internal::file_dialog
|
||||
{
|
||||
public:
|
||||
open_file(std::string const &title,
|
||||
std::string const &default_path = "",
|
||||
std::vector<std::string> const &filters = { "All Files", "*" },
|
||||
opt options = opt::none);
|
||||
|
||||
#if defined(__has_cpp_attribute)
|
||||
#if __has_cpp_attribute(deprecated)
|
||||
// Backwards compatibility
|
||||
[[deprecated("Use opt::multiselect instead of allow_multiselect")]]
|
||||
#endif
|
||||
#endif
|
||||
open_file(std::string const &title,
|
||||
std::string const &default_path,
|
||||
std::vector<std::string> const &filters,
|
||||
bool allow_multiselect);
|
||||
|
||||
std::vector<std::string> result();
|
||||
};
|
||||
|
||||
class save_file : public internal::file_dialog
|
||||
{
|
||||
public:
|
||||
save_file(std::string const &title,
|
||||
std::string const &default_path = "",
|
||||
std::vector<std::string> const &filters = { "All Files", "*" },
|
||||
opt options = opt::none);
|
||||
|
||||
#if defined(__has_cpp_attribute)
|
||||
#if __has_cpp_attribute(deprecated)
|
||||
// Backwards compatibility
|
||||
[[deprecated("Use opt::force_overwrite instead of confirm_overwrite")]]
|
||||
#endif
|
||||
#endif
|
||||
save_file(std::string const &title,
|
||||
std::string const &default_path,
|
||||
std::vector<std::string> const &filters,
|
||||
bool confirm_overwrite);
|
||||
|
||||
std::string result();
|
||||
};
|
||||
|
||||
class select_folder : public internal::file_dialog
|
||||
{
|
||||
public:
|
||||
select_folder(std::string const &title,
|
||||
std::string const &default_path = "",
|
||||
opt options = opt::none);
|
||||
|
||||
std::string result();
|
||||
};
|
||||
|
||||
//
|
||||
// Below this are all the method implementations. You may choose to define the
|
||||
// macro SKIP_IMPLEMENTATION everywhere before including this header except
|
||||
// in one place. This may reduce compilation times.
|
||||
//
|
||||
|
||||
#if !defined SKIP_IMPLEMENTATION
|
||||
|
||||
// internal free functions implementations
|
||||
|
||||
namespace internal
|
||||
{
|
||||
// This is necessary until C++20 which will have std::string::ends_with() etc.
|
||||
|
||||
static inline bool ends_with(std::string const &str, std::string const &suffix)
|
||||
{
|
||||
return suffix.size() <= str.size() &&
|
||||
str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
|
||||
}
|
||||
|
||||
static inline bool starts_with(std::string const &str, std::string const &prefix)
|
||||
{
|
||||
return prefix.size() <= str.size() &&
|
||||
str.compare(0, prefix.size(), prefix) == 0;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// settings implementation
|
||||
|
||||
inline settings::settings(bool resync)
|
||||
{
|
||||
flags(flag::is_scanned) &= !resync;
|
||||
|
||||
if (flags(flag::is_scanned))
|
||||
return;
|
||||
|
||||
flags(flag::has_zenity) = check_program("zenity");
|
||||
flags(flag::has_matedialog) = check_program("matedialog");
|
||||
flags(flag::has_qarma) = check_program("qarma");
|
||||
flags(flag::has_kdialog) = check_program("kdialog");
|
||||
|
||||
// If multiple helpers are available, try to default to the best one
|
||||
if (flags(flag::has_zenity) && flags(flag::has_kdialog))
|
||||
{
|
||||
auto desktop_name = std::getenv("XDG_SESSION_DESKTOP");
|
||||
if (desktop_name && desktop_name == std::string("gnome"))
|
||||
flags(flag::has_kdialog) = false;
|
||||
else if (desktop_name && desktop_name == std::string("KDE"))
|
||||
flags(flag::has_zenity) = false;
|
||||
}
|
||||
|
||||
flags(flag::is_scanned) = true;
|
||||
}
|
||||
|
||||
inline bool settings::available()
|
||||
{
|
||||
settings tmp;
|
||||
return tmp.flags(flag::has_zenity) ||
|
||||
tmp.flags(flag::has_matedialog) ||
|
||||
tmp.flags(flag::has_qarma) ||
|
||||
tmp.flags(flag::has_kdialog);
|
||||
}
|
||||
|
||||
inline void settings::verbose(bool value)
|
||||
{
|
||||
settings().flags(flag::is_verbose) = value;
|
||||
}
|
||||
|
||||
inline void settings::rescan()
|
||||
{
|
||||
settings(/* resync = */ true);
|
||||
}
|
||||
|
||||
// Check whether a program is present using “which”.
|
||||
inline bool settings::check_program(std::string const &program)
|
||||
{
|
||||
int exit_code = -1;
|
||||
internal::executor async;
|
||||
async.start_process({"/bin/sh", "-c", "which " + program});
|
||||
async.result(&exit_code);
|
||||
return exit_code == 0;
|
||||
}
|
||||
|
||||
inline bool settings::is_zenity() const
|
||||
{
|
||||
return flags(flag::has_zenity) ||
|
||||
flags(flag::has_matedialog) ||
|
||||
flags(flag::has_qarma);
|
||||
}
|
||||
|
||||
inline bool settings::is_kdialog() const
|
||||
{
|
||||
return flags(flag::has_kdialog);
|
||||
}
|
||||
|
||||
inline bool const &settings::flags(flag in_flag) const
|
||||
{
|
||||
static bool flags[size_t(flag::max_flag)];
|
||||
return flags[size_t(in_flag)];
|
||||
}
|
||||
|
||||
inline bool &settings::flags(flag in_flag)
|
||||
{
|
||||
return const_cast<bool &>(static_cast<settings const *>(this)->flags(in_flag));
|
||||
}
|
||||
|
||||
// executor implementation
|
||||
|
||||
inline std::string internal::executor::result(int *exit_code /* = nullptr */)
|
||||
{
|
||||
stop();
|
||||
if (exit_code)
|
||||
*exit_code = m_exit_code;
|
||||
return m_stdout;
|
||||
}
|
||||
|
||||
inline bool internal::executor::kill()
|
||||
{
|
||||
::kill(m_pid, SIGKILL);
|
||||
stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
inline void internal::executor::start_process(std::vector<std::string> const &command)
|
||||
{
|
||||
stop();
|
||||
m_stdout.clear();
|
||||
m_exit_code = -1;
|
||||
|
||||
int in[2], out[2];
|
||||
if (pipe(in) != 0 || pipe(out) != 0)
|
||||
return;
|
||||
|
||||
m_pid = fork();
|
||||
if (m_pid < 0)
|
||||
return;
|
||||
|
||||
close(in[m_pid ? 0 : 1]);
|
||||
close(out[m_pid ? 1 : 0]);
|
||||
|
||||
if (m_pid == 0)
|
||||
{
|
||||
dup2(in[0], STDIN_FILENO);
|
||||
dup2(out[1], STDOUT_FILENO);
|
||||
|
||||
// Ignore stderr so that it doesn’t pollute the console (e.g. GTK+ errors from zenity)
|
||||
int fd = open("/dev/null", O_WRONLY);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
|
||||
std::vector<char *> args;
|
||||
std::transform(command.cbegin(), command.cend(), std::back_inserter(args),
|
||||
[](std::string const &s) { return const_cast<char *>(s.c_str()); });
|
||||
args.push_back(nullptr); // null-terminate argv[]
|
||||
|
||||
execvp(args[0], args.data());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
close(in[1]);
|
||||
m_fd = out[0];
|
||||
auto flags = fcntl(m_fd, F_GETFL);
|
||||
fcntl(m_fd, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
m_running = true;
|
||||
}
|
||||
|
||||
inline internal::executor::~executor()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
inline bool internal::executor::ready(int timeout /* = default_wait_timeout */)
|
||||
{
|
||||
if (!m_running)
|
||||
return true;
|
||||
|
||||
char buf[BUFSIZ];
|
||||
ssize_t received = read(m_fd, buf, BUFSIZ); // Flawfinder: ignore
|
||||
if (received > 0)
|
||||
{
|
||||
m_stdout += std::string(buf, received);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reap child process if it is dead. It is possible that the system has already reaped it
|
||||
// (this happens when the calling application handles or ignores SIG_CHLD) and results in
|
||||
// waitpid() failing with ECHILD. Otherwise we assume the child is running and we sleep for
|
||||
// a little while.
|
||||
int status;
|
||||
pid_t child = waitpid(m_pid, &status, WNOHANG);
|
||||
if (child != m_pid && (child >= 0 || errno != ECHILD))
|
||||
{
|
||||
// FIXME: this happens almost always at first iteration
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
|
||||
return false;
|
||||
}
|
||||
|
||||
close(m_fd);
|
||||
m_exit_code = WEXITSTATUS(status);
|
||||
|
||||
m_running = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void internal::executor::stop()
|
||||
{
|
||||
// Loop until the user closes the dialog
|
||||
while (!ready())
|
||||
;
|
||||
}
|
||||
|
||||
// dialog implementation
|
||||
|
||||
inline bool internal::dialog::ready(int timeout /* = default_wait_timeout */) const
|
||||
{
|
||||
return m_async->ready(timeout);
|
||||
}
|
||||
|
||||
inline bool internal::dialog::kill() const
|
||||
{
|
||||
return m_async->kill();
|
||||
}
|
||||
|
||||
inline internal::dialog::dialog()
|
||||
: m_async(std::make_shared<executor>())
|
||||
{
|
||||
}
|
||||
|
||||
inline std::vector<std::string> internal::dialog::desktop_helper() const
|
||||
{
|
||||
return { flags(flag::has_zenity) ? "zenity"
|
||||
: flags(flag::has_matedialog) ? "matedialog"
|
||||
: flags(flag::has_qarma) ? "qarma"
|
||||
: flags(flag::has_kdialog) ? "kdialog"
|
||||
: "echo" };
|
||||
}
|
||||
|
||||
inline std::string internal::dialog::buttons_to_name(choice _choice)
|
||||
{
|
||||
switch (_choice)
|
||||
{
|
||||
case choice::ok_cancel: return "okcancel";
|
||||
case choice::yes_no: return "yesno";
|
||||
case choice::yes_no_cancel: return "yesnocancel";
|
||||
case choice::retry_cancel: return "retrycancel";
|
||||
case choice::abort_retry_ignore: return "abortretryignore";
|
||||
/* case choice::ok: */ default: return "ok";
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string internal::dialog::get_icon_name(icon _icon)
|
||||
{
|
||||
switch (_icon)
|
||||
{
|
||||
case icon::warning: return "warning";
|
||||
case icon::error: return "error";
|
||||
case icon::question: return "question";
|
||||
// Zenity wants "information" but WinForms wants "info"
|
||||
/* case icon::info: */ default:
|
||||
return "information";
|
||||
}
|
||||
}
|
||||
|
||||
// THis is only used for debugging purposes
|
||||
inline std::ostream& operator <<(std::ostream &s, std::vector<std::string> const &v)
|
||||
{
|
||||
int not_first = 0;
|
||||
for (auto &e : v)
|
||||
s << (not_first++ ? " " : "") << e;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Properly quote a string for Powershell: replace ' or " with '' or ""
|
||||
// FIXME: we should probably get rid of newlines!
|
||||
// FIXME: the \" sequence seems unsafe, too!
|
||||
// XXX: this is no longer used but I would like to keep it around just in case
|
||||
inline std::string internal::dialog::powershell_quote(std::string const &str) const
|
||||
{
|
||||
return "'" + std::regex_replace(str, std::regex("['\"]"), "$&$&") + "'";
|
||||
}
|
||||
|
||||
// Properly quote a string for the shell: just replace ' with '\''
|
||||
// XXX: this is no longer used but I would like to keep it around just in case
|
||||
inline std::string internal::dialog::shell_quote(std::string const &str) const
|
||||
{
|
||||
return "'" + std::regex_replace(str, std::regex("'"), "'\\''") + "'";
|
||||
}
|
||||
|
||||
// file_dialog implementation
|
||||
|
||||
inline internal::file_dialog::file_dialog(type in_type,
|
||||
std::string const &title,
|
||||
std::string const &default_path /* = "" */,
|
||||
std::vector<std::string> const &filters /* = {} */,
|
||||
opt options /* = opt::none */)
|
||||
{
|
||||
|
||||
auto command = desktop_helper();
|
||||
|
||||
if (is_zenity())
|
||||
{
|
||||
command.push_back("--file-selection");
|
||||
command.push_back("--filename=" + default_path);
|
||||
command.push_back("--title");
|
||||
command.push_back(title);
|
||||
command.push_back("--separator=\n");
|
||||
|
||||
for (size_t i = 0; i < filters.size() / 2; ++i)
|
||||
{
|
||||
command.push_back("--file-filter");
|
||||
command.push_back(filters[2 * i] + "|" + filters[2 * i + 1]);
|
||||
}
|
||||
|
||||
if (in_type == type::save)
|
||||
command.push_back("--save");
|
||||
if (in_type == type::folder)
|
||||
command.push_back("--directory");
|
||||
if (!(options & opt::force_overwrite))
|
||||
command.push_back("--confirm-overwrite");
|
||||
if (options & opt::multiselect)
|
||||
command.push_back("--multiple");
|
||||
}
|
||||
else if (is_kdialog())
|
||||
{
|
||||
switch (in_type)
|
||||
{
|
||||
case type::save: command.push_back("--getsavefilename"); break;
|
||||
case type::open: command.push_back("--getopenfilename"); break;
|
||||
case type::folder: command.push_back("--getexistingdirectory"); break;
|
||||
}
|
||||
if (options & opt::multiselect)
|
||||
command.push_back(" --multiple");
|
||||
|
||||
command.push_back(default_path);
|
||||
|
||||
std::string filter;
|
||||
for (size_t i = 0; i < filters.size() / 2; ++i)
|
||||
filter += (i == 0 ? "" : " | ") + filters[2 * i] + "(" + filters[2 * i + 1] + ")";
|
||||
command.push_back(filter);
|
||||
|
||||
command.push_back("--title");
|
||||
command.push_back(title);
|
||||
}
|
||||
|
||||
if (flags(flag::is_verbose))
|
||||
std::cerr << command << std::endl;
|
||||
|
||||
m_async->start_process(command);
|
||||
}
|
||||
|
||||
inline std::string internal::file_dialog::string_result()
|
||||
{
|
||||
auto ret = m_async->result();
|
||||
// Strip potential trailing newline (zenity). Also strip trailing slash.
|
||||
while (!ret.empty() && (ret.back() == '\n' || ret.back() == '/'))
|
||||
ret.pop_back();
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline std::vector<std::string> internal::file_dialog::vector_result()
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
auto result = m_async->result();
|
||||
for (;;)
|
||||
{
|
||||
// Split result along newline characters
|
||||
auto i = result.find('\n');
|
||||
if (i == 0 || i == std::string::npos)
|
||||
break;
|
||||
ret.push_back(result.substr(0, i));
|
||||
result = result.substr(i + 1, result.size());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// notify implementation
|
||||
|
||||
inline notify::notify(std::string const &title,
|
||||
std::string const &message,
|
||||
icon _icon /* = icon::info */)
|
||||
{
|
||||
if (_icon == icon::question) // Not supported by notifications
|
||||
_icon = icon::info;
|
||||
|
||||
|
||||
auto command = desktop_helper();
|
||||
|
||||
if (is_zenity())
|
||||
{
|
||||
command.push_back("--notification");
|
||||
command.push_back("--window-icon");
|
||||
command.push_back(get_icon_name(_icon));
|
||||
command.push_back("--text");
|
||||
command.push_back(title + "\n" + message);
|
||||
}
|
||||
else if (is_kdialog())
|
||||
{
|
||||
command.push_back("--icon");
|
||||
command.push_back(get_icon_name(_icon));
|
||||
command.push_back("--title");
|
||||
command.push_back(title);
|
||||
command.push_back("--passivepopup");
|
||||
command.push_back(message);
|
||||
command.push_back("5");
|
||||
}
|
||||
|
||||
if (flags(flag::is_verbose))
|
||||
std::cerr << command << std::endl;
|
||||
|
||||
m_async->start_process(command);
|
||||
}
|
||||
|
||||
// message implementation
|
||||
|
||||
inline message::message(std::string const &title,
|
||||
std::string const &text,
|
||||
choice _choice /* = choice::ok_cancel */,
|
||||
icon _icon /* = icon::info */)
|
||||
{
|
||||
auto command = desktop_helper();
|
||||
if (is_zenity())
|
||||
{
|
||||
switch (_choice)
|
||||
{
|
||||
case choice::ok_cancel:
|
||||
command.insert(command.end(), { "--question", "--cancel-label=Cancel", "--ok-label=OK" }); break;
|
||||
case choice::yes_no:
|
||||
// Do not use standard --question because it causes “No” to return -1,
|
||||
// which is inconsistent with the “Yes/No/Cancel” mode below.
|
||||
command.insert(command.end(), { "--question", "--switch", "--extra-button=No", "--extra-button=Yes" }); break;
|
||||
case choice::yes_no_cancel:
|
||||
command.insert(command.end(), { "--question", "--switch", "--extra-button=Cancel", "--extra-button=No", "--extra-button=Yes" }); break;
|
||||
case choice::retry_cancel:
|
||||
command.insert(command.end(), { "--question", "--switch", "--extra-button=Cancel", "--extra-button=Retry" }); break;
|
||||
case choice::abort_retry_ignore:
|
||||
command.insert(command.end(), { "--question", "--switch", "--extra-button=Ignore", "--extra-button=Abort", "--extra-button=Retry" }); break;
|
||||
case choice::ok:
|
||||
default:
|
||||
switch (_icon)
|
||||
{
|
||||
case icon::error: command.push_back("--error"); break;
|
||||
case icon::warning: command.push_back("--warning"); break;
|
||||
default: command.push_back("--info"); break;
|
||||
}
|
||||
}
|
||||
|
||||
command.insert(command.end(), { "--title", title,
|
||||
"--width=300", "--height=0", // sensible defaults
|
||||
"--text", text,
|
||||
"--icon-name=dialog-" + get_icon_name(_icon) });
|
||||
}
|
||||
else if (is_kdialog())
|
||||
{
|
||||
if (_choice == choice::ok)
|
||||
{
|
||||
switch (_icon)
|
||||
{
|
||||
case icon::error: command.push_back("--error"); break;
|
||||
case icon::warning: command.push_back("--sorry"); break;
|
||||
default: command.push_back("--msgbox"); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string flag = "--";
|
||||
if (_icon == icon::warning || _icon == icon::error)
|
||||
flag += "warning";
|
||||
flag += "yesno";
|
||||
if (_choice == choice::yes_no_cancel)
|
||||
flag += "cancel";
|
||||
command.push_back(flag);
|
||||
if (_choice == choice::yes_no || _choice == choice::yes_no_cancel)
|
||||
{
|
||||
m_mappings[0] = button::yes;
|
||||
m_mappings[256] = button::no;
|
||||
}
|
||||
}
|
||||
|
||||
command.push_back(text);
|
||||
command.push_back("--title");
|
||||
command.push_back(title);
|
||||
|
||||
// Must be after the above part
|
||||
if (_choice == choice::ok_cancel)
|
||||
command.insert(command.end(), { "--yes-label", "OK", "--no-label", "Cancel" });
|
||||
}
|
||||
|
||||
if (flags(flag::is_verbose))
|
||||
std::cerr << command << std::endl;
|
||||
|
||||
m_async->start_process(command);
|
||||
}
|
||||
|
||||
inline button message::result()
|
||||
{
|
||||
int exit_code;
|
||||
auto ret = m_async->result(&exit_code);
|
||||
if (exit_code < 0 || // this means cancel
|
||||
internal::ends_with(ret, "Cancel\n"))
|
||||
return button::cancel;
|
||||
if (internal::ends_with(ret, "OK\n"))
|
||||
return button::ok;
|
||||
if (internal::ends_with(ret, "Yes\n"))
|
||||
return button::yes;
|
||||
if (internal::ends_with(ret, "No\n"))
|
||||
return button::no;
|
||||
if (internal::ends_with(ret, "Abort\n"))
|
||||
return button::abort;
|
||||
if (internal::ends_with(ret, "Retry\n"))
|
||||
return button::retry;
|
||||
if (internal::ends_with(ret, "Ignore\n"))
|
||||
return button::ignore;
|
||||
if (m_mappings.count(exit_code) != 0)
|
||||
return m_mappings[exit_code];
|
||||
return exit_code == 0 ? button::ok : button::cancel;
|
||||
}
|
||||
|
||||
// open_file implementation
|
||||
|
||||
inline open_file::open_file(std::string const &title,
|
||||
std::string const &default_path /* = "" */,
|
||||
std::vector<std::string> const &filters /* = { "All Files", "*" } */,
|
||||
opt options /* = opt::none */)
|
||||
: file_dialog(type::open, title, default_path, filters, options)
|
||||
{
|
||||
}
|
||||
|
||||
inline open_file::open_file(std::string const &title,
|
||||
std::string const &default_path,
|
||||
std::vector<std::string> const &filters,
|
||||
bool allow_multiselect)
|
||||
: open_file(title, default_path, filters,
|
||||
(allow_multiselect ? opt::multiselect : opt::none))
|
||||
{
|
||||
}
|
||||
|
||||
inline std::vector<std::string> open_file::result()
|
||||
{
|
||||
return vector_result();
|
||||
}
|
||||
|
||||
// save_file implementation
|
||||
|
||||
inline save_file::save_file(std::string const &title,
|
||||
std::string const &default_path /* = "" */,
|
||||
std::vector<std::string> const &filters /* = { "All Files", "*" } */,
|
||||
opt options /* = opt::none */)
|
||||
: file_dialog(type::save, title, default_path, filters, options)
|
||||
{
|
||||
}
|
||||
|
||||
inline save_file::save_file(std::string const &title,
|
||||
std::string const &default_path,
|
||||
std::vector<std::string> const &filters,
|
||||
bool confirm_overwrite)
|
||||
: save_file(title, default_path, filters,
|
||||
(confirm_overwrite ? opt::none : opt::force_overwrite))
|
||||
{
|
||||
}
|
||||
|
||||
inline std::string save_file::result()
|
||||
{
|
||||
return string_result();
|
||||
}
|
||||
|
||||
// select_folder implementation
|
||||
|
||||
inline select_folder::select_folder(std::string const &title,
|
||||
std::string const &default_path /* = "" */,
|
||||
opt options /* = opt::none */)
|
||||
: file_dialog(type::folder, title, default_path, {}, options)
|
||||
{
|
||||
}
|
||||
|
||||
inline std::string select_folder::result()
|
||||
{
|
||||
return string_result();
|
||||
}
|
||||
|
||||
#endif // SKIP_IMPLEMENTATION
|
||||
659
raytracer/nvpro_core/nvp/nvpfilesystem.cpp
Normal file
659
raytracer/nvpro_core/nvp/nvpfilesystem.cpp
Normal file
|
|
@ -0,0 +1,659 @@
|
|||
/*
|
||||
* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "nvpfilesystem.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <string.h>
|
||||
|
||||
using namespace nvp;
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <locale>
|
||||
#include <filesystem>
|
||||
#include <Windows.h>
|
||||
|
||||
void logLastWindowError(std::string context)
|
||||
{
|
||||
LPVOID messageBuffer;
|
||||
DWORD dw = GetLastError();
|
||||
DWORD numChars = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&messageBuffer, 0, NULL);
|
||||
assert(numChars);
|
||||
|
||||
#ifndef UNICODE
|
||||
std::string errorStr((char*)messageBuffer);
|
||||
#else
|
||||
#error Not implemented
|
||||
#endif
|
||||
LOGE("%s, error %lu: %s\n", context.c_str(), dw, errorStr.c_str());
|
||||
|
||||
LocalFree(messageBuffer);
|
||||
}
|
||||
|
||||
struct PathKey
|
||||
{
|
||||
std::string path;
|
||||
uint32_t eventMask;
|
||||
bool operator==(const PathKey& other) const { return path == other.path && eventMask == other.eventMask; }
|
||||
};
|
||||
|
||||
template <class A, class B>
|
||||
size_t combine_hash(const A& a, const B& b)
|
||||
{
|
||||
return std::hash<A>()(a) ^ (std::hash<B>()(b) << 1);
|
||||
}
|
||||
template <class A, class B, class C>
|
||||
size_t combine_hash(const A& a, const B& b, const C& c)
|
||||
{
|
||||
return (combine_hash(a, b) >> 1) ^ std::hash<C>()(c);
|
||||
}
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<PathKey>
|
||||
{
|
||||
std::size_t operator()(const PathKey& k) const { return ::combine_hash(k.path, k.eventMask); }
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
// Converts a UTF-8 string to a UTF-16 string. Avoids using <codecvt>, due to
|
||||
// https://github.com/microsoft/STL/issues/443.
|
||||
std::wstring utf8ToWideString(std::string utf8String)
|
||||
{
|
||||
if(utf8String.size() > std::numeric_limits<int>::max())
|
||||
{
|
||||
assert(!"Too many characters for UTF8-to-UTF16 API!");
|
||||
return L"";
|
||||
}
|
||||
const int utf8Bytes = static_cast<int>(utf8String.size());
|
||||
const int utf16Characters = MultiByteToWideChar(CP_UTF8, 0, utf8String.data(), utf8Bytes, nullptr, 0);
|
||||
if(utf16Characters < 0)
|
||||
{
|
||||
assert(!"Error counting UTF-16 characters!");
|
||||
return L"";
|
||||
}
|
||||
std::wstring result(utf16Characters, 0);
|
||||
(void)MultiByteToWideChar(CP_UTF8, 0, utf8String.data(), utf8Bytes, result.data(), utf16Characters);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Converts a UTF-16 string to a UTF-8 string. Avoids using <codecvt>, due to
|
||||
// https://github.com/microsoft/STL/issues/443.
|
||||
std::string wideToUTF8String(std::wstring utf16String)
|
||||
{
|
||||
if(utf16String.size() > std::numeric_limits<int>::max())
|
||||
{
|
||||
assert(!"Too many characters for UTF16-to-UTF8 API!");
|
||||
return "";
|
||||
}
|
||||
const int utf16Characters = static_cast<int>(utf16String.size());
|
||||
const int utf8Bytes = WideCharToMultiByte(CP_UTF8, 0, utf16String.data(), utf16Characters, nullptr, 0, nullptr, nullptr);
|
||||
if(utf8Bytes < 0)
|
||||
{
|
||||
assert(!"Error counting UTF-8 bytes!");
|
||||
return "";
|
||||
}
|
||||
std::string result(utf8Bytes, 0);
|
||||
(void)WideCharToMultiByte(CP_UTF8, 0, utf16String.data(), utf16Characters, result.data(), utf8Bytes, nullptr, nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct PathInstance
|
||||
{
|
||||
FileSystemMonitor::PathID id;
|
||||
void* userPtr;
|
||||
|
||||
bool operator==(const FileSystemMonitor::PathID& pathID) const { return id == pathID; }
|
||||
};
|
||||
|
||||
/** Class to handle receiving per-directory filesystem events
|
||||
*
|
||||
* This class is reused to provide per-file events and distribute events to multiple listeners.
|
||||
* Each WindowsPathMonitor can only be a file monitor (filtered events) or a directory monitor (all events), not both.
|
||||
*
|
||||
* Structure:
|
||||
* - WindowsPathMonitor - monitored directory, receiving OS events
|
||||
* - Instances (PathID + userData) for multiple directory listeners of per-directory monitoring
|
||||
* - Sub-paths for per-file monitoring
|
||||
* - Instances (PathID + userData) for multiple file listeners
|
||||
*/
|
||||
struct WindowsPathMonitor : PathKey
|
||||
{
|
||||
WindowsPathMonitor(PathKey key)
|
||||
: PathKey{key}
|
||||
, m_overlapped{}
|
||||
, m_eventsRequested{false}
|
||||
{
|
||||
// Translate the event mask
|
||||
m_winEventFilter = 0;
|
||||
if(eventMask & (FileSystemMonitor::FSM_CREATE | FileSystemMonitor::FSM_DELETE))
|
||||
m_winEventFilter |= FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||
if(eventMask & FileSystemMonitor::FSM_MODIFY)
|
||||
m_winEventFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
|
||||
// Open the path to receive events from it
|
||||
std::wstring pathW = utf8ToWideString(path);
|
||||
DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||
DWORD flags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
|
||||
m_dirHandle = CreateFileW(pathW.c_str(), GENERIC_READ, shareMode, NULL, OPEN_EXISTING, flags, NULL);
|
||||
if(m_dirHandle == INVALID_HANDLE_VALUE)
|
||||
logLastWindowError(std::string("FileSystemMonitor: Error in CreateFileW for path ") + path);
|
||||
}
|
||||
|
||||
~WindowsPathMonitor()
|
||||
{
|
||||
if(m_eventsRequested)
|
||||
cancelAsync();
|
||||
|
||||
if(m_dirHandle != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(m_dirHandle);
|
||||
}
|
||||
|
||||
bool getEventsAsync()
|
||||
{
|
||||
BOOL result = FALSE;
|
||||
#if(_WIN32_WINNT >= 0x0400)
|
||||
#if(NTDDI_VERSION >= NTDDI_WIN10_RS3)
|
||||
m_eventsRequested = true;
|
||||
result = ReadDirectoryChangesExW(m_dirHandle, m_eventBuffer.data(), (DWORD)m_eventBuffer.size(), TRUE,
|
||||
m_winEventFilter, NULL, &m_overlapped, NULL, ReadDirectoryNotifyExtendedInformation);
|
||||
if(result == FALSE)
|
||||
logLastWindowError(std::string("FileSystemMonitor: Error in ReadDirectoryChangesW for path ") + path);
|
||||
#endif
|
||||
#endif
|
||||
return result == TRUE;
|
||||
}
|
||||
|
||||
void cancelAsync()
|
||||
{
|
||||
assert(m_eventsRequested);
|
||||
m_eventsRequested = false;
|
||||
CancelIoEx(m_dirHandle, &m_overlapped);
|
||||
}
|
||||
|
||||
// Avoid throwing an exception in the constructor with an is-valid method.
|
||||
bool isValid() { return m_dirHandle != INVALID_HANDLE_VALUE; }
|
||||
|
||||
HANDLE m_dirHandle;
|
||||
DWORD m_winEventFilter;
|
||||
std::array<uint8_t, 63 * 1024> m_eventBuffer;
|
||||
OVERLAPPED m_overlapped;
|
||||
bool m_eventsRequested;
|
||||
std::unordered_map<std::string, std::vector<PathInstance>> m_fileInstances;
|
||||
std::vector<PathInstance> m_directoryInstances;
|
||||
};
|
||||
|
||||
class FileSystemMonitorWindows : public FileSystemMonitor
|
||||
{
|
||||
friend class FileSystemMonitor;
|
||||
|
||||
virtual PathID add(const std::string& path, uint32_t eventMask, void* userPtr) override
|
||||
{
|
||||
std::string monitorDir{path};
|
||||
|
||||
// Workaround monitoring individual files by monitoring the entire directory
|
||||
std::filesystem::path pathObj{path};
|
||||
bool isDir = std::filesystem::is_directory(pathObj);
|
||||
if(!isDir)
|
||||
{
|
||||
monitorDir = pathObj.parent_path().string();
|
||||
}
|
||||
|
||||
// Track the path if it isn't tracked already
|
||||
WindowsPathMonitor* monitor;
|
||||
auto key = PathKey{monitorDir, eventMask};
|
||||
auto it = m_paths.find(key);
|
||||
if(it != m_paths.end())
|
||||
{
|
||||
monitor = it->second.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto pathMonitor = std::make_unique<WindowsPathMonitor>(key);
|
||||
if(!pathMonitor->isValid())
|
||||
return false;
|
||||
|
||||
// Get async events for this path on the main port
|
||||
ULONG_PTR objectPtr = reinterpret_cast<ULONG_PTR>(pathMonitor.get());
|
||||
if(CreateIoCompletionPort(pathMonitor->m_dirHandle, m_ioCompletionPort, objectPtr, 1) != m_ioCompletionPort)
|
||||
{
|
||||
logLastWindowError("CreateIoCompletionPort in path monitoring");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!pathMonitor->getEventsAsync())
|
||||
return false;
|
||||
|
||||
monitor = pathMonitor.get();
|
||||
m_paths[key] = std::move(pathMonitor);
|
||||
}
|
||||
|
||||
auto id = nextPathID();
|
||||
PathInstance instance{id, userPtr};
|
||||
|
||||
if(isDir)
|
||||
monitor->m_directoryInstances.push_back(instance);
|
||||
else
|
||||
monitor->m_fileInstances[pathObj.filename().string()].push_back(instance);
|
||||
|
||||
m_idToMonitor[id] = monitor;
|
||||
return id;
|
||||
}
|
||||
|
||||
virtual void remove(const PathID& pathID) override
|
||||
{
|
||||
auto& monitor = *m_idToMonitor[pathID];
|
||||
bool isDirMonitor = monitor.m_fileInstances.empty();
|
||||
bool instancesEmpty;
|
||||
// TODO: lots of slow linear searching here, although both monitoring many files and remove() is expected to be uncommon.
|
||||
if(isDirMonitor)
|
||||
{
|
||||
// Find and remove the pathID instance in m_directoryInstances.
|
||||
auto it = std::find(monitor.m_directoryInstances.begin(), monitor.m_directoryInstances.end(), pathID);
|
||||
monitor.m_directoryInstances.erase(it);
|
||||
instancesEmpty = monitor.m_directoryInstances.empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find and remove the pathID instance in m_fileInstances. It could be inside any one of the monitored files (sub-paths).
|
||||
for(auto& file : monitor.m_fileInstances)
|
||||
{
|
||||
auto& instances = file.second;
|
||||
auto it = std::find(instances.begin(), instances.end(), pathID);
|
||||
if(it == instances.end())
|
||||
continue;
|
||||
instances.erase(it);
|
||||
if(instances.empty())
|
||||
monitor.m_fileInstances.erase(file.first);
|
||||
break;
|
||||
}
|
||||
instancesEmpty = monitor.m_fileInstances.empty();
|
||||
}
|
||||
|
||||
// If there are no instances left, remove the monitor.
|
||||
if(instancesEmpty)
|
||||
m_paths.erase((PathKey)monitor);
|
||||
|
||||
m_idToMonitor.erase(pathID);
|
||||
}
|
||||
|
||||
void handleEvent(WindowsPathMonitor* pathMonitor, const FILE_NOTIFY_EXTENDED_INFORMATION* notifyInfo, const Callback& callback)
|
||||
{
|
||||
std::wstring subPathW(notifyInfo->FileName, notifyInfo->FileNameLength / sizeof(WCHAR));
|
||||
std::string subPath = wideToUTF8String(subPathW);
|
||||
|
||||
bool isDirMonitor = pathMonitor->m_fileInstances.empty();
|
||||
std::vector<PathInstance>* instances;
|
||||
if(!isDirMonitor)
|
||||
{
|
||||
// Filter out files not monitored in this directoriy
|
||||
auto it = pathMonitor->m_fileInstances.find(subPath);
|
||||
if(it == pathMonitor->m_fileInstances.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
instances = &it->second;
|
||||
}
|
||||
else
|
||||
instances = &pathMonitor->m_directoryInstances;
|
||||
|
||||
std::filesystem::path fullPath = pathMonitor->path;
|
||||
fullPath /= subPath; // "/" adds the paths
|
||||
|
||||
LOGI("FileSystemMonitor %p event (mask %lx) for '%s'\n", m_ioCompletionPort, notifyInfo->Action, fullPath.string().c_str());
|
||||
|
||||
for(auto& instance : *instances)
|
||||
{
|
||||
switch(notifyInfo->Action)
|
||||
{
|
||||
case FILE_ACTION_ADDED:
|
||||
case FILE_ACTION_RENAMED_NEW_NAME:
|
||||
if(pathMonitor->eventMask & FSM_CREATE)
|
||||
callback(EventData{FSM_CREATE, fullPath.string(), instance.userPtr});
|
||||
break;
|
||||
case FILE_ACTION_REMOVED:
|
||||
case FILE_ACTION_RENAMED_OLD_NAME:
|
||||
if(pathMonitor->eventMask & FSM_DELETE)
|
||||
callback(EventData{FSM_DELETE, fullPath.string(), instance.userPtr});
|
||||
break;
|
||||
case FILE_ACTION_MODIFIED:
|
||||
if(pathMonitor->eventMask & FSM_MODIFY)
|
||||
callback(EventData{FSM_MODIFY, fullPath.string(), instance.userPtr});
|
||||
break;
|
||||
default:
|
||||
// TODO: don't throw away free information
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool checkEvents(const Callback& callback) override
|
||||
{
|
||||
DWORD bytesTransferred = 0; // FILE_NOTIFY_EXTENDED_INFORMATION struct size
|
||||
ULONG_PTR userObject = 0;
|
||||
OVERLAPPED* overlapped = NULL;
|
||||
if(GetQueuedCompletionStatus(m_ioCompletionPort, &bytesTransferred, &userObject, &overlapped, INFINITE) == FALSE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle the case that cancel() unblocked the call
|
||||
if(reinterpret_cast<ULONG_PTR>(this) == userObject)
|
||||
{
|
||||
bool cancelling = m_cancelling;
|
||||
m_cancelling = false;
|
||||
return !cancelling;
|
||||
}
|
||||
|
||||
auto pathMonitor = reinterpret_cast<WindowsPathMonitor*>(userObject);
|
||||
const auto& buffer = pathMonitor->m_eventBuffer;
|
||||
|
||||
size_t offset = 0;
|
||||
for(;;)
|
||||
{
|
||||
auto notifyInfo = reinterpret_cast<const FILE_NOTIFY_EXTENDED_INFORMATION*>(buffer.data() + offset);
|
||||
handleEvent(pathMonitor, notifyInfo, callback);
|
||||
|
||||
// Re-arm the event
|
||||
if(!pathMonitor->getEventsAsync())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!notifyInfo->NextEntryOffset)
|
||||
break;
|
||||
|
||||
offset += notifyInfo->NextEntryOffset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void cancel() override
|
||||
{
|
||||
ULONG_PTR objectPtr = reinterpret_cast<ULONG_PTR>(this);
|
||||
m_cancelling = true;
|
||||
PostQueuedCompletionStatus(m_ioCompletionPort, 0, objectPtr, NULL);
|
||||
}
|
||||
|
||||
FileSystemMonitorWindows()
|
||||
{
|
||||
m_ioCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
|
||||
assert(m_ioCompletionPort != NULL);
|
||||
}
|
||||
|
||||
virtual ~FileSystemMonitorWindows() override
|
||||
{
|
||||
assert(!m_cancelling);
|
||||
CloseHandle(m_ioCompletionPort);
|
||||
}
|
||||
|
||||
HANDLE m_ioCompletionPort;
|
||||
bool m_cancelling = false;
|
||||
|
||||
std::unordered_map<PathKey, std::unique_ptr<WindowsPathMonitor>> m_paths;
|
||||
|
||||
// Reuse directory monitors that are created to monitor specific files
|
||||
std::unordered_map<PathID, WindowsPathMonitor*> m_idToMonitor;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(LINUX)
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
/** Object to track monitored paths
|
||||
*
|
||||
* inotify will not create multiple watches if the same path is added twice.
|
||||
* To handle multiple FileSystemMonitor clients, this is faked with a list of
|
||||
* instances per path.
|
||||
*/
|
||||
struct InotifyPath
|
||||
{
|
||||
/** File or directory name */
|
||||
std::string path;
|
||||
|
||||
/** Union of events that all instances request */
|
||||
uint32_t inotifyMaskAll;
|
||||
|
||||
/** Per-instance structure requesting events for this path */
|
||||
struct MonitorInstance
|
||||
{
|
||||
void* userPtr;
|
||||
uint32_t inotifyMask;
|
||||
};
|
||||
|
||||
std::unordered_map<FileSystemMonitor::PathID, MonitorInstance> instances;
|
||||
};
|
||||
|
||||
class FileSystemMonitorInotify : public FileSystemMonitor
|
||||
{
|
||||
friend class FileSystemMonitor;
|
||||
|
||||
virtual int add(const std::string& path, uint32_t eventMask, void* userPtr) override
|
||||
{
|
||||
// Translate the event mask
|
||||
uint32_t inotifyMask = 0;
|
||||
if(eventMask & FSM_CREATE)
|
||||
inotifyMask |= IN_CREATE;
|
||||
if(eventMask & FSM_MODIFY)
|
||||
inotifyMask |= IN_MODIFY;
|
||||
if(eventMask & FSM_DELETE)
|
||||
inotifyMask |= IN_DELETE;
|
||||
|
||||
assert(inotifyMask);
|
||||
if(!inotifyMask)
|
||||
return INVALID_PATH_ID;
|
||||
|
||||
int watchDescriptor = inotify_add_watch(m_inotifyFd, path.c_str(), inotifyMask);
|
||||
if(watchDescriptor == -1)
|
||||
return INVALID_PATH_ID;
|
||||
|
||||
auto id = nextPathID();
|
||||
//LOGI("FileSystemMonitor %i added %i %s\n", m_inotifyFd, id, path.c_str());
|
||||
|
||||
auto it = m_paths.find(watchDescriptor);
|
||||
if(it != m_paths.end())
|
||||
{
|
||||
// Path has already been added before. May need to combine the mask and re-add.
|
||||
auto allBits = it->second.inotifyMaskAll | inotifyMask;
|
||||
if(allBits != inotifyMask)
|
||||
(void)inotify_add_watch(m_inotifyFd, path.c_str(), allBits);
|
||||
it->second.inotifyMaskAll = allBits;
|
||||
|
||||
it->second.instances[id] = {userPtr, inotifyMask};
|
||||
}
|
||||
else
|
||||
{
|
||||
m_paths[watchDescriptor] = InotifyPath{path, inotifyMask, {{id, InotifyPath::MonitorInstance{userPtr, inotifyMask}}}};
|
||||
}
|
||||
m_idToWatchDescriptor[id] = watchDescriptor;
|
||||
return id;
|
||||
}
|
||||
|
||||
virtual void remove(const PathID& pathID) override
|
||||
{
|
||||
int watchDescriptor = m_idToWatchDescriptor[pathID];
|
||||
auto& path = m_paths[watchDescriptor];
|
||||
//LOGI("FileSystemMonitor %i removed %i %s\n", m_inotifyFd, pathID, path.path.c_str());
|
||||
m_idToWatchDescriptor.erase(pathID);
|
||||
path.instances.erase(pathID);
|
||||
if(path.instances.empty())
|
||||
{
|
||||
//LOGI("FileSystemMonitor %i removing inotify watch\n", m_inotifyFd);
|
||||
int result = inotify_rm_watch(m_inotifyFd, watchDescriptor);
|
||||
assert(result == 0);
|
||||
m_paths.erase(watchDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool checkEvents(const Callback& callback) override
|
||||
{
|
||||
struct pollfd fds[]{
|
||||
{m_inotifyFd, POLLIN},
|
||||
{m_cancelFd, POLLIN},
|
||||
};
|
||||
const auto nfds = sizeof(fds) / sizeof(fds[0]);
|
||||
|
||||
// Block until there are inotify events, or cancel() is called.
|
||||
//LOGI("FileSystemMonitor %i poll enter\n", m_inotifyFd);
|
||||
int fdsReady = poll(fds, nfds, -1);
|
||||
//LOGI("FileSystemMonitor %i poll exit\n", m_inotifyFd);
|
||||
assert(fdsReady >= 0);
|
||||
if(fdsReady == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(fdsReady == -1)
|
||||
{
|
||||
if(errno == EINTR || errno == EAGAIN)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stop checking events because some error happened
|
||||
LOGE("Error in poll(inotify-fd)\n");
|
||||
return false;
|
||||
}
|
||||
if(fds[1].revents & POLLIN)
|
||||
{
|
||||
uint64_t val = 0;
|
||||
const ssize_t numBytes = read(m_cancelFd, &val, sizeof(val));
|
||||
assert(val == 1);
|
||||
assert(numBytes == sizeof(val));
|
||||
|
||||
// Stop checking events because cancel() was called
|
||||
return false;
|
||||
}
|
||||
|
||||
// There was no error and m_cancelFd wasn't set. Must be an inotify event.
|
||||
assert(fds[0].revents & POLLIN);
|
||||
|
||||
// Read event data
|
||||
auto readFrom = m_eventBuffer.data() + m_eventBufferBytes;
|
||||
auto bytesLeft = m_eventBuffer.size() - m_eventBufferBytes;
|
||||
size_t bytesRead = read(m_inotifyFd, readFrom, bytesLeft);
|
||||
m_eventBufferBytes += bytesRead;
|
||||
|
||||
// Process whole events in the buffer
|
||||
size_t offset = 0;
|
||||
while(offset + sizeof(inotify_event) <= m_eventBufferBytes)
|
||||
{
|
||||
const auto& event = *reinterpret_cast<inotify_event*>(m_eventBuffer.data() + offset);
|
||||
size_t eventSize = sizeof(inotify_event) + event.len;
|
||||
if(offset + eventSize > m_eventBufferBytes)
|
||||
{
|
||||
// Incomplete event read() into buffer
|
||||
break;
|
||||
}
|
||||
|
||||
// If remove() is called, there may still be queued events. Ignore any for
|
||||
// unknown watch descriptors. IN_Q_OVERFLOW can also generate wd == -1.
|
||||
auto pathIt = m_paths.find(event.wd);
|
||||
if(pathIt != m_paths.end())
|
||||
{
|
||||
const auto& path = pathIt->second;
|
||||
for(const auto& instanceIt : path.instances)
|
||||
{
|
||||
const auto& instance = instanceIt.second;
|
||||
auto reportMask = event.mask & instance.inotifyMask;
|
||||
|
||||
// inotify only gives a name when watching directories, not files.
|
||||
auto filename = event.len ? std::string(event.name) : path.path;
|
||||
LOGI("FileSystemMonitor %i event (mask %x) for '%s'\n", m_inotifyFd, reportMask, filename.c_str());
|
||||
if(reportMask & IN_CREATE)
|
||||
callback(EventData{FSM_CREATE, filename, instance.userPtr});
|
||||
if(reportMask & IN_MODIFY)
|
||||
callback(EventData{FSM_MODIFY, filename, instance.userPtr});
|
||||
if(reportMask & IN_DELETE)
|
||||
callback(EventData{FSM_DELETE, filename, instance.userPtr});
|
||||
}
|
||||
}
|
||||
offset += eventSize;
|
||||
}
|
||||
|
||||
// Shift any remainder to the start of the buffer
|
||||
m_eventBufferBytes -= offset;
|
||||
if(offset && m_eventBufferBytes)
|
||||
{
|
||||
memmove(m_eventBuffer.data(), m_eventBuffer.data() + offset, m_eventBufferBytes);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void cancel() override
|
||||
{
|
||||
uint64_t val = 1;
|
||||
const ssize_t numBytes = write(m_cancelFd, &val, sizeof(val));
|
||||
assert(numBytes == sizeof(val));
|
||||
}
|
||||
|
||||
FileSystemMonitorInotify()
|
||||
{
|
||||
m_inotifyFd = inotify_init();
|
||||
m_cancelFd = eventfd(0, 0);
|
||||
}
|
||||
|
||||
virtual ~FileSystemMonitorInotify() override
|
||||
{
|
||||
close(m_cancelFd);
|
||||
close(m_inotifyFd);
|
||||
}
|
||||
|
||||
int m_inotifyFd;
|
||||
int m_cancelFd;
|
||||
|
||||
/** inotify provides a stream of event data. This buffer is here to hold incomplete reads. Sized to hold at least one
|
||||
* event for the max path length.
|
||||
*/
|
||||
std::array<uint8_t, sizeof(inotify_event) + PATH_MAX> m_eventBuffer;
|
||||
|
||||
size_t m_eventBufferBytes = 0;
|
||||
|
||||
/** Monitored paths, indexed by the inotify watch descriptor */
|
||||
std::unordered_map<int, InotifyPath> m_paths;
|
||||
|
||||
/* Path instance lookup, to provide multiple events for the same path */
|
||||
std::unordered_map<PathID, int> m_idToWatchDescriptor;
|
||||
};
|
||||
#endif
|
||||
|
||||
FileSystemMonitor* FileSystemMonitor::create()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return new FileSystemMonitorWindows();
|
||||
#elif defined(LINUX)
|
||||
return new FileSystemMonitorInotify();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FileSystemMonitor::destroy(FileSystemMonitor* monitor)
|
||||
{
|
||||
delete monitor;
|
||||
}
|
||||
305
raytracer/nvpro_core/nvp/nvpfilesystem.hpp
Normal file
305
raytracer/nvpro_core/nvp/nvpfilesystem.hpp
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <nvh/nvprint.hpp>
|
||||
#include <nvh/threading.hpp>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace nvp {
|
||||
|
||||
/** @DOC_START
|
||||
# class nvp::FileSystemMonitor
|
||||
|
||||
Monitors files and/or directories for changes
|
||||
|
||||
This cross-platform wrapper does not create any threads, but is designed for
|
||||
it. checkEvents() will block until either an event is generated, or cancel()
|
||||
is called. See ModifiedFilesMonitor for an example.
|
||||
@DOC_END */
|
||||
class FileSystemMonitor
|
||||
{
|
||||
public:
|
||||
/** File events.
|
||||
*
|
||||
* \note Visual studio saves files to temporary filenames and then swaps them with renames. Renames/moves are not
|
||||
* exposed explicitly yet.
|
||||
*/
|
||||
enum Event
|
||||
{
|
||||
FSM_CREATE = (1 << 0),
|
||||
FSM_DELETE = (1 << 1),
|
||||
FSM_MODIFY = (1 << 2),
|
||||
};
|
||||
|
||||
struct EventData
|
||||
{
|
||||
Event event;
|
||||
const std::string path;
|
||||
void* userPtr;
|
||||
};
|
||||
|
||||
using CallbackType = void(const EventData&);
|
||||
using Callback = std::function<CallbackType>;
|
||||
using PathID = int;
|
||||
|
||||
static const PathID INVALID_PATH_ID = 0;
|
||||
|
||||
static FileSystemMonitor* create();
|
||||
static void destroy(FileSystemMonitor* monitor);
|
||||
|
||||
/** Add a file or directory to be monitored for events
|
||||
*
|
||||
* It is not safe to call add() at the same time as checkEvents(). Call cancel() first.
|
||||
*
|
||||
* \return PathID that can be passed to remove() on success, 0 on failure.
|
||||
*/
|
||||
virtual PathID add(const std::string& path, uint32_t eventMask, void* userPtr = nullptr) = 0;
|
||||
|
||||
/** Remove a file or directory from being monitored
|
||||
*
|
||||
* It is not safe to call remove() at the same time as checkEvents(). Call cancel() first. */
|
||||
virtual void remove(const PathID& pathID) = 0;
|
||||
|
||||
/** Process the event queue, blocking until there is another event or cancel() is called.
|
||||
*
|
||||
* \return True if the thread should keep looping, i.e. there are no errors and cancel() has not been called. It is
|
||||
* expected this is called in a loop by an event thread.
|
||||
*/
|
||||
virtual bool checkEvents(const Callback& callback) = 0;
|
||||
|
||||
/** Abort checkEvents, if it is currently being called. */
|
||||
virtual void cancel() = 0;
|
||||
|
||||
protected:
|
||||
FileSystemMonitor() = default;
|
||||
virtual ~FileSystemMonitor() = default;
|
||||
PathID nextPathID()
|
||||
{
|
||||
while(++m_nextPathID == INVALID_PATH_ID)
|
||||
;
|
||||
return m_nextPathID;
|
||||
}
|
||||
|
||||
private:
|
||||
PathID m_nextPathID = 0;
|
||||
};
|
||||
|
||||
/** @DOC_START
|
||||
# class nvp::FSMRunner
|
||||
|
||||
Adds a thread to nvp::FileSystemMonitor that repeatedly calls
|
||||
nvp::FileSystemMonitor::checkEvents().
|
||||
@DOC_END */
|
||||
class FSMRunner
|
||||
{
|
||||
public:
|
||||
FSMRunner(const FileSystemMonitor::Callback& callback)
|
||||
: m_callback(callback)
|
||||
{
|
||||
m_monitor = FileSystemMonitor::create();
|
||||
}
|
||||
~FSMRunner()
|
||||
{
|
||||
stop();
|
||||
FileSystemMonitor::destroy(m_monitor);
|
||||
}
|
||||
|
||||
protected:
|
||||
void start()
|
||||
{
|
||||
assert(!m_thread.joinable());
|
||||
m_thread = std::thread(&FSMRunner::entrypoint, this);
|
||||
}
|
||||
void stop()
|
||||
{
|
||||
if(m_thread.joinable())
|
||||
{
|
||||
m_monitor->cancel();
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
FileSystemMonitor* m_monitor;
|
||||
|
||||
private:
|
||||
void entrypoint()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(!m_monitor->checkEvents(m_callback))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::thread m_thread;
|
||||
FileSystemMonitor::Callback m_callback;
|
||||
};
|
||||
|
||||
/** @DOC_START
|
||||
# class nvp::FSMCallbacks
|
||||
|
||||
Utility class to get per-path callbacks.
|
||||
|
||||
Make sure PathSetCallback objects returned by add() do not outlive the FSMCallbacks.
|
||||
Be careful not to destroy a PathCallback during a callback.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
FSMCallbacks callbacks;
|
||||
|
||||
auto callbackFile1 = callbacks.add(std::vector<std::string>{"file1.txt"}, nvp::FileSystemMonitor::FSM_MODIFY,
|
||||
[this](nvp::FileSystemMonitor::EventData ev) {
|
||||
// Do something with file1.txt
|
||||
});
|
||||
|
||||
auto callbackFile2 = callbacks.add(std::vector<std::string>{"file2.txt"}, nvp::FileSystemMonitor::FSM_MODIFY,
|
||||
[this](nvp::FileSystemMonitor::EventData ev) {
|
||||
// Do something with file2.txt
|
||||
});
|
||||
|
||||
// When callbackFile1 goes out of scope, file1.txt stops being monitored
|
||||
callbackFile1.reset()
|
||||
```
|
||||
@DOC_END */
|
||||
class FSMCallbacks : public FSMRunner
|
||||
{
|
||||
public:
|
||||
using Clock = nvh::DefaultDelayClock;
|
||||
using Duration = nvh::DefaultDelayDuration;
|
||||
|
||||
struct PathSetCallbackData
|
||||
{
|
||||
FSMCallbacks& owner;
|
||||
std::vector<FileSystemMonitor::PathID> ids;
|
||||
FileSystemMonitor::Callback callback;
|
||||
const Duration consolidateDelay;
|
||||
nvh::delayed_call<Clock, Duration> delayedCall;
|
||||
~PathSetCallbackData()
|
||||
{
|
||||
if(!ids.empty())
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(owner.m_monitorMutex);
|
||||
owner.stop();
|
||||
for(const auto& id : ids)
|
||||
owner.m_monitor->remove(id);
|
||||
owner.start();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using PathSetCallback = std::shared_ptr<PathSetCallbackData>;
|
||||
|
||||
FSMCallbacks()
|
||||
: FSMRunner(&FSMCallbacks::callback)
|
||||
{
|
||||
}
|
||||
|
||||
template <class List>
|
||||
PathSetCallback add(const List& pathList,
|
||||
uint32_t eventMask,
|
||||
const FileSystemMonitor::Callback& callback,
|
||||
const Duration& consolidateDelay = Duration::zero())
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_monitorMutex);
|
||||
stop();
|
||||
|
||||
PathSetCallback pathSetCallback{new PathSetCallbackData{*this, {}, callback, consolidateDelay}};
|
||||
for(const auto& path : pathList)
|
||||
{
|
||||
auto pathID = m_monitor->add(path, eventMask, pathSetCallback.get());
|
||||
if(pathID == FileSystemMonitor::INVALID_PATH_ID)
|
||||
{
|
||||
LOGE("Failed to watch '%s' for changes\n", path.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
pathSetCallback->ids.push_back(pathID);
|
||||
}
|
||||
}
|
||||
|
||||
start();
|
||||
return pathSetCallback;
|
||||
}
|
||||
|
||||
private:
|
||||
static void callback(const nvp::FileSystemMonitor::EventData& ev)
|
||||
{
|
||||
auto cbData = reinterpret_cast<PathSetCallbackData*>(ev.userPtr);
|
||||
|
||||
if(cbData->consolidateDelay != Duration::zero())
|
||||
{
|
||||
// This may block if the previous delayed call started and is still running. Rather than do anything clever here,
|
||||
// it is left to the user to layer on functionality and make sure this callback loop is not blocked.
|
||||
if(!cbData->delayedCall.delay_for(cbData->consolidateDelay))
|
||||
cbData->delayedCall = nvh::delay_noreturn_for<Clock>(cbData->consolidateDelay, cbData->callback, ev);
|
||||
}
|
||||
else
|
||||
cbData->callback(ev);
|
||||
}
|
||||
|
||||
// Protect against various threads racing FSMRunner::stop()/start() calls during add() and ~PathSetCallbackData().
|
||||
std::mutex m_monitorMutex;
|
||||
};
|
||||
|
||||
/** @DOC_START
|
||||
# class nvp::ModifiedFilesMonitor
|
||||
|
||||
Monitors files and/or directories for changes.
|
||||
|
||||
Starts a thread at creation. Be careful to keep it in scope while events are needed.
|
||||
|
||||
This cross-platform wrapper does not create any threads, but is designed to be
|
||||
used with one. checkEvents() will block until either an event is generated, or
|
||||
cancel() is called.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
std::vector<std::string> dirs = {"shaders_bin"};
|
||||
nvp::FileSystemMonitor::Callback callback = [](nvp::FileSystemMonitor::EventData ev){
|
||||
g_reloadShaders = true;
|
||||
};
|
||||
auto fileMonitor = std::make_unique<nvp::ModifiedFilesMonitor>(dirs, callback);
|
||||
```
|
||||
@DOC_END */
|
||||
class ModifiedFilesMonitor : public FSMRunner
|
||||
{
|
||||
public:
|
||||
template <class List>
|
||||
ModifiedFilesMonitor(const List& paths, const FileSystemMonitor::Callback& callback)
|
||||
: FSMRunner(callback)
|
||||
{
|
||||
for(const auto& path : paths)
|
||||
{
|
||||
if(m_monitor->add(path, FileSystemMonitor::FSM_MODIFY | FileSystemMonitor::FSM_CREATE) == FileSystemMonitor::INVALID_PATH_ID)
|
||||
{
|
||||
LOGE("Failed to watch '%s' for changes\n", path.c_str());
|
||||
}
|
||||
}
|
||||
start();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace nvp
|
||||
104
raytracer/nvpro_core/nvp/nvpsystem.cpp
Normal file
104
raytracer/nvpro_core/nvp/nvpsystem.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#include "nvpsystem.hpp"
|
||||
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#ifdef NVP_SUPPORTS_SOCKETS
|
||||
#include "socketSampleMessages.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
static bool s_sysInit = false;
|
||||
|
||||
static void cb_errorfun(int, const char* str)
|
||||
{
|
||||
LOGE("%s\n", str);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Message pump
|
||||
void NVPSystem::pollEvents()
|
||||
{
|
||||
#ifdef NVP_SUPPORTS_SOCKETS
|
||||
// check the stack of messages from remote connection, first
|
||||
processRemoteMessages();
|
||||
#endif
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
void NVPSystem::waitEvents()
|
||||
{
|
||||
glfwWaitEvents();
|
||||
}
|
||||
|
||||
void NVPSystem::postTiming(float ms, int fps, const char* details)
|
||||
{
|
||||
#ifdef NVP_SUPPORTS_SOCKETS
|
||||
::postTiming(ms, fps, details);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
double NVPSystem::getTime()
|
||||
{
|
||||
return glfwGetTime();
|
||||
}
|
||||
|
||||
|
||||
void NVPSystem::init(const char* projectName)
|
||||
{
|
||||
std::string logfile = std::string("log_") + std::string(projectName) + std::string(".txt");
|
||||
nvprintSetLogFileName(logfile.c_str());
|
||||
|
||||
int result = glfwInit();
|
||||
if(!result)
|
||||
{
|
||||
LOGE("could not init glfw\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
glfwSetErrorCallback(cb_errorfun);
|
||||
|
||||
//initNSight();
|
||||
#ifdef NVP_SUPPORTS_SOCKETS
|
||||
//
|
||||
// Socket init if needed
|
||||
//
|
||||
startSocketServer(/*port*/ 1056);
|
||||
#endif
|
||||
|
||||
s_sysInit = true;
|
||||
platformInit();
|
||||
}
|
||||
|
||||
void NVPSystem::deinit()
|
||||
{
|
||||
platformDeinit();
|
||||
glfwTerminate();
|
||||
}
|
||||
|
||||
bool NVPSystem::isInited()
|
||||
{
|
||||
return s_sysInit;
|
||||
}
|
||||
102
raytracer/nvpro_core/nvp/nvpsystem.hpp
Normal file
102
raytracer/nvpro_core/nvp/nvpsystem.hpp
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef __NVPSYSTEM_H__
|
||||
#define __NVPSYSTEM_H__
|
||||
|
||||
#include "platform.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
|
||||
#if defined(WIN32) && defined(MEMORY_LEAKS_CHECK)
|
||||
|
||||
#define _CRTDBG_MAP_ALLOC
|
||||
#include <crtdbg.h>
|
||||
#include <stdlib.h>
|
||||
inline void* operator new(size_t size, const char* file, int line)
|
||||
{
|
||||
return ::operator new(size, 1, file, line);
|
||||
}
|
||||
|
||||
inline void __cdecl operator delete(void* ptr, const char* file, int line)
|
||||
{
|
||||
::operator delete(ptr, _NORMAL_BLOCK, file, line);
|
||||
}
|
||||
|
||||
#define DEBUG_NEW new(__FILE__, __LINE__)
|
||||
#define MALLOC_DBG(x) _malloc_dbg(x, 1, __FILE__, __LINE__);
|
||||
|
||||
#else
|
||||
|
||||
#define DEBUG_NEW new
|
||||
#define MALLOC_DBG malloc
|
||||
|
||||
#endif // #if defined(WIN32) && defined(MEMORY_LEAKS_CHECK)
|
||||
|
||||
|
||||
#include <nvh/nvprint.hpp>
|
||||
|
||||
/* @DOC_START
|
||||
# class NVPSystem
|
||||
> NVPSystem is a utility class to handle some basic system
|
||||
functionality that all projects likely make use of.
|
||||
It does not require any window to be opened.
|
||||
Typical usage is calling init right after main and deinit
|
||||
in the end, or use the NVPSystem object for that.
|
||||
init
|
||||
- calls glfwInit and registers the error callback for it
|
||||
- sets up and log filename based on projectName via nvprintSetLogFileName
|
||||
@DOC_END */
|
||||
|
||||
class NVPSystem
|
||||
{
|
||||
public:
|
||||
static void init(const char* projectName);
|
||||
static void deinit();
|
||||
|
||||
static void pollEvents(); ///< polls events. Non blicking
|
||||
static void waitEvents(); ///< wait for events. Will return when at least 1 event happened
|
||||
static void postTiming(float ms, int fps, const char* details = NULL);
|
||||
|
||||
static double getTime(); ///< returns time in seconds
|
||||
static void sleep(double seconds);
|
||||
|
||||
static std::string exePath(); ///< exePath() can be called without init called before
|
||||
|
||||
static bool isInited();
|
||||
|
||||
/// for sake of debugging/automated testing
|
||||
static void windowScreenshot(struct GLFWwindow* glfwin, const char* filename);
|
||||
static void windowClear(struct GLFWwindow* glfwin, uint32_t r, uint32_t g, uint32_t b);
|
||||
/// \namesimple modal dialog
|
||||
/// @{
|
||||
static std::string windowOpenFileDialog(struct GLFWwindow* glfwin, const char* title, const char* exts);
|
||||
static std::string windowSaveFileDialog(struct GLFWwindow* glfwin, const char* title, const char* exts);
|
||||
/// @}
|
||||
NVPSystem(const char* projectName) { init(projectName); }
|
||||
~NVPSystem() { deinit(); }
|
||||
|
||||
private:
|
||||
static void platformInit();
|
||||
static void platformDeinit();
|
||||
};
|
||||
|
||||
#endif
|
||||
362
raytracer/nvpro_core/nvp/nvpsystem_linux.cpp
Normal file
362
raytracer/nvpro_core/nvp/nvpsystem_linux.cpp
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#include "nvpsystem.hpp"
|
||||
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
#define GLFW_EXPOSE_NATIVE_X11
|
||||
#include <GLFW/glfw3native.h>
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
#include <memory>
|
||||
#include <sys/shm.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
|
||||
// Samples include their own definitions of stb_image. Use STB_IMAGE_WRITE_STATIC to avoid issues with multiple
|
||||
// definitions in the nvpro_core static lib at the cost of having the code exist multiple times.
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_STATIC
|
||||
#include <stb_image_write.h>
|
||||
|
||||
#include "linux_file_dialog.h"
|
||||
|
||||
union Pixel
|
||||
{
|
||||
uint32_t data;
|
||||
struct
|
||||
{
|
||||
uint8_t r, g, b, a;
|
||||
} channels;
|
||||
};
|
||||
|
||||
// Object to allocate and hold a shared memory XImage and a shared memory segment
|
||||
// This allows reading/writing an XImage in one IPC call
|
||||
// Check XShmQueryExtension() before using
|
||||
class XShmImage
|
||||
{
|
||||
public:
|
||||
XShmImage(Display* display, int width, int height)
|
||||
: m_display{display}
|
||||
{
|
||||
// Allocate a shared XImage
|
||||
int screen = XDefaultScreen(m_display);
|
||||
Visual* visual = XDefaultVisual(m_display, screen);
|
||||
int depth = DefaultDepth(m_display, screen);
|
||||
m_image = XShmCreateImage(m_display, visual, depth, ZPixmap, nullptr, &m_shmSegmentInfo, width, height);
|
||||
if(!m_image)
|
||||
{
|
||||
LOGE("Error: XShmCreateImage() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the shared memory, used by XShmGetImage()
|
||||
int permissions = 0600;
|
||||
m_shmID = shmget(IPC_PRIVATE, height * m_image->bytes_per_line, IPC_CREAT | permissions);
|
||||
if(m_shmID == -1)
|
||||
{
|
||||
LOGE("Error: shmget() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Map the shared memory segment into the address space of this process
|
||||
m_shmAddr = shmat(m_shmID, 0, 0);
|
||||
if(reinterpret_cast<intptr_t>(m_shmAddr) == -1)
|
||||
{
|
||||
LOGE("Error: shmat() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the allocated shared memory for the XImage
|
||||
m_shmSegmentInfo.shmid = m_shmID;
|
||||
m_shmSegmentInfo.shmaddr = reinterpret_cast<char*>(m_shmAddr);
|
||||
m_shmSegmentInfo.readOnly = false;
|
||||
m_image->data = m_shmSegmentInfo.shmaddr;
|
||||
|
||||
// Get the X server to attach the shared memory segment on its side and sync
|
||||
if(!XShmAttach(m_display, &m_shmSegmentInfo))
|
||||
LOGE("Error: XShmAttach() failed\n");
|
||||
if(!XSync(m_display, false))
|
||||
LOGE("Error: XSync() failed\n");
|
||||
return;
|
||||
}
|
||||
~XShmImage()
|
||||
{
|
||||
if(m_image)
|
||||
{
|
||||
if(!XShmDetach(m_display, &m_shmSegmentInfo))
|
||||
LOGE("Error: XShmDetach() failed\n");
|
||||
if(!XDestroyImage(m_image))
|
||||
LOGE("Error: XDestroyImage() failed\n");
|
||||
}
|
||||
if(reinterpret_cast<intptr_t>(m_shmAddr) != -1 && shmdt(m_shmAddr) == -1)
|
||||
LOGE("Error: shmdt() failed\n");
|
||||
if(m_shmID != -1 && shmctl(m_shmID, IPC_RMID, nullptr) == -1)
|
||||
LOGE("Error: shmctl(IPC_RMID) failed\n");
|
||||
}
|
||||
|
||||
// Get the X server to copy the window contents into the shared memory
|
||||
bool read(Window window) { return XShmGetImage(m_display, window, m_image, 0, 0, AllPlanes); }
|
||||
|
||||
// In lieu of exceptions, call this after constructing to see if the constructor failed
|
||||
bool valid() const { return m_shmID != -1 && reinterpret_cast<intptr_t>(m_shmAddr) != -1; }
|
||||
|
||||
// Returns the XImage object to be accessed after calling read()
|
||||
XImage* image() const { return m_image; }
|
||||
|
||||
private:
|
||||
int m_shmID{-1};
|
||||
void* m_shmAddr{reinterpret_cast<void*>(-1)};
|
||||
XShmSegmentInfo m_shmSegmentInfo{};
|
||||
Display* m_display{};
|
||||
XImage* m_image{};
|
||||
};
|
||||
|
||||
void NVPSystem::windowScreenshot(struct GLFWwindow* glfwin, const char* filename)
|
||||
{
|
||||
const int bytesPerPixel = sizeof(Pixel);
|
||||
int width{};
|
||||
int height{};
|
||||
std::unique_ptr<XShmImage> shmImage;
|
||||
std::vector<Pixel> imageData;
|
||||
XImage* fallbackXImage{};
|
||||
|
||||
Display* display = glfwGetX11Display();
|
||||
Window window = glfwGetX11Window(glfwin);
|
||||
glfwGetWindowSize(glfwin, &width, &height);
|
||||
|
||||
if(XShmQueryExtension(display))
|
||||
{
|
||||
// Use the shared memory extension if it is supported to avoid expensive XGetPixel calls
|
||||
// Shared memory allows X11 to copy all the image data at once
|
||||
shmImage = std::make_unique<XShmImage>(display, width, height);
|
||||
if(shmImage->valid() && bytesPerPixel * 8 == shmImage->image()->bits_per_pixel)
|
||||
{
|
||||
if(shmImage->read(window))
|
||||
{
|
||||
XImage* ximg = shmImage->image();
|
||||
imageData.reserve(width * height);
|
||||
for(int y = 0; y < ximg->height; ++y)
|
||||
{
|
||||
Pixel* ximgData = reinterpret_cast<Pixel*>(ximg->data + ximg->bytes_per_line * y);
|
||||
imageData.insert(imageData.end(), ximgData, ximgData + ximg->width);
|
||||
}
|
||||
|
||||
// bgr to rgb
|
||||
for(Pixel& pixel : imageData)
|
||||
{
|
||||
std::swap(pixel.channels.r, pixel.channels.b);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGE("Error: Failed to get window contents for screenshot. Falling back to XGetPixel()\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGE("Error: Failed to create XShm Image for screenshot. Falling back to XGetPixel()\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(imageData.empty())
|
||||
{
|
||||
fallbackXImage = XGetImage(display, window, 0, 0, width, height, AllPlanes, ZPixmap);
|
||||
if(!fallbackXImage)
|
||||
{
|
||||
LOGE("Error: XGetImage() failed to get window contents for screenshot\n");
|
||||
return;
|
||||
}
|
||||
if(bytesPerPixel * 8 != fallbackXImage->bits_per_pixel)
|
||||
{
|
||||
LOGE("Error: XGetImage() returned an image with %i bits per pixel but only %i is supported\n",
|
||||
fallbackXImage->bits_per_pixel, bytesPerPixel * 8);
|
||||
return;
|
||||
}
|
||||
imageData.reserve(width * height);
|
||||
for(int y = 0; y < height; ++y)
|
||||
{
|
||||
for(int x = 0; x < width; ++x)
|
||||
{
|
||||
Pixel pixel;
|
||||
pixel.data = static_cast<uint32_t>(XGetPixel(fallbackXImage, x, y));
|
||||
std::swap(pixel.channels.r, pixel.channels.b); // bgr to rgb
|
||||
pixel.channels.a = 0xff; // set full alpha
|
||||
imageData.push_back(pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!stbi_write_png(filename, width, height, 4, imageData.data(), width * bytesPerPixel))
|
||||
{
|
||||
LOGE("Error: Writing %s failed\n", filename);
|
||||
}
|
||||
|
||||
if(fallbackXImage && !XDestroyImage(fallbackXImage))
|
||||
LOGE("Error: XDestroyImage() failed\n");
|
||||
}
|
||||
|
||||
void NVPSystem::windowClear(struct GLFWwindow* glfwin, uint32_t r, uint32_t g, uint32_t b)
|
||||
{
|
||||
Window hwnd = glfwGetX11Window(glfwin);
|
||||
assert(0 && "not yet implemented");
|
||||
}
|
||||
|
||||
static void fixSingleFilter(std::string* pFilter);
|
||||
|
||||
static std::vector<std::string> toFilterArgs(const char* exts)
|
||||
{
|
||||
// Convert exts list to filter format recognized by portable-file-dialogs
|
||||
// Try to match implemented nvpsystem on Windows behavior:
|
||||
// Alternate between human-readable descriptions and filter strings.
|
||||
// | separates strings
|
||||
// ; separates filters e.g. .png|.gif
|
||||
// Case-insensitive e.g. .png = .PNG = .pNg
|
||||
|
||||
// Split strings by |
|
||||
std::vector<std::string> filterArgs(1);
|
||||
for(const char* pC = exts; pC != nullptr && *pC != '\0'; ++pC)
|
||||
{
|
||||
char c = *pC;
|
||||
if(c == '|')
|
||||
filterArgs.emplace_back();
|
||||
else
|
||||
filterArgs.back().push_back(c);
|
||||
}
|
||||
|
||||
// Default arguments
|
||||
if(filterArgs.size() < 2)
|
||||
{
|
||||
filterArgs = {"All files", "*"};
|
||||
}
|
||||
|
||||
// Split filters by ; and fix those filters.
|
||||
for(size_t i = 1; i < filterArgs.size(); i += 2)
|
||||
{
|
||||
std::string& arg = filterArgs[i];
|
||||
std::string newArg;
|
||||
std::string singleFilter;
|
||||
for(char c : arg)
|
||||
{
|
||||
if(c == ';')
|
||||
{
|
||||
fixSingleFilter(&singleFilter);
|
||||
newArg += std::move(singleFilter);
|
||||
singleFilter.clear();
|
||||
newArg += ' '; // portable-file-dialogs uses spaces to separate... win32 wants no spaces in filters at all so presumably this is fine.
|
||||
}
|
||||
else
|
||||
{
|
||||
singleFilter.push_back(c);
|
||||
}
|
||||
}
|
||||
fixSingleFilter(&singleFilter);
|
||||
newArg += std::move(singleFilter);
|
||||
arg = std::move(newArg);
|
||||
}
|
||||
|
||||
return filterArgs;
|
||||
}
|
||||
|
||||
static void fixSingleFilter(std::string* pFilter)
|
||||
{
|
||||
// Make case insensitive.
|
||||
std::string newFilter;
|
||||
for(char c : *pFilter)
|
||||
{
|
||||
if(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))
|
||||
{
|
||||
// replace c with [cC] to make case-insensitive. TODO: Unicode support, not sure how to implement for multibyte utf-8 characters.
|
||||
newFilter.push_back('[');
|
||||
newFilter.push_back(c);
|
||||
newFilter.push_back(char(c ^ 32));
|
||||
newFilter.push_back(']');
|
||||
}
|
||||
else
|
||||
{
|
||||
newFilter.push_back(c);
|
||||
}
|
||||
}
|
||||
*pFilter = std::move(newFilter);
|
||||
}
|
||||
|
||||
std::string NVPSystem::windowOpenFileDialog(struct GLFWwindow* glfwin, const char* title, const char* exts)
|
||||
{
|
||||
// Not sure yet how to use this; maybe make as a child window somehow?
|
||||
[[maybe_unused]] Window hwnd = glfwGetX11Window(glfwin);
|
||||
|
||||
std::vector<std::string> filterArgs = toFilterArgs(exts);
|
||||
std::vector<std::string> resultVector = open_file(title, ".", filterArgs).result();
|
||||
assert(resultVector.size() <= 1);
|
||||
return resultVector.empty() ? "" : std::move(resultVector[0]);
|
||||
}
|
||||
|
||||
std::string NVPSystem::windowSaveFileDialog(struct GLFWwindow* glfwin, const char* title, const char* exts)
|
||||
{
|
||||
// Not sure yet how to use this; maybe make as a child window somehow?
|
||||
[[maybe_unused]] Window hwnd = glfwGetX11Window(glfwin);
|
||||
|
||||
std::vector<std::string> filterArgs = toFilterArgs(exts);
|
||||
return save_file(title, ".", filterArgs).result();
|
||||
}
|
||||
|
||||
void NVPSystem::sleep(double seconds)
|
||||
{
|
||||
::sleep(seconds);
|
||||
}
|
||||
|
||||
void NVPSystem::platformInit()
|
||||
{
|
||||
}
|
||||
|
||||
void NVPSystem::platformDeinit()
|
||||
{
|
||||
}
|
||||
|
||||
static bool s_exePathInit = false;
|
||||
|
||||
std::string NVPSystem::exePath()
|
||||
{
|
||||
static std::string s_exePath;
|
||||
|
||||
if(!s_exePathInit)
|
||||
{
|
||||
char modulePath[PATH_MAX];
|
||||
ssize_t modulePathLength = readlink( "/proc/self/exe", modulePath, PATH_MAX );
|
||||
|
||||
s_exePath = std::string(modulePath, modulePathLength > 0 ? modulePathLength : 0);
|
||||
|
||||
size_t last = s_exePath.rfind('/');
|
||||
if(last != std::string::npos)
|
||||
{
|
||||
s_exePath = s_exePath.substr(0, last) + std::string("/");
|
||||
}
|
||||
|
||||
s_exePathInit = true;
|
||||
}
|
||||
|
||||
return s_exePath;
|
||||
}
|
||||
325
raytracer/nvpro_core/nvp/nvpsystem_win32.cpp
Normal file
325
raytracer/nvpro_core/nvp/nvpsystem_win32.cpp
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#include "nvpsystem.hpp"
|
||||
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
#define GLFW_EXPOSE_NATIVE_WIN32
|
||||
#include <GLFW/glfw3native.h>
|
||||
|
||||
#include <commdlg.h>
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include "resources.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Samples include their own definitions of stb_image. Use STB_IMAGE_WRITE_STATIC to avoid issues with multiple
|
||||
// definitions in the nvpro_core static lib at the cost of having the code exist multiple times.
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_STATIC
|
||||
#include <stb_image_write.h>
|
||||
#include <memory>
|
||||
|
||||
// Executables (but not DLLs) exporting this symbol with this value will be
|
||||
// automatically directed to the high-performance GPU on Nvidia Optimus systems
|
||||
// with up-to-date drivers
|
||||
//
|
||||
extern "C" {
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
||||
}
|
||||
|
||||
// from https://docs.microsoft.com/en-us/windows/desktop/gdi/capturing-an-image
|
||||
|
||||
static int CaptureAnImage(HWND hWnd, const char* filename)
|
||||
{
|
||||
struct LocalResources
|
||||
{
|
||||
LocalResources(HWND _hWnd)
|
||||
: hWnd(_hWnd)
|
||||
{
|
||||
}
|
||||
~LocalResources()
|
||||
{
|
||||
if(hMemoryDC != nullptr && hOldBitmap != nullptr)
|
||||
SelectObject(hMemoryDC, hOldBitmap);
|
||||
if(hBitmap != nullptr)
|
||||
DeleteObject(hBitmap);
|
||||
if(hMemoryDC != nullptr)
|
||||
DeleteDC(hMemoryDC);
|
||||
if(hWnd != nullptr && hWindowDC != nullptr)
|
||||
ReleaseDC(hWnd, hWindowDC);
|
||||
}
|
||||
HDC hMemoryDC = nullptr;
|
||||
HDC hWindowDC = nullptr;
|
||||
HBITMAP hOldBitmap = nullptr;
|
||||
HBITMAP hBitmap = nullptr;
|
||||
HWND hWnd = nullptr;
|
||||
};
|
||||
std::unique_ptr<LocalResources> res = std::make_unique<LocalResources>(hWnd);
|
||||
|
||||
// Get the window's device context
|
||||
res->hWindowDC = GetDC(hWnd);
|
||||
if(res->hWindowDC == nullptr)
|
||||
{
|
||||
LOGE("Failed to Create Compatible Bitmap");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the window's width and height
|
||||
RECT rect;
|
||||
if(!GetClientRect(res->hWnd, &rect))
|
||||
{
|
||||
LOGE("Failed to retrieves a handle to a device context");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create a compatible device context
|
||||
res->hMemoryDC = CreateCompatibleDC(res->hWindowDC);
|
||||
if(res->hMemoryDC == nullptr)
|
||||
{
|
||||
LOGE("Failed to creates a memory device context");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int width = rect.right - rect.left;
|
||||
int height = rect.bottom - rect.top;
|
||||
|
||||
// Create a bitmap and select it into the device context
|
||||
res->hBitmap = CreateCompatibleBitmap(res->hWindowDC, width, height);
|
||||
if(res->hBitmap == nullptr)
|
||||
{
|
||||
LOGE("Failed to creates a bitmap compatible with the device");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res->hOldBitmap = (HBITMAP)SelectObject(res->hMemoryDC, res->hBitmap);
|
||||
if(res->hOldBitmap == nullptr)
|
||||
{
|
||||
LOGE("Failed to selects an object into the specified device context");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copy the window's device context to the bitmap
|
||||
if(BitBlt(res->hMemoryDC, 0, 0, width, height, res->hWindowDC, 0, 0, SRCCOPY) == 0)
|
||||
{
|
||||
LOGE("Failed to bit-block transfer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Prepare the bitmap info header
|
||||
BITMAPINFOHEADER bi = {};
|
||||
bi.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bi.biWidth = width;
|
||||
bi.biHeight = -height; // Negative height to ensure top-down orientation
|
||||
bi.biPlanes = 1;
|
||||
bi.biBitCount = 24; // 24 bits per pixel (RGB)
|
||||
bi.biCompression = BI_RGB;
|
||||
bi.biSizeImage = 0;
|
||||
bi.biXPelsPerMeter = 0;
|
||||
bi.biYPelsPerMeter = 0;
|
||||
bi.biClrUsed = 0;
|
||||
bi.biClrImportant = 0;
|
||||
|
||||
// Allocate memory for the bitmap data
|
||||
std::vector<uint8_t> pixels(width * height * 3);
|
||||
|
||||
// Copy the bitmap data into the pixel buffer
|
||||
if(GetDIBits(res->hMemoryDC, res->hBitmap, 0, height, pixels.data(), (BITMAPINFO*)&bi, DIB_RGB_COLORS) == 0)
|
||||
{
|
||||
LOGE("Failed to retrieves the bits of the specified compatible bitmap");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Convert BGR to RGB
|
||||
for(int i = 0; i < width * height; ++i)
|
||||
{
|
||||
uint8_t temp = pixels[i * 3];
|
||||
pixels[i * 3] = pixels[i * 3 + 2];
|
||||
pixels[i * 3 + 2] = temp;
|
||||
}
|
||||
|
||||
// Save the bitmap as a PNG file using stb_image
|
||||
if(stbi_write_png(filename, width, height, 3, pixels.data(), width * 3) == 0)
|
||||
{
|
||||
LOGE("Failed to write: %s", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void NVPSystem::windowScreenshot(struct GLFWwindow* glfwin, const char* filename)
|
||||
{
|
||||
if(!glfwin)
|
||||
{
|
||||
assert(!"Attempted to fall windowScreenshot() on null window!");
|
||||
return;
|
||||
}
|
||||
CaptureAnImage(glfwGetWin32Window(glfwin), filename);
|
||||
}
|
||||
|
||||
void NVPSystem::windowClear(struct GLFWwindow* glfwin, uint32_t r, uint32_t g, uint32_t b)
|
||||
{
|
||||
if(!glfwin)
|
||||
{
|
||||
assert(!"Attempted to fall windowClear() on null window!");
|
||||
return;
|
||||
}
|
||||
HWND hwnd = glfwGetWin32Window(glfwin);
|
||||
|
||||
HDC hdcWindow = GetDC(hwnd);
|
||||
|
||||
RECT rcClient;
|
||||
GetClientRect(hwnd, &rcClient);
|
||||
HBRUSH hbr = CreateSolidBrush(RGB(r, g, b));
|
||||
|
||||
FillRect(hdcWindow, &rcClient, hbr);
|
||||
|
||||
ReleaseDC(hwnd, hdcWindow);
|
||||
DeleteBrush(hbr);
|
||||
}
|
||||
|
||||
static std::string fileDialog(struct GLFWwindow* glfwin, const char* title, const char* exts, bool openToLoad)
|
||||
{
|
||||
if(!glfwin)
|
||||
{
|
||||
assert(!"Attempted to fall fileDialog() on null window!");
|
||||
return std::string();
|
||||
}
|
||||
HWND hwnd = glfwGetWin32Window(glfwin);
|
||||
|
||||
std::vector<char> extsfixed;
|
||||
for(size_t i = 0; i < strlen(exts); i++)
|
||||
{
|
||||
if(exts[i] == '|')
|
||||
{
|
||||
extsfixed.push_back(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
extsfixed.push_back(exts[i]);
|
||||
}
|
||||
}
|
||||
extsfixed.push_back(0);
|
||||
extsfixed.push_back(0);
|
||||
|
||||
OPENFILENAME ofn; // common dialog box structure
|
||||
char szFile[1024]{}; // buffer for file name
|
||||
|
||||
// Initialize OPENFILENAME
|
||||
ZeroMemory(&ofn, sizeof(ofn));
|
||||
ofn.lStructSize = sizeof(ofn);
|
||||
ofn.hwndOwner = hwnd;
|
||||
ofn.lpstrFile = szFile;
|
||||
// Set lpstrFile[0] to '\0' so that GetOpenFileName does not
|
||||
// use the contents of szFile to initialize itself.
|
||||
ofn.lpstrFile[0] = '\0';
|
||||
ofn.nMaxFile = sizeof(szFile);
|
||||
ofn.lpstrFilter = extsfixed.data();
|
||||
ofn.nFilterIndex = 1;
|
||||
ofn.lpstrFileTitle = NULL;
|
||||
ofn.nMaxFileTitle = 0;
|
||||
ofn.lpstrInitialDir = NULL;
|
||||
ofn.Flags = OFN_PATHMUSTEXIST;
|
||||
ofn.lpstrTitle = title;
|
||||
|
||||
// Display the Open dialog box.
|
||||
|
||||
if(openToLoad)
|
||||
{
|
||||
ofn.Flags |= OFN_FILEMUSTEXIST;
|
||||
if(GetOpenFileNameA(&ofn) == TRUE)
|
||||
{
|
||||
return ofn.lpstrFile;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ofn.Flags |= OFN_OVERWRITEPROMPT;
|
||||
if(GetSaveFileNameA(&ofn) == TRUE)
|
||||
{
|
||||
return ofn.lpstrFile;
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string NVPSystem::windowOpenFileDialog(struct GLFWwindow* glfwin, const char* title, const char* exts)
|
||||
{
|
||||
return fileDialog(glfwin, title, exts, true);
|
||||
}
|
||||
|
||||
std::string NVPSystem::windowSaveFileDialog(struct GLFWwindow* glfwin, const char* title, const char* exts)
|
||||
{
|
||||
return fileDialog(glfwin, title, exts, false);
|
||||
}
|
||||
|
||||
void NVPSystem::sleep(double seconds)
|
||||
{
|
||||
::Sleep(DWORD(seconds * 1000.0));
|
||||
}
|
||||
|
||||
void NVPSystem::platformInit()
|
||||
{
|
||||
#ifdef MEMORY_LEAKS_CHECK
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW);
|
||||
#endif
|
||||
}
|
||||
|
||||
void NVPSystem::platformDeinit()
|
||||
{
|
||||
// Because we set _CRTDBG_LEAK_CHECK_DF in platformInit(), we don't need to
|
||||
// call _CrtDumpMemoryLeaks() in platformDeinit(). If we did, then we might
|
||||
// see allocations for static objects that haven't been destroyed yet.
|
||||
}
|
||||
|
||||
static std::string s_exePath;
|
||||
static bool s_exePathInit = false;
|
||||
|
||||
std::string NVPSystem::exePath()
|
||||
{
|
||||
if(!s_exePathInit)
|
||||
{
|
||||
char modulePath[MAX_PATH];
|
||||
size_t modulePathLength = GetModuleFileNameA(NULL, modulePath, MAX_PATH);
|
||||
s_exePath = std::string(modulePath, modulePathLength);
|
||||
|
||||
std::replace(s_exePath.begin(), s_exePath.end(), '\\', '/');
|
||||
size_t last = s_exePath.rfind('/');
|
||||
if(last != std::string::npos)
|
||||
{
|
||||
s_exePath = s_exePath.substr(0, last) + std::string("/");
|
||||
}
|
||||
|
||||
s_exePathInit = true;
|
||||
}
|
||||
|
||||
return s_exePath;
|
||||
}
|
||||
405
raytracer/nvpro_core/nvp/nvpwindow.cpp
Normal file
405
raytracer/nvpro_core/nvp/nvpwindow.cpp
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2018-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "nvpwindow.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
static_assert(NVPWindow::BUTTON_RELEASE == GLFW_RELEASE, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::BUTTON_PRESS == GLFW_PRESS, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::BUTTON_REPEAT == GLFW_REPEAT, "glfw/nvpwindow mismatch");
|
||||
|
||||
static_assert(NVPWindow::MOUSE_BUTTON_LEFT == GLFW_MOUSE_BUTTON_LEFT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::MOUSE_BUTTON_RIGHT == GLFW_MOUSE_BUTTON_RIGHT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::MOUSE_BUTTON_MIDDLE == GLFW_MOUSE_BUTTON_MIDDLE, "glfw/nvpwindow mismatch");
|
||||
|
||||
static_assert(NVPWindow::KMOD_SHIFT == GLFW_MOD_SHIFT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KMOD_CONTROL == GLFW_MOD_CONTROL, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KMOD_ALT == GLFW_MOD_ALT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KMOD_SUPER == GLFW_MOD_SUPER, "glfw/nvpwindow mismatch");
|
||||
|
||||
/*
|
||||
for key in keysheader:gmatch("#define ([%w_]+)") do
|
||||
print("static_assert(NVPWindow::"..key:sub(6,-1).." == "..key..", \"glfw/nvpwindow mismatch\");")
|
||||
end
|
||||
*/
|
||||
|
||||
static_assert(NVPWindow::KEY_UNKNOWN == GLFW_KEY_UNKNOWN, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_SPACE == GLFW_KEY_SPACE, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_APOSTROPHE == GLFW_KEY_APOSTROPHE, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_COMMA == GLFW_KEY_COMMA, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_MINUS == GLFW_KEY_MINUS, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_PERIOD == GLFW_KEY_PERIOD, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_SLASH == GLFW_KEY_SLASH, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_0 == GLFW_KEY_0, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_1 == GLFW_KEY_1, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_2 == GLFW_KEY_2, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_3 == GLFW_KEY_3, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_4 == GLFW_KEY_4, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_5 == GLFW_KEY_5, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_6 == GLFW_KEY_6, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_7 == GLFW_KEY_7, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_8 == GLFW_KEY_8, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_9 == GLFW_KEY_9, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_SEMICOLON == GLFW_KEY_SEMICOLON, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_EQUAL == GLFW_KEY_EQUAL, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_A == GLFW_KEY_A, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_B == GLFW_KEY_B, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_C == GLFW_KEY_C, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_D == GLFW_KEY_D, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_E == GLFW_KEY_E, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F == GLFW_KEY_F, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_G == GLFW_KEY_G, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_H == GLFW_KEY_H, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_I == GLFW_KEY_I, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_J == GLFW_KEY_J, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_K == GLFW_KEY_K, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_L == GLFW_KEY_L, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_M == GLFW_KEY_M, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_N == GLFW_KEY_N, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_O == GLFW_KEY_O, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_P == GLFW_KEY_P, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_Q == GLFW_KEY_Q, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_R == GLFW_KEY_R, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_S == GLFW_KEY_S, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_T == GLFW_KEY_T, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_U == GLFW_KEY_U, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_V == GLFW_KEY_V, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_W == GLFW_KEY_W, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_X == GLFW_KEY_X, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_Y == GLFW_KEY_Y, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_Z == GLFW_KEY_Z, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_LEFT_BRACKET == GLFW_KEY_LEFT_BRACKET, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_BACKSLASH == GLFW_KEY_BACKSLASH, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_RIGHT_BRACKET == GLFW_KEY_RIGHT_BRACKET, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_GRAVE_ACCENT == GLFW_KEY_GRAVE_ACCENT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_WORLD_1 == GLFW_KEY_WORLD_1, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_WORLD_2 == GLFW_KEY_WORLD_2, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_ESCAPE == GLFW_KEY_ESCAPE, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_ENTER == GLFW_KEY_ENTER, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_TAB == GLFW_KEY_TAB, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_BACKSPACE == GLFW_KEY_BACKSPACE, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_INSERT == GLFW_KEY_INSERT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_DELETE == GLFW_KEY_DELETE, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_RIGHT == GLFW_KEY_RIGHT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_LEFT == GLFW_KEY_LEFT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_DOWN == GLFW_KEY_DOWN, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_UP == GLFW_KEY_UP, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_PAGE_UP == GLFW_KEY_PAGE_UP, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_PAGE_DOWN == GLFW_KEY_PAGE_DOWN, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_HOME == GLFW_KEY_HOME, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_END == GLFW_KEY_END, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_CAPS_LOCK == GLFW_KEY_CAPS_LOCK, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_SCROLL_LOCK == GLFW_KEY_SCROLL_LOCK, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_NUM_LOCK == GLFW_KEY_NUM_LOCK, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_PRINT_SCREEN == GLFW_KEY_PRINT_SCREEN, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_PAUSE == GLFW_KEY_PAUSE, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F1 == GLFW_KEY_F1, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F2 == GLFW_KEY_F2, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F3 == GLFW_KEY_F3, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F4 == GLFW_KEY_F4, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F5 == GLFW_KEY_F5, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F6 == GLFW_KEY_F6, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F7 == GLFW_KEY_F7, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F8 == GLFW_KEY_F8, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F9 == GLFW_KEY_F9, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F10 == GLFW_KEY_F10, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F11 == GLFW_KEY_F11, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F12 == GLFW_KEY_F12, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F13 == GLFW_KEY_F13, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F14 == GLFW_KEY_F14, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F15 == GLFW_KEY_F15, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F16 == GLFW_KEY_F16, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F17 == GLFW_KEY_F17, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F18 == GLFW_KEY_F18, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F19 == GLFW_KEY_F19, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F20 == GLFW_KEY_F20, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F21 == GLFW_KEY_F21, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F22 == GLFW_KEY_F22, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F23 == GLFW_KEY_F23, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F24 == GLFW_KEY_F24, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_F25 == GLFW_KEY_F25, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_0 == GLFW_KEY_KP_0, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_1 == GLFW_KEY_KP_1, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_2 == GLFW_KEY_KP_2, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_3 == GLFW_KEY_KP_3, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_4 == GLFW_KEY_KP_4, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_5 == GLFW_KEY_KP_5, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_6 == GLFW_KEY_KP_6, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_7 == GLFW_KEY_KP_7, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_8 == GLFW_KEY_KP_8, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_9 == GLFW_KEY_KP_9, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_DECIMAL == GLFW_KEY_KP_DECIMAL, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_DIVIDE == GLFW_KEY_KP_DIVIDE, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_MULTIPLY == GLFW_KEY_KP_MULTIPLY, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_SUBTRACT == GLFW_KEY_KP_SUBTRACT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_ADD == GLFW_KEY_KP_ADD, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_ENTER == GLFW_KEY_KP_ENTER, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_KP_EQUAL == GLFW_KEY_KP_EQUAL, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_LEFT_SHIFT == GLFW_KEY_LEFT_SHIFT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_LEFT_CONTROL == GLFW_KEY_LEFT_CONTROL, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_LEFT_ALT == GLFW_KEY_LEFT_ALT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_LEFT_SUPER == GLFW_KEY_LEFT_SUPER, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_RIGHT_SHIFT == GLFW_KEY_RIGHT_SHIFT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_RIGHT_CONTROL == GLFW_KEY_RIGHT_CONTROL, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_RIGHT_ALT == GLFW_KEY_RIGHT_ALT, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_RIGHT_SUPER == GLFW_KEY_RIGHT_SUPER, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_MENU == GLFW_KEY_MENU, "glfw/nvpwindow mismatch");
|
||||
static_assert(NVPWindow::KEY_LAST == GLFW_KEY_LAST, "glfw/nvpwindow mismatch");
|
||||
|
||||
void NVPWindow::cb_windowrefreshfun(GLFWwindow* glfwwin)
|
||||
{
|
||||
NVPWindow* win = (NVPWindow*)glfwGetWindowUserPointer(glfwwin);
|
||||
if(win->isClosing())
|
||||
return;
|
||||
win->onWindowRefresh();
|
||||
}
|
||||
|
||||
void NVPWindow::cb_windowsizefun(GLFWwindow* glfwwin, int w, int h)
|
||||
{
|
||||
NVPWindow* win = (NVPWindow*)glfwGetWindowUserPointer(glfwwin);
|
||||
if(win->isClosing())
|
||||
return;
|
||||
win->m_windowSize[0] = w;
|
||||
win->m_windowSize[1] = h;
|
||||
win->onWindowResize(w, h);
|
||||
}
|
||||
void NVPWindow::cb_windowclosefun(GLFWwindow* glfwwin)
|
||||
{
|
||||
NVPWindow* win = (NVPWindow*)glfwGetWindowUserPointer(glfwwin);
|
||||
win->m_isClosing = true;
|
||||
win->onWindowClose();
|
||||
}
|
||||
|
||||
void NVPWindow::cb_mousebuttonfun(GLFWwindow* glfwwin, int button, int action, int mods)
|
||||
{
|
||||
double x, y;
|
||||
glfwGetCursorPos(glfwwin, &x, &y);
|
||||
|
||||
NVPWindow* win = (NVPWindow*)glfwGetWindowUserPointer(glfwwin);
|
||||
if(win->isClosing())
|
||||
return;
|
||||
win->m_keyModifiers = mods;
|
||||
win->m_mouseX = int(x);
|
||||
win->m_mouseY = int(y);
|
||||
win->onMouseButton((NVPWindow::MouseButton)button, (NVPWindow::ButtonAction)action, mods, win->m_mouseX, win->m_mouseY);
|
||||
}
|
||||
void NVPWindow::cb_cursorposfun(GLFWwindow* glfwwin, double x, double y)
|
||||
{
|
||||
NVPWindow* win = (NVPWindow*)glfwGetWindowUserPointer(glfwwin);
|
||||
if(win->isClosing())
|
||||
return;
|
||||
win->m_mouseX = int(x);
|
||||
win->m_mouseY = int(y);
|
||||
win->onMouseMotion(win->m_mouseX, win->m_mouseY);
|
||||
}
|
||||
void NVPWindow::cb_scrollfun(GLFWwindow* glfwwin, double x, double y)
|
||||
{
|
||||
NVPWindow* win = (NVPWindow*)glfwGetWindowUserPointer(glfwwin);
|
||||
if(win->isClosing())
|
||||
return;
|
||||
win->m_mouseWheel += int(y);
|
||||
win->onMouseWheel(int(y));
|
||||
}
|
||||
void NVPWindow::cb_keyfun(GLFWwindow* glfwwin, int key, int scancode, int action, int mods)
|
||||
{
|
||||
NVPWindow* win = (NVPWindow*)glfwGetWindowUserPointer(glfwwin);
|
||||
if(win->isClosing())
|
||||
return;
|
||||
win->m_keyModifiers = mods;
|
||||
win->onKeyboard((NVPWindow::KeyCode)key, (NVPWindow::ButtonAction)action, mods, win->m_mouseX, win->m_mouseY);
|
||||
}
|
||||
void NVPWindow::cb_charfun(GLFWwindow* glfwwin, unsigned int codepoint)
|
||||
{
|
||||
NVPWindow* win = (NVPWindow*)glfwGetWindowUserPointer(glfwwin);
|
||||
if(win->isClosing())
|
||||
return;
|
||||
win->onKeyboardChar(codepoint, win->m_keyModifiers, win->m_mouseX, win->m_mouseY);
|
||||
}
|
||||
|
||||
void NVPWindow::cb_dropfun(GLFWwindow* glfwwin, int count, const char** paths)
|
||||
{
|
||||
NVPWindow* win = (NVPWindow*)glfwGetWindowUserPointer(glfwwin);
|
||||
if(win->isClosing())
|
||||
return;
|
||||
win->onDragDrop(count, paths);
|
||||
}
|
||||
|
||||
bool NVPWindow::isClosing() const
|
||||
{
|
||||
return m_isClosing || glfwWindowShouldClose(m_internal);
|
||||
}
|
||||
|
||||
bool NVPWindow::isOpen() const
|
||||
{
|
||||
return glfwGetWindowAttrib(m_internal, GLFW_VISIBLE) == GLFW_TRUE
|
||||
&& glfwGetWindowAttrib(m_internal, GLFW_ICONIFIED) == GLFW_FALSE && !isClosing();
|
||||
}
|
||||
|
||||
bool NVPWindow::open(int posX, int posY, int width, int height, const char* title, bool requireGLContext)
|
||||
{
|
||||
NV_ASSERT(NVPSystem::isInited() && "NVPSystem::Init not called");
|
||||
|
||||
m_windowSize[0] = width;
|
||||
m_windowSize[1] = height;
|
||||
|
||||
m_windowName = title ? title : "Sample";
|
||||
|
||||
#ifdef _WIN32
|
||||
(void)requireGLContext;
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
#else
|
||||
if(!requireGLContext)
|
||||
{
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
}
|
||||
else
|
||||
{
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
|
||||
// Some samples make use of compatibility profile features
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
|
||||
#ifndef NDEBUG
|
||||
#ifdef GLFW_CONTEXT_DEBUG // Since GLFW_CONTEXT_DEBUG is new in GLFW 3.4
|
||||
glfwWindowHint(GLFW_CONTEXT_DEBUG, 1);
|
||||
#else
|
||||
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, 1);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
m_internal = glfwCreateWindow(width, height, title, nullptr, nullptr);
|
||||
if(!m_internal)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(posX != 0 || posY != 0)
|
||||
{
|
||||
glfwSetWindowPos(m_internal, posX, posY);
|
||||
}
|
||||
glfwSetWindowUserPointer(m_internal, this);
|
||||
glfwSetWindowRefreshCallback(m_internal, cb_windowrefreshfun);
|
||||
glfwSetWindowCloseCallback(m_internal, cb_windowclosefun);
|
||||
glfwSetCursorPosCallback(m_internal, cb_cursorposfun);
|
||||
glfwSetMouseButtonCallback(m_internal, cb_mousebuttonfun);
|
||||
glfwSetKeyCallback(m_internal, cb_keyfun);
|
||||
glfwSetScrollCallback(m_internal, cb_scrollfun);
|
||||
glfwSetCharCallback(m_internal, cb_charfun);
|
||||
glfwSetWindowSizeCallback(m_internal, cb_windowsizefun);
|
||||
glfwSetDropCallback(m_internal, cb_dropfun);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NVPWindow::deinit()
|
||||
{
|
||||
glfwDestroyWindow(m_internal);
|
||||
m_internal = nullptr;
|
||||
m_windowSize[0] = 0;
|
||||
m_windowSize[1] = 0;
|
||||
m_windowName = std::string();
|
||||
}
|
||||
|
||||
void NVPWindow::close()
|
||||
{
|
||||
glfwSetWindowShouldClose(m_internal, GLFW_TRUE);
|
||||
}
|
||||
|
||||
void NVPWindow::setTitle(const char* title)
|
||||
{
|
||||
glfwSetWindowTitle(m_internal, title);
|
||||
}
|
||||
|
||||
void NVPWindow::maximize()
|
||||
{
|
||||
glfwMaximizeWindow(m_internal);
|
||||
}
|
||||
|
||||
void NVPWindow::restore()
|
||||
{
|
||||
glfwRestoreWindow(m_internal);
|
||||
}
|
||||
|
||||
void NVPWindow::minimize()
|
||||
{
|
||||
glfwIconifyWindow(m_internal);
|
||||
}
|
||||
|
||||
void NVPWindow::setWindowPos(int x, int y)
|
||||
{
|
||||
glfwSetWindowPos(m_internal, x, y);
|
||||
}
|
||||
|
||||
void NVPWindow::setWindowSize(int w, int h)
|
||||
{
|
||||
glfwSetWindowSize(m_internal, w, h);
|
||||
}
|
||||
|
||||
std::string NVPWindow::openFileDialog(const char* title, const char* exts)
|
||||
{
|
||||
return NVPSystem::windowOpenFileDialog(m_internal, title, exts);
|
||||
}
|
||||
std::string NVPWindow::saveFileDialog(const char* title, const char* exts)
|
||||
{
|
||||
return NVPSystem::windowSaveFileDialog(m_internal, title, exts);
|
||||
}
|
||||
void NVPWindow::screenshot(const char* filename)
|
||||
{
|
||||
NVPSystem::windowScreenshot(m_internal, filename);
|
||||
}
|
||||
void NVPWindow::clear(uint32_t r, uint32_t g, uint32_t b)
|
||||
{
|
||||
NVPSystem::windowClear(m_internal, r, g, b);
|
||||
}
|
||||
|
||||
void NVPWindow::setFullScreen(bool bYes)
|
||||
{
|
||||
if(bYes == m_isFullScreen)
|
||||
return;
|
||||
|
||||
GLFWmonitor* monitor = glfwGetWindowMonitor(m_internal);
|
||||
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
||||
|
||||
if(bYes)
|
||||
{
|
||||
glfwGetWindowPos(m_internal, &m_preFullScreenPos[0], &m_preFullScreenPos[1]);
|
||||
glfwGetWindowSize(m_internal, &m_preFullScreenSize[0], &m_preFullScreenSize[1]);
|
||||
glfwSetWindowMonitor(m_internal, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
|
||||
glfwSetWindowAttrib(m_internal, GLFW_RESIZABLE, GLFW_FALSE);
|
||||
glfwSetWindowAttrib(m_internal, GLFW_DECORATED, GLFW_FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
glfwSetWindowMonitor(m_internal, nullptr, m_preFullScreenPos[0], m_preFullScreenPos[1], m_preFullScreenSize[0],
|
||||
m_preFullScreenSize[1], 0);
|
||||
glfwSetWindowAttrib(m_internal, GLFW_RESIZABLE, GLFW_TRUE);
|
||||
glfwSetWindowAttrib(m_internal, GLFW_DECORATED, GLFW_TRUE);
|
||||
}
|
||||
|
||||
m_isFullScreen = bYes;
|
||||
}
|
||||
297
raytracer/nvpro_core/nvp/nvpwindow.hpp
Normal file
297
raytracer/nvpro_core/nvp/nvpwindow.hpp
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2018-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
#ifndef __NVPWINDOW_H__
|
||||
#define __NVPWINDOW_H__
|
||||
|
||||
#include "nvpsystem.hpp"
|
||||
|
||||
/* @DOC_START
|
||||
# class NVPWindow
|
||||
> base class for a window, to catch events
|
||||
Using and deriving of NVPWindow base-class is optional.
|
||||
However one must always make use of the NVPSystem
|
||||
That takes care of glfwInit/terminate as well.
|
||||
@DOC_END */
|
||||
class NVPWindow
|
||||
{
|
||||
public:
|
||||
// these are taken from GLFW3 and must be kept in a matching state
|
||||
enum ButtonAction
|
||||
{
|
||||
BUTTON_RELEASE = 0,
|
||||
BUTTON_PRESS = 1,
|
||||
BUTTON_REPEAT = 2,
|
||||
};
|
||||
|
||||
enum MouseButton
|
||||
{
|
||||
MOUSE_BUTTON_LEFT = 0,
|
||||
MOUSE_BUTTON_RIGHT = 1,
|
||||
MOUSE_BUTTON_MIDDLE = 2,
|
||||
NUM_MOUSE_BUTTONIDX,
|
||||
};
|
||||
|
||||
enum MouseButtonFlag
|
||||
{
|
||||
MOUSE_BUTTONFLAG_NONE = 0,
|
||||
MOUSE_BUTTONFLAG_LEFT = (1 << MOUSE_BUTTON_LEFT),
|
||||
MOUSE_BUTTONFLAG_RIGHT = (1 << MOUSE_BUTTON_RIGHT),
|
||||
MOUSE_BUTTONFLAG_MIDDLE = (1 << MOUSE_BUTTON_MIDDLE)
|
||||
};
|
||||
|
||||
enum KeyCode
|
||||
{
|
||||
KEY_UNKNOWN = -1,
|
||||
KEY_SPACE = 32,
|
||||
KEY_APOSTROPHE = 39, /* ' */
|
||||
KEY_LEFT_PARENTHESIS = 40, /* ( */
|
||||
KEY_RIGHT_PARENTHESIS = 41, /* ) */
|
||||
KEY_ASTERISK = 42, /* * */
|
||||
KEY_PLUS = 43, /* + */
|
||||
KEY_COMMA = 44, /* , */
|
||||
KEY_MINUS = 45, /* - */
|
||||
KEY_PERIOD = 46, /* . */
|
||||
KEY_SLASH = 47, /* / */
|
||||
KEY_0 = 48,
|
||||
KEY_1 = 49,
|
||||
KEY_2 = 50,
|
||||
KEY_3 = 51,
|
||||
KEY_4 = 52,
|
||||
KEY_5 = 53,
|
||||
KEY_6 = 54,
|
||||
KEY_7 = 55,
|
||||
KEY_8 = 56,
|
||||
KEY_9 = 57,
|
||||
KEY_SEMICOLON = 59, /* ; */
|
||||
KEY_EQUAL = 61, /* = */
|
||||
KEY_A = 65,
|
||||
KEY_B = 66,
|
||||
KEY_C = 67,
|
||||
KEY_D = 68,
|
||||
KEY_E = 69,
|
||||
KEY_F = 70,
|
||||
KEY_G = 71,
|
||||
KEY_H = 72,
|
||||
KEY_I = 73,
|
||||
KEY_J = 74,
|
||||
KEY_K = 75,
|
||||
KEY_L = 76,
|
||||
KEY_M = 77,
|
||||
KEY_N = 78,
|
||||
KEY_O = 79,
|
||||
KEY_P = 80,
|
||||
KEY_Q = 81,
|
||||
KEY_R = 82,
|
||||
KEY_S = 83,
|
||||
KEY_T = 84,
|
||||
KEY_U = 85,
|
||||
KEY_V = 86,
|
||||
KEY_W = 87,
|
||||
KEY_X = 88,
|
||||
KEY_Y = 89,
|
||||
KEY_Z = 90,
|
||||
KEY_LEFT_BRACKET = 91, /* [ */
|
||||
KEY_BACKSLASH = 92, /* \ */
|
||||
KEY_RIGHT_BRACKET = 93, /* ] */
|
||||
KEY_GRAVE_ACCENT = 96, /* ` */
|
||||
KEY_WORLD_1 = 161, /* non-US #1 */
|
||||
KEY_WORLD_2 = 162, /* non-US #2 */
|
||||
|
||||
/* Function keys */
|
||||
KEY_ESCAPE = 256,
|
||||
KEY_ENTER = 257,
|
||||
KEY_TAB = 258,
|
||||
KEY_BACKSPACE = 259,
|
||||
KEY_INSERT = 260,
|
||||
KEY_DELETE = 261,
|
||||
KEY_RIGHT = 262,
|
||||
KEY_LEFT = 263,
|
||||
KEY_DOWN = 264,
|
||||
KEY_UP = 265,
|
||||
KEY_PAGE_UP = 266,
|
||||
KEY_PAGE_DOWN = 267,
|
||||
KEY_HOME = 268,
|
||||
KEY_END = 269,
|
||||
KEY_CAPS_LOCK = 280,
|
||||
KEY_SCROLL_LOCK = 281,
|
||||
KEY_NUM_LOCK = 282,
|
||||
KEY_PRINT_SCREEN = 283,
|
||||
KEY_PAUSE = 284,
|
||||
KEY_F1 = 290,
|
||||
KEY_F2 = 291,
|
||||
KEY_F3 = 292,
|
||||
KEY_F4 = 293,
|
||||
KEY_F5 = 294,
|
||||
KEY_F6 = 295,
|
||||
KEY_F7 = 296,
|
||||
KEY_F8 = 297,
|
||||
KEY_F9 = 298,
|
||||
KEY_F10 = 299,
|
||||
KEY_F11 = 300,
|
||||
KEY_F12 = 301,
|
||||
KEY_F13 = 302,
|
||||
KEY_F14 = 303,
|
||||
KEY_F15 = 304,
|
||||
KEY_F16 = 305,
|
||||
KEY_F17 = 306,
|
||||
KEY_F18 = 307,
|
||||
KEY_F19 = 308,
|
||||
KEY_F20 = 309,
|
||||
KEY_F21 = 310,
|
||||
KEY_F22 = 311,
|
||||
KEY_F23 = 312,
|
||||
KEY_F24 = 313,
|
||||
KEY_F25 = 314,
|
||||
KEY_KP_0 = 320,
|
||||
KEY_KP_1 = 321,
|
||||
KEY_KP_2 = 322,
|
||||
KEY_KP_3 = 323,
|
||||
KEY_KP_4 = 324,
|
||||
KEY_KP_5 = 325,
|
||||
KEY_KP_6 = 326,
|
||||
KEY_KP_7 = 327,
|
||||
KEY_KP_8 = 328,
|
||||
KEY_KP_9 = 329,
|
||||
KEY_KP_DECIMAL = 330,
|
||||
KEY_KP_DIVIDE = 331,
|
||||
KEY_KP_MULTIPLY = 332,
|
||||
KEY_KP_SUBTRACT = 333,
|
||||
KEY_KP_ADD = 334,
|
||||
KEY_KP_ENTER = 335,
|
||||
KEY_KP_EQUAL = 336,
|
||||
KEY_LEFT_SHIFT = 340,
|
||||
KEY_LEFT_CONTROL = 341,
|
||||
KEY_LEFT_ALT = 342,
|
||||
KEY_LEFT_SUPER = 343,
|
||||
KEY_RIGHT_SHIFT = 344,
|
||||
KEY_RIGHT_CONTROL = 345,
|
||||
KEY_RIGHT_ALT = 346,
|
||||
KEY_RIGHT_SUPER = 347,
|
||||
KEY_MENU = 348,
|
||||
KEY_LAST = KEY_MENU,
|
||||
};
|
||||
|
||||
enum KeyModifiers
|
||||
{
|
||||
KMOD_SHIFT = 1,
|
||||
KMOD_CONTROL = 2,
|
||||
KMOD_ALT = 4,
|
||||
KMOD_SUPER = 8,
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct GLFWwindow* m_internal = nullptr; ///< internal delegate to GLFWwindow
|
||||
std::string m_windowName;
|
||||
|
||||
|
||||
inline bool pollEvents() // returns false on exit, can do while(pollEvents()){ ... }
|
||||
{
|
||||
NVPSystem::pollEvents();
|
||||
return !isClosing();
|
||||
}
|
||||
inline void waitEvents() { NVPSystem::waitEvents(); }
|
||||
inline double getTime() { return NVPSystem::getTime(); }
|
||||
inline std::string exePath() { return NVPSystem::exePath(); }
|
||||
|
||||
// Accessors
|
||||
inline int getWidth() const { return m_windowSize[0]; }
|
||||
inline int getHeight() const { return m_windowSize[1]; }
|
||||
inline int getMouseWheel() const { return m_mouseWheel; }
|
||||
inline int getKeyModifiers() const { return m_keyModifiers; }
|
||||
inline int getMouseX() const { return m_mouseX; }
|
||||
inline int getMouseY() const { return m_mouseY; }
|
||||
|
||||
void setTitle(const char* title);
|
||||
void setFullScreen(bool bYes);
|
||||
void setWindowPos(int x, int y);
|
||||
void setWindowSize(int w, int h);
|
||||
inline void setKeyModifiers(int m) { m_keyModifiers = m; }
|
||||
inline void setMouse(int x, int y)
|
||||
{
|
||||
m_mouseX = x;
|
||||
m_mouseY = y;
|
||||
}
|
||||
|
||||
inline bool isFullScreen() const { return m_isFullScreen; }
|
||||
bool isClosing() const;
|
||||
bool isOpen() const;
|
||||
|
||||
virtual bool open(int posX, int posY, int width, int height, const char* title, bool requireGLContext); ///< creates internal window and opens it
|
||||
void deinit(); ///< destroys internal window
|
||||
|
||||
void close(); ///< triggers closing event, still needs deinit for final cleanup
|
||||
void maximize();
|
||||
void restore();
|
||||
void minimize();
|
||||
|
||||
// uses operating system specific code for sake of debugging/automated testing
|
||||
void screenshot(const char* filename);
|
||||
void clear(uint32_t r, uint32_t g, uint32_t b);
|
||||
|
||||
/// \defgroup dialog
|
||||
/// simple modal file dialog, uses OS basic api
|
||||
/// the exts string must be a | separated list that has two items per possible extension
|
||||
/// "extension descripton|*.ext"
|
||||
/// @{
|
||||
std::string openFileDialog(const char* title, const char* exts);
|
||||
std::string saveFileDialog(const char* title, const char* exts);
|
||||
/// @}
|
||||
|
||||
/// \name derived windows/apps should override to handle events
|
||||
/// @{
|
||||
virtual void onWindowClose() {}
|
||||
virtual void onWindowResize(int w, int h) {}
|
||||
virtual void onWindowRefresh() {}
|
||||
virtual void onMouseMotion(int x, int y) {}
|
||||
virtual void onMouseWheel(int delta) {}
|
||||
virtual void onMouseButton(MouseButton button, ButtonAction action, int mods, int x, int y) {}
|
||||
virtual void onKeyboard(KeyCode key, ButtonAction action, int mods, int x, int y) {}
|
||||
virtual void onKeyboardChar(unsigned char key, int mods, int x, int y) {}
|
||||
virtual void onDragDrop(int num, const char** paths) {}
|
||||
/// @}
|
||||
|
||||
private:
|
||||
int m_mouseX = 0;
|
||||
int m_mouseY = 0;
|
||||
int m_mouseWheel = 0;
|
||||
int m_windowSize[2] = {0, 0};
|
||||
int m_keyModifiers = 0;
|
||||
bool m_isFullScreen = false;
|
||||
bool m_isClosing = false;
|
||||
int m_preFullScreenPos[2] = {0, 0};
|
||||
int m_preFullScreenSize[2] = {0, 0};
|
||||
|
||||
/// \name Callbacks
|
||||
/// @{
|
||||
static void cb_windowrefreshfun(GLFWwindow* glfwwin);
|
||||
static void cb_windowsizefun(GLFWwindow* glfwwin, int w, int h);
|
||||
static void cb_windowclosefun(GLFWwindow* glfwwin);
|
||||
static void cb_mousebuttonfun(GLFWwindow* glfwwin, int button, int action, int mods);
|
||||
static void cb_cursorposfun(GLFWwindow* glfwwin, double x, double y);
|
||||
static void cb_scrollfun(GLFWwindow* glfwwin, double x, double y);
|
||||
static void cb_keyfun(GLFWwindow* glfwwin, int key, int scancode, int action, int mods);
|
||||
static void cb_charfun(GLFWwindow* glfwwin, unsigned int codepoint);
|
||||
static void cb_dropfun(GLFWwindow* glfwwin, int count, const char** paths);
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif
|
||||
35
raytracer/nvpro_core/nvp/perproject_globals.cpp
Normal file
35
raytracer/nvpro_core/nvp/perproject_globals.cpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <nvp/perproject_globals.hpp>
|
||||
|
||||
std::string getProjectName()
|
||||
{
|
||||
return PROJECT_NAME;
|
||||
}
|
||||
|
||||
bool isAftermathAvailable()
|
||||
{
|
||||
#ifdef NVVK_SUPPORTS_AFTERMATH
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
27
raytracer/nvpro_core/nvp/perproject_globals.hpp
Normal file
27
raytracer/nvpro_core/nvp/perproject_globals.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/// @DOC_SKIP
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
extern std::string getProjectName();
|
||||
extern bool isAftermathAvailable();
|
||||
59
raytracer/nvpro_core/nvp/platform.h
Normal file
59
raytracer/nvpro_core/nvp/platform.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
/// @DOC_SKIP
|
||||
|
||||
#include "NvFoundation.h"
|
||||
|
||||
#ifndef NVP_PLATFORM_H__
|
||||
#define NVP_PLATFORM_H__
|
||||
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4))
|
||||
|
||||
#define NV_NOOP(...)
|
||||
#define NV_BARRIER() __sync_synchronize()
|
||||
|
||||
/*
|
||||
// maybe better than __sync_synchronize?
|
||||
#if defined(__i386__ ) || defined(__x64__)
|
||||
#define NVP_BARRIER() __asm__ __volatile__ ("mfence" ::: "memory")
|
||||
#endif
|
||||
|
||||
#if defined(__arm__)
|
||||
#define NVP_BARRIER() __asm__ __volatile__ ("dmb" :::"memory")
|
||||
#endif
|
||||
*/
|
||||
|
||||
#elif defined(__MSC__) || defined(_MSC_VER)
|
||||
|
||||
#include <emmintrin.h>
|
||||
|
||||
#pragma warning(disable:4142) // redefinition of same type
|
||||
#if (_MSC_VER >= 1400) // VC8+
|
||||
#pragma warning(disable : 4996) // Either disable all deprecation warnings,
|
||||
#endif // VC8+
|
||||
|
||||
#define NV_NOOP __noop
|
||||
#define NV_BARRIER() _mm_mfence()
|
||||
|
||||
#else
|
||||
#error "compiler unkown"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
30
raytracer/nvpro_core/nvp/resources.h
Normal file
30
raytracer/nvpro_core/nvp/resources.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by Bak3dView.rc
|
||||
/// @DOC_SKIP
|
||||
#define IDC_MYICON 2
|
||||
#define IDD_OPENGL_DIALOG 102
|
||||
#define IDD_ABOUTBOX 103
|
||||
#define IDS_APP_TITLE 103
|
||||
#define IDM_ABOUT 104
|
||||
#define IDM_EXIT 105
|
||||
#define IDS_HELLO 106
|
||||
#define IDI_OPENGL_ICON 107
|
||||
#define IDC_OPENGL_MENU 108
|
||||
#define IDC_OPENGL_ACCELERATOR 109
|
||||
#define IDC_OPENGL 110
|
||||
#define IDR_MAINFRAME 128
|
||||
#define IDR_CGFX1 129
|
||||
#define IDR_DEFAULTEFFECT 130
|
||||
#define IDC_STATIC -1
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 131
|
||||
#define _APS_NEXT_COMMAND_VALUE 32771
|
||||
#define _APS_NEXT_CONTROL_VALUE 1000
|
||||
#define _APS_NEXT_SYMED_VALUE 110
|
||||
#endif
|
||||
#endif
|
||||
137
raytracer/nvpro_core/nvp/resources.rc
Normal file
137
raytracer/nvpro_core/nvp/resources.rc
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resources.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#define APSTUDIO_HIDDEN_SYMBOLS
|
||||
#include "windows.h"
|
||||
#undef APSTUDIO_HIDDEN_SYMBOLS
|
||||
#include "resources.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_OPENGL_ICON ICON "resources/opengl.ico"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Menu
|
||||
//
|
||||
|
||||
IDC_OPENGL_MENU MENU
|
||||
BEGIN
|
||||
POPUP "&File"
|
||||
BEGIN
|
||||
MENUITEM "E&xit", IDM_EXIT
|
||||
END
|
||||
POPUP "&Help"
|
||||
BEGIN
|
||||
MENUITEM "&About ...", IDM_ABOUT
|
||||
END
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Accelerator
|
||||
//
|
||||
|
||||
IDC_OPENGL_ACCELERATOR ACCELERATORS
|
||||
BEGIN
|
||||
"?", IDM_ABOUT, ASCII, ALT
|
||||
"/", IDM_ABOUT, ASCII, ALT
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_ABOUTBOX DIALOG 22, 17, 230, 75
|
||||
STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "About"
|
||||
FONT 8, "System"
|
||||
BEGIN
|
||||
ICON IDI_OPENGL_ICON,IDC_MYICON,14,9,20,20
|
||||
LTEXT "OpenGL Sample v1.0",IDC_STATIC,49,10,119,8,SS_NOPREFIX
|
||||
LTEXT "Copyright (C) ",IDC_STATIC,49,20,119,8
|
||||
DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
|
||||
END
|
||||
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
|
||||
"#include ""windows.h""\r\n"
|
||||
"#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
|
||||
"#include ""resource.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_APP_TITLE "opengl"
|
||||
IDC_OPENGL "OPENGL"
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue