From 4ec4dc691f5a41ef85d8ee07295e897e368bc8c7 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 21 Dec 2018 00:17:44 +0100 Subject: [PATCH 01/58] Use stb_vorbis.h as header only --- examples/others/audio_standalone.c | 2 +- projects/Geany/raylib_compile_sources.bat | 4 +- projects/Notepad++/npes_saved_mingw.txt | Bin 7730 -> 7620 bytes projects/Notepad++/npes_saved_tcc.txt | Bin 7656 -> 7546 bytes projects/VS2015.UWP/raylib/raylib.vcxproj | 1 - projects/VS2015/raylib/raylib.vcxproj | 1 - projects/VS2017/raylib/raylib.vcxproj | 1 - src/CMakeLists.txt | 3 +- src/Makefile | 9 +- src/audio.c | 2 +- src/external/stb_vorbis.c | 5074 -------------------- src/external/stb_vorbis.h | 5145 ++++++++++++++++++++- 12 files changed, 5120 insertions(+), 5122 deletions(-) delete mode 100644 src/external/stb_vorbis.c diff --git a/examples/others/audio_standalone.c b/examples/others/audio_standalone.c index d6e75066..f08fbf82 100644 --- a/examples/others/audio_standalone.c +++ b/examples/others/audio_standalone.c @@ -6,7 +6,7 @@ * * DEPENDENCIES: * mini_al.h - Audio device management lib (http://kcat.strangesoft.net/openal.html) -* stb_vorbis.c - Ogg audio files loading (http://www.nothings.org/stb_vorbis/) +* stb_vorbis.h - Ogg audio files loading (http://www.nothings.org/stb_vorbis/) * jar_xm.h - XM module file loading * jar_mod.h - MOD audio file loading * dr_flac.h - FLAC audio file loading diff --git a/projects/Geany/raylib_compile_sources.bat b/projects/Geany/raylib_compile_sources.bat index 105857ef..8a5c1e7b 100644 --- a/projects/Geany/raylib_compile_sources.bat +++ b/projects/Geany/raylib_compile_sources.bat @@ -24,13 +24,13 @@ gcc -O2 -c textures.c -std=c99 -Wall -DPLATFORM_DESKTOP gcc -O2 -c text.c -std=c99 -Wall -DPLATFORM_DESKTOP gcc -O2 -c models.c -std=c99 -Wall -DPLATFORM_DESKTOP gcc -O2 -c audio.c -std=c99 -Wall -DPLATFORM_DESKTOP -gcc -O2 -c external/stb_vorbis.c -Wall -I. gcc -O2 -c external/mini_al.c -Wall -I. gcc -O2 -c utils.c -std=c99 -Wall -DPLATFORM_DESKTOP + :: . :: . > Generate raylib library :: ------------------------------ -ar rcs libraylib.a core.o rglfw.o shapes.o textures.o text.o models.o audio.o mini_al.o stb_vorbis.o utils.o +ar rcs libraylib.a core.o rglfw.o shapes.o textures.o text.o models.o audio.o mini_al.o utils.o :: . :: > Installing raylib library :: ----------------------------- diff --git a/projects/Notepad++/npes_saved_mingw.txt b/projects/Notepad++/npes_saved_mingw.txt index 7ed0662d3239b78d5e48579c441af85f0d3bbae1..6857fa13de2869a8e67352b1dd85bb23a3fd1667 100644 GIT binary patch delta 17 YcmdmFbHsWBhuG#Au_DIJFT}mr06wP%RsaA1 delta 63 zcmX?Ny~$<+huGv@qC$ej3?&Rn4Dk$Q4EYR2Kvw4Dha!fXImC(>g)yWUiZ>UF2eJVG DLdX#8 diff --git a/projects/Notepad++/npes_saved_tcc.txt b/projects/Notepad++/npes_saved_tcc.txt index d3c4fb0e99ef169a79b41cdd345f559cf822b4e6..6b8d6f2e2e649b6b8df50e469c96befefe88019c 100644 GIT binary patch delta 17 ZcmaE1{mW{@Ez!+-VnvLbcZut<0RT*h2N?hW delta 67 zcmexm^}>3?Ez!w6qC$ej3?&Rn4Dk$Q4EYR2Kvw4DhobV6qeO%@-x4ii6vmKdDBkQU HZpsD# - diff --git a/projects/VS2015/raylib/raylib.vcxproj b/projects/VS2015/raylib/raylib.vcxproj index 3925c3e0..5d592146 100644 --- a/projects/VS2015/raylib/raylib.vcxproj +++ b/projects/VS2015/raylib/raylib.vcxproj @@ -235,7 +235,6 @@ - diff --git a/projects/VS2017/raylib/raylib.vcxproj b/projects/VS2017/raylib/raylib.vcxproj index 1e8f9491..df745e16 100644 --- a/projects/VS2017/raylib/raylib.vcxproj +++ b/projects/VS2017/raylib/raylib.vcxproj @@ -162,7 +162,6 @@ - diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1b1d0d06..7a91f13a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,8 +47,7 @@ add_definitions("-DRAYLIB_CMAKE=1") if(USE_AUDIO) file(GLOB mini_al external/mini_al.c) MESSAGE(STATUS "Audio Backend: mini_al") - file(GLOB stb_vorbis external/stb_vorbis.c) - set(sources ${raylib_sources} ${mini_al} ${stb_vorbis}) + set(sources ${raylib_sources} ${mini_al}) else() MESSAGE(STATUS "Audio Backend: None (-DUSE_AUDIO=OFF)") set(INCLUDE_AUDIO_MODULE 0) diff --git a/src/Makefile b/src/Makefile index 7e1069d8..e3e9d31b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -404,7 +404,6 @@ endif ifeq ($(INCLUDE_AUDIO_MODULE),TRUE) OBJS += audio.o - OBJS += stb_vorbis.o OBJS += mini_al.o endif @@ -537,10 +536,6 @@ audio.o : audio.c raylib.h mini_al.o : external/mini_al.c external/mini_al.h $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM) -# Compile stb_vorbis library -stb_vorbis.o: external/stb_vorbis.c external/stb_vorbis.h - $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM) - # Compile utils module utils.o : utils.c utils.h $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM) @@ -628,9 +623,9 @@ endif # Clean everything clean: ifeq ($(PLATFORM_OS),WINDOWS) - del *.o $(RAYLIB_RELEASE_PATH)/libraylib.a $(RAYLIB_RELEASE_PATH)/libraylib.bc $(RAYLIB_RELEASE_PATH)/libraylib.so external/stb_vorbis.o + del *.o $(RAYLIB_RELEASE_PATH)/libraylib.a $(RAYLIB_RELEASE_PATH)/libraylib.bc $(RAYLIB_RELEASE_PATH)/libraylib.so else - rm -fv *.o $(RAYLIB_RELEASE_PATH)/libraylib.a $(RAYLIB_RELEASE_PATH)/libraylib.bc $(RAYLIB_RELEASE_PATH)/libraylib.so* external/stb_vorbis.o + rm -fv *.o $(RAYLIB_RELEASE_PATH)/libraylib.a $(RAYLIB_RELEASE_PATH)/libraylib.bc $(RAYLIB_RELEASE_PATH)/libraylib.so* endif ifeq ($(PLATFORM),PLATFORM_ANDROID) rm -rf $(ANDROID_TOOLCHAIN) diff --git a/src/audio.c b/src/audio.c index d945301a..41aa1b61 100644 --- a/src/audio.c +++ b/src/audio.c @@ -91,7 +91,7 @@ #include // Required for: FILE, fopen(), fclose(), fread() #if defined(SUPPORT_FILEFORMAT_OGG) - //#define STB_VORBIS_HEADER_ONLY + #define STB_VORBIS_IMPLEMENTATION #include "external/stb_vorbis.h" // OGG loading functions #endif diff --git a/src/external/stb_vorbis.c b/src/external/stb_vorbis.c deleted file mode 100644 index b40ae3b6..00000000 --- a/src/external/stb_vorbis.c +++ /dev/null @@ -1,5074 +0,0 @@ -#include "stb_vorbis.h" - -#ifndef STB_VORBIS_HEADER_ONLY - -// global configuration settings (e.g. set these in the project/makefile), -// or just set them in this file at the top (although ideally the first few -// should be visible when the header file is compiled too, although it's not -// crucial) - -// STB_VORBIS_NO_PUSHDATA_API -// does not compile the code for the various stb_vorbis_*_pushdata() -// functions -// #define STB_VORBIS_NO_PUSHDATA_API - -// STB_VORBIS_NO_PULLDATA_API -// does not compile the code for the non-pushdata APIs -// #define STB_VORBIS_NO_PULLDATA_API - -// STB_VORBIS_NO_STDIO -// does not compile the code for the APIs that use FILE *s internally -// or externally (implied by STB_VORBIS_NO_PULLDATA_API) -// #define STB_VORBIS_NO_STDIO - -// STB_VORBIS_NO_INTEGER_CONVERSION -// does not compile the code for converting audio sample data from -// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) -// #define STB_VORBIS_NO_INTEGER_CONVERSION - -// STB_VORBIS_NO_FAST_SCALED_FLOAT -// does not use a fast float-to-int trick to accelerate float-to-int on -// most platforms which requires endianness be defined correctly. -//#define STB_VORBIS_NO_FAST_SCALED_FLOAT - - -// STB_VORBIS_MAX_CHANNELS [number] -// globally define this to the maximum number of channels you need. -// The spec does not put a restriction on channels except that -// the count is stored in a byte, so 255 is the hard limit. -// Reducing this saves about 16 bytes per value, so using 16 saves -// (255-16)*16 or around 4KB. Plus anything other memory usage -// I forgot to account for. Can probably go as low as 8 (7.1 audio), -// 6 (5.1 audio), or 2 (stereo only). -#ifndef STB_VORBIS_MAX_CHANNELS -#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? -#endif - -// STB_VORBIS_PUSHDATA_CRC_COUNT [number] -// after a flush_pushdata(), stb_vorbis begins scanning for the -// next valid page, without backtracking. when it finds something -// that looks like a page, it streams through it and verifies its -// CRC32. Should that validation fail, it keeps scanning. But it's -// possible that _while_ streaming through to check the CRC32 of -// one candidate page, it sees another candidate page. This #define -// determines how many "overlapping" candidate pages it can search -// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas -// garbage pages could be as big as 64KB, but probably average ~16KB. -// So don't hose ourselves by scanning an apparent 64KB page and -// missing a ton of real ones in the interim; so minimum of 2 -#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT -#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 -#endif - -// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] -// sets the log size of the huffman-acceleration table. Maximum -// supported value is 24. with larger numbers, more decodings are O(1), -// but the table size is larger so worse cache missing, so you'll have -// to probe (and try multiple ogg vorbis files) to find the sweet spot. -#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH -#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 -#endif - -// STB_VORBIS_FAST_BINARY_LENGTH [number] -// sets the log size of the binary-search acceleration table. this -// is used in similar fashion to the fast-huffman size to set initial -// parameters for the binary search - -// STB_VORBIS_FAST_HUFFMAN_INT -// The fast huffman tables are much more efficient if they can be -// stored as 16-bit results instead of 32-bit results. This restricts -// the codebooks to having only 65535 possible outcomes, though. -// (At least, accelerated by the huffman table.) -#ifndef STB_VORBIS_FAST_HUFFMAN_INT -#define STB_VORBIS_FAST_HUFFMAN_SHORT -#endif - -// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH -// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls -// back on binary searching for the correct one. This requires storing -// extra tables with the huffman codes in sorted order. Defining this -// symbol trades off space for speed by forcing a linear search in the -// non-fast case, except for "sparse" codebooks. -// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH - -// STB_VORBIS_DIVIDES_IN_RESIDUE -// stb_vorbis precomputes the result of the scalar residue decoding -// that would otherwise require a divide per chunk. you can trade off -// space for time by defining this symbol. -// #define STB_VORBIS_DIVIDES_IN_RESIDUE - -// STB_VORBIS_DIVIDES_IN_CODEBOOK -// vorbis VQ codebooks can be encoded two ways: with every case explicitly -// stored, or with all elements being chosen from a small range of values, -// and all values possible in all elements. By default, stb_vorbis expands -// this latter kind out to look like the former kind for ease of decoding, -// because otherwise an integer divide-per-vector-element is required to -// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can -// trade off storage for speed. -//#define STB_VORBIS_DIVIDES_IN_CODEBOOK - -#ifdef STB_VORBIS_CODEBOOK_SHORTS -#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" -#endif - -// STB_VORBIS_DIVIDE_TABLE -// this replaces small integer divides in the floor decode loop with -// table lookups. made less than 1% difference, so disabled by default. - -// STB_VORBIS_NO_INLINE_DECODE -// disables the inlining of the scalar codebook fast-huffman decode. -// might save a little codespace; useful for debugging -// #define STB_VORBIS_NO_INLINE_DECODE - -// STB_VORBIS_NO_DEFER_FLOOR -// Normally we only decode the floor without synthesizing the actual -// full curve. We can instead synthesize the curve immediately. This -// requires more memory and is very likely slower, so I don't think -// you'd ever want to do it except for debugging. -// #define STB_VORBIS_NO_DEFER_FLOOR - - - - -////////////////////////////////////////////////////////////////////////////// - -#ifdef STB_VORBIS_NO_PULLDATA_API - #define STB_VORBIS_NO_INTEGER_CONVERSION - #define STB_VORBIS_NO_STDIO -#endif - -#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) - #define STB_VORBIS_NO_STDIO 1 -#endif - -#ifndef STB_VORBIS_NO_INTEGER_CONVERSION -#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT - - // only need endianness for fast-float-to-int, which we don't - // use for pushdata - - #ifndef STB_VORBIS_BIG_ENDIAN - #define STB_VORBIS_ENDIAN 0 - #else - #define STB_VORBIS_ENDIAN 1 - #endif - -#endif -#endif - - -#ifndef STB_VORBIS_NO_STDIO -#include -#endif - -#ifndef STB_VORBIS_NO_CRT - #include - #include - #include - #include - - // find definition of alloca if it's not in stdlib.h: - #if defined(_MSC_VER) || defined(__MINGW32__) - #include - #endif - #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) - #include - #endif -#else // STB_VORBIS_NO_CRT - #define NULL 0 - #define malloc(s) 0 - #define free(s) ((void) 0) - #define realloc(s) 0 -#endif // STB_VORBIS_NO_CRT - -#include - -#ifdef __MINGW32__ - // eff you mingw: - // "fixed": - // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/ - // "no that broke the build, reverted, who cares about C": - // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/ - #ifdef __forceinline - #undef __forceinline - #endif - #define __forceinline - #define alloca __builtin_alloca -#elif !defined(_MSC_VER) - #if __GNUC__ - #define __forceinline inline - #else - #define __forceinline - #endif -#endif - -#if STB_VORBIS_MAX_CHANNELS > 256 -#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" -#endif - -#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 -#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" -#endif - - -#if 0 -#include -#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) -#else -#define CHECK(f) ((void) 0) -#endif - -#define MAX_BLOCKSIZE_LOG 13 // from specification -#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) - - -typedef unsigned char uint8; -typedef signed char int8; -typedef unsigned short uint16; -typedef signed short int16; -typedef unsigned int uint32; -typedef signed int int32; - -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif - -typedef float codetype; - -// @NOTE -// -// Some arrays below are tagged "//varies", which means it's actually -// a variable-sized piece of data, but rather than malloc I assume it's -// small enough it's better to just allocate it all together with the -// main thing -// -// Most of the variables are specified with the smallest size I could pack -// them into. It might give better performance to make them all full-sized -// integers. It should be safe to freely rearrange the structures or change -// the sizes larger--nothing relies on silently truncating etc., nor the -// order of variables. - -#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) -#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) - -typedef struct -{ - int dimensions, entries; - uint8 *codeword_lengths; - float minimum_value; - float delta_value; - uint8 value_bits; - uint8 lookup_type; - uint8 sequence_p; - uint8 sparse; - uint32 lookup_values; - codetype *multiplicands; - uint32 *codewords; - #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT - int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; - #else - int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; - #endif - uint32 *sorted_codewords; - int *sorted_values; - int sorted_entries; -} Codebook; - -typedef struct -{ - uint8 order; - uint16 rate; - uint16 bark_map_size; - uint8 amplitude_bits; - uint8 amplitude_offset; - uint8 number_of_books; - uint8 book_list[16]; // varies -} Floor0; - -typedef struct -{ - uint8 partitions; - uint8 partition_class_list[32]; // varies - uint8 class_dimensions[16]; // varies - uint8 class_subclasses[16]; // varies - uint8 class_masterbooks[16]; // varies - int16 subclass_books[16][8]; // varies - uint16 Xlist[31*8+2]; // varies - uint8 sorted_order[31*8+2]; - uint8 neighbors[31*8+2][2]; - uint8 floor1_multiplier; - uint8 rangebits; - int values; -} Floor1; - -typedef union -{ - Floor0 floor0; - Floor1 floor1; -} Floor; - -typedef struct -{ - uint32 begin, end; - uint32 part_size; - uint8 classifications; - uint8 classbook; - uint8 **classdata; - int16 (*residue_books)[8]; -} Residue; - -typedef struct -{ - uint8 magnitude; - uint8 angle; - uint8 mux; -} MappingChannel; - -typedef struct -{ - uint16 coupling_steps; - MappingChannel *chan; - uint8 submaps; - uint8 submap_floor[15]; // varies - uint8 submap_residue[15]; // varies -} Mapping; - -typedef struct -{ - uint8 blockflag; - uint8 mapping; - uint16 windowtype; - uint16 transformtype; -} Mode; - -typedef struct -{ - uint32 goal_crc; // expected crc if match - int bytes_left; // bytes left in packet - uint32 crc_so_far; // running crc - int bytes_done; // bytes processed in _current_ chunk - uint32 sample_loc; // granule pos encoded in page -} CRCscan; - -typedef struct -{ - uint32 page_start, page_end; - uint32 last_decoded_sample; -} ProbedPage; - -struct stb_vorbis -{ - // user-accessible info - unsigned int sample_rate; - int channels; - - unsigned int setup_memory_required; - unsigned int temp_memory_required; - unsigned int setup_temp_memory_required; - - // input config -#ifndef STB_VORBIS_NO_STDIO - FILE *f; - uint32 f_start; - int close_on_free; -#endif - - uint8 *stream; - uint8 *stream_start; - uint8 *stream_end; - - uint32 stream_len; - - uint8 push_mode; - - uint32 first_audio_page_offset; - - ProbedPage p_first, p_last; - - // memory management - stb_vorbis_alloc alloc; - int setup_offset; - int temp_offset; - - // run-time results - int eof; - enum STBVorbisError error; - - // user-useful data - - // header info - int blocksize[2]; - int blocksize_0, blocksize_1; - int codebook_count; - Codebook *codebooks; - int floor_count; - uint16 floor_types[64]; // varies - Floor *floor_config; - int residue_count; - uint16 residue_types[64]; // varies - Residue *residue_config; - int mapping_count; - Mapping *mapping; - int mode_count; - Mode mode_config[64]; // varies - - uint32 total_samples; - - // decode buffer - float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; - float *outputs [STB_VORBIS_MAX_CHANNELS]; - - float *previous_window[STB_VORBIS_MAX_CHANNELS]; - int previous_length; - - #ifndef STB_VORBIS_NO_DEFER_FLOOR - int16 *finalY[STB_VORBIS_MAX_CHANNELS]; - #else - float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; - #endif - - uint32 current_loc; // sample location of next frame to decode - int current_loc_valid; - - // per-blocksize precomputed data - - // twiddle factors - float *A[2],*B[2],*C[2]; - float *window[2]; - uint16 *bit_reverse[2]; - - // current page/packet/segment streaming info - uint32 serial; // stream serial number for verification - int last_page; - int segment_count; - uint8 segments[255]; - uint8 page_flag; - uint8 bytes_in_seg; - uint8 first_decode; - int next_seg; - int last_seg; // flag that we're on the last segment - int last_seg_which; // what was the segment number of the last seg? - uint32 acc; - int valid_bits; - int packet_bytes; - int end_seg_with_known_loc; - uint32 known_loc_for_packet; - int discard_samples_deferred; - uint32 samples_output; - - // push mode scanning - int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching -#ifndef STB_VORBIS_NO_PUSHDATA_API - CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; -#endif - - // sample-access - int channel_buffer_start; - int channel_buffer_end; -}; - -#if defined(STB_VORBIS_NO_PUSHDATA_API) - #define IS_PUSH_MODE(f) FALSE -#elif defined(STB_VORBIS_NO_PULLDATA_API) - #define IS_PUSH_MODE(f) TRUE -#else - #define IS_PUSH_MODE(f) ((f)->push_mode) -#endif - -typedef struct stb_vorbis vorb; - -static int error(vorb *f, enum STBVorbisError e) -{ - f->error = e; - if (!f->eof && e != VORBIS_need_more_data) { - f->error=e; // breakpoint for debugging - } - return 0; -} - - -// these functions are used for allocating temporary memory -// while decoding. if you can afford the stack space, use -// alloca(); otherwise, provide a temp buffer and it will -// allocate out of those. - -#define array_size_required(count,size) (count*(sizeof(void *)+(size))) - -#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) -#define temp_free(f,p) 0 -#define temp_alloc_save(f) ((f)->temp_offset) -#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) - -#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) - -// given a sufficiently large block of memory, make an array of pointers to subblocks of it -static void *make_block_array(void *mem, int count, int size) -{ - int i; - void ** p = (void **) mem; - char *q = (char *) (p + count); - for (i=0; i < count; ++i) { - p[i] = q; - q += size; - } - return p; -} - -static void *setup_malloc(vorb *f, int sz) -{ - sz = (sz+3) & ~3; - f->setup_memory_required += sz; - if (f->alloc.alloc_buffer) { - void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; - if (f->setup_offset + sz > f->temp_offset) return NULL; - f->setup_offset += sz; - return p; - } - return sz ? malloc(sz) : NULL; -} - -static void setup_free(vorb *f, void *p) -{ - if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack - free(p); -} - -static void *setup_temp_malloc(vorb *f, int sz) -{ - sz = (sz+3) & ~3; - if (f->alloc.alloc_buffer) { - if (f->temp_offset - sz < f->setup_offset) return NULL; - f->temp_offset -= sz; - return (char *) f->alloc.alloc_buffer + f->temp_offset; - } - return malloc(sz); -} - -static void setup_temp_free(vorb *f, void *p, int sz) -{ - if (f->alloc.alloc_buffer) { - f->temp_offset += (sz+3)&~3; - return; - } - free(p); -} - -#define CRC32_POLY 0x04c11db7 // from spec - -static uint32 crc_table[256]; -static void crc32_init(void) -{ - int i,j; - uint32 s; - for(i=0; i < 256; i++) { - for (s=(uint32) i << 24, j=0; j < 8; ++j) - s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); - crc_table[i] = s; - } -} - -static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) -{ - return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; -} - - -// used in setup, and for huffman that doesn't go fast path -static unsigned int bit_reverse(unsigned int n) -{ - n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); - n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); - n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); - n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); - return (n >> 16) | (n << 16); -} - -static float square(float x) -{ - return x*x; -} - -// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 -// as required by the specification. fast(?) implementation from stb.h -// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup -static int ilog(int32 n) -{ - static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; - - if (n < 0) return 0; // signed n returns 0 - - // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) - if (n < (1 << 14)) - if (n < (1 << 4)) return 0 + log2_4[n ]; - else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; - else return 10 + log2_4[n >> 10]; - else if (n < (1 << 24)) - if (n < (1 << 19)) return 15 + log2_4[n >> 15]; - else return 20 + log2_4[n >> 20]; - else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; - else return 30 + log2_4[n >> 30]; -} - -#ifndef M_PI - #define M_PI 3.14159265358979323846264f // from CRC -#endif - -// code length assigned to a value with no huffman encoding -#define NO_CODE 255 - -/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// -// -// these functions are only called at setup, and only a few times -// per file - -static float float32_unpack(uint32 x) -{ - // from the specification - uint32 mantissa = x & 0x1fffff; - uint32 sign = x & 0x80000000; - uint32 exp = (x & 0x7fe00000) >> 21; - double res = sign ? -(double)mantissa : (double)mantissa; - return (float) ldexp((float)res, exp-788); -} - - -// zlib & jpeg huffman tables assume that the output symbols -// can either be arbitrarily arranged, or have monotonically -// increasing frequencies--they rely on the lengths being sorted; -// this makes for a very simple generation algorithm. -// vorbis allows a huffman table with non-sorted lengths. This -// requires a more sophisticated construction, since symbols in -// order do not map to huffman codes "in order". -static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) -{ - if (!c->sparse) { - c->codewords [symbol] = huff_code; - } else { - c->codewords [count] = huff_code; - c->codeword_lengths[count] = len; - values [count] = symbol; - } -} - -static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) -{ - int i,k,m=0; - uint32 available[32]; - - memset(available, 0, sizeof(available)); - // find the first entry - for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; - if (k == n) { assert(c->sorted_entries == 0); return TRUE; } - // add to the list - add_entry(c, 0, k, m++, len[k], values); - // add all available leaves - for (i=1; i <= len[k]; ++i) - available[i] = 1U << (32-i); - // note that the above code treats the first case specially, - // but it's really the same as the following code, so they - // could probably be combined (except the initial code is 0, - // and I use 0 in available[] to mean 'empty') - for (i=k+1; i < n; ++i) { - uint32 res; - int z = len[i], y; - if (z == NO_CODE) continue; - // find lowest available leaf (should always be earliest, - // which is what the specification calls for) - // note that this property, and the fact we can never have - // more than one free leaf at a given level, isn't totally - // trivial to prove, but it seems true and the assert never - // fires, so! - while (z > 0 && !available[z]) --z; - if (z == 0) { return FALSE; } - res = available[z]; - assert(z >= 0 && z < 32); - available[z] = 0; - add_entry(c, bit_reverse(res), i, m++, len[i], values); - // propogate availability up the tree - if (z != len[i]) { - assert(len[i] >= 0 && len[i] < 32); - for (y=len[i]; y > z; --y) { - assert(available[y] == 0); - available[y] = res + (1 << (32-y)); - } - } - } - return TRUE; -} - -// accelerated huffman table allows fast O(1) match of all symbols -// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH -static void compute_accelerated_huffman(Codebook *c) -{ - int i, len; - for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) - c->fast_huffman[i] = -1; - - len = c->sparse ? c->sorted_entries : c->entries; - #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT - if (len > 32767) len = 32767; // largest possible value we can encode! - #endif - for (i=0; i < len; ++i) { - if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { - uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; - // set table entries for all bit combinations in the higher bits - while (z < FAST_HUFFMAN_TABLE_SIZE) { - c->fast_huffman[z] = i; - z += 1 << c->codeword_lengths[i]; - } - } - } -} - -#ifdef _MSC_VER -#define STBV_CDECL __cdecl -#else -#define STBV_CDECL -#endif - -static int STBV_CDECL uint32_compare(const void *p, const void *q) -{ - uint32 x = * (uint32 *) p; - uint32 y = * (uint32 *) q; - return x < y ? -1 : x > y; -} - -static int include_in_sort(Codebook *c, uint8 len) -{ - if (c->sparse) { assert(len != NO_CODE); return TRUE; } - if (len == NO_CODE) return FALSE; - if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; - return FALSE; -} - -// if the fast table above doesn't work, we want to binary -// search them... need to reverse the bits -static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) -{ - int i, len; - // build a list of all the entries - // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. - // this is kind of a frivolous optimization--I don't see any performance improvement, - // but it's like 4 extra lines of code, so. - if (!c->sparse) { - int k = 0; - for (i=0; i < c->entries; ++i) - if (include_in_sort(c, lengths[i])) - c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); - assert(k == c->sorted_entries); - } else { - for (i=0; i < c->sorted_entries; ++i) - c->sorted_codewords[i] = bit_reverse(c->codewords[i]); - } - - qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); - c->sorted_codewords[c->sorted_entries] = 0xffffffff; - - len = c->sparse ? c->sorted_entries : c->entries; - // now we need to indicate how they correspond; we could either - // #1: sort a different data structure that says who they correspond to - // #2: for each sorted entry, search the original list to find who corresponds - // #3: for each original entry, find the sorted entry - // #1 requires extra storage, #2 is slow, #3 can use binary search! - for (i=0; i < len; ++i) { - int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; - if (include_in_sort(c,huff_len)) { - uint32 code = bit_reverse(c->codewords[i]); - int x=0, n=c->sorted_entries; - while (n > 1) { - // invariant: sc[x] <= code < sc[x+n] - int m = x + (n >> 1); - if (c->sorted_codewords[m] <= code) { - x = m; - n -= (n>>1); - } else { - n >>= 1; - } - } - assert(c->sorted_codewords[x] == code); - if (c->sparse) { - c->sorted_values[x] = values[i]; - c->codeword_lengths[x] = huff_len; - } else { - c->sorted_values[x] = i; - } - } - } -} - -// only run while parsing the header (3 times) -static int vorbis_validate(uint8 *data) -{ - static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; - return memcmp(data, vorbis, 6) == 0; -} - -// called from setup only, once per code book -// (formula implied by specification) -static int lookup1_values(int entries, int dim) -{ - int r = (int) floor(exp((float) log((float) entries) / dim)); - if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; - ++r; // floor() to avoid _ftol() when non-CRT - assert(pow((float) r+1, dim) > entries); - assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above - return r; -} - -// called twice per file -static void compute_twiddle_factors(int n, float *A, float *B, float *C) -{ - int n4 = n >> 2, n8 = n >> 3; - int k,k2; - - for (k=k2=0; k < n4; ++k,k2+=2) { - A[k2 ] = (float) cos(4*k*M_PI/n); - A[k2+1] = (float) -sin(4*k*M_PI/n); - B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; - B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; - } - for (k=k2=0; k < n8; ++k,k2+=2) { - C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); - C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); - } -} - -static void compute_window(int n, float *window) -{ - int n2 = n >> 1, i; - for (i=0; i < n2; ++i) - window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); -} - -static void compute_bitreverse(int n, uint16 *rev) -{ - int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions - int i, n8 = n >> 3; - for (i=0; i < n8; ++i) - rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; -} - -static int init_blocksize(vorb *f, int b, int n) -{ - int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; - f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); - f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); - f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); - if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); - compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); - f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); - if (!f->window[b]) return error(f, VORBIS_outofmem); - compute_window(n, f->window[b]); - f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); - if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); - compute_bitreverse(n, f->bit_reverse[b]); - return TRUE; -} - -static void neighbors(uint16 *x, int n, int *plow, int *phigh) -{ - int low = -1; - int high = 65536; - int i; - for (i=0; i < n; ++i) { - if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } - if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } - } -} - -// this has been repurposed so y is now the original index instead of y -typedef struct -{ - uint16 x,id; -} stbv__floor_ordering; - -static int STBV_CDECL point_compare(const void *p, const void *q) -{ - stbv__floor_ordering *a = (stbv__floor_ordering *) p; - stbv__floor_ordering *b = (stbv__floor_ordering *) q; - return a->x < b->x ? -1 : a->x > b->x; -} - -// -/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// - - -#if defined(STB_VORBIS_NO_STDIO) - #define USE_MEMORY(z) TRUE -#else - #define USE_MEMORY(z) ((z)->stream) -#endif - -static uint8 get8(vorb *z) -{ - if (USE_MEMORY(z)) { - if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } - return *z->stream++; - } - - #ifndef STB_VORBIS_NO_STDIO - { - int c = fgetc(z->f); - if (c == EOF) { z->eof = TRUE; return 0; } - return c; - } - #endif -} - -static uint32 get32(vorb *f) -{ - uint32 x; - x = get8(f); - x += get8(f) << 8; - x += get8(f) << 16; - x += (uint32) get8(f) << 24; - return x; -} - -static int getn(vorb *z, uint8 *data, int n) -{ - if (USE_MEMORY(z)) { - if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } - memcpy(data, z->stream, n); - z->stream += n; - return 1; - } - - #ifndef STB_VORBIS_NO_STDIO - if (fread(data, n, 1, z->f) == 1) - return 1; - else { - z->eof = 1; - return 0; - } - #endif -} - -static void skip(vorb *z, int n) -{ - if (USE_MEMORY(z)) { - z->stream += n; - if (z->stream >= z->stream_end) z->eof = 1; - return; - } - #ifndef STB_VORBIS_NO_STDIO - { - long x = ftell(z->f); - fseek(z->f, x+n, SEEK_SET); - } - #endif -} - -static int set_file_offset(stb_vorbis *f, unsigned int loc) -{ - #ifndef STB_VORBIS_NO_PUSHDATA_API - if (f->push_mode) return 0; - #endif - f->eof = 0; - if (USE_MEMORY(f)) { - if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { - f->stream = f->stream_end; - f->eof = 1; - return 0; - } else { - f->stream = f->stream_start + loc; - return 1; - } - } - #ifndef STB_VORBIS_NO_STDIO - if (loc + f->f_start < loc || loc >= 0x80000000) { - loc = 0x7fffffff; - f->eof = 1; - } else { - loc += f->f_start; - } - if (!fseek(f->f, loc, SEEK_SET)) - return 1; - f->eof = 1; - fseek(f->f, f->f_start, SEEK_END); - return 0; - #endif -} - - -static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; - -static int capture_pattern(vorb *f) -{ - if (0x4f != get8(f)) return FALSE; - if (0x67 != get8(f)) return FALSE; - if (0x67 != get8(f)) return FALSE; - if (0x53 != get8(f)) return FALSE; - return TRUE; -} - -#define PAGEFLAG_continued_packet 1 -#define PAGEFLAG_first_page 2 -#define PAGEFLAG_last_page 4 - -static int start_page_no_capturepattern(vorb *f) -{ - uint32 loc0,loc1,n; - // stream structure version - if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); - // header flag - f->page_flag = get8(f); - // absolute granule position - loc0 = get32(f); - loc1 = get32(f); - // @TODO: validate loc0,loc1 as valid positions? - // stream serial number -- vorbis doesn't interleave, so discard - get32(f); - //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); - // page sequence number - n = get32(f); - f->last_page = n; - // CRC32 - get32(f); - // page_segments - f->segment_count = get8(f); - if (!getn(f, f->segments, f->segment_count)) - return error(f, VORBIS_unexpected_eof); - // assume we _don't_ know any the sample position of any segments - f->end_seg_with_known_loc = -2; - if (loc0 != ~0U || loc1 != ~0U) { - int i; - // determine which packet is the last one that will complete - for (i=f->segment_count-1; i >= 0; --i) - if (f->segments[i] < 255) - break; - // 'i' is now the index of the _last_ segment of a packet that ends - if (i >= 0) { - f->end_seg_with_known_loc = i; - f->known_loc_for_packet = loc0; - } - } - if (f->first_decode) { - int i,len; - ProbedPage p; - len = 0; - for (i=0; i < f->segment_count; ++i) - len += f->segments[i]; - len += 27 + f->segment_count; - p.page_start = f->first_audio_page_offset; - p.page_end = p.page_start + len; - p.last_decoded_sample = loc0; - f->p_first = p; - } - f->next_seg = 0; - return TRUE; -} - -static int start_page(vorb *f) -{ - if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); - return start_page_no_capturepattern(f); -} - -static int start_packet(vorb *f) -{ - while (f->next_seg == -1) { - if (!start_page(f)) return FALSE; - if (f->page_flag & PAGEFLAG_continued_packet) - return error(f, VORBIS_continued_packet_flag_invalid); - } - f->last_seg = FALSE; - f->valid_bits = 0; - f->packet_bytes = 0; - f->bytes_in_seg = 0; - // f->next_seg is now valid - return TRUE; -} - -static int maybe_start_packet(vorb *f) -{ - if (f->next_seg == -1) { - int x = get8(f); - if (f->eof) return FALSE; // EOF at page boundary is not an error! - if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); - if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); - if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); - if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); - if (!start_page_no_capturepattern(f)) return FALSE; - if (f->page_flag & PAGEFLAG_continued_packet) { - // set up enough state that we can read this packet if we want, - // e.g. during recovery - f->last_seg = FALSE; - f->bytes_in_seg = 0; - return error(f, VORBIS_continued_packet_flag_invalid); - } - } - return start_packet(f); -} - -static int next_segment(vorb *f) -{ - int len; - if (f->last_seg) return 0; - if (f->next_seg == -1) { - f->last_seg_which = f->segment_count-1; // in case start_page fails - if (!start_page(f)) { f->last_seg = 1; return 0; } - if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); - } - len = f->segments[f->next_seg++]; - if (len < 255) { - f->last_seg = TRUE; - f->last_seg_which = f->next_seg-1; - } - if (f->next_seg >= f->segment_count) - f->next_seg = -1; - assert(f->bytes_in_seg == 0); - f->bytes_in_seg = len; - return len; -} - -#define EOP (-1) -#define INVALID_BITS (-1) - -static int get8_packet_raw(vorb *f) -{ - if (!f->bytes_in_seg) { // CLANG! - if (f->last_seg) return EOP; - else if (!next_segment(f)) return EOP; - } - assert(f->bytes_in_seg > 0); - --f->bytes_in_seg; - ++f->packet_bytes; - return get8(f); -} - -static int get8_packet(vorb *f) -{ - int x = get8_packet_raw(f); - f->valid_bits = 0; - return x; -} - -static void flush_packet(vorb *f) -{ - while (get8_packet_raw(f) != EOP); -} - -// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important -// as the huffman decoder? -static uint32 get_bits(vorb *f, int n) -{ - uint32 z; - - if (f->valid_bits < 0) return 0; - if (f->valid_bits < n) { - if (n > 24) { - // the accumulator technique below would not work correctly in this case - z = get_bits(f, 24); - z += get_bits(f, n-24) << 24; - return z; - } - if (f->valid_bits == 0) f->acc = 0; - while (f->valid_bits < n) { - int z = get8_packet_raw(f); - if (z == EOP) { - f->valid_bits = INVALID_BITS; - return 0; - } - f->acc += z << f->valid_bits; - f->valid_bits += 8; - } - } - if (f->valid_bits < 0) return 0; - z = f->acc & ((1 << n)-1); - f->acc >>= n; - f->valid_bits -= n; - return z; -} - -// @OPTIMIZE: primary accumulator for huffman -// expand the buffer to as many bits as possible without reading off end of packet -// it might be nice to allow f->valid_bits and f->acc to be stored in registers, -// e.g. cache them locally and decode locally -static __forceinline void prep_huffman(vorb *f) -{ - if (f->valid_bits <= 24) { - if (f->valid_bits == 0) f->acc = 0; - do { - int z; - if (f->last_seg && !f->bytes_in_seg) return; - z = get8_packet_raw(f); - if (z == EOP) return; - f->acc += (unsigned) z << f->valid_bits; - f->valid_bits += 8; - } while (f->valid_bits <= 24); - } -} - -enum -{ - VORBIS_packet_id = 1, - VORBIS_packet_comment = 3, - VORBIS_packet_setup = 5 -}; - -static int codebook_decode_scalar_raw(vorb *f, Codebook *c) -{ - int i; - prep_huffman(f); - - if (c->codewords == NULL && c->sorted_codewords == NULL) - return -1; - - // cases to use binary search: sorted_codewords && !c->codewords - // sorted_codewords && c->entries > 8 - if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { - // binary search - uint32 code = bit_reverse(f->acc); - int x=0, n=c->sorted_entries, len; - - while (n > 1) { - // invariant: sc[x] <= code < sc[x+n] - int m = x + (n >> 1); - if (c->sorted_codewords[m] <= code) { - x = m; - n -= (n>>1); - } else { - n >>= 1; - } - } - // x is now the sorted index - if (!c->sparse) x = c->sorted_values[x]; - // x is now sorted index if sparse, or symbol otherwise - len = c->codeword_lengths[x]; - if (f->valid_bits >= len) { - f->acc >>= len; - f->valid_bits -= len; - return x; - } - - f->valid_bits = 0; - return -1; - } - - // if small, linear search - assert(!c->sparse); - for (i=0; i < c->entries; ++i) { - if (c->codeword_lengths[i] == NO_CODE) continue; - if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { - if (f->valid_bits >= c->codeword_lengths[i]) { - f->acc >>= c->codeword_lengths[i]; - f->valid_bits -= c->codeword_lengths[i]; - return i; - } - f->valid_bits = 0; - return -1; - } - } - - error(f, VORBIS_invalid_stream); - f->valid_bits = 0; - return -1; -} - -#ifndef STB_VORBIS_NO_INLINE_DECODE - -#define DECODE_RAW(var, f,c) \ - if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ - prep_huffman(f); \ - var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ - var = c->fast_huffman[var]; \ - if (var >= 0) { \ - int n = c->codeword_lengths[var]; \ - f->acc >>= n; \ - f->valid_bits -= n; \ - if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ - } else { \ - var = codebook_decode_scalar_raw(f,c); \ - } - -#else - -static int codebook_decode_scalar(vorb *f, Codebook *c) -{ - int i; - if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) - prep_huffman(f); - // fast huffman table lookup - i = f->acc & FAST_HUFFMAN_TABLE_MASK; - i = c->fast_huffman[i]; - if (i >= 0) { - f->acc >>= c->codeword_lengths[i]; - f->valid_bits -= c->codeword_lengths[i]; - if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } - return i; - } - return codebook_decode_scalar_raw(f,c); -} - -#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); - -#endif - -#define DECODE(var,f,c) \ - DECODE_RAW(var,f,c) \ - if (c->sparse) var = c->sorted_values[var]; - -#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) -#else - #define DECODE_VQ(var,f,c) DECODE(var,f,c) -#endif - - - - - - -// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case -// where we avoid one addition -#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) -#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) -#define CODEBOOK_ELEMENT_BASE(c) (0) - -static int codebook_decode_start(vorb *f, Codebook *c) -{ - int z = -1; - - // type 0 is only legal in a scalar context - if (c->lookup_type == 0) - error(f, VORBIS_invalid_stream); - else { - DECODE_VQ(z,f,c); - if (c->sparse) assert(z < c->sorted_entries); - if (z < 0) { // check for EOP - if (!f->bytes_in_seg) - if (f->last_seg) - return z; - error(f, VORBIS_invalid_stream); - } - } - return z; -} - -static int codebook_decode(vorb *f, Codebook *c, float *output, int len) -{ - int i,z = codebook_decode_start(f,c); - if (z < 0) return FALSE; - if (len > c->dimensions) len = c->dimensions; - -#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (c->lookup_type == 1) { - float last = CODEBOOK_ELEMENT_BASE(c); - int div = 1; - for (i=0; i < len; ++i) { - int off = (z / div) % c->lookup_values; - float val = CODEBOOK_ELEMENT_FAST(c,off) + last; - output[i] += val; - if (c->sequence_p) last = val + c->minimum_value; - div *= c->lookup_values; - } - return TRUE; - } -#endif - - z *= c->dimensions; - if (c->sequence_p) { - float last = CODEBOOK_ELEMENT_BASE(c); - for (i=0; i < len; ++i) { - float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - output[i] += val; - last = val + c->minimum_value; - } - } else { - float last = CODEBOOK_ELEMENT_BASE(c); - for (i=0; i < len; ++i) { - output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; - } - } - - return TRUE; -} - -static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) -{ - int i,z = codebook_decode_start(f,c); - float last = CODEBOOK_ELEMENT_BASE(c); - if (z < 0) return FALSE; - if (len > c->dimensions) len = c->dimensions; - -#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (c->lookup_type == 1) { - int div = 1; - for (i=0; i < len; ++i) { - int off = (z / div) % c->lookup_values; - float val = CODEBOOK_ELEMENT_FAST(c,off) + last; - output[i*step] += val; - if (c->sequence_p) last = val; - div *= c->lookup_values; - } - return TRUE; - } -#endif - - z *= c->dimensions; - for (i=0; i < len; ++i) { - float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - output[i*step] += val; - if (c->sequence_p) last = val; - } - - return TRUE; -} - -static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) -{ - int c_inter = *c_inter_p; - int p_inter = *p_inter_p; - int i,z, effective = c->dimensions; - - // type 0 is only legal in a scalar context - if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); - - while (total_decode > 0) { - float last = CODEBOOK_ELEMENT_BASE(c); - DECODE_VQ(z,f,c); - #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - assert(!c->sparse || z < c->sorted_entries); - #endif - if (z < 0) { - if (!f->bytes_in_seg) - if (f->last_seg) return FALSE; - return error(f, VORBIS_invalid_stream); - } - - // if this will take us off the end of the buffers, stop short! - // we check by computing the length of the virtual interleaved - // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), - // and the length we'll be using (effective) - if (c_inter + p_inter*ch + effective > len * ch) { - effective = len*ch - (p_inter*ch - c_inter); - } - - #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (c->lookup_type == 1) { - int div = 1; - for (i=0; i < effective; ++i) { - int off = (z / div) % c->lookup_values; - float val = CODEBOOK_ELEMENT_FAST(c,off) + last; - if (outputs[c_inter]) - outputs[c_inter][p_inter] += val; - if (++c_inter == ch) { c_inter = 0; ++p_inter; } - if (c->sequence_p) last = val; - div *= c->lookup_values; - } - } else - #endif - { - z *= c->dimensions; - if (c->sequence_p) { - for (i=0; i < effective; ++i) { - float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - if (outputs[c_inter]) - outputs[c_inter][p_inter] += val; - if (++c_inter == ch) { c_inter = 0; ++p_inter; } - last = val; - } - } else { - for (i=0; i < effective; ++i) { - float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; - if (outputs[c_inter]) - outputs[c_inter][p_inter] += val; - if (++c_inter == ch) { c_inter = 0; ++p_inter; } - } - } - } - - total_decode -= effective; - } - *c_inter_p = c_inter; - *p_inter_p = p_inter; - return TRUE; -} - -static int predict_point(int x, int x0, int x1, int y0, int y1) -{ - int dy = y1 - y0; - int adx = x1 - x0; - // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? - int err = abs(dy) * (x - x0); - int off = err / adx; - return dy < 0 ? y0 - off : y0 + off; -} - -// the following table is block-copied from the specification -static float inverse_db_table[256] = -{ - 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, - 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, - 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, - 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, - 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, - 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, - 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, - 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, - 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, - 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, - 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, - 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, - 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, - 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, - 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, - 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, - 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, - 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, - 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, - 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, - 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, - 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, - 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, - 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, - 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, - 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, - 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, - 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, - 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, - 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, - 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, - 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, - 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, - 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, - 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, - 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, - 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, - 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, - 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, - 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, - 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, - 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, - 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, - 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, - 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, - 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, - 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, - 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, - 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, - 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, - 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, - 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, - 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, - 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, - 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, - 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, - 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, - 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, - 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, - 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, - 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, - 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, - 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, - 0.82788260f, 0.88168307f, 0.9389798f, 1.0f -}; - - -// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, -// note that you must produce bit-identical output to decode correctly; -// this specific sequence of operations is specified in the spec (it's -// drawing integer-quantized frequency-space lines that the encoder -// expects to be exactly the same) -// ... also, isn't the whole point of Bresenham's algorithm to NOT -// have to divide in the setup? sigh. -#ifndef STB_VORBIS_NO_DEFER_FLOOR -#define LINE_OP(a,b) a *= b -#else -#define LINE_OP(a,b) a = b -#endif - -#ifdef STB_VORBIS_DIVIDE_TABLE -#define DIVTAB_NUMER 32 -#define DIVTAB_DENOM 64 -int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB -#endif - -static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) -{ - int dy = y1 - y0; - int adx = x1 - x0; - int ady = abs(dy); - int base; - int x=x0,y=y0; - int err = 0; - int sy; - -#ifdef STB_VORBIS_DIVIDE_TABLE - if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { - if (dy < 0) { - base = -integer_divide_table[ady][adx]; - sy = base-1; - } else { - base = integer_divide_table[ady][adx]; - sy = base+1; - } - } else { - base = dy / adx; - if (dy < 0) - sy = base - 1; - else - sy = base+1; - } -#else - base = dy / adx; - if (dy < 0) - sy = base - 1; - else - sy = base+1; -#endif - ady -= abs(base) * adx; - if (x1 > n) x1 = n; - if (x < x1) { - LINE_OP(output[x], inverse_db_table[y]); - for (++x; x < x1; ++x) { - err += ady; - if (err >= adx) { - err -= adx; - y += sy; - } else - y += base; - LINE_OP(output[x], inverse_db_table[y]); - } - } -} - -static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) -{ - int k; - if (rtype == 0) { - int step = n / book->dimensions; - for (k=0; k < step; ++k) - if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) - return FALSE; - } else { - for (k=0; k < n; ) { - if (!codebook_decode(f, book, target+offset, n-k)) - return FALSE; - k += book->dimensions; - offset += book->dimensions; - } - } - return TRUE; -} - -// n is 1/2 of the blocksize -- -// specification: "Correct per-vector decode length is [n]/2" -static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) -{ - int i,j,pass; - Residue *r = f->residue_config + rn; - int rtype = f->residue_types[rn]; - int c = r->classbook; - int classwords = f->codebooks[c].dimensions; - unsigned int actual_size = rtype == 2 ? n*2 : n; - unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); - unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); - int n_read = limit_r_end - limit_r_begin; - int part_read = n_read / r->part_size; - int temp_alloc_point = temp_alloc_save(f); - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); - #else - int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); - #endif - - CHECK(f); - - for (i=0; i < ch; ++i) - if (!do_not_decode[i]) - memset(residue_buffers[i], 0, sizeof(float) * n); - - if (rtype == 2 && ch != 1) { - for (j=0; j < ch; ++j) - if (!do_not_decode[j]) - break; - if (j == ch) - goto done; - - for (pass=0; pass < 8; ++pass) { - int pcount = 0, class_set = 0; - if (ch == 2) { - while (pcount < part_read) { - int z = r->begin + pcount*r->part_size; - int c_inter = (z & 1), p_inter = z>>1; - if (pass == 0) { - Codebook *c = f->codebooks+r->classbook; - int q; - DECODE(q,f,c); - if (q == EOP) goto done; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[0][class_set] = r->classdata[q]; - #else - for (i=classwords-1; i >= 0; --i) { - classifications[0][i+pcount] = q % r->classifications; - q /= r->classifications; - } - #endif - } - for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { - int z = r->begin + pcount*r->part_size; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[0][class_set][i]; - #else - int c = classifications[0][pcount]; - #endif - int b = r->residue_books[c][pass]; - if (b >= 0) { - Codebook *book = f->codebooks + b; - #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; - #else - // saves 1% - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; - #endif - } else { - z += r->part_size; - c_inter = z & 1; - p_inter = z >> 1; - } - } - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; - #endif - } - } else if (ch == 1) { - while (pcount < part_read) { - int z = r->begin + pcount*r->part_size; - int c_inter = 0, p_inter = z; - if (pass == 0) { - Codebook *c = f->codebooks+r->classbook; - int q; - DECODE(q,f,c); - if (q == EOP) goto done; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[0][class_set] = r->classdata[q]; - #else - for (i=classwords-1; i >= 0; --i) { - classifications[0][i+pcount] = q % r->classifications; - q /= r->classifications; - } - #endif - } - for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { - int z = r->begin + pcount*r->part_size; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[0][class_set][i]; - #else - int c = classifications[0][pcount]; - #endif - int b = r->residue_books[c][pass]; - if (b >= 0) { - Codebook *book = f->codebooks + b; - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; - } else { - z += r->part_size; - c_inter = 0; - p_inter = z; - } - } - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; - #endif - } - } else { - while (pcount < part_read) { - int z = r->begin + pcount*r->part_size; - int c_inter = z % ch, p_inter = z/ch; - if (pass == 0) { - Codebook *c = f->codebooks+r->classbook; - int q; - DECODE(q,f,c); - if (q == EOP) goto done; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[0][class_set] = r->classdata[q]; - #else - for (i=classwords-1; i >= 0; --i) { - classifications[0][i+pcount] = q % r->classifications; - q /= r->classifications; - } - #endif - } - for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { - int z = r->begin + pcount*r->part_size; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[0][class_set][i]; - #else - int c = classifications[0][pcount]; - #endif - int b = r->residue_books[c][pass]; - if (b >= 0) { - Codebook *book = f->codebooks + b; - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; - } else { - z += r->part_size; - c_inter = z % ch; - p_inter = z / ch; - } - } - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; - #endif - } - } - } - goto done; - } - CHECK(f); - - for (pass=0; pass < 8; ++pass) { - int pcount = 0, class_set=0; - while (pcount < part_read) { - if (pass == 0) { - for (j=0; j < ch; ++j) { - if (!do_not_decode[j]) { - Codebook *c = f->codebooks+r->classbook; - int temp; - DECODE(temp,f,c); - if (temp == EOP) goto done; - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[j][class_set] = r->classdata[temp]; - #else - for (i=classwords-1; i >= 0; --i) { - classifications[j][i+pcount] = temp % r->classifications; - temp /= r->classifications; - } - #endif - } - } - } - for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { - for (j=0; j < ch; ++j) { - if (!do_not_decode[j]) { - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[j][class_set][i]; - #else - int c = classifications[j][pcount]; - #endif - int b = r->residue_books[c][pass]; - if (b >= 0) { - float *target = residue_buffers[j]; - int offset = r->begin + pcount * r->part_size; - int n = r->part_size; - Codebook *book = f->codebooks + b; - if (!residue_decode(f, book, target, offset, n, rtype)) - goto done; - } - } - } - } - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; - #endif - } - } - done: - CHECK(f); - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - temp_free(f,part_classdata); - #else - temp_free(f,classifications); - #endif - temp_alloc_restore(f,temp_alloc_point); -} - - -#if 0 -// slow way for debugging -void inverse_mdct_slow(float *buffer, int n) -{ - int i,j; - int n2 = n >> 1; - float *x = (float *) malloc(sizeof(*x) * n2); - memcpy(x, buffer, sizeof(*x) * n2); - for (i=0; i < n; ++i) { - float acc = 0; - for (j=0; j < n2; ++j) - // formula from paper: - //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); - // formula from wikipedia - //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); - // these are equivalent, except the formula from the paper inverts the multiplier! - // however, what actually works is NO MULTIPLIER!?! - //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); - acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); - buffer[i] = acc; - } - free(x); -} -#elif 0 -// same as above, but just barely able to run in real time on modern machines -void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) -{ - float mcos[16384]; - int i,j; - int n2 = n >> 1, nmask = (n << 2) -1; - float *x = (float *) malloc(sizeof(*x) * n2); - memcpy(x, buffer, sizeof(*x) * n2); - for (i=0; i < 4*n; ++i) - mcos[i] = (float) cos(M_PI / 2 * i / n); - - for (i=0; i < n; ++i) { - float acc = 0; - for (j=0; j < n2; ++j) - acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; - buffer[i] = acc; - } - free(x); -} -#elif 0 -// transform to use a slow dct-iv; this is STILL basically trivial, -// but only requires half as many ops -void dct_iv_slow(float *buffer, int n) -{ - float mcos[16384]; - float x[2048]; - int i,j; - int n2 = n >> 1, nmask = (n << 3) - 1; - memcpy(x, buffer, sizeof(*x) * n); - for (i=0; i < 8*n; ++i) - mcos[i] = (float) cos(M_PI / 4 * i / n); - for (i=0; i < n; ++i) { - float acc = 0; - for (j=0; j < n; ++j) - acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; - buffer[i] = acc; - } -} - -void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) -{ - int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; - float temp[4096]; - - memcpy(temp, buffer, n2 * sizeof(float)); - dct_iv_slow(temp, n2); // returns -c'-d, a-b' - - for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' - for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' - for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d -} -#endif - -#ifndef LIBVORBIS_MDCT -#define LIBVORBIS_MDCT 0 -#endif - -#if LIBVORBIS_MDCT -// directly call the vorbis MDCT using an interface documented -// by Jeff Roberts... useful for performance comparison -typedef struct -{ - int n; - int log2n; - - float *trig; - int *bitrev; - - float scale; -} mdct_lookup; - -extern void mdct_init(mdct_lookup *lookup, int n); -extern void mdct_clear(mdct_lookup *l); -extern void mdct_backward(mdct_lookup *init, float *in, float *out); - -mdct_lookup M1,M2; - -void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) -{ - mdct_lookup *M; - if (M1.n == n) M = &M1; - else if (M2.n == n) M = &M2; - else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } - else { - if (M2.n) __asm int 3; - mdct_init(&M2, n); - M = &M2; - } - - mdct_backward(M, buffer, buffer); -} -#endif - - -// the following were split out into separate functions while optimizing; -// they could be pushed back up but eh. __forceinline showed no change; -// they're probably already being inlined. -static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) -{ - float *ee0 = e + i_off; - float *ee2 = ee0 + k_off; - int i; - - assert((n & 3) == 0); - for (i=(n>>2); i > 0; --i) { - float k00_20, k01_21; - k00_20 = ee0[ 0] - ee2[ 0]; - k01_21 = ee0[-1] - ee2[-1]; - ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; - ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; - ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; - ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; - A += 8; - - k00_20 = ee0[-2] - ee2[-2]; - k01_21 = ee0[-3] - ee2[-3]; - ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; - ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; - ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; - ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; - A += 8; - - k00_20 = ee0[-4] - ee2[-4]; - k01_21 = ee0[-5] - ee2[-5]; - ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; - ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; - ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; - ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; - A += 8; - - k00_20 = ee0[-6] - ee2[-6]; - k01_21 = ee0[-7] - ee2[-7]; - ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; - ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; - ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; - ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; - A += 8; - ee0 -= 8; - ee2 -= 8; - } -} - -static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) -{ - int i; - float k00_20, k01_21; - - float *e0 = e + d0; - float *e2 = e0 + k_off; - - for (i=lim >> 2; i > 0; --i) { - k00_20 = e0[-0] - e2[-0]; - k01_21 = e0[-1] - e2[-1]; - e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; - e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; - e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; - e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; - - A += k1; - - k00_20 = e0[-2] - e2[-2]; - k01_21 = e0[-3] - e2[-3]; - e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; - e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; - e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; - e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; - - A += k1; - - k00_20 = e0[-4] - e2[-4]; - k01_21 = e0[-5] - e2[-5]; - e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; - e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; - e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; - e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; - - A += k1; - - k00_20 = e0[-6] - e2[-6]; - k01_21 = e0[-7] - e2[-7]; - e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; - e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; - e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; - e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; - - e0 -= 8; - e2 -= 8; - - A += k1; - } -} - -static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) -{ - int i; - float A0 = A[0]; - float A1 = A[0+1]; - float A2 = A[0+a_off]; - float A3 = A[0+a_off+1]; - float A4 = A[0+a_off*2+0]; - float A5 = A[0+a_off*2+1]; - float A6 = A[0+a_off*3+0]; - float A7 = A[0+a_off*3+1]; - - float k00,k11; - - float *ee0 = e +i_off; - float *ee2 = ee0+k_off; - - for (i=n; i > 0; --i) { - k00 = ee0[ 0] - ee2[ 0]; - k11 = ee0[-1] - ee2[-1]; - ee0[ 0] = ee0[ 0] + ee2[ 0]; - ee0[-1] = ee0[-1] + ee2[-1]; - ee2[ 0] = (k00) * A0 - (k11) * A1; - ee2[-1] = (k11) * A0 + (k00) * A1; - - k00 = ee0[-2] - ee2[-2]; - k11 = ee0[-3] - ee2[-3]; - ee0[-2] = ee0[-2] + ee2[-2]; - ee0[-3] = ee0[-3] + ee2[-3]; - ee2[-2] = (k00) * A2 - (k11) * A3; - ee2[-3] = (k11) * A2 + (k00) * A3; - - k00 = ee0[-4] - ee2[-4]; - k11 = ee0[-5] - ee2[-5]; - ee0[-4] = ee0[-4] + ee2[-4]; - ee0[-5] = ee0[-5] + ee2[-5]; - ee2[-4] = (k00) * A4 - (k11) * A5; - ee2[-5] = (k11) * A4 + (k00) * A5; - - k00 = ee0[-6] - ee2[-6]; - k11 = ee0[-7] - ee2[-7]; - ee0[-6] = ee0[-6] + ee2[-6]; - ee0[-7] = ee0[-7] + ee2[-7]; - ee2[-6] = (k00) * A6 - (k11) * A7; - ee2[-7] = (k11) * A6 + (k00) * A7; - - ee0 -= k0; - ee2 -= k0; - } -} - -static __forceinline void iter_54(float *z) -{ - float k00,k11,k22,k33; - float y0,y1,y2,y3; - - k00 = z[ 0] - z[-4]; - y0 = z[ 0] + z[-4]; - y2 = z[-2] + z[-6]; - k22 = z[-2] - z[-6]; - - z[-0] = y0 + y2; // z0 + z4 + z2 + z6 - z[-2] = y0 - y2; // z0 + z4 - z2 - z6 - - // done with y0,y2 - - k33 = z[-3] - z[-7]; - - z[-4] = k00 + k33; // z0 - z4 + z3 - z7 - z[-6] = k00 - k33; // z0 - z4 - z3 + z7 - - // done with k33 - - k11 = z[-1] - z[-5]; - y1 = z[-1] + z[-5]; - y3 = z[-3] + z[-7]; - - z[-1] = y1 + y3; // z1 + z5 + z3 + z7 - z[-3] = y1 - y3; // z1 + z5 - z3 - z7 - z[-5] = k11 - k22; // z1 - z5 + z2 - z6 - z[-7] = k11 + k22; // z1 - z5 - z2 + z6 -} - -static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) -{ - int a_off = base_n >> 3; - float A2 = A[0+a_off]; - float *z = e + i_off; - float *base = z - 16 * n; - - while (z > base) { - float k00,k11; - - k00 = z[-0] - z[-8]; - k11 = z[-1] - z[-9]; - z[-0] = z[-0] + z[-8]; - z[-1] = z[-1] + z[-9]; - z[-8] = k00; - z[-9] = k11 ; - - k00 = z[ -2] - z[-10]; - k11 = z[ -3] - z[-11]; - z[ -2] = z[ -2] + z[-10]; - z[ -3] = z[ -3] + z[-11]; - z[-10] = (k00+k11) * A2; - z[-11] = (k11-k00) * A2; - - k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation - k11 = z[ -5] - z[-13]; - z[ -4] = z[ -4] + z[-12]; - z[ -5] = z[ -5] + z[-13]; - z[-12] = k11; - z[-13] = k00; - - k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation - k11 = z[ -7] - z[-15]; - z[ -6] = z[ -6] + z[-14]; - z[ -7] = z[ -7] + z[-15]; - z[-14] = (k00+k11) * A2; - z[-15] = (k00-k11) * A2; - - iter_54(z); - iter_54(z-8); - z -= 16; - } -} - -static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) -{ - int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; - int ld; - // @OPTIMIZE: reduce register pressure by using fewer variables? - int save_point = temp_alloc_save(f); - float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); - float *u=NULL,*v=NULL; - // twiddle factors - float *A = f->A[blocktype]; - - // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" - // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. - - // kernel from paper - - - // merged: - // copy and reflect spectral data - // step 0 - - // note that it turns out that the items added together during - // this step are, in fact, being added to themselves (as reflected - // by step 0). inexplicable inefficiency! this became obvious - // once I combined the passes. - - // so there's a missing 'times 2' here (for adding X to itself). - // this propogates through linearly to the end, where the numbers - // are 1/2 too small, and need to be compensated for. - - { - float *d,*e, *AA, *e_stop; - d = &buf2[n2-2]; - AA = A; - e = &buffer[0]; - e_stop = &buffer[n2]; - while (e != e_stop) { - d[1] = (e[0] * AA[0] - e[2]*AA[1]); - d[0] = (e[0] * AA[1] + e[2]*AA[0]); - d -= 2; - AA += 2; - e += 4; - } - - e = &buffer[n2-3]; - while (d >= buf2) { - d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); - d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); - d -= 2; - AA += 2; - e -= 4; - } - } - - // now we use symbolic names for these, so that we can - // possibly swap their meaning as we change which operations - // are in place - - u = buffer; - v = buf2; - - // step 2 (paper output is w, now u) - // this could be in place, but the data ends up in the wrong - // place... _somebody_'s got to swap it, so this is nominated - { - float *AA = &A[n2-8]; - float *d0,*d1, *e0, *e1; - - e0 = &v[n4]; - e1 = &v[0]; - - d0 = &u[n4]; - d1 = &u[0]; - - while (AA >= A) { - float v40_20, v41_21; - - v41_21 = e0[1] - e1[1]; - v40_20 = e0[0] - e1[0]; - d0[1] = e0[1] + e1[1]; - d0[0] = e0[0] + e1[0]; - d1[1] = v41_21*AA[4] - v40_20*AA[5]; - d1[0] = v40_20*AA[4] + v41_21*AA[5]; - - v41_21 = e0[3] - e1[3]; - v40_20 = e0[2] - e1[2]; - d0[3] = e0[3] + e1[3]; - d0[2] = e0[2] + e1[2]; - d1[3] = v41_21*AA[0] - v40_20*AA[1]; - d1[2] = v40_20*AA[0] + v41_21*AA[1]; - - AA -= 8; - - d0 += 4; - d1 += 4; - e0 += 4; - e1 += 4; - } - } - - // step 3 - ld = ilog(n) - 1; // ilog is off-by-one from normal definitions - - // optimized step 3: - - // the original step3 loop can be nested r inside s or s inside r; - // it's written originally as s inside r, but this is dumb when r - // iterates many times, and s few. So I have two copies of it and - // switch between them halfway. - - // this is iteration 0 of step 3 - imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); - imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); - - // this is iteration 1 of step 3 - imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); - imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); - imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); - imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); - - l=2; - for (; l < (ld-3)>>1; ++l) { - int k0 = n >> (l+2), k0_2 = k0>>1; - int lim = 1 << (l+1); - int i; - for (i=0; i < lim; ++i) - imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); - } - - for (; l < ld-6; ++l) { - int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; - int rlim = n >> (l+6), r; - int lim = 1 << (l+1); - int i_off; - float *A0 = A; - i_off = n2-1; - for (r=rlim; r > 0; --r) { - imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); - A0 += k1*4; - i_off -= 8; - } - } - - // iterations with count: - // ld-6,-5,-4 all interleaved together - // the big win comes from getting rid of needless flops - // due to the constants on pass 5 & 4 being all 1 and 0; - // combining them to be simultaneous to improve cache made little difference - imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); - - // output is u - - // step 4, 5, and 6 - // cannot be in-place because of step 5 - { - uint16 *bitrev = f->bit_reverse[blocktype]; - // weirdly, I'd have thought reading sequentially and writing - // erratically would have been better than vice-versa, but in - // fact that's not what my testing showed. (That is, with - // j = bitreverse(i), do you read i and write j, or read j and write i.) - - float *d0 = &v[n4-4]; - float *d1 = &v[n2-4]; - while (d0 >= v) { - int k4; - - k4 = bitrev[0]; - d1[3] = u[k4+0]; - d1[2] = u[k4+1]; - d0[3] = u[k4+2]; - d0[2] = u[k4+3]; - - k4 = bitrev[1]; - d1[1] = u[k4+0]; - d1[0] = u[k4+1]; - d0[1] = u[k4+2]; - d0[0] = u[k4+3]; - - d0 -= 4; - d1 -= 4; - bitrev += 2; - } - } - // (paper output is u, now v) - - - // data must be in buf2 - assert(v == buf2); - - // step 7 (paper output is v, now v) - // this is now in place - { - float *C = f->C[blocktype]; - float *d, *e; - - d = v; - e = v + n2 - 4; - - while (d < e) { - float a02,a11,b0,b1,b2,b3; - - a02 = d[0] - e[2]; - a11 = d[1] + e[3]; - - b0 = C[1]*a02 + C[0]*a11; - b1 = C[1]*a11 - C[0]*a02; - - b2 = d[0] + e[ 2]; - b3 = d[1] - e[ 3]; - - d[0] = b2 + b0; - d[1] = b3 + b1; - e[2] = b2 - b0; - e[3] = b1 - b3; - - a02 = d[2] - e[0]; - a11 = d[3] + e[1]; - - b0 = C[3]*a02 + C[2]*a11; - b1 = C[3]*a11 - C[2]*a02; - - b2 = d[2] + e[ 0]; - b3 = d[3] - e[ 1]; - - d[2] = b2 + b0; - d[3] = b3 + b1; - e[0] = b2 - b0; - e[1] = b1 - b3; - - C += 4; - d += 4; - e -= 4; - } - } - - // data must be in buf2 - - - // step 8+decode (paper output is X, now buffer) - // this generates pairs of data a la 8 and pushes them directly through - // the decode kernel (pushing rather than pulling) to avoid having - // to make another pass later - - // this cannot POSSIBLY be in place, so we refer to the buffers directly - - { - float *d0,*d1,*d2,*d3; - - float *B = f->B[blocktype] + n2 - 8; - float *e = buf2 + n2 - 8; - d0 = &buffer[0]; - d1 = &buffer[n2-4]; - d2 = &buffer[n2]; - d3 = &buffer[n-4]; - while (e >= v) { - float p0,p1,p2,p3; - - p3 = e[6]*B[7] - e[7]*B[6]; - p2 = -e[6]*B[6] - e[7]*B[7]; - - d0[0] = p3; - d1[3] = - p3; - d2[0] = p2; - d3[3] = p2; - - p1 = e[4]*B[5] - e[5]*B[4]; - p0 = -e[4]*B[4] - e[5]*B[5]; - - d0[1] = p1; - d1[2] = - p1; - d2[1] = p0; - d3[2] = p0; - - p3 = e[2]*B[3] - e[3]*B[2]; - p2 = -e[2]*B[2] - e[3]*B[3]; - - d0[2] = p3; - d1[1] = - p3; - d2[2] = p2; - d3[1] = p2; - - p1 = e[0]*B[1] - e[1]*B[0]; - p0 = -e[0]*B[0] - e[1]*B[1]; - - d0[3] = p1; - d1[0] = - p1; - d2[3] = p0; - d3[0] = p0; - - B -= 8; - e -= 8; - d0 += 4; - d2 += 4; - d1 -= 4; - d3 -= 4; - } - } - - temp_free(f,buf2); - temp_alloc_restore(f,save_point); -} - -#if 0 -// this is the original version of the above code, if you want to optimize it from scratch -void inverse_mdct_naive(float *buffer, int n) -{ - float s; - float A[1 << 12], B[1 << 12], C[1 << 11]; - int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; - int n3_4 = n - n4, ld; - // how can they claim this only uses N words?! - // oh, because they're only used sparsely, whoops - float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; - // set up twiddle factors - - for (k=k2=0; k < n4; ++k,k2+=2) { - A[k2 ] = (float) cos(4*k*M_PI/n); - A[k2+1] = (float) -sin(4*k*M_PI/n); - B[k2 ] = (float) cos((k2+1)*M_PI/n/2); - B[k2+1] = (float) sin((k2+1)*M_PI/n/2); - } - for (k=k2=0; k < n8; ++k,k2+=2) { - C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); - C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); - } - - // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" - // Note there are bugs in that pseudocode, presumably due to them attempting - // to rename the arrays nicely rather than representing the way their actual - // implementation bounces buffers back and forth. As a result, even in the - // "some formulars corrected" version, a direct implementation fails. These - // are noted below as "paper bug". - - // copy and reflect spectral data - for (k=0; k < n2; ++k) u[k] = buffer[k]; - for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; - // kernel from paper - // step 1 - for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { - v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; - v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; - } - // step 2 - for (k=k4=0; k < n8; k+=1, k4+=4) { - w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; - w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; - w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; - w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; - } - // step 3 - ld = ilog(n) - 1; // ilog is off-by-one from normal definitions - for (l=0; l < ld-3; ++l) { - int k0 = n >> (l+2), k1 = 1 << (l+3); - int rlim = n >> (l+4), r4, r; - int s2lim = 1 << (l+2), s2; - for (r=r4=0; r < rlim; r4+=4,++r) { - for (s2=0; s2 < s2lim; s2+=2) { - u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; - u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; - u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] - - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; - u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] - + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; - } - } - if (l+1 < ld-3) { - // paper bug: ping-ponging of u&w here is omitted - memcpy(w, u, sizeof(u)); - } - } - - // step 4 - for (i=0; i < n8; ++i) { - int j = bit_reverse(i) >> (32-ld+3); - assert(j < n8); - if (i == j) { - // paper bug: original code probably swapped in place; if copying, - // need to directly copy in this case - int i8 = i << 3; - v[i8+1] = u[i8+1]; - v[i8+3] = u[i8+3]; - v[i8+5] = u[i8+5]; - v[i8+7] = u[i8+7]; - } else if (i < j) { - int i8 = i << 3, j8 = j << 3; - v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; - v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; - v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; - v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; - } - } - // step 5 - for (k=0; k < n2; ++k) { - w[k] = v[k*2+1]; - } - // step 6 - for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { - u[n-1-k2] = w[k4]; - u[n-2-k2] = w[k4+1]; - u[n3_4 - 1 - k2] = w[k4+2]; - u[n3_4 - 2 - k2] = w[k4+3]; - } - // step 7 - for (k=k2=0; k < n8; ++k, k2 += 2) { - v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; - v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; - v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; - v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; - } - // step 8 - for (k=k2=0; k < n4; ++k,k2 += 2) { - X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; - X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; - } - - // decode kernel to output - // determined the following value experimentally - // (by first figuring out what made inverse_mdct_slow work); then matching that here - // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) - s = 0.5; // theoretically would be n4 - - // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, - // so it needs to use the "old" B values to behave correctly, or else - // set s to 1.0 ]]] - for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; - for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; - for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; -} -#endif - -static float *get_window(vorb *f, int len) -{ - len <<= 1; - if (len == f->blocksize_0) return f->window[0]; - if (len == f->blocksize_1) return f->window[1]; - assert(0); - return NULL; -} - -#ifndef STB_VORBIS_NO_DEFER_FLOOR -typedef int16 YTYPE; -#else -typedef int YTYPE; -#endif -static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) -{ - int n2 = n >> 1; - int s = map->chan[i].mux, floor; - floor = map->submap_floor[s]; - if (f->floor_types[floor] == 0) { - return error(f, VORBIS_invalid_stream); - } else { - Floor1 *g = &f->floor_config[floor].floor1; - int j,q; - int lx = 0, ly = finalY[0] * g->floor1_multiplier; - for (q=1; q < g->values; ++q) { - j = g->sorted_order[q]; - #ifndef STB_VORBIS_NO_DEFER_FLOOR - if (finalY[j] >= 0) - #else - if (step2_flag[j]) - #endif - { - int hy = finalY[j] * g->floor1_multiplier; - int hx = g->Xlist[j]; - if (lx != hx) - draw_line(target, lx,ly, hx,hy, n2); - CHECK(f); - lx = hx, ly = hy; - } - } - if (lx < n2) { - // optimization of: draw_line(target, lx,ly, n,ly, n2); - for (j=lx; j < n2; ++j) - LINE_OP(target[j], inverse_db_table[ly]); - CHECK(f); - } - } - return TRUE; -} - -// The meaning of "left" and "right" -// -// For a given frame: -// we compute samples from 0..n -// window_center is n/2 -// we'll window and mix the samples from left_start to left_end with data from the previous frame -// all of the samples from left_end to right_start can be output without mixing; however, -// this interval is 0-length except when transitioning between short and long frames -// all of the samples from right_start to right_end need to be mixed with the next frame, -// which we don't have, so those get saved in a buffer -// frame N's right_end-right_start, the number of samples to mix with the next frame, -// has to be the same as frame N+1's left_end-left_start (which they are by -// construction) - -static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) -{ - Mode *m; - int i, n, prev, next, window_center; - f->channel_buffer_start = f->channel_buffer_end = 0; - - retry: - if (f->eof) return FALSE; - if (!maybe_start_packet(f)) - return FALSE; - // check packet type - if (get_bits(f,1) != 0) { - if (IS_PUSH_MODE(f)) - return error(f,VORBIS_bad_packet_type); - while (EOP != get8_packet(f)); - goto retry; - } - - if (f->alloc.alloc_buffer) - assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - - i = get_bits(f, ilog(f->mode_count-1)); - if (i == EOP) return FALSE; - if (i >= f->mode_count) return FALSE; - *mode = i; - m = f->mode_config + i; - if (m->blockflag) { - n = f->blocksize_1; - prev = get_bits(f,1); - next = get_bits(f,1); - } else { - prev = next = 0; - n = f->blocksize_0; - } - -// WINDOWING - - window_center = n >> 1; - if (m->blockflag && !prev) { - *p_left_start = (n - f->blocksize_0) >> 2; - *p_left_end = (n + f->blocksize_0) >> 2; - } else { - *p_left_start = 0; - *p_left_end = window_center; - } - if (m->blockflag && !next) { - *p_right_start = (n*3 - f->blocksize_0) >> 2; - *p_right_end = (n*3 + f->blocksize_0) >> 2; - } else { - *p_right_start = window_center; - *p_right_end = n; - } - - return TRUE; -} - -static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) -{ - Mapping *map; - int i,j,k,n,n2; - int zero_channel[256]; - int really_zero_channel[256]; - -// WINDOWING - - n = f->blocksize[m->blockflag]; - map = &f->mapping[m->mapping]; - -// FLOORS - n2 = n >> 1; - - CHECK(f); - - for (i=0; i < f->channels; ++i) { - int s = map->chan[i].mux, floor; - zero_channel[i] = FALSE; - floor = map->submap_floor[s]; - if (f->floor_types[floor] == 0) { - return error(f, VORBIS_invalid_stream); - } else { - Floor1 *g = &f->floor_config[floor].floor1; - if (get_bits(f, 1)) { - short *finalY; - uint8 step2_flag[256]; - static int range_list[4] = { 256, 128, 86, 64 }; - int range = range_list[g->floor1_multiplier-1]; - int offset = 2; - finalY = f->finalY[i]; - finalY[0] = get_bits(f, ilog(range)-1); - finalY[1] = get_bits(f, ilog(range)-1); - for (j=0; j < g->partitions; ++j) { - int pclass = g->partition_class_list[j]; - int cdim = g->class_dimensions[pclass]; - int cbits = g->class_subclasses[pclass]; - int csub = (1 << cbits)-1; - int cval = 0; - if (cbits) { - Codebook *c = f->codebooks + g->class_masterbooks[pclass]; - DECODE(cval,f,c); - } - for (k=0; k < cdim; ++k) { - int book = g->subclass_books[pclass][cval & csub]; - cval = cval >> cbits; - if (book >= 0) { - int temp; - Codebook *c = f->codebooks + book; - DECODE(temp,f,c); - finalY[offset++] = temp; - } else - finalY[offset++] = 0; - } - } - if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec - step2_flag[0] = step2_flag[1] = 1; - for (j=2; j < g->values; ++j) { - int low, high, pred, highroom, lowroom, room, val; - low = g->neighbors[j][0]; - high = g->neighbors[j][1]; - //neighbors(g->Xlist, j, &low, &high); - pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); - val = finalY[j]; - highroom = range - pred; - lowroom = pred; - if (highroom < lowroom) - room = highroom * 2; - else - room = lowroom * 2; - if (val) { - step2_flag[low] = step2_flag[high] = 1; - step2_flag[j] = 1; - if (val >= room) - if (highroom > lowroom) - finalY[j] = val - lowroom + pred; - else - finalY[j] = pred - val + highroom - 1; - else - if (val & 1) - finalY[j] = pred - ((val+1)>>1); - else - finalY[j] = pred + (val>>1); - } else { - step2_flag[j] = 0; - finalY[j] = pred; - } - } - -#ifdef STB_VORBIS_NO_DEFER_FLOOR - do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); -#else - // defer final floor computation until _after_ residue - for (j=0; j < g->values; ++j) { - if (!step2_flag[j]) - finalY[j] = -1; - } -#endif - } else { - error: - zero_channel[i] = TRUE; - } - // So we just defer everything else to later - - // at this point we've decoded the floor into buffer - } - } - CHECK(f); - // at this point we've decoded all floors - - if (f->alloc.alloc_buffer) - assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - - // re-enable coupled channels if necessary - memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); - for (i=0; i < map->coupling_steps; ++i) - if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { - zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; - } - - CHECK(f); -// RESIDUE DECODE - for (i=0; i < map->submaps; ++i) { - float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; - int r; - uint8 do_not_decode[256]; - int ch = 0; - for (j=0; j < f->channels; ++j) { - if (map->chan[j].mux == i) { - if (zero_channel[j]) { - do_not_decode[ch] = TRUE; - residue_buffers[ch] = NULL; - } else { - do_not_decode[ch] = FALSE; - residue_buffers[ch] = f->channel_buffers[j]; - } - ++ch; - } - } - r = map->submap_residue[i]; - decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); - } - - if (f->alloc.alloc_buffer) - assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - CHECK(f); - -// INVERSE COUPLING - for (i = map->coupling_steps-1; i >= 0; --i) { - int n2 = n >> 1; - float *m = f->channel_buffers[map->chan[i].magnitude]; - float *a = f->channel_buffers[map->chan[i].angle ]; - for (j=0; j < n2; ++j) { - float a2,m2; - if (m[j] > 0) - if (a[j] > 0) - m2 = m[j], a2 = m[j] - a[j]; - else - a2 = m[j], m2 = m[j] + a[j]; - else - if (a[j] > 0) - m2 = m[j], a2 = m[j] + a[j]; - else - a2 = m[j], m2 = m[j] - a[j]; - m[j] = m2; - a[j] = a2; - } - } - CHECK(f); - - // finish decoding the floors -#ifndef STB_VORBIS_NO_DEFER_FLOOR - for (i=0; i < f->channels; ++i) { - if (really_zero_channel[i]) { - memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); - } else { - do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); - } - } -#else - for (i=0; i < f->channels; ++i) { - if (really_zero_channel[i]) { - memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); - } else { - for (j=0; j < n2; ++j) - f->channel_buffers[i][j] *= f->floor_buffers[i][j]; - } - } -#endif - -// INVERSE MDCT - CHECK(f); - for (i=0; i < f->channels; ++i) - inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); - CHECK(f); - - // this shouldn't be necessary, unless we exited on an error - // and want to flush to get to the next packet - flush_packet(f); - - if (f->first_decode) { - // assume we start so first non-discarded sample is sample 0 - // this isn't to spec, but spec would require us to read ahead - // and decode the size of all current frames--could be done, - // but presumably it's not a commonly used feature - f->current_loc = -n2; // start of first frame is positioned for discard - // we might have to discard samples "from" the next frame too, - // if we're lapping a large block then a small at the start? - f->discard_samples_deferred = n - right_end; - f->current_loc_valid = TRUE; - f->first_decode = FALSE; - } else if (f->discard_samples_deferred) { - if (f->discard_samples_deferred >= right_start - left_start) { - f->discard_samples_deferred -= (right_start - left_start); - left_start = right_start; - *p_left = left_start; - } else { - left_start += f->discard_samples_deferred; - *p_left = left_start; - f->discard_samples_deferred = 0; - } - } else if (f->previous_length == 0 && f->current_loc_valid) { - // we're recovering from a seek... that means we're going to discard - // the samples from this packet even though we know our position from - // the last page header, so we need to update the position based on - // the discarded samples here - // but wait, the code below is going to add this in itself even - // on a discard, so we don't need to do it here... - } - - // check if we have ogg information about the sample # for this packet - if (f->last_seg_which == f->end_seg_with_known_loc) { - // if we have a valid current loc, and this is final: - if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { - uint32 current_end = f->known_loc_for_packet; - // then let's infer the size of the (probably) short final frame - if (current_end < f->current_loc + (right_end-left_start)) { - if (current_end < f->current_loc) { - // negative truncation, that's impossible! - *len = 0; - } else { - *len = current_end - f->current_loc; - } - *len += left_start; // this doesn't seem right, but has no ill effect on my test files - if (*len > right_end) *len = right_end; // this should never happen - f->current_loc += *len; - return TRUE; - } - } - // otherwise, just set our sample loc - // guess that the ogg granule pos refers to the _middle_ of the - // last frame? - // set f->current_loc to the position of left_start - f->current_loc = f->known_loc_for_packet - (n2-left_start); - f->current_loc_valid = TRUE; - } - if (f->current_loc_valid) - f->current_loc += (right_start - left_start); - - if (f->alloc.alloc_buffer) - assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - *len = right_end; // ignore samples after the window goes to 0 - CHECK(f); - - return TRUE; -} - -static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) -{ - int mode, left_end, right_end; - if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; - return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); -} - -static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) -{ - int prev,i,j; - // we use right&left (the start of the right- and left-window sin()-regions) - // to determine how much to return, rather than inferring from the rules - // (same result, clearer code); 'left' indicates where our sin() window - // starts, therefore where the previous window's right edge starts, and - // therefore where to start mixing from the previous buffer. 'right' - // indicates where our sin() ending-window starts, therefore that's where - // we start saving, and where our returned-data ends. - - // mixin from previous window - if (f->previous_length) { - int i,j, n = f->previous_length; - float *w = get_window(f, n); - for (i=0; i < f->channels; ++i) { - for (j=0; j < n; ++j) - f->channel_buffers[i][left+j] = - f->channel_buffers[i][left+j]*w[ j] + - f->previous_window[i][ j]*w[n-1-j]; - } - } - - prev = f->previous_length; - - // last half of this data becomes previous window - f->previous_length = len - right; - - // @OPTIMIZE: could avoid this copy by double-buffering the - // output (flipping previous_window with channel_buffers), but - // then previous_window would have to be 2x as large, and - // channel_buffers couldn't be temp mem (although they're NOT - // currently temp mem, they could be (unless we want to level - // performance by spreading out the computation)) - for (i=0; i < f->channels; ++i) - for (j=0; right+j < len; ++j) - f->previous_window[i][j] = f->channel_buffers[i][right+j]; - - if (!prev) - // there was no previous packet, so this data isn't valid... - // this isn't entirely true, only the would-have-overlapped data - // isn't valid, but this seems to be what the spec requires - return 0; - - // truncate a short frame - if (len < right) right = len; - - f->samples_output += right-left; - - return right - left; -} - -static int vorbis_pump_first_frame(stb_vorbis *f) -{ - int len, right, left, res; - res = vorbis_decode_packet(f, &len, &left, &right); - if (res) - vorbis_finish_frame(f, len, left, right); - return res; -} - -#ifndef STB_VORBIS_NO_PUSHDATA_API -static int is_whole_packet_present(stb_vorbis *f, int end_page) -{ - // make sure that we have the packet available before continuing... - // this requires a full ogg parse, but we know we can fetch from f->stream - - // instead of coding this out explicitly, we could save the current read state, - // read the next packet with get8() until end-of-packet, check f->eof, then - // reset the state? but that would be slower, esp. since we'd have over 256 bytes - // of state to restore (primarily the page segment table) - - int s = f->next_seg, first = TRUE; - uint8 *p = f->stream; - - if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag - for (; s < f->segment_count; ++s) { - p += f->segments[s]; - if (f->segments[s] < 255) // stop at first short segment - break; - } - // either this continues, or it ends it... - if (end_page) - if (s < f->segment_count-1) return error(f, VORBIS_invalid_stream); - if (s == f->segment_count) - s = -1; // set 'crosses page' flag - if (p > f->stream_end) return error(f, VORBIS_need_more_data); - first = FALSE; - } - for (; s == -1;) { - uint8 *q; - int n; - - // check that we have the page header ready - if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); - // validate the page - if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); - if (p[4] != 0) return error(f, VORBIS_invalid_stream); - if (first) { // the first segment must NOT have 'continued_packet', later ones MUST - if (f->previous_length) - if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); - // if no previous length, we're resynching, so we can come in on a continued-packet, - // which we'll just drop - } else { - if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); - } - n = p[26]; // segment counts - q = p+27; // q points to segment table - p = q + n; // advance past header - // make sure we've read the segment table - if (p > f->stream_end) return error(f, VORBIS_need_more_data); - for (s=0; s < n; ++s) { - p += q[s]; - if (q[s] < 255) - break; - } - if (end_page) - if (s < n-1) return error(f, VORBIS_invalid_stream); - if (s == n) - s = -1; // set 'crosses page' flag - if (p > f->stream_end) return error(f, VORBIS_need_more_data); - first = FALSE; - } - return TRUE; -} -#endif // !STB_VORBIS_NO_PUSHDATA_API - -static int start_decoder(vorb *f) -{ - uint8 header[6], x,y; - int len,i,j,k, max_submaps = 0; - int longest_floorlist=0; - - // first page, first packet - - if (!start_page(f)) return FALSE; - // validate page flag - if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); - if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); - if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); - // check for expected packet length - if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); - if (f->segments[0] != 30) return error(f, VORBIS_invalid_first_page); - // read packet - // check packet header - if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); - if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); - if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); - // vorbis_version - if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); - f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); - if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); - f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); - get32(f); // bitrate_maximum - get32(f); // bitrate_nominal - get32(f); // bitrate_minimum - x = get8(f); - { - int log0,log1; - log0 = x & 15; - log1 = x >> 4; - f->blocksize_0 = 1 << log0; - f->blocksize_1 = 1 << log1; - if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); - if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); - if (log0 > log1) return error(f, VORBIS_invalid_setup); - } - - // framing_flag - x = get8(f); - if (!(x & 1)) return error(f, VORBIS_invalid_first_page); - - // second packet! - if (!start_page(f)) return FALSE; - - if (!start_packet(f)) return FALSE; - do { - len = next_segment(f); - skip(f, len); - f->bytes_in_seg = 0; - } while (len); - - // third packet! - if (!start_packet(f)) return FALSE; - - #ifndef STB_VORBIS_NO_PUSHDATA_API - if (IS_PUSH_MODE(f)) { - if (!is_whole_packet_present(f, TRUE)) { - // convert error in ogg header to write type - if (f->error == VORBIS_invalid_stream) - f->error = VORBIS_invalid_setup; - return FALSE; - } - } - #endif - - crc32_init(); // always init it, to avoid multithread race conditions - - if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); - for (i=0; i < 6; ++i) header[i] = get8_packet(f); - if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); - - // codebooks - - f->codebook_count = get_bits(f,8) + 1; - f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); - if (f->codebooks == NULL) return error(f, VORBIS_outofmem); - memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); - for (i=0; i < f->codebook_count; ++i) { - uint32 *values; - int ordered, sorted_count; - int total=0; - uint8 *lengths; - Codebook *c = f->codebooks+i; - CHECK(f); - x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); - x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); - x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); - x = get_bits(f, 8); - c->dimensions = (get_bits(f, 8)<<8) + x; - x = get_bits(f, 8); - y = get_bits(f, 8); - c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; - ordered = get_bits(f,1); - c->sparse = ordered ? 0 : get_bits(f,1); - - if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); - - if (c->sparse) - lengths = (uint8 *) setup_temp_malloc(f, c->entries); - else - lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); - - if (!lengths) return error(f, VORBIS_outofmem); - - if (ordered) { - int current_entry = 0; - int current_length = get_bits(f,5) + 1; - while (current_entry < c->entries) { - int limit = c->entries - current_entry; - int n = get_bits(f, ilog(limit)); - if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } - memset(lengths + current_entry, current_length, n); - current_entry += n; - ++current_length; - } - } else { - for (j=0; j < c->entries; ++j) { - int present = c->sparse ? get_bits(f,1) : 1; - if (present) { - lengths[j] = get_bits(f, 5) + 1; - ++total; - if (lengths[j] == 32) - return error(f, VORBIS_invalid_setup); - } else { - lengths[j] = NO_CODE; - } - } - } - - if (c->sparse && total >= c->entries >> 2) { - // convert sparse items to non-sparse! - if (c->entries > (int) f->setup_temp_memory_required) - f->setup_temp_memory_required = c->entries; - - c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); - if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); - memcpy(c->codeword_lengths, lengths, c->entries); - setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! - lengths = c->codeword_lengths; - c->sparse = 0; - } - - // compute the size of the sorted tables - if (c->sparse) { - sorted_count = total; - } else { - sorted_count = 0; - #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH - for (j=0; j < c->entries; ++j) - if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) - ++sorted_count; - #endif - } - - c->sorted_entries = sorted_count; - values = NULL; - - CHECK(f); - if (!c->sparse) { - c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); - if (!c->codewords) return error(f, VORBIS_outofmem); - } else { - unsigned int size; - if (c->sorted_entries) { - c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); - if (!c->codeword_lengths) return error(f, VORBIS_outofmem); - c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); - if (!c->codewords) return error(f, VORBIS_outofmem); - values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); - if (!values) return error(f, VORBIS_outofmem); - } - size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; - if (size > f->setup_temp_memory_required) - f->setup_temp_memory_required = size; - } - - if (!compute_codewords(c, lengths, c->entries, values)) { - if (c->sparse) setup_temp_free(f, values, 0); - return error(f, VORBIS_invalid_setup); - } - - if (c->sorted_entries) { - // allocate an extra slot for sentinels - c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); - if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); - // allocate an extra slot at the front so that c->sorted_values[-1] is defined - // so that we can catch that case without an extra if - c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); - if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); - ++c->sorted_values; - c->sorted_values[-1] = -1; - compute_sorted_huffman(c, lengths, values); - } - - if (c->sparse) { - setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); - setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); - setup_temp_free(f, lengths, c->entries); - c->codewords = NULL; - } - - compute_accelerated_huffman(c); - - CHECK(f); - c->lookup_type = get_bits(f, 4); - if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); - if (c->lookup_type > 0) { - uint16 *mults; - c->minimum_value = float32_unpack(get_bits(f, 32)); - c->delta_value = float32_unpack(get_bits(f, 32)); - c->value_bits = get_bits(f, 4)+1; - c->sequence_p = get_bits(f,1); - if (c->lookup_type == 1) { - c->lookup_values = lookup1_values(c->entries, c->dimensions); - } else { - c->lookup_values = c->entries * c->dimensions; - } - if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); - mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); - if (mults == NULL) return error(f, VORBIS_outofmem); - for (j=0; j < (int) c->lookup_values; ++j) { - int q = get_bits(f, c->value_bits); - if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } - mults[j] = q; - } - -#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (c->lookup_type == 1) { - int len, sparse = c->sparse; - float last=0; - // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop - if (sparse) { - if (c->sorted_entries == 0) goto skip; - c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); - } else - c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); - if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } - len = sparse ? c->sorted_entries : c->entries; - for (j=0; j < len; ++j) { - unsigned int z = sparse ? c->sorted_values[j] : j; - unsigned int div=1; - for (k=0; k < c->dimensions; ++k) { - int off = (z / div) % c->lookup_values; - float val = mults[off]; - val = mults[off]*c->delta_value + c->minimum_value + last; - c->multiplicands[j*c->dimensions + k] = val; - if (c->sequence_p) - last = val; - if (k+1 < c->dimensions) { - if (div > UINT_MAX / (unsigned int) c->lookup_values) { - setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); - return error(f, VORBIS_invalid_setup); - } - div *= c->lookup_values; - } - } - } - c->lookup_type = 2; - } - else -#endif - { - float last=0; - CHECK(f); - c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); - if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } - for (j=0; j < (int) c->lookup_values; ++j) { - float val = mults[j] * c->delta_value + c->minimum_value + last; - c->multiplicands[j] = val; - if (c->sequence_p) - last = val; - } - } -#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - skip:; -#endif - setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); - - CHECK(f); - } - CHECK(f); - } - - // time domain transfers (notused) - - x = get_bits(f, 6) + 1; - for (i=0; i < x; ++i) { - uint32 z = get_bits(f, 16); - if (z != 0) return error(f, VORBIS_invalid_setup); - } - - // Floors - f->floor_count = get_bits(f, 6)+1; - f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); - if (f->floor_config == NULL) return error(f, VORBIS_outofmem); - for (i=0; i < f->floor_count; ++i) { - f->floor_types[i] = get_bits(f, 16); - if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); - if (f->floor_types[i] == 0) { - Floor0 *g = &f->floor_config[i].floor0; - g->order = get_bits(f,8); - g->rate = get_bits(f,16); - g->bark_map_size = get_bits(f,16); - g->amplitude_bits = get_bits(f,6); - g->amplitude_offset = get_bits(f,8); - g->number_of_books = get_bits(f,4) + 1; - for (j=0; j < g->number_of_books; ++j) - g->book_list[j] = get_bits(f,8); - return error(f, VORBIS_feature_not_supported); - } else { - stbv__floor_ordering p[31*8+2]; - Floor1 *g = &f->floor_config[i].floor1; - int max_class = -1; - g->partitions = get_bits(f, 5); - for (j=0; j < g->partitions; ++j) { - g->partition_class_list[j] = get_bits(f, 4); - if (g->partition_class_list[j] > max_class) - max_class = g->partition_class_list[j]; - } - for (j=0; j <= max_class; ++j) { - g->class_dimensions[j] = get_bits(f, 3)+1; - g->class_subclasses[j] = get_bits(f, 2); - if (g->class_subclasses[j]) { - g->class_masterbooks[j] = get_bits(f, 8); - if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); - } - for (k=0; k < 1 << g->class_subclasses[j]; ++k) { - g->subclass_books[j][k] = get_bits(f,8)-1; - if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); - } - } - g->floor1_multiplier = get_bits(f,2)+1; - g->rangebits = get_bits(f,4); - g->Xlist[0] = 0; - g->Xlist[1] = 1 << g->rangebits; - g->values = 2; - for (j=0; j < g->partitions; ++j) { - int c = g->partition_class_list[j]; - for (k=0; k < g->class_dimensions[c]; ++k) { - g->Xlist[g->values] = get_bits(f, g->rangebits); - ++g->values; - } - } - // precompute the sorting - for (j=0; j < g->values; ++j) { - p[j].x = g->Xlist[j]; - p[j].id = j; - } - qsort(p, g->values, sizeof(p[0]), point_compare); - for (j=0; j < g->values; ++j) - g->sorted_order[j] = (uint8) p[j].id; - // precompute the neighbors - for (j=2; j < g->values; ++j) { - int low,hi; - neighbors(g->Xlist, j, &low,&hi); - g->neighbors[j][0] = low; - g->neighbors[j][1] = hi; - } - - if (g->values > longest_floorlist) - longest_floorlist = g->values; - } - } - - // Residue - f->residue_count = get_bits(f, 6)+1; - f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); - if (f->residue_config == NULL) return error(f, VORBIS_outofmem); - memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); - for (i=0; i < f->residue_count; ++i) { - uint8 residue_cascade[64]; - Residue *r = f->residue_config+i; - f->residue_types[i] = get_bits(f, 16); - if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); - r->begin = get_bits(f, 24); - r->end = get_bits(f, 24); - if (r->end < r->begin) return error(f, VORBIS_invalid_setup); - r->part_size = get_bits(f,24)+1; - r->classifications = get_bits(f,6)+1; - r->classbook = get_bits(f,8); - if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); - for (j=0; j < r->classifications; ++j) { - uint8 high_bits=0; - uint8 low_bits=get_bits(f,3); - if (get_bits(f,1)) - high_bits = get_bits(f,5); - residue_cascade[j] = high_bits*8 + low_bits; - } - r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); - if (r->residue_books == NULL) return error(f, VORBIS_outofmem); - for (j=0; j < r->classifications; ++j) { - for (k=0; k < 8; ++k) { - if (residue_cascade[j] & (1 << k)) { - r->residue_books[j][k] = get_bits(f, 8); - if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); - } else { - r->residue_books[j][k] = -1; - } - } - } - // precompute the classifications[] array to avoid inner-loop mod/divide - // call it 'classdata' since we already have r->classifications - r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); - if (!r->classdata) return error(f, VORBIS_outofmem); - memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); - for (j=0; j < f->codebooks[r->classbook].entries; ++j) { - int classwords = f->codebooks[r->classbook].dimensions; - int temp = j; - r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); - if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); - for (k=classwords-1; k >= 0; --k) { - r->classdata[j][k] = temp % r->classifications; - temp /= r->classifications; - } - } - } - - f->mapping_count = get_bits(f,6)+1; - f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); - if (f->mapping == NULL) return error(f, VORBIS_outofmem); - memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); - for (i=0; i < f->mapping_count; ++i) { - Mapping *m = f->mapping + i; - int mapping_type = get_bits(f,16); - if (mapping_type != 0) return error(f, VORBIS_invalid_setup); - m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); - if (m->chan == NULL) return error(f, VORBIS_outofmem); - if (get_bits(f,1)) - m->submaps = get_bits(f,4)+1; - else - m->submaps = 1; - if (m->submaps > max_submaps) - max_submaps = m->submaps; - if (get_bits(f,1)) { - m->coupling_steps = get_bits(f,8)+1; - for (k=0; k < m->coupling_steps; ++k) { - m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); - m->chan[k].angle = get_bits(f, ilog(f->channels-1)); - if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); - if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); - if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); - } - } else - m->coupling_steps = 0; - - // reserved field - if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); - if (m->submaps > 1) { - for (j=0; j < f->channels; ++j) { - m->chan[j].mux = get_bits(f, 4); - if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); - } - } else - // @SPECIFICATION: this case is missing from the spec - for (j=0; j < f->channels; ++j) - m->chan[j].mux = 0; - - for (j=0; j < m->submaps; ++j) { - get_bits(f,8); // discard - m->submap_floor[j] = get_bits(f,8); - m->submap_residue[j] = get_bits(f,8); - if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); - if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); - } - } - - // Modes - f->mode_count = get_bits(f, 6)+1; - for (i=0; i < f->mode_count; ++i) { - Mode *m = f->mode_config+i; - m->blockflag = get_bits(f,1); - m->windowtype = get_bits(f,16); - m->transformtype = get_bits(f,16); - m->mapping = get_bits(f,8); - if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); - if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); - if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); - } - - flush_packet(f); - - f->previous_length = 0; - - for (i=0; i < f->channels; ++i) { - f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); - f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); - f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); - if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); - memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); - #ifdef STB_VORBIS_NO_DEFER_FLOOR - f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); - if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); - #endif - } - - if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; - if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; - f->blocksize[0] = f->blocksize_0; - f->blocksize[1] = f->blocksize_1; - -#ifdef STB_VORBIS_DIVIDE_TABLE - if (integer_divide_table[1][1]==0) - for (i=0; i < DIVTAB_NUMER; ++i) - for (j=1; j < DIVTAB_DENOM; ++j) - integer_divide_table[i][j] = i / j; -#endif - - // compute how much temporary memory is needed - - // 1. - { - uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); - uint32 classify_mem; - int i,max_part_read=0; - for (i=0; i < f->residue_count; ++i) { - Residue *r = f->residue_config + i; - unsigned int actual_size = f->blocksize_1 / 2; - unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; - unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; - int n_read = limit_r_end - limit_r_begin; - int part_read = n_read / r->part_size; - if (part_read > max_part_read) - max_part_read = part_read; - } - #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); - #else - classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); - #endif - - // maximum reasonable partition size is f->blocksize_1 - - f->temp_memory_required = classify_mem; - if (imdct_mem > f->temp_memory_required) - f->temp_memory_required = imdct_mem; - } - - f->first_decode = TRUE; - - if (f->alloc.alloc_buffer) { - assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); - // check if there's enough temp memory so we don't error later - if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) - return error(f, VORBIS_outofmem); - } - - f->first_audio_page_offset = stb_vorbis_get_file_offset(f); - - return TRUE; -} - -static void vorbis_deinit(stb_vorbis *p) -{ - int i,j; - if (p->residue_config) { - for (i=0; i < p->residue_count; ++i) { - Residue *r = p->residue_config+i; - if (r->classdata) { - for (j=0; j < p->codebooks[r->classbook].entries; ++j) - setup_free(p, r->classdata[j]); - setup_free(p, r->classdata); - } - setup_free(p, r->residue_books); - } - } - - if (p->codebooks) { - CHECK(p); - for (i=0; i < p->codebook_count; ++i) { - Codebook *c = p->codebooks + i; - setup_free(p, c->codeword_lengths); - setup_free(p, c->multiplicands); - setup_free(p, c->codewords); - setup_free(p, c->sorted_codewords); - // c->sorted_values[-1] is the first entry in the array - setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); - } - setup_free(p, p->codebooks); - } - setup_free(p, p->floor_config); - setup_free(p, p->residue_config); - if (p->mapping) { - for (i=0; i < p->mapping_count; ++i) - setup_free(p, p->mapping[i].chan); - setup_free(p, p->mapping); - } - CHECK(p); - for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { - setup_free(p, p->channel_buffers[i]); - setup_free(p, p->previous_window[i]); - #ifdef STB_VORBIS_NO_DEFER_FLOOR - setup_free(p, p->floor_buffers[i]); - #endif - setup_free(p, p->finalY[i]); - } - for (i=0; i < 2; ++i) { - setup_free(p, p->A[i]); - setup_free(p, p->B[i]); - setup_free(p, p->C[i]); - setup_free(p, p->window[i]); - setup_free(p, p->bit_reverse[i]); - } - #ifndef STB_VORBIS_NO_STDIO - if (p->close_on_free) fclose(p->f); - #endif -} - -void stb_vorbis_close(stb_vorbis *p) -{ - if (p == NULL) return; - vorbis_deinit(p); - setup_free(p,p); -} - -static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) -{ - memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start - if (z) { - p->alloc = *z; - p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; - p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; - } - p->eof = 0; - p->error = VORBIS__no_error; - p->stream = NULL; - p->codebooks = NULL; - p->page_crc_tests = -1; - #ifndef STB_VORBIS_NO_STDIO - p->close_on_free = FALSE; - p->f = NULL; - #endif -} - -int stb_vorbis_get_sample_offset(stb_vorbis *f) -{ - if (f->current_loc_valid) - return f->current_loc; - else - return -1; -} - -stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) -{ - stb_vorbis_info d; - d.channels = f->channels; - d.sample_rate = f->sample_rate; - d.setup_memory_required = f->setup_memory_required; - d.setup_temp_memory_required = f->setup_temp_memory_required; - d.temp_memory_required = f->temp_memory_required; - d.max_frame_size = f->blocksize_1 >> 1; - return d; -} - -int stb_vorbis_get_error(stb_vorbis *f) -{ - int e = f->error; - f->error = VORBIS__no_error; - return e; -} - -static stb_vorbis * vorbis_alloc(stb_vorbis *f) -{ - stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); - return p; -} - -#ifndef STB_VORBIS_NO_PUSHDATA_API - -void stb_vorbis_flush_pushdata(stb_vorbis *f) -{ - f->previous_length = 0; - f->page_crc_tests = 0; - f->discard_samples_deferred = 0; - f->current_loc_valid = FALSE; - f->first_decode = FALSE; - f->samples_output = 0; - f->channel_buffer_start = 0; - f->channel_buffer_end = 0; -} - -static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) -{ - int i,n; - for (i=0; i < f->page_crc_tests; ++i) - f->scan[i].bytes_done = 0; - - // if we have room for more scans, search for them first, because - // they may cause us to stop early if their header is incomplete - if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { - if (data_len < 4) return 0; - data_len -= 3; // need to look for 4-byte sequence, so don't miss - // one that straddles a boundary - for (i=0; i < data_len; ++i) { - if (data[i] == 0x4f) { - if (0==memcmp(data+i, ogg_page_header, 4)) { - int j,len; - uint32 crc; - // make sure we have the whole page header - if (i+26 >= data_len || i+27+data[i+26] >= data_len) { - // only read up to this page start, so hopefully we'll - // have the whole page header start next time - data_len = i; - break; - } - // ok, we have it all; compute the length of the page - len = 27 + data[i+26]; - for (j=0; j < data[i+26]; ++j) - len += data[i+27+j]; - // scan everything up to the embedded crc (which we must 0) - crc = 0; - for (j=0; j < 22; ++j) - crc = crc32_update(crc, data[i+j]); - // now process 4 0-bytes - for ( ; j < 26; ++j) - crc = crc32_update(crc, 0); - // len is the total number of bytes we need to scan - n = f->page_crc_tests++; - f->scan[n].bytes_left = len-j; - f->scan[n].crc_so_far = crc; - f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); - // if the last frame on a page is continued to the next, then - // we can't recover the sample_loc immediately - if (data[i+27+data[i+26]-1] == 255) - f->scan[n].sample_loc = ~0; - else - f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); - f->scan[n].bytes_done = i+j; - if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) - break; - // keep going if we still have room for more - } - } - } - } - - for (i=0; i < f->page_crc_tests;) { - uint32 crc; - int j; - int n = f->scan[i].bytes_done; - int m = f->scan[i].bytes_left; - if (m > data_len - n) m = data_len - n; - // m is the bytes to scan in the current chunk - crc = f->scan[i].crc_so_far; - for (j=0; j < m; ++j) - crc = crc32_update(crc, data[n+j]); - f->scan[i].bytes_left -= m; - f->scan[i].crc_so_far = crc; - if (f->scan[i].bytes_left == 0) { - // does it match? - if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { - // Houston, we have page - data_len = n+m; // consumption amount is wherever that scan ended - f->page_crc_tests = -1; // drop out of page scan mode - f->previous_length = 0; // decode-but-don't-output one frame - f->next_seg = -1; // start a new page - f->current_loc = f->scan[i].sample_loc; // set the current sample location - // to the amount we'd have decoded had we decoded this page - f->current_loc_valid = f->current_loc != ~0U; - return data_len; - } - // delete entry - f->scan[i] = f->scan[--f->page_crc_tests]; - } else { - ++i; - } - } - - return data_len; -} - -// return value: number of bytes we used -int stb_vorbis_decode_frame_pushdata( - stb_vorbis *f, // the file we're decoding - const uint8 *data, int data_len, // the memory available for decoding - int *channels, // place to write number of float * buffers - float ***output, // place to write float ** array of float * buffers - int *samples // place to write number of output samples - ) -{ - int i; - int len,right,left; - - if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); - - if (f->page_crc_tests >= 0) { - *samples = 0; - return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); - } - - f->stream = (uint8 *) data; - f->stream_end = (uint8 *) data + data_len; - f->error = VORBIS__no_error; - - // check that we have the entire packet in memory - if (!is_whole_packet_present(f, FALSE)) { - *samples = 0; - return 0; - } - - if (!vorbis_decode_packet(f, &len, &left, &right)) { - // save the actual error we encountered - enum STBVorbisError error = f->error; - if (error == VORBIS_bad_packet_type) { - // flush and resynch - f->error = VORBIS__no_error; - while (get8_packet(f) != EOP) - if (f->eof) break; - *samples = 0; - return (int) (f->stream - data); - } - if (error == VORBIS_continued_packet_flag_invalid) { - if (f->previous_length == 0) { - // we may be resynching, in which case it's ok to hit one - // of these; just discard the packet - f->error = VORBIS__no_error; - while (get8_packet(f) != EOP) - if (f->eof) break; - *samples = 0; - return (int) (f->stream - data); - } - } - // if we get an error while parsing, what to do? - // well, it DEFINITELY won't work to continue from where we are! - stb_vorbis_flush_pushdata(f); - // restore the error that actually made us bail - f->error = error; - *samples = 0; - return 1; - } - - // success! - len = vorbis_finish_frame(f, len, left, right); - for (i=0; i < f->channels; ++i) - f->outputs[i] = f->channel_buffers[i] + left; - - if (channels) *channels = f->channels; - *samples = len; - *output = f->outputs; - return (int) (f->stream - data); -} - -stb_vorbis *stb_vorbis_open_pushdata( - const unsigned char *data, int data_len, // the memory available for decoding - int *data_used, // only defined if result is not NULL - int *error, const stb_vorbis_alloc *alloc) -{ - stb_vorbis *f, p; - vorbis_init(&p, alloc); - p.stream = (uint8 *) data; - p.stream_end = (uint8 *) data + data_len; - p.push_mode = TRUE; - if (!start_decoder(&p)) { - if (p.eof) - *error = VORBIS_need_more_data; - else - *error = p.error; - return NULL; - } - f = vorbis_alloc(&p); - if (f) { - *f = p; - *data_used = (int) (f->stream - data); - *error = 0; - return f; - } else { - vorbis_deinit(&p); - return NULL; - } -} -#endif // STB_VORBIS_NO_PUSHDATA_API - -unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) -{ - #ifndef STB_VORBIS_NO_PUSHDATA_API - if (f->push_mode) return 0; - #endif - if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); - #ifndef STB_VORBIS_NO_STDIO - return (unsigned int) (ftell(f->f) - f->f_start); - #endif -} - -#ifndef STB_VORBIS_NO_PULLDATA_API -// -// DATA-PULLING API -// - -static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) -{ - for(;;) { - int n; - if (f->eof) return 0; - n = get8(f); - if (n == 0x4f) { // page header candidate - unsigned int retry_loc = stb_vorbis_get_file_offset(f); - int i; - // check if we're off the end of a file_section stream - if (retry_loc - 25 > f->stream_len) - return 0; - // check the rest of the header - for (i=1; i < 4; ++i) - if (get8(f) != ogg_page_header[i]) - break; - if (f->eof) return 0; - if (i == 4) { - uint8 header[27]; - uint32 i, crc, goal, len; - for (i=0; i < 4; ++i) - header[i] = ogg_page_header[i]; - for (; i < 27; ++i) - header[i] = get8(f); - if (f->eof) return 0; - if (header[4] != 0) goto invalid; - goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); - for (i=22; i < 26; ++i) - header[i] = 0; - crc = 0; - for (i=0; i < 27; ++i) - crc = crc32_update(crc, header[i]); - len = 0; - for (i=0; i < header[26]; ++i) { - int s = get8(f); - crc = crc32_update(crc, s); - len += s; - } - if (len && f->eof) return 0; - for (i=0; i < len; ++i) - crc = crc32_update(crc, get8(f)); - // finished parsing probable page - if (crc == goal) { - // we could now check that it's either got the last - // page flag set, OR it's followed by the capture - // pattern, but I guess TECHNICALLY you could have - // a file with garbage between each ogg page and recover - // from it automatically? So even though that paranoia - // might decrease the chance of an invalid decode by - // another 2^32, not worth it since it would hose those - // invalid-but-useful files? - if (end) - *end = stb_vorbis_get_file_offset(f); - if (last) { - if (header[5] & 0x04) - *last = 1; - else - *last = 0; - } - set_file_offset(f, retry_loc-1); - return 1; - } - } - invalid: - // not a valid page, so rewind and look for next one - set_file_offset(f, retry_loc); - } - } -} - - -#define SAMPLE_unknown 0xffffffff - -// seeking is implemented with a binary search, which narrows down the range to -// 64K, before using a linear search (because finding the synchronization -// pattern can be expensive, and the chance we'd find the end page again is -// relatively high for small ranges) -// -// two initial interpolation-style probes are used at the start of the search -// to try to bound either side of the binary search sensibly, while still -// working in O(log n) time if they fail. - -static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) -{ - uint8 header[27], lacing[255]; - int i,len; - - // record where the page starts - z->page_start = stb_vorbis_get_file_offset(f); - - // parse the header - getn(f, header, 27); - if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') - return 0; - getn(f, lacing, header[26]); - - // determine the length of the payload - len = 0; - for (i=0; i < header[26]; ++i) - len += lacing[i]; - - // this implies where the page ends - z->page_end = z->page_start + 27 + header[26] + len; - - // read the last-decoded sample out of the data - z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); - - // restore file state to where we were - set_file_offset(f, z->page_start); - return 1; -} - -// rarely used function to seek back to the preceeding page while finding the -// start of a packet -static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) -{ - unsigned int previous_safe, end; - - // now we want to seek back 64K from the limit - if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) - previous_safe = limit_offset - 65536; - else - previous_safe = f->first_audio_page_offset; - - set_file_offset(f, previous_safe); - - while (vorbis_find_page(f, &end, NULL)) { - if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) - return 1; - set_file_offset(f, end); - } - - return 0; -} - -// implements the search logic for finding a page and starting decoding. if -// the function succeeds, current_loc_valid will be true and current_loc will -// be less than or equal to the provided sample number (the closer the -// better). -static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) -{ - ProbedPage left, right, mid; - int i, start_seg_with_known_loc, end_pos, page_start; - uint32 delta, stream_length, padding; - double offset, bytes_per_sample; - int probe = 0; - - // find the last page and validate the target sample - stream_length = stb_vorbis_stream_length_in_samples(f); - if (stream_length == 0) return error(f, VORBIS_seek_without_length); - if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); - - // this is the maximum difference between the window-center (which is the - // actual granule position value), and the right-start (which the spec - // indicates should be the granule position (give or take one)). - padding = ((f->blocksize_1 - f->blocksize_0) >> 2); - if (sample_number < padding) - sample_number = 0; - else - sample_number -= padding; - - left = f->p_first; - while (left.last_decoded_sample == ~0U) { - // (untested) the first page does not have a 'last_decoded_sample' - set_file_offset(f, left.page_end); - if (!get_seek_page_info(f, &left)) goto error; - } - - right = f->p_last; - assert(right.last_decoded_sample != ~0U); - - // starting from the start is handled differently - if (sample_number <= left.last_decoded_sample) { - if (stb_vorbis_seek_start(f)) - return 1; - return 0; - } - - while (left.page_end != right.page_start) { - assert(left.page_end < right.page_start); - // search range in bytes - delta = right.page_start - left.page_end; - if (delta <= 65536) { - // there's only 64K left to search - handle it linearly - set_file_offset(f, left.page_end); - } else { - if (probe < 2) { - if (probe == 0) { - // first probe (interpolate) - double data_bytes = right.page_end - left.page_start; - bytes_per_sample = data_bytes / right.last_decoded_sample; - offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample); - } else { - // second probe (try to bound the other side) - double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample; - if (error >= 0 && error < 8000) error = 8000; - if (error < 0 && error > -8000) error = -8000; - offset += error * 2; - } - - // ensure the offset is valid - if (offset < left.page_end) - offset = left.page_end; - if (offset > right.page_start - 65536) - offset = right.page_start - 65536; - - set_file_offset(f, (unsigned int) offset); - } else { - // binary search for large ranges (offset by 32K to ensure - // we don't hit the right page) - set_file_offset(f, left.page_end + (delta / 2) - 32768); - } - - if (!vorbis_find_page(f, NULL, NULL)) goto error; - } - - for (;;) { - if (!get_seek_page_info(f, &mid)) goto error; - if (mid.last_decoded_sample != ~0U) break; - // (untested) no frames end on this page - set_file_offset(f, mid.page_end); - assert(mid.page_start < right.page_start); - } - - // if we've just found the last page again then we're in a tricky file, - // and we're close enough. - if (mid.page_start == right.page_start) - break; - - if (sample_number < mid.last_decoded_sample) - right = mid; - else - left = mid; - - ++probe; - } - - // seek back to start of the last packet - page_start = left.page_start; - set_file_offset(f, page_start); - if (!start_page(f)) return error(f, VORBIS_seek_failed); - end_pos = f->end_seg_with_known_loc; - assert(end_pos >= 0); - - for (;;) { - for (i = end_pos; i > 0; --i) - if (f->segments[i-1] != 255) - break; - - start_seg_with_known_loc = i; - - if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) - break; - - // (untested) the final packet begins on an earlier page - if (!go_to_page_before(f, page_start)) - goto error; - - page_start = stb_vorbis_get_file_offset(f); - if (!start_page(f)) goto error; - end_pos = f->segment_count - 1; - } - - // prepare to start decoding - f->current_loc_valid = FALSE; - f->last_seg = FALSE; - f->valid_bits = 0; - f->packet_bytes = 0; - f->bytes_in_seg = 0; - f->previous_length = 0; - f->next_seg = start_seg_with_known_loc; - - for (i = 0; i < start_seg_with_known_loc; i++) - skip(f, f->segments[i]); - - // start decoding (optimizable - this frame is generally discarded) - if (!vorbis_pump_first_frame(f)) - return 0; - if (f->current_loc > sample_number) - return error(f, VORBIS_seek_failed); - return 1; - -error: - // try to restore the file to a valid state - stb_vorbis_seek_start(f); - return error(f, VORBIS_seek_failed); -} - -// the same as vorbis_decode_initial, but without advancing -static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) -{ - int bits_read, bytes_read; - - if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) - return 0; - - // either 1 or 2 bytes were read, figure out which so we can rewind - bits_read = 1 + ilog(f->mode_count-1); - if (f->mode_config[*mode].blockflag) - bits_read += 2; - bytes_read = (bits_read + 7) / 8; - - f->bytes_in_seg += bytes_read; - f->packet_bytes -= bytes_read; - skip(f, -bytes_read); - if (f->next_seg == -1) - f->next_seg = f->segment_count - 1; - else - f->next_seg--; - f->valid_bits = 0; - - return 1; -} - -int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) -{ - uint32 max_frame_samples; - - if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); - - // fast page-level search - if (!seek_to_sample_coarse(f, sample_number)) - return 0; - - assert(f->current_loc_valid); - assert(f->current_loc <= sample_number); - - // linear search for the relevant packet - max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; - while (f->current_loc < sample_number) { - int left_start, left_end, right_start, right_end, mode, frame_samples; - if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) - return error(f, VORBIS_seek_failed); - // calculate the number of samples returned by the next frame - frame_samples = right_start - left_start; - if (f->current_loc + frame_samples > sample_number) { - return 1; // the next frame will contain the sample - } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { - // there's a chance the frame after this could contain the sample - vorbis_pump_first_frame(f); - } else { - // this frame is too early to be relevant - f->current_loc += frame_samples; - f->previous_length = 0; - maybe_start_packet(f); - flush_packet(f); - } - } - // the next frame will start with the sample - assert(f->current_loc == sample_number); - return 1; -} - -int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) -{ - if (!stb_vorbis_seek_frame(f, sample_number)) - return 0; - - if (sample_number != f->current_loc) { - int n; - uint32 frame_start = f->current_loc; - stb_vorbis_get_frame_float(f, &n, NULL); - assert(sample_number > frame_start); - assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); - f->channel_buffer_start += (sample_number - frame_start); - } - - return 1; -} - -int stb_vorbis_seek_start(stb_vorbis *f) -{ - if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } - set_file_offset(f, f->first_audio_page_offset); - f->previous_length = 0; - f->first_decode = TRUE; - f->next_seg = -1; - return vorbis_pump_first_frame(f); -} - -unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) -{ - unsigned int restore_offset, previous_safe; - unsigned int end, last_page_loc; - - if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); - if (!f->total_samples) { - unsigned int last; - uint32 lo,hi; - char header[6]; - - // first, store the current decode position so we can restore it - restore_offset = stb_vorbis_get_file_offset(f); - - // now we want to seek back 64K from the end (the last page must - // be at most a little less than 64K, but let's allow a little slop) - if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) - previous_safe = f->stream_len - 65536; - else - previous_safe = f->first_audio_page_offset; - - set_file_offset(f, previous_safe); - // previous_safe is now our candidate 'earliest known place that seeking - // to will lead to the final page' - - if (!vorbis_find_page(f, &end, &last)) { - // if we can't find a page, we're hosed! - f->error = VORBIS_cant_find_last_page; - f->total_samples = 0xffffffff; - goto done; - } - - // check if there are more pages - last_page_loc = stb_vorbis_get_file_offset(f); - - // stop when the last_page flag is set, not when we reach eof; - // this allows us to stop short of a 'file_section' end without - // explicitly checking the length of the section - while (!last) { - set_file_offset(f, end); - if (!vorbis_find_page(f, &end, &last)) { - // the last page we found didn't have the 'last page' flag - // set. whoops! - break; - } - previous_safe = last_page_loc+1; - last_page_loc = stb_vorbis_get_file_offset(f); - } - - set_file_offset(f, last_page_loc); - - // parse the header - getn(f, (unsigned char *)header, 6); - // extract the absolute granule position - lo = get32(f); - hi = get32(f); - if (lo == 0xffffffff && hi == 0xffffffff) { - f->error = VORBIS_cant_find_last_page; - f->total_samples = SAMPLE_unknown; - goto done; - } - if (hi) - lo = 0xfffffffe; // saturate - f->total_samples = lo; - - f->p_last.page_start = last_page_loc; - f->p_last.page_end = end; - f->p_last.last_decoded_sample = lo; - - done: - set_file_offset(f, restore_offset); - } - return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; -} - -float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) -{ - return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; -} - - - -int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) -{ - int len, right,left,i; - if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); - - if (!vorbis_decode_packet(f, &len, &left, &right)) { - f->channel_buffer_start = f->channel_buffer_end = 0; - return 0; - } - - len = vorbis_finish_frame(f, len, left, right); - for (i=0; i < f->channels; ++i) - f->outputs[i] = f->channel_buffers[i] + left; - - f->channel_buffer_start = left; - f->channel_buffer_end = left+len; - - if (channels) *channels = f->channels; - if (output) *output = f->outputs; - return len; -} - -#ifndef STB_VORBIS_NO_STDIO - -stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) -{ - stb_vorbis *f, p; - vorbis_init(&p, alloc); - p.f = file; - p.f_start = (uint32) ftell(file); - p.stream_len = length; - p.close_on_free = close_on_free; - if (start_decoder(&p)) { - f = vorbis_alloc(&p); - if (f) { - *f = p; - vorbis_pump_first_frame(f); - return f; - } - } - if (error) *error = p.error; - vorbis_deinit(&p); - return NULL; -} - -stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) -{ - unsigned int len, start; - start = (unsigned int) ftell(file); - fseek(file, 0, SEEK_END); - len = (unsigned int) (ftell(file) - start); - fseek(file, start, SEEK_SET); - return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); -} - -stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) -{ - FILE *f = fopen(filename, "rb"); - if (f) - return stb_vorbis_open_file(f, TRUE, error, alloc); - if (error) *error = VORBIS_file_open_failure; - return NULL; -} -#endif // STB_VORBIS_NO_STDIO - -stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) -{ - stb_vorbis *f, p; - if (data == NULL) return NULL; - vorbis_init(&p, alloc); - p.stream = (uint8 *) data; - p.stream_end = (uint8 *) data + len; - p.stream_start = (uint8 *) p.stream; - p.stream_len = len; - p.push_mode = FALSE; - if (start_decoder(&p)) { - f = vorbis_alloc(&p); - if (f) { - *f = p; - vorbis_pump_first_frame(f); - if (error) *error = VORBIS__no_error; - return f; - } - } - if (error) *error = p.error; - vorbis_deinit(&p); - return NULL; -} - -#ifndef STB_VORBIS_NO_INTEGER_CONVERSION -#define PLAYBACK_MONO 1 -#define PLAYBACK_LEFT 2 -#define PLAYBACK_RIGHT 4 - -#define L (PLAYBACK_LEFT | PLAYBACK_MONO) -#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) -#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) - -static int8 channel_position[7][6] = -{ - { 0 }, - { C }, - { L, R }, - { L, C, R }, - { L, R, L, R }, - { L, C, R, L, R }, - { L, C, R, L, R, C }, -}; - - -#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT - typedef union { - float f; - int i; - } float_conv; - typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; - #define FASTDEF(x) float_conv x - // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round - #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) - #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) - #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) - #define check_endianness() -#else - #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) - #define check_endianness() - #define FASTDEF(x) -#endif - -static void copy_samples(short *dest, float *src, int len) -{ - int i; - check_endianness(); - for (i=0; i < len; ++i) { - FASTDEF(temp); - int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); - if ((unsigned int) (v + 32768) > 65535) - v = v < 0 ? -32768 : 32767; - dest[i] = v; - } -} - -static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) -{ - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE; - check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE) { - memset(buffer, 0, sizeof(buffer)); - if (o + n > len) n = len - o; - for (j=0; j < num_c; ++j) { - if (channel_position[num_c][j] & mask) { - for (i=0; i < n; ++i) - buffer[i] += data[j][d_offset+o+i]; - } - } - for (i=0; i < n; ++i) { - FASTDEF(temp); - int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); - if ((unsigned int) (v + 32768) > 65535) - v = v < 0 ? -32768 : 32767; - output[o+i] = v; - } - } -} - -static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) -{ - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE >> 1; - // o is the offset in the source data - check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE >> 1) { - // o2 is the offset in the output data - int o2 = o << 1; - memset(buffer, 0, sizeof(buffer)); - if (o + n > len) n = len - o; - for (j=0; j < num_c; ++j) { - int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); - if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { - for (i=0; i < n; ++i) { - buffer[i*2+0] += data[j][d_offset+o+i]; - buffer[i*2+1] += data[j][d_offset+o+i]; - } - } else if (m == PLAYBACK_LEFT) { - for (i=0; i < n; ++i) { - buffer[i*2+0] += data[j][d_offset+o+i]; - } - } else if (m == PLAYBACK_RIGHT) { - for (i=0; i < n; ++i) { - buffer[i*2+1] += data[j][d_offset+o+i]; - } - } - } - for (i=0; i < (n<<1); ++i) { - FASTDEF(temp); - int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); - if ((unsigned int) (v + 32768) > 65535) - v = v < 0 ? -32768 : 32767; - output[o2+i] = v; - } - } -} - -static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) -{ - int i; - if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { - static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; - for (i=0; i < buf_c; ++i) - compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); - } else { - int limit = buf_c < data_c ? buf_c : data_c; - for (i=0; i < limit; ++i) - copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); - for ( ; i < buf_c; ++i) - memset(buffer[i]+b_offset, 0, sizeof(short) * samples); - } -} - -int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) -{ - float **output; - int len = stb_vorbis_get_frame_float(f, NULL, &output); - if (len > num_samples) len = num_samples; - if (len) - convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); - return len; -} - -static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) -{ - int i; - check_endianness(); - if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { - assert(buf_c == 2); - for (i=0; i < buf_c; ++i) - compute_stereo_samples(buffer, data_c, data, d_offset, len); - } else { - int limit = buf_c < data_c ? buf_c : data_c; - int j; - for (j=0; j < len; ++j) { - for (i=0; i < limit; ++i) { - FASTDEF(temp); - float f = data[i][d_offset+j]; - int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); - if ((unsigned int) (v + 32768) > 65535) - v = v < 0 ? -32768 : 32767; - *buffer++ = v; - } - for ( ; i < buf_c; ++i) - *buffer++ = 0; - } - } -} - -int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) -{ - float **output; - int len; - if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); - len = stb_vorbis_get_frame_float(f, NULL, &output); - if (len) { - if (len*num_c > num_shorts) len = num_shorts / num_c; - convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); - } - return len; -} - -int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) -{ - float **outputs; - int len = num_shorts / channels; - int n=0; - int z = f->channels; - if (z > channels) z = channels; - while (n < len) { - int k = f->channel_buffer_end - f->channel_buffer_start; - if (n+k >= len) k = len - n; - if (k) - convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); - buffer += k*channels; - n += k; - f->channel_buffer_start += k; - if (n == len) break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; - } - return n; -} - -int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) -{ - float **outputs; - int n=0; - int z = f->channels; - if (z > channels) z = channels; - while (n < len) { - int k = f->channel_buffer_end - f->channel_buffer_start; - if (n+k >= len) k = len - n; - if (k) - convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); - n += k; - f->channel_buffer_start += k; - if (n == len) break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; - } - return n; -} - -#ifndef STB_VORBIS_NO_STDIO -int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) -{ - int data_len, offset, total, limit, error; - short *data; - stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); - if (v == NULL) return -1; - limit = v->channels * 4096; - *channels = v->channels; - if (sample_rate) - *sample_rate = v->sample_rate; - offset = data_len = 0; - total = limit; - data = (short *) malloc(total * sizeof(*data)); - if (data == NULL) { - stb_vorbis_close(v); - return -2; - } - for (;;) { - int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); - if (n == 0) break; - data_len += n; - offset += n * v->channels; - if (offset + limit > total) { - short *data2; - total *= 2; - data2 = (short *) realloc(data, total * sizeof(*data)); - if (data2 == NULL) { - free(data); - stb_vorbis_close(v); - return -2; - } - data = data2; - } - } - *output = data; - stb_vorbis_close(v); - return data_len; -} -#endif // NO_STDIO - -int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) -{ - int data_len, offset, total, limit, error; - short *data; - stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); - if (v == NULL) return -1; - limit = v->channels * 4096; - *channels = v->channels; - if (sample_rate) - *sample_rate = v->sample_rate; - offset = data_len = 0; - total = limit; - data = (short *) malloc(total * sizeof(*data)); - if (data == NULL) { - stb_vorbis_close(v); - return -2; - } - for (;;) { - int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); - if (n == 0) break; - data_len += n; - offset += n * v->channels; - if (offset + limit > total) { - short *data2; - total *= 2; - data2 = (short *) realloc(data, total * sizeof(*data)); - if (data2 == NULL) { - free(data); - stb_vorbis_close(v); - return -2; - } - data = data2; - } - } - *output = data; - stb_vorbis_close(v); - return data_len; -} -#endif // STB_VORBIS_NO_INTEGER_CONVERSION - -int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) -{ - float **outputs; - int len = num_floats / channels; - int n=0; - int z = f->channels; - if (z > channels) z = channels; - while (n < len) { - int i,j; - int k = f->channel_buffer_end - f->channel_buffer_start; - if (n+k >= len) k = len - n; - for (j=0; j < k; ++j) { - for (i=0; i < z; ++i) - *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; - for ( ; i < channels; ++i) - *buffer++ = 0; - } - n += k; - f->channel_buffer_start += k; - if (n == len) - break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) - break; - } - return n; -} - -int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) -{ - float **outputs; - int n=0; - int z = f->channels; - if (z > channels) z = channels; - while (n < num_samples) { - int i; - int k = f->channel_buffer_end - f->channel_buffer_start; - if (n+k >= num_samples) k = num_samples - n; - if (k) { - for (i=0; i < z; ++i) - memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); - for ( ; i < channels; ++i) - memset(buffer[i]+n, 0, sizeof(float) * k); - } - n += k; - f->channel_buffer_start += k; - if (n == num_samples) - break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) - break; - } - return n; -} -#endif // STB_VORBIS_NO_PULLDATA_API - -/* Version history - 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files - 1.11 - 2017-07-23 - fix MinGW compilation - 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory - 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version - 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; - avoid discarding last frame of audio data - 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API - some more crash fixes when out of memory or with corrupt files - 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) - some crash fixes when out of memory or with corrupt files - 1.05 - 2015-04-19 - don't define __forceinline if it's redundant - 1.04 - 2014-08-27 - fix missing const-correct case in API - 1.03 - 2014-08-07 - Warning fixes - 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows - 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float - 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel - (API change) report sample rate for decode-full-file funcs - 0.99996 - bracket #include for macintosh compilation by Laurent Gomila - 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem - 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence - 0.99993 - remove assert that fired on legal files with empty tables - 0.99992 - rewind-to-start - 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo - 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ - 0.9998 - add a full-decode function with a memory source - 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition - 0.9996 - query length of vorbis stream in samples/seconds - 0.9995 - bugfix to another optimization that only happened in certain files - 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors - 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation - 0.9992 - performance improvement of IMDCT; now performs close to reference implementation - 0.9991 - performance improvement of IMDCT - 0.999 - (should have been 0.9990) performance improvement of IMDCT - 0.998 - no-CRT support from Casey Muratori - 0.997 - bugfixes for bugs found by Terje Mathisen - 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen - 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen - 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen - 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen - 0.992 - fixes for MinGW warning - 0.991 - turn fast-float-conversion on by default - 0.990 - fix push-mode seek recovery if you seek into the headers - 0.98b - fix to bad release of 0.98 - 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode - 0.97 - builds under c++ (typecasting, don't use 'class' keyword) - 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code - 0.95 - clamping code for 16-bit functions - 0.94 - not publically released - 0.93 - fixed all-zero-floor case (was decoding garbage) - 0.92 - fixed a memory leak - 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION - 0.90 - first public release -*/ - -#endif // STB_VORBIS_HEADER_ONLY - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/src/external/stb_vorbis.h b/src/external/stb_vorbis.h index dccfd65b..90f3b061 100644 --- a/src/external/stb_vorbis.h +++ b/src/external/stb_vorbis.h @@ -30,14 +30,14 @@ // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart // manxorist@github saga musix github:infatum -// Timur Gagiev +// Timur Gagiev BareRose // // Partial history: // 1.14 - 2018-02-11 - delete bogus dealloca usage // 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) // 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files // 1.11 - 2017-07-23 - fix MinGW compilation -// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory +// 1.10 - 2017-03-03 - more robust seeking; fix negative stbv_ilog(); clear error in open_memory // 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version // 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame // 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const @@ -64,7 +64,7 @@ #define STB_VORBIS_INCLUDE_STB_VORBIS_H #if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) -#define STB_VORBIS_NO_STDIO 1 +#define STB_VORBIS_NO_STDIO #endif #ifndef STB_VORBIS_NO_STDIO @@ -80,6 +80,12 @@ extern "C" { #endif +#ifdef STB_VORBIS_STATIC +#define STBVDEF static +#else +#define STBVDEF extern +#endif + /////////// THREAD SAFETY // Individual stb_vorbis* handles are not thread-safe; you cannot decode from @@ -95,8 +101,8 @@ extern "C" { // data in the file and how you set the compile flags for speed // vs. size. In my test files the maximal-size usage is ~150KB.) // -// You can modify the wrapper functions in the source (setup_malloc, -// setup_temp_malloc, temp_malloc) to change this behavior, or you +// You can modify the wrapper functions in the source (stbv_setup_malloc, +// stbv_setup_temp_malloc, temp_malloc) to change this behavior, or you // can use a simpler allocation model: you pass in a buffer from // which stb_vorbis will allocate _all_ its memory (including the // temp memory). "open" may fail with a VORBIS_outofmem if you @@ -133,24 +139,24 @@ typedef struct } stb_vorbis_info; // get general information about the file -extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); +STBVDEF stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); // get the last error detected (clears it, too) -extern int stb_vorbis_get_error(stb_vorbis *f); +STBVDEF int stb_vorbis_get_error(stb_vorbis *f); // close an ogg vorbis file and free all memory in use -extern void stb_vorbis_close(stb_vorbis *f); +STBVDEF void stb_vorbis_close(stb_vorbis *f); // this function returns the offset (in samples) from the beginning of the // file that will be returned by the next decode, if it is known, or -1 // otherwise. after a flush_pushdata() call, this may take a while before // it becomes valid again. // NOT WORKING YET after a seek with PULLDATA API -extern int stb_vorbis_get_sample_offset(stb_vorbis *f); +STBVDEF int stb_vorbis_get_sample_offset(stb_vorbis *f); // returns the current seek point within the file, or offset from the beginning // of the memory buffer. In pushdata mode it returns 0. -extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); +STBVDEF unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); /////////// PUSHDATA API @@ -163,7 +169,7 @@ extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); // need to give it the same data again PLUS more. Note that the Vorbis // specification does not bound the size of an individual frame. -extern stb_vorbis *stb_vorbis_open_pushdata( +STBVDEF stb_vorbis *stb_vorbis_open_pushdata( const unsigned char * datablock, int datablock_length_in_bytes, int *datablock_memory_consumed_in_bytes, int *error, @@ -177,7 +183,7 @@ extern stb_vorbis *stb_vorbis_open_pushdata( // if returns NULL and *error is VORBIS_need_more_data, then the input block was // incomplete and you need to pass in a larger block from the start of the file -extern int stb_vorbis_decode_frame_pushdata( +STBVDEF int stb_vorbis_decode_frame_pushdata( stb_vorbis *f, const unsigned char *datablock, int datablock_length_in_bytes, int *channels, // place to write number of float * buffers @@ -207,7 +213,7 @@ extern int stb_vorbis_decode_frame_pushdata( // the first channel, and (*output)[1][0] contains the first sample from // the second channel. -extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +STBVDEF void stb_vorbis_flush_pushdata(stb_vorbis *f); // inform stb_vorbis that your next datablock will not be contiguous with // previous ones (e.g. you've seeked in the data); future attempts to decode // frames will cause stb_vorbis to resynchronize (as noted above), and @@ -232,28 +238,28 @@ extern void stb_vorbis_flush_pushdata(stb_vorbis *f); // just want to go ahead and use pushdata.) #if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) -extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +STBVDEF int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); #endif -#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) -extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +STBVDEF int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); #endif // decode an entire file and output the data interleaved into a malloc()ed // buffer stored in *output. The return value is the number of samples // decoded, or -1 if the file could not be opened or was not an ogg vorbis file. // When you're done with it, just free() the pointer returned in *output. -extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, +STBVDEF stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from an ogg vorbis stream in memory (note // this must be the entire stream!). on failure, returns NULL and sets *error #ifndef STB_VORBIS_NO_STDIO -extern stb_vorbis * stb_vorbis_open_filename(const char *filename, +STBVDEF stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from a filename via fopen(). on failure, // returns NULL and sets *error (possibly to VORBIS_file_open_failure). -extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, +STBVDEF stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell). on failure, returns NULL and sets *error. @@ -263,7 +269,7 @@ extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, // owns the _entire_ rest of the file after the start point. Use the next // function, stb_vorbis_open_file_section(), to limit it. -extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, +STBVDEF stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell); the stream will be of length 'len' bytes. @@ -272,8 +278,8 @@ extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_cl // confused. #endif -extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); -extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +STBVDEF int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +STBVDEF int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); // these functions seek in the Vorbis file to (approximately) 'sample_number'. // after calling seek_frame(), the next call to get_frame_*() will include // the specified sample. after calling stb_vorbis_seek(), the next call to @@ -281,14 +287,14 @@ extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); // do not need to seek to EXACTLY the target sample when using get_samples_*, // you can also use seek_frame(). -extern int stb_vorbis_seek_start(stb_vorbis *f); +STBVDEF int stb_vorbis_seek_start(stb_vorbis *f); // this function is equivalent to stb_vorbis_seek(f,0) -extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); -extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +STBVDEF unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +STBVDEF float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); // these functions return the total length of the vorbis stream -extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +STBVDEF int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); // decode the next frame and return the number of samples. the number of // channels returned are stored in *channels (which can be NULL--it is always // the same as the number of channels reported by get_info). *output will @@ -299,8 +305,8 @@ extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***out // and stb_vorbis_get_samples_*(), since the latter calls the former. #ifndef STB_VORBIS_NO_INTEGER_CONVERSION -extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); -extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +STBVDEF int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +STBVDEF int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); #endif // decode the next frame and return the number of *samples* per channel. // Note that for interleaved data, you pass in the number of shorts (the @@ -327,16 +333,16 @@ extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, shor // Note that this is not _good_ surround etc. mixing at all! It's just so // you get something useful. -extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); -extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +STBVDEF int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +STBVDEF int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); // gets num_samples samples, not necessarily on a frame boundary--this requires // buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. // Returns the number of samples stored per channel; it may be less than requested // at the end of the file. If there are no more samples in the file, returns 0. #ifndef STB_VORBIS_NO_INTEGER_CONVERSION -extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); -extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +STBVDEF int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +STBVDEF int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); #endif // gets num_samples samples, not necessarily on a frame boundary--this requires // buffering so you have to supply the buffers. Applies the coercion rules above @@ -392,3 +398,5078 @@ enum STBVorbisError // HEADER ENDS HERE // ////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_IMPLEMENTATION + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +// #define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +#ifdef STB_VORBIS_CODEBOOK_SHORTS +#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT + #include + #include + #include + #include + + // find definition of alloca if it's not in stdlib.h: + #if defined(_MSC_VER) || defined(__MINGW32__) + #include + #endif + #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) + #include + #endif +#else // STB_VORBIS_NO_CRT + #define NULL 0 + #define malloc(s) 0 + #define free(s) ((void) 0) + #define realloc(s) 0 +#endif // STB_VORBIS_NO_CRT + +#include + +#ifdef __MINGW32__ + // eff you mingw: + // "fixed": + // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/ + // "no that broke the build, reverted, who cares about C": + // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/ + #ifdef __forceinline + #undef __forceinline + #endif + #define __forceinline + #ifndef alloca + #define alloca(s) __builtin_alloca(s) + #endif +#elif !defined(_MSC_VER) + #if __GNUC__ + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#if 0 +#include +#define STBV_CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#else +#define STBV_CHECK(f) ((void) 0) +#endif + +#define STBV_MAX_BLOCKSIZE_LOG 13 // from specification +#define STBV_MAX_BLOCKSIZE (1 << STBV_MAX_BLOCKSIZE_LOG) + + +typedef unsigned char stbv_uint8; +typedef signed char stbv_int8; +typedef unsigned short stbv_uint16; +typedef signed short stbv_int16; +typedef unsigned int stbv_uint32; +typedef signed int stbv_int32; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef float stbv_codetype; + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define STBV_FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define STBV_FAST_HUFFMAN_TABLE_MASK (STBV_FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + stbv_uint8 *codeword_lengths; + float minimum_value; + float delta_value; + stbv_uint8 value_bits; + stbv_uint8 lookup_type; + stbv_uint8 sequence_p; + stbv_uint8 sparse; + stbv_uint32 lookup_values; + stbv_codetype *multiplicands; + stbv_uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + stbv_int16 fast_huffman[STBV_FAST_HUFFMAN_TABLE_SIZE]; + #else + stbv_int32 fast_huffman[STBV_FAST_HUFFMAN_TABLE_SIZE]; + #endif + stbv_uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} StbvCodebook; + +typedef struct +{ + stbv_uint8 order; + stbv_uint16 rate; + stbv_uint16 bark_map_size; + stbv_uint8 amplitude_bits; + stbv_uint8 amplitude_offset; + stbv_uint8 number_of_books; + stbv_uint8 book_list[16]; // varies +} StbvFloor0; + +typedef struct +{ + stbv_uint8 partitions; + stbv_uint8 partition_class_list[32]; // varies + stbv_uint8 class_dimensions[16]; // varies + stbv_uint8 class_subclasses[16]; // varies + stbv_uint8 class_masterbooks[16]; // varies + stbv_int16 subclass_books[16][8]; // varies + stbv_uint16 Xlist[31*8+2]; // varies + stbv_uint8 sorted_order[31*8+2]; + stbv_uint8 stbv_neighbors[31*8+2][2]; + stbv_uint8 floor1_multiplier; + stbv_uint8 rangebits; + int values; +} StbvFloor1; + +typedef union +{ + StbvFloor0 floor0; + StbvFloor1 floor1; +} StbvFloor; + +typedef struct +{ + stbv_uint32 begin, end; + stbv_uint32 part_size; + stbv_uint8 classifications; + stbv_uint8 classbook; + stbv_uint8 **classdata; + stbv_int16 (*residue_books)[8]; +} StbvResidue; + +typedef struct +{ + stbv_uint8 magnitude; + stbv_uint8 angle; + stbv_uint8 mux; +} StbvMappingChannel; + +typedef struct +{ + stbv_uint16 coupling_steps; + StbvMappingChannel *chan; + stbv_uint8 submaps; + stbv_uint8 submap_floor[15]; // varies + stbv_uint8 submap_residue[15]; // varies +} StbvMapping; + +typedef struct +{ + stbv_uint8 blockflag; + stbv_uint8 mapping; + stbv_uint16 windowtype; + stbv_uint16 transformtype; +} StbvMode; + +typedef struct +{ + stbv_uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + stbv_uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + stbv_uint32 sample_loc; // granule pos encoded in page +} StbvCRCscan; + +typedef struct +{ + stbv_uint32 page_start, page_end; + stbv_uint32 last_decoded_sample; +} StbvProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + stbv_uint32 f_start; + int close_on_free; +#endif + + stbv_uint8 *stream; + stbv_uint8 *stream_start; + stbv_uint8 *stream_end; + + stbv_uint32 stream_len; + + stbv_uint8 push_mode; + + stbv_uint32 first_audio_page_offset; + + StbvProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + StbvCodebook *codebooks; + int floor_count; + stbv_uint16 floor_types[64]; // varies + StbvFloor *floor_config; + int residue_count; + stbv_uint16 residue_types[64]; // varies + StbvResidue *residue_config; + int mapping_count; + StbvMapping *mapping; + int mode_count; + StbvMode mode_config[64]; // varies + + stbv_uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + stbv_int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + stbv_uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + stbv_uint16 *stbv_bit_reverse[2]; + + // current page/packet/segment streaming info + stbv_uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + stbv_uint8 segments[255]; + stbv_uint8 page_flag; + stbv_uint8 bytes_in_seg; + stbv_uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + stbv_uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + stbv_uint32 known_loc_for_packet; + int discard_samples_deferred; + stbv_uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + StbvCRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define STBV_IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define STBV_IS_PUSH_MODE(f) TRUE +#else + #define STBV_IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis stbv_vorb; + +static int stbv_error(stbv_vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define stbv_array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define stbv_temp_alloc(f,size) (f->alloc.alloc_buffer ? stbv_setup_temp_malloc(f,size) : alloca(size)) +#define stbv_temp_free(f,p) 0 +#define stbv_temp_alloc_save(f) ((f)->temp_offset) +#define stbv_temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define stbv_temp_block_array(f,count,size) stbv_make_block_array(stbv_temp_alloc(f,stbv_array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *stbv_make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *stbv_setup_malloc(stbv_vorb *f, int sz) +{ + sz = (sz+3) & ~3; + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void stbv_setup_free(stbv_vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack + free(p); +} + +static void *stbv_setup_temp_malloc(stbv_vorb *f, int sz) +{ + sz = (sz+3) & ~3; + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void stbv_setup_temp_free(stbv_vorb *f, void *p, int sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+3)&~3; + return; + } + free(p); +} + +#define STBV_CRC32_POLY 0x04c11db7 // from spec + +static stbv_uint32 stbv_crc_table[256]; +static void stbv_crc32_init(void) +{ + int i,j; + stbv_uint32 s; + for(i=0; i < 256; i++) { + for (s=(stbv_uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? STBV_CRC32_POLY : 0); + stbv_crc_table[i] = s; + } +} + +static __forceinline stbv_uint32 stbv_crc32_update(stbv_uint32 crc, stbv_uint8 byte) +{ + return (crc << 8) ^ stbv_crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int stbv_bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float stbv_square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int stbv_ilog(stbv_int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + if (n < 0) return 0; // signed n returns 0 + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else return 30 + log2_4[n >> 30]; +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float stbv_float32_unpack(stbv_uint32 x) +{ + // from the specification + stbv_uint32 mantissa = x & 0x1fffff; + stbv_uint32 sign = x & 0x80000000; + stbv_uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void stbv_add_entry(StbvCodebook *c, stbv_uint32 huff_code, int symbol, int count, int len, stbv_uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int stbv_compute_codewords(StbvCodebook *c, stbv_uint8 *len, int n, stbv_uint32 *values) +{ + int i,k,m=0; + stbv_uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + // add to the list + stbv_add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + stbv_uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + assert(z >= 0 && z < 32); + available[z] = 0; + stbv_add_entry(c, stbv_bit_reverse(res), i, m++, len[i], values); + // propogate availability up the tree + if (z != len[i]) { + assert(len[i] >= 0 && len[i] < 32); + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void stbv_compute_accelerated_huffman(StbvCodebook *c) +{ + int i, len; + for (i=0; i < STBV_FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + stbv_uint32 z = c->sparse ? stbv_bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < STBV_FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL stbv_uint32_compare(const void *p, const void *q) +{ + stbv_uint32 x = * (stbv_uint32 *) p; + stbv_uint32 y = * (stbv_uint32 *) q; + return x < y ? -1 : x > y; +} + +static int stbv_include_in_sort(StbvCodebook *c, stbv_uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void stbv_compute_sorted_huffman(StbvCodebook *c, stbv_uint8 *lengths, stbv_uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (stbv_include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = stbv_bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = stbv_bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), stbv_uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (stbv_include_in_sort(c,huff_len)) { + stbv_uint32 code = stbv_bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int stbv_vorbis_validate(stbv_uint8 *data) +{ + static stbv_uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int stbv_lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + assert(pow((float) r+1, dim) > entries); + assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above + return r; +} + +// called twice per file +static void stbv_compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void stbv_compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * stbv_square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void stbv_compute_bitreverse(int n, stbv_uint16 *rev) +{ + int ld = stbv_ilog(n) - 1; // stbv_ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (stbv_bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int stbv_init_blocksize(stbv_vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) stbv_setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) stbv_setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) stbv_setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return stbv_error(f, VORBIS_outofmem); + stbv_compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) stbv_setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return stbv_error(f, VORBIS_outofmem); + stbv_compute_window(n, f->window[b]); + f->stbv_bit_reverse[b] = (stbv_uint16 *) stbv_setup_malloc(f, sizeof(stbv_uint16) * n8); + if (!f->stbv_bit_reverse[b]) return stbv_error(f, VORBIS_outofmem); + stbv_compute_bitreverse(n, f->stbv_bit_reverse[b]); + return TRUE; +} + +static void stbv_neighbors(stbv_uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + stbv_uint16 x,id; +} stbv_floor_ordering; + +static int STBV_CDECL stbv_point_compare(const void *p, const void *q) +{ + stbv_floor_ordering *a = (stbv_floor_ordering *) p; + stbv_floor_ordering *b = (stbv_floor_ordering *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define STBV_USE_MEMORY(z) TRUE +#else + #define STBV_USE_MEMORY(z) ((z)->stream) +#endif + +static stbv_uint8 stbv_get8(stbv_vorb *z) +{ + if (STBV_USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static stbv_uint32 stbv_get32(stbv_vorb *f) +{ + stbv_uint32 x; + x = stbv_get8(f); + x += stbv_get8(f) << 8; + x += stbv_get8(f) << 16; + x += (stbv_uint32) stbv_get8(f) << 24; + return x; +} + +static int stbv_getn(stbv_vorb *z, stbv_uint8 *data, int n) +{ + if (STBV_USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void stbv_skip(stbv_vorb *z, int n) +{ + if (STBV_USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int stbv_set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (STBV_USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static stbv_uint8 stbv_ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int stbv_capture_pattern(stbv_vorb *f) +{ + if (0x4f != stbv_get8(f)) return FALSE; + if (0x67 != stbv_get8(f)) return FALSE; + if (0x67 != stbv_get8(f)) return FALSE; + if (0x53 != stbv_get8(f)) return FALSE; + return TRUE; +} + +#define STBV_PAGEFLAG_continued_packet 1 +#define STBV_PAGEFLAG_first_page 2 +#define STBV_PAGEFLAG_last_page 4 + +static int stbv_start_page_no_capturepattern(stbv_vorb *f) +{ + stbv_uint32 loc0,loc1,n; + // stream structure version + if (0 != stbv_get8(f)) return stbv_error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = stbv_get8(f); + // absolute granule position + loc0 = stbv_get32(f); + loc1 = stbv_get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + stbv_get32(f); + //if (f->serial != stbv_get32(f)) return stbv_error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = stbv_get32(f); + f->last_page = n; + // CRC32 + stbv_get32(f); + // page_segments + f->segment_count = stbv_get8(f); + if (!stbv_getn(f, f->segments, f->segment_count)) + return stbv_error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + StbvProbedPage p; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + p.page_start = f->first_audio_page_offset; + p.page_end = p.page_start + len; + p.last_decoded_sample = loc0; + f->p_first = p; + } + f->next_seg = 0; + return TRUE; +} + +static int stbv_start_page(stbv_vorb *f) +{ + if (!stbv_capture_pattern(f)) return stbv_error(f, VORBIS_missing_capture_pattern); + return stbv_start_page_no_capturepattern(f); +} + +static int stbv_start_packet(stbv_vorb *f) +{ + while (f->next_seg == -1) { + if (!stbv_start_page(f)) return FALSE; + if (f->page_flag & STBV_PAGEFLAG_continued_packet) + return stbv_error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int stbv_maybe_start_packet(stbv_vorb *f) +{ + if (f->next_seg == -1) { + int x = stbv_get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return stbv_error(f, VORBIS_missing_capture_pattern); + if (0x67 != stbv_get8(f)) return stbv_error(f, VORBIS_missing_capture_pattern); + if (0x67 != stbv_get8(f)) return stbv_error(f, VORBIS_missing_capture_pattern); + if (0x53 != stbv_get8(f)) return stbv_error(f, VORBIS_missing_capture_pattern); + if (!stbv_start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & STBV_PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return stbv_error(f, VORBIS_continued_packet_flag_invalid); + } + } + return stbv_start_packet(f); +} + +static int stbv_next_segment(stbv_vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case stbv_start_page fails + if (!stbv_start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & STBV_PAGEFLAG_continued_packet)) return stbv_error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define STBV_EOP (-1) +#define STBV_INVALID_BITS (-1) + +static int stbv_get8_packet_raw(stbv_vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return STBV_EOP; + else if (!stbv_next_segment(f)) return STBV_EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return stbv_get8(f); +} + +static int stbv_get8_packet(stbv_vorb *f) +{ + int x = stbv_get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static void stbv_flush_packet(stbv_vorb *f) +{ + while (stbv_get8_packet_raw(f) != STBV_EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static stbv_uint32 stbv_get_bits(stbv_vorb *f, int n) +{ + stbv_uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = stbv_get_bits(f, 24); + z += stbv_get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = stbv_get8_packet_raw(f); + if (z == STBV_EOP) { + f->valid_bits = STBV_INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + if (f->valid_bits < 0) return 0; + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +static __forceinline void stbv_prep_huffman(stbv_vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = stbv_get8_packet_raw(f); + if (z == STBV_EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + STBV_VORBIS_packet_id = 1, + STBV_VORBIS_packet_comment = 3, + STBV_VORBIS_packet_setup = 5 +}; + +static int stbv_codebook_decode_scalar_raw(stbv_vorb *f, StbvCodebook *c) +{ + int i; + stbv_prep_huffman(f); + + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; + + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + stbv_uint32 code = stbv_bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + stbv_error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define STBV_DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + stbv_prep_huffman(f); \ + var = f->acc & STBV_FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = stbv_codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int stbv_codebook_decode_scalar(stbv_vorb *f, StbvCodebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + stbv_prep_huffman(f); + // fast huffman table lookup + i = f->acc & STBV_FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return stbv_codebook_decode_scalar_raw(f,c); +} + +#define STBV_DECODE_RAW(var,f,c) var = stbv_codebook_decode_scalar(f,c); + +#endif + +#define STBV_DECODE(var,f,c) \ + STBV_DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) STBV_DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) STBV_DECODE(var,f,c) +#endif + + + + + + +// STBV_CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#define STBV_CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define STBV_CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define STBV_CODEBOOK_ELEMENT_BASE(c) (0) + +static int stbv_codebook_decode_start(stbv_vorb *f, StbvCodebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + stbv_error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for STBV_EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + stbv_error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int stbv_codebook_decode(stbv_vorb *f, StbvCodebook *c, float *output, int len) +{ + int i,z = stbv_codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = STBV_CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = STBV_CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = STBV_CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = STBV_CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int stbv_codebook_decode_step(stbv_vorb *f, StbvCodebook *c, float *output, int len, int step) +{ + int i,z = stbv_codebook_decode_start(f,c); + float last = STBV_CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = STBV_CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int stbv_codebook_decode_deinterleave_repeat(stbv_vorb *f, StbvCodebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return stbv_error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = STBV_CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return stbv_error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = STBV_CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = STBV_CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int stbv_predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float stbv_inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define STBV_LINE_OP(a,b) a *= b +#else +#define STBV_LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define STBV_DIVTAB_NUMER 32 +#define STBV_DIVTAB_DENOM 64 +stbv_int8 stbv_integer_divide_table[STBV_DIVTAB_NUMER][STBV_DIVTAB_DENOM]; // 2KB +#endif + +static __forceinline void stbv_draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < STBV_DIVTAB_DENOM && ady < STBV_DIVTAB_NUMER) { + if (dy < 0) { + base = -stbv_integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = stbv_integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + if (x < x1) { + STBV_LINE_OP(output[x], stbv_inverse_db_table[y]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + STBV_LINE_OP(output[x], stbv_inverse_db_table[y]); + } + } +} + +static int stbv_residue_decode(stbv_vorb *f, StbvCodebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!stbv_codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!stbv_codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +// n is 1/2 of the blocksize -- +// specification: "Correct per-vector decode length is [n]/2" +static void stbv_decode_residue(stbv_vorb *f, float *residue_buffers[], int ch, int n, int rn, stbv_uint8 *do_not_decode) +{ + int i,j,pass; + StbvResidue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + unsigned int actual_size = rtype == 2 ? n*2 : n; + unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); + unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = stbv_temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + stbv_uint8 ***part_classdata = (stbv_uint8 ***) stbv_temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) stbv_temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + STBV_CHECK(f); + + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + StbvCodebook *c = f->codebooks+r->classbook; + int q; + STBV_DECODE(q,f,c); + if (q == STBV_EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + StbvCodebook *book = f->codebooks + b; + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!stbv_codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!stbv_codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch == 1) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = 0, p_inter = z; + if (pass == 0) { + StbvCodebook *c = f->codebooks+r->classbook; + int q; + STBV_DECODE(q,f,c); + if (q == STBV_EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + StbvCodebook *book = f->codebooks + b; + if (!stbv_codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = 0; + p_inter = z; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + StbvCodebook *c = f->codebooks+r->classbook; + int q; + STBV_DECODE(q,f,c); + if (q == STBV_EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + StbvCodebook *book = f->codebooks + b; + if (!stbv_codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + STBV_CHECK(f); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + StbvCodebook *c = f->codebooks+r->classbook; + int temp; + STBV_DECODE(temp,f,c); + if (temp == STBV_EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + StbvCodebook *book = f->codebooks + b; + if (!stbv_residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + STBV_CHECK(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + stbv_temp_free(f,part_classdata); + #else + stbv_temp_free(f,classifications); + #endif + stbv_temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, stbv_vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, stbv_vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void stbv_imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void stbv_imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void stbv_imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +static __forceinline void stbv_iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void stbv_imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + + k00 = z[-0] - z[-8]; + k11 = z[-1] - z[-9]; + z[-0] = z[-0] + z[-8]; + z[-1] = z[-1] + z[-9]; + z[-8] = k00; + z[-9] = k11 ; + + k00 = z[ -2] - z[-10]; + k11 = z[ -3] - z[-11]; + z[ -2] = z[ -2] + z[-10]; + z[ -3] = z[ -3] + z[-11]; + z[-10] = (k00+k11) * A2; + z[-11] = (k11-k00) * A2; + + k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k11 = z[ -5] - z[-13]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[-12] = k11; + z[-13] = k00; + + k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation + k11 = z[ -7] - z[-15]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-14] = (k00+k11) * A2; + z[-15] = (k00-k11) * A2; + + stbv_iter_54(z); + stbv_iter_54(z-8); + z -= 16; + } +} + +static void stbv_inverse_mdct(float *buffer, int n, stbv_vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = stbv_temp_alloc_save(f); + float *buf2 = (float *) stbv_temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propogates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = stbv_ilog(n) - 1; // stbv_ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + stbv_imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + stbv_imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + stbv_imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + stbv_imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + stbv_imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + stbv_imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + stbv_imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + stbv_imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + stbv_imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + stbv_uint16 *bitrev = f->stbv_bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + stbv_temp_free(f,buf2); + stbv_temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = stbv_ilog(n) - 1; // stbv_ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = stbv_bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *stbv_get_window(stbv_vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + assert(0); + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef stbv_int16 STBV_YTYPE; +#else +typedef int STBV_YTYPE; +#endif +static int stbv_do_floor(stbv_vorb *f, StbvMapping *map, int i, int n, float *target, STBV_YTYPE *finalY, stbv_uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return stbv_error(f, VORBIS_invalid_stream); + } else { + StbvFloor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + stbv_draw_line(target, lx,ly, hx,hy, n2); + STBV_CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: stbv_draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + STBV_LINE_OP(target[j], stbv_inverse_db_table[ly]); + STBV_CHECK(f); + } + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int stbv_vorbis_decode_initial(stbv_vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + StbvMode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!stbv_maybe_start_packet(f)) + return FALSE; + // check packet type + if (stbv_get_bits(f,1) != 0) { + if (STBV_IS_PUSH_MODE(f)) + return stbv_error(f,VORBIS_bad_packet_type); + while (STBV_EOP != stbv_get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = stbv_get_bits(f, stbv_ilog(f->mode_count-1)); + if (i == STBV_EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = stbv_get_bits(f,1); + next = stbv_get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + + return TRUE; +} + +static int stbv_vorbis_decode_packet_rest(stbv_vorb *f, int *len, StbvMode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + StbvMapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + STBV_CHECK(f); + + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return stbv_error(f, VORBIS_invalid_stream); + } else { + StbvFloor1 *g = &f->floor_config[floor].floor1; + if (stbv_get_bits(f, 1)) { + short *finalY; + stbv_uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = stbv_get_bits(f, stbv_ilog(range)-1); + finalY[1] = stbv_get_bits(f, stbv_ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + StbvCodebook *c = f->codebooks + g->class_masterbooks[pclass]; + STBV_DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + StbvCodebook *c = f->codebooks + book; + STBV_DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == STBV_INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->stbv_neighbors[j][0]; + high = g->stbv_neighbors[j][1]; + //stbv_neighbors(g->Xlist, j, &low, &high); + pred = stbv_predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + stbv_do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + STBV_CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + STBV_CHECK(f); +// RESIDUE STBV_DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + stbv_uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + stbv_decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + STBV_CHECK(f); + +// INVERSE COUPLING + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + STBV_CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + stbv_do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + STBV_CHECK(f); + for (i=0; i < f->channels; ++i) + stbv_inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + STBV_CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + stbv_flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = -n2; // start of first frame is positioned for discard + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & STBV_PAGEFLAG_last_page)) { + stbv_uint32 current_end = f->known_loc_for_packet; + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end-left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; // this doesn't seem right, but has no ill effect on my test files + if (*len > right_end) *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + STBV_CHECK(f); + + return TRUE; +} + +static int stbv_vorbis_decode_packet(stbv_vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!stbv_vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return stbv_vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int stbv_vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = stbv_get_window(f, n); + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static int stbv_vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left, res; + res = stbv_vorbis_decode_packet(f, &len, &left, &right); + if (res) + stbv_vorbis_finish_frame(f, len, left, right); + return res; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int stbv_is_whole_packet_present(stb_vorbis *f, int end_page) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with stbv_get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + stbv_uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (end_page) + if (s < f->segment_count-1) return stbv_error(f, VORBIS_invalid_stream); + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return stbv_error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + stbv_uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return stbv_error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, stbv_ogg_page_header, 4)) return stbv_error(f, VORBIS_invalid_stream); + if (p[4] != 0) return stbv_error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & STBV_PAGEFLAG_continued_packet)) return stbv_error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & STBV_PAGEFLAG_continued_packet)) return stbv_error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return stbv_error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (end_page) + if (s < n-1) return stbv_error(f, VORBIS_invalid_stream); + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return stbv_error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int stbv_start_decoder(stbv_vorb *f) +{ + stbv_uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + + if (!stbv_start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & STBV_PAGEFLAG_first_page)) return stbv_error(f, VORBIS_invalid_first_page); + if (f->page_flag & STBV_PAGEFLAG_last_page) return stbv_error(f, VORBIS_invalid_first_page); + if (f->page_flag & STBV_PAGEFLAG_continued_packet) return stbv_error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return stbv_error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) return stbv_error(f, VORBIS_invalid_first_page); + // read packet + // check packet header + if (stbv_get8(f) != STBV_VORBIS_packet_id) return stbv_error(f, VORBIS_invalid_first_page); + if (!stbv_getn(f, header, 6)) return stbv_error(f, VORBIS_unexpected_eof); + if (!stbv_vorbis_validate(header)) return stbv_error(f, VORBIS_invalid_first_page); + // vorbis_version + if (stbv_get32(f) != 0) return stbv_error(f, VORBIS_invalid_first_page); + f->channels = stbv_get8(f); if (!f->channels) return stbv_error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return stbv_error(f, VORBIS_too_many_channels); + f->sample_rate = stbv_get32(f); if (!f->sample_rate) return stbv_error(f, VORBIS_invalid_first_page); + stbv_get32(f); // bitrate_maximum + stbv_get32(f); // bitrate_nominal + stbv_get32(f); // bitrate_minimum + x = stbv_get8(f); + { + int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return stbv_error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return stbv_error(f, VORBIS_invalid_setup); + if (log0 > log1) return stbv_error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = stbv_get8(f); + if (!(x & 1)) return stbv_error(f, VORBIS_invalid_first_page); + + // second packet! + if (!stbv_start_page(f)) return FALSE; + + if (!stbv_start_packet(f)) return FALSE; + do { + len = stbv_next_segment(f); + stbv_skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!stbv_start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (STBV_IS_PUSH_MODE(f)) { + if (!stbv_is_whole_packet_present(f, TRUE)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + stbv_crc32_init(); // always init it, to avoid multithread race conditions + + if (stbv_get8_packet(f) != STBV_VORBIS_packet_setup) return stbv_error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = stbv_get8_packet(f); + if (!stbv_vorbis_validate(header)) return stbv_error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = stbv_get_bits(f,8) + 1; + f->codebooks = (StbvCodebook *) stbv_setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return stbv_error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + stbv_uint32 *values; + int ordered, sorted_count; + int total=0; + stbv_uint8 *lengths; + StbvCodebook *c = f->codebooks+i; + STBV_CHECK(f); + x = stbv_get_bits(f, 8); if (x != 0x42) return stbv_error(f, VORBIS_invalid_setup); + x = stbv_get_bits(f, 8); if (x != 0x43) return stbv_error(f, VORBIS_invalid_setup); + x = stbv_get_bits(f, 8); if (x != 0x56) return stbv_error(f, VORBIS_invalid_setup); + x = stbv_get_bits(f, 8); + c->dimensions = (stbv_get_bits(f, 8)<<8) + x; + x = stbv_get_bits(f, 8); + y = stbv_get_bits(f, 8); + c->entries = (stbv_get_bits(f, 8)<<16) + (y<<8) + x; + ordered = stbv_get_bits(f,1); + c->sparse = ordered ? 0 : stbv_get_bits(f,1); + + if (c->dimensions == 0 && c->entries != 0) return stbv_error(f, VORBIS_invalid_setup); + + if (c->sparse) + lengths = (stbv_uint8 *) stbv_setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (stbv_uint8 *) stbv_setup_malloc(f, c->entries); + + if (!lengths) return stbv_error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = stbv_get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = stbv_get_bits(f, stbv_ilog(limit)); + if (current_entry + n > (int) c->entries) { return stbv_error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? stbv_get_bits(f,1) : 1; + if (present) { + lengths[j] = stbv_get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) + return stbv_error(f, VORBIS_invalid_setup); + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (stbv_uint8 *) stbv_setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return stbv_error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + stbv_setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + STBV_CHECK(f); + if (!c->sparse) { + c->codewords = (stbv_uint32 *) stbv_setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return stbv_error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (stbv_uint8 *) stbv_setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return stbv_error(f, VORBIS_outofmem); + c->codewords = (stbv_uint32 *) stbv_setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return stbv_error(f, VORBIS_outofmem); + values = (stbv_uint32 *) stbv_setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return stbv_error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!stbv_compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) stbv_setup_temp_free(f, values, 0); + return stbv_error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (stbv_uint32 *) stbv_setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return stbv_error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) stbv_setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return stbv_error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + stbv_compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + stbv_setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + stbv_setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + stbv_setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + stbv_compute_accelerated_huffman(c); + + STBV_CHECK(f); + c->lookup_type = stbv_get_bits(f, 4); + if (c->lookup_type > 2) return stbv_error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + stbv_uint16 *mults; + c->minimum_value = stbv_float32_unpack(stbv_get_bits(f, 32)); + c->delta_value = stbv_float32_unpack(stbv_get_bits(f, 32)); + c->value_bits = stbv_get_bits(f, 4)+1; + c->sequence_p = stbv_get_bits(f,1); + if (c->lookup_type == 1) { + c->lookup_values = stbv_lookup1_values(c->entries, c->dimensions); + } else { + c->lookup_values = c->entries * c->dimensions; + } + if (c->lookup_values == 0) return stbv_error(f, VORBIS_invalid_setup); + mults = (stbv_uint16 *) stbv_setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return stbv_error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = stbv_get_bits(f, c->value_bits); + if (q == STBV_EOP) { stbv_setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return stbv_error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last=0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto stbv_skip; + c->multiplicands = (stbv_codetype *) stbv_setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (stbv_codetype *) stbv_setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { stbv_setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return stbv_error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]; + val = mults[off]*c->delta_value + c->minimum_value + last; + c->multiplicands[j*c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k+1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int) c->lookup_values) { + stbv_setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + return stbv_error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } + else +#endif + { + float last=0; + STBV_CHECK(f); + c->multiplicands = (stbv_codetype *) stbv_setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { stbv_setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return stbv_error(f, VORBIS_outofmem); } + for (j=0; j < (int) c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + stbv_skip:; +#endif + stbv_setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); + + STBV_CHECK(f); + } + STBV_CHECK(f); + } + + // time domain transfers (notused) + + x = stbv_get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + stbv_uint32 z = stbv_get_bits(f, 16); + if (z != 0) return stbv_error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = stbv_get_bits(f, 6)+1; + f->floor_config = (StbvFloor *) stbv_setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return stbv_error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = stbv_get_bits(f, 16); + if (f->floor_types[i] > 1) return stbv_error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + StbvFloor0 *g = &f->floor_config[i].floor0; + g->order = stbv_get_bits(f,8); + g->rate = stbv_get_bits(f,16); + g->bark_map_size = stbv_get_bits(f,16); + g->amplitude_bits = stbv_get_bits(f,6); + g->amplitude_offset = stbv_get_bits(f,8); + g->number_of_books = stbv_get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = stbv_get_bits(f,8); + return stbv_error(f, VORBIS_feature_not_supported); + } else { + stbv_floor_ordering p[31*8+2]; + StbvFloor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = stbv_get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = stbv_get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = stbv_get_bits(f, 3)+1; + g->class_subclasses[j] = stbv_get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = stbv_get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return stbv_error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = stbv_get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return stbv_error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = stbv_get_bits(f,2)+1; + g->rangebits = stbv_get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = stbv_get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].id = j; + } + qsort(p, g->values, sizeof(p[0]), stbv_point_compare); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (stbv_uint8) p[j].id; + // precompute the stbv_neighbors + for (j=2; j < g->values; ++j) { + int low,hi; + stbv_neighbors(g->Xlist, j, &low,&hi); + g->stbv_neighbors[j][0] = low; + g->stbv_neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // StbvResidue + f->residue_count = stbv_get_bits(f, 6)+1; + f->residue_config = (StbvResidue *) stbv_setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return stbv_error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + stbv_uint8 residue_cascade[64]; + StbvResidue *r = f->residue_config+i; + f->residue_types[i] = stbv_get_bits(f, 16); + if (f->residue_types[i] > 2) return stbv_error(f, VORBIS_invalid_setup); + r->begin = stbv_get_bits(f, 24); + r->end = stbv_get_bits(f, 24); + if (r->end < r->begin) return stbv_error(f, VORBIS_invalid_setup); + r->part_size = stbv_get_bits(f,24)+1; + r->classifications = stbv_get_bits(f,6)+1; + r->classbook = stbv_get_bits(f,8); + if (r->classbook >= f->codebook_count) return stbv_error(f, VORBIS_invalid_setup); + for (j=0; j < r->classifications; ++j) { + stbv_uint8 high_bits=0; + stbv_uint8 low_bits=stbv_get_bits(f,3); + if (stbv_get_bits(f,1)) + high_bits = stbv_get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) stbv_setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return stbv_error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = stbv_get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return stbv_error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (stbv_uint8 **) stbv_setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return stbv_error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (stbv_uint8 *) stbv_setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return stbv_error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = stbv_get_bits(f,6)+1; + f->mapping = (StbvMapping *) stbv_setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return stbv_error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + StbvMapping *m = f->mapping + i; + int mapping_type = stbv_get_bits(f,16); + if (mapping_type != 0) return stbv_error(f, VORBIS_invalid_setup); + m->chan = (StbvMappingChannel *) stbv_setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return stbv_error(f, VORBIS_outofmem); + if (stbv_get_bits(f,1)) + m->submaps = stbv_get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (stbv_get_bits(f,1)) { + m->coupling_steps = stbv_get_bits(f,8)+1; + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = stbv_get_bits(f, stbv_ilog(f->channels-1)); + m->chan[k].angle = stbv_get_bits(f, stbv_ilog(f->channels-1)); + if (m->chan[k].magnitude >= f->channels) return stbv_error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return stbv_error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return stbv_error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (stbv_get_bits(f,2)) return stbv_error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = stbv_get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return stbv_error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + stbv_get_bits(f,8); // discard + m->submap_floor[j] = stbv_get_bits(f,8); + m->submap_residue[j] = stbv_get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return stbv_error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return stbv_error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = stbv_get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + StbvMode *m = f->mode_config+i; + m->blockflag = stbv_get_bits(f,1); + m->windowtype = stbv_get_bits(f,16); + m->transformtype = stbv_get_bits(f,16); + m->mapping = stbv_get_bits(f,8); + if (m->windowtype != 0) return stbv_error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return stbv_error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return stbv_error(f, VORBIS_invalid_setup); + } + + stbv_flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) stbv_setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) stbv_setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (stbv_int16 *) stbv_setup_malloc(f, sizeof(stbv_int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return stbv_error(f, VORBIS_outofmem); + memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) stbv_setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return stbv_error(f, VORBIS_outofmem); + #endif + } + + if (!stbv_init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!stbv_init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (stbv_integer_divide_table[1][1]==0) + for (i=0; i < STBV_DIVTAB_NUMER; ++i) + for (j=1; j < STBV_DIVTAB_DENOM; ++j) + stbv_integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + stbv_uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + stbv_uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + StbvResidue *r = f->residue_config + i; + unsigned int actual_size = f->blocksize_1 / 2; + unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; + unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(stbv_uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + // maximum reasonable partition size is f->blocksize_1 + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + f->first_decode = TRUE; + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return stbv_error(f, VORBIS_outofmem); + } + + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + + return TRUE; +} + +static void stbv_vorbis_deinit(stb_vorbis *p) +{ + int i,j; + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + StbvResidue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + stbv_setup_free(p, r->classdata[j]); + stbv_setup_free(p, r->classdata); + } + stbv_setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + STBV_CHECK(p); + for (i=0; i < p->codebook_count; ++i) { + StbvCodebook *c = p->codebooks + i; + stbv_setup_free(p, c->codeword_lengths); + stbv_setup_free(p, c->multiplicands); + stbv_setup_free(p, c->codewords); + stbv_setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + stbv_setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + stbv_setup_free(p, p->codebooks); + } + stbv_setup_free(p, p->floor_config); + stbv_setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + stbv_setup_free(p, p->mapping[i].chan); + stbv_setup_free(p, p->mapping); + } + STBV_CHECK(p); + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + stbv_setup_free(p, p->channel_buffers[i]); + stbv_setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + stbv_setup_free(p, p->floor_buffers[i]); + #endif + stbv_setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + stbv_setup_free(p, p->A[i]); + stbv_setup_free(p, p->B[i]); + stbv_setup_free(p, p->C[i]); + stbv_setup_free(p, p->window[i]); + stbv_setup_free(p, p->stbv_bit_reverse[i]); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +STBVDEF void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + stbv_vorbis_deinit(p); + stbv_setup_free(p,p); +} + +static void stbv_vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +STBVDEF int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +STBVDEF stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +STBVDEF int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * stbv_vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) stbv_setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +STBVDEF void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int stbv_vorbis_search_for_page_pushdata(stbv_vorb *f, stbv_uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, stbv_ogg_page_header, 4)) { + int j,len; + stbv_uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = stbv_crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = stbv_crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + stbv_uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = stbv_crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +STBVDEF int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + const stbv_uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!STBV_IS_PUSH_MODE(f)) return stbv_error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return stbv_vorbis_search_for_page_pushdata(f, (stbv_uint8 *) data, data_len); + } + + f->stream = (stbv_uint8 *) data; + f->stream_end = (stbv_uint8 *) data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!stbv_is_whole_packet_present(f, FALSE)) { + *samples = 0; + return 0; + } + + if (!stbv_vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (stbv_get8_packet(f) != STBV_EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (stbv_get8_packet(f) != STBV_EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = stbv_vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int) (f->stream - data); +} + +STBVDEF stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + stbv_vorbis_init(&p, alloc); + p.stream = (stbv_uint8 *) data; + p.stream_end = (stbv_uint8 *) data + data_len; + p.push_mode = TRUE; + if (!stbv_start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + return NULL; + } + f = stbv_vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int) (f->stream - data); + *error = 0; + return f; + } else { + stbv_vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +STBVDEF unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (STBV_USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + #ifndef STB_VORBIS_NO_STDIO + return (unsigned int) (ftell(f->f) - f->f_start); + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static stbv_uint32 stbv_vorbis_find_page(stb_vorbis *f, stbv_uint32 *end, stbv_uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = stbv_get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (stbv_get8(f) != stbv_ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + stbv_uint8 header[27]; + stbv_uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = stbv_ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = stbv_get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = stbv_crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = stbv_get8(f); + crc = stbv_crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = stbv_crc32_update(crc, stbv_get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + stbv_set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + stbv_set_file_offset(f, retry_loc); + } + } +} + + +#define STBV_SAMPLE_unknown 0xffffffff + +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int stbv_get_seek_page_info(stb_vorbis *f, StbvProbedPage *z) +{ + stbv_uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + stbv_getn(f, header, 27); + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + stbv_getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + stbv_set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceeding page while finding the +// start of a packet +static int stbv_go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe, end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + stbv_set_file_offset(f, previous_safe); + + while (stbv_vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + stbv_set_file_offset(f, end); + } + + return 0; +} + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int stbv_seek_to_sample_coarse(stb_vorbis *f, stbv_uint32 sample_number) +{ + StbvProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + stbv_uint32 delta, stream_length, padding; + double offset, bytes_per_sample; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return stbv_error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return stbv_error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + sample_number = 0; + else + sample_number -= padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + stbv_set_file_offset(f, left.page_end); + if (!stbv_get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (sample_number <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) + return 1; + return 0; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + stbv_set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + stbv_set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + stbv_set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!stbv_vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!stbv_get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + stbv_set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough. + if (mid.page_start == right.page_start) + break; + + if (sample_number < mid.last_decoded_sample) + right = mid; + else + left = mid; + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + stbv_set_file_offset(f, page_start); + if (!stbv_start_page(f)) return stbv_error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & STBV_PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!stbv_go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!stbv_start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + stbv_skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + if (!stbv_vorbis_pump_first_frame(f)) + return 0; + if (f->current_loc > sample_number) + return stbv_error(f, VORBIS_seek_failed); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return stbv_error(f, VORBIS_seek_failed); +} + +// the same as stbv_vorbis_decode_initial, but without advancing +static int stbv_peek_decode_initial(stbv_vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!stbv_vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + stbv_ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + stbv_skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +STBVDEF int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + stbv_uint32 max_frame_samples; + + if (STBV_IS_PUSH_MODE(f)) return stbv_error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!stbv_seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!stbv_peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return stbv_error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + stbv_vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + stbv_maybe_start_packet(f); + stbv_flush_packet(f); + } + } + // the next frame will start with the sample + assert(f->current_loc == sample_number); + return 1; +} + +STBVDEF int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + stbv_uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +STBVDEF int stb_vorbis_seek_start(stb_vorbis *f) +{ + if (STBV_IS_PUSH_MODE(f)) { return stbv_error(f, VORBIS_invalid_api_mixing); } + stbv_set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + return stbv_vorbis_pump_first_frame(f); +} + +STBVDEF unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (STBV_IS_PUSH_MODE(f)) return stbv_error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + unsigned int last; + stbv_uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + stbv_set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!stbv_vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + stbv_set_file_offset(f, end); + if (!stbv_vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + previous_safe = last_page_loc+1; + last_page_loc = stb_vorbis_get_file_offset(f); + } + + stbv_set_file_offset(f, last_page_loc); + + // parse the header + stbv_getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = stbv_get32(f); + hi = stbv_get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = STBV_SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + stbv_set_file_offset(f, restore_offset); + } + return f->total_samples == STBV_SAMPLE_unknown ? 0 : f->total_samples; +} + +STBVDEF float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +STBVDEF int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (STBV_IS_PUSH_MODE(f)) return stbv_error(f, VORBIS_invalid_api_mixing); + + if (!stbv_vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = stbv_vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +STBVDEF stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + stbv_vorbis_init(&p, alloc); + p.f = file; + p.f_start = (stbv_uint32) ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (stbv_start_decoder(&p)) { + f = stbv_vorbis_alloc(&p); + if (f) { + *f = p; + stbv_vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + stbv_vorbis_deinit(&p); + return NULL; +} + +STBVDEF stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = (unsigned int) ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int) (ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +STBVDEF stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +{ + FILE *f = fopen(filename, "rb"); + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +STBVDEF stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (data == NULL) return NULL; + stbv_vorbis_init(&p, alloc); + p.stream = (stbv_uint8 *) data; + p.stream_end = (stbv_uint8 *) data + len; + p.stream_start = (stbv_uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (stbv_start_decoder(&p)) { + f = stbv_vorbis_alloc(&p); + if (f) { + *f = p; + stbv_vorbis_pump_first_frame(f); + if (error) *error = VORBIS__no_error; + return f; + } + } + if (error) *error = p.error; + stbv_vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define STBV_PLAYBACK_MONO 1 +#define STBV_PLAYBACK_LEFT 2 +#define STBV_PLAYBACK_RIGHT 4 + +#define STBV_L (STBV_PLAYBACK_LEFT | STBV_PLAYBACK_MONO) +#define STBV_C (STBV_PLAYBACK_LEFT | STBV_PLAYBACK_RIGHT | STBV_PLAYBACK_MONO) +#define STBV_R (STBV_PLAYBACK_RIGHT | STBV_PLAYBACK_MONO) + +static stbv_int8 stbv_channel_position[7][6] = +{ + { 0 }, + { STBV_C }, + { STBV_L, STBV_R }, + { STBV_L, STBV_C, STBV_R }, + { STBV_L, STBV_R, STBV_L, STBV_R }, + { STBV_L, STBV_C, STBV_R, STBV_L, STBV_R }, + { STBV_L, STBV_C, STBV_R, STBV_L, STBV_R, STBV_C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } stbv_float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define STBV_FASTDEF(x) stbv_float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define STBV_MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define STBV_ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define STBV_FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + STBV_MAGIC(s), temp.i - STBV_ADDEND(s)) + #define stbv_check_endianness() +#else + #define STBV_FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define stbv_check_endianness() + #define STBV_FASTDEF(x) +#endif + +static void stbv_copy_samples(short *dest, float *src, int len) +{ + int i; + stbv_check_endianness(); + for (i=0; i < len; ++i) { + STBV_FASTDEF(temp); + int v = STBV_FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void stbv_compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE; + stbv_check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (stbv_channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + STBV_FASTDEF(temp); + int v = STBV_FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } +} + +static void stbv_compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE >> 1; + // o is the offset in the source data + stbv_check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = stbv_channel_position[num_c][j] & (STBV_PLAYBACK_LEFT | STBV_PLAYBACK_RIGHT); + if (m == (STBV_PLAYBACK_LEFT | STBV_PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == STBV_PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == STBV_PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + STBV_FASTDEF(temp); + int v = STBV_FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } +} + +static void stbv_convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {STBV_PLAYBACK_MONO}, {STBV_PLAYBACK_LEFT, STBV_PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + stbv_compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + stbv_copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +STBVDEF int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + stbv_convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void stbv_convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + stbv_check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + stbv_compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + STBV_FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = STBV_FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +STBVDEF int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + stbv_convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +STBVDEF int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + stbv_convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +STBVDEF int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + stbv_convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +STBVDEF int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +STBVDEF int stb_vorbis_decode_memory(const stbv_uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +STBVDEF int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +STBVDEF int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files + 1.11 - 2017-07-23 - fix MinGW compilation + 1.10 - 2017-03-03 - more robust seeking; fix negative stbv_ilog(); clear error in open_memory + 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; + avoid discarding last frame of audio data + 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API + some more crash fixes when out of memory or with corrupt files + 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015-04-19 - don't define __forceinline if it's redundant + 1.04 - 2014-08-27 - fix missing const-correct case in API + 1.03 - 2014-08-07 - Warning fixes + 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float + 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_IMPLEMENTATION + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ From 92f68ac6be5b85b85ebab64841c8322405e08d51 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 24 Dec 2018 14:09:51 +0100 Subject: [PATCH 02/58] Review DrawPolyEx() Also reviewed rlCheckBufferLimit() --- src/raylib.h | 4 ++-- src/rlgl.h | 4 ++-- src/shapes.c | 31 +++++++------------------------ 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 430e66db..023d35c7 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -470,7 +470,7 @@ typedef enum { KEY_X = 88, KEY_Y = 89, KEY_Z = 90, - + // Function keys KEY_SPACE = 32, KEY_ESCAPE = 256, @@ -517,7 +517,7 @@ typedef enum { KEY_BACKSLASH = 92, KEY_RIGHT_BRACKET = 93, KEY_GRAVE = 96, - + // Keypad keys KEY_KP_0 = 320, KEY_KP_1 = 321, diff --git a/src/rlgl.h b/src/rlgl.h index e1b9a98b..8b229e9c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -435,7 +435,7 @@ void rlglClose(void); // De-inititialize rlgl (buffers void rlglDraw(void); // Update and draw default internal buffers int rlGetVersion(void); // Returns current OpenGL version -bool rlCheckBufferLimit(int type, int vCount); // Check internal buffer overflow for a given number of vertex +bool rlCheckBufferLimit(int vCount); // Check internal buffer overflow for a given number of vertex void rlSetDebugMarker(const char *text); // Set debug marker for analysis void rlLoadExtensions(void *loader); // Load OpenGL extensions Vector3 rlUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates @@ -1738,7 +1738,7 @@ int rlGetVersion(void) } // Check internal buffer overflow for a given number of vertex -bool rlCheckBufferLimit(int type, int vCount) +bool rlCheckBufferLimit(int vCount) { bool overflow = false; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) diff --git a/src/shapes.c b/src/shapes.c index 80808526..7a6d1d49 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -186,7 +186,7 @@ void DrawCircle(int centerX, int centerY, float radius, Color color) // NOTE: Gradient goes from center (color1) to border (color2) void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2) { - if (rlCheckBufferLimit(RL_TRIANGLES, 3*36)) rlglDraw(); + if (rlCheckBufferLimit(3*36)) rlglDraw(); rlBegin(RL_TRIANGLES); for (int i = 0; i < 360; i += 10) @@ -206,7 +206,7 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co void DrawCircleV(Vector2 center, float radius, Color color) { #if defined(SUPPORT_QUADS_DRAW_MODE) - if (rlCheckBufferLimit(RL_QUADS, 4*(36/2))) rlglDraw(); + if (rlCheckBufferLimit(4*(36/2))) rlglDraw(); rlEnableTexture(GetShapesTexture().id); @@ -231,7 +231,7 @@ void DrawCircleV(Vector2 center, float radius, Color color) rlDisableTexture(); #else - if (rlCheckBufferLimit(RL_TRIANGLES, 3*(36/2))) rlglDraw(); + if (rlCheckBufferLimit(3*(36/2))) rlglDraw(); rlBegin(RL_TRIANGLES); for (int i = 0; i < 360; i += 10) @@ -249,7 +249,7 @@ void DrawCircleV(Vector2 center, float radius, Color color) // Draw circle outline void DrawCircleLines(int centerX, int centerY, float radius, Color color) { - if (rlCheckBufferLimit(RL_LINES, 2*36)) rlglDraw(); + if (rlCheckBufferLimit(2*36)) rlglDraw(); rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -440,7 +440,7 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col { if (sides < 3) sides = 3; - if (rlCheckBufferLimit(RL_QUADS, 4*(360/sides))) rlglDraw(); + if (rlCheckBufferLimit(4*(360/sides))) rlglDraw(); rlPushMatrix(); rlTranslatef(center.x, center.y, 0.0); @@ -488,24 +488,8 @@ void DrawPolyEx(Vector2 *points, int pointsCount, Color color) { if (pointsCount >= 3) { - if (rlCheckBufferLimit(RL_QUADS, pointsCount)) rlglDraw(); + if (rlCheckBufferLimit(pointsCount)) rlglDraw(); -#if defined(SUPPORT_QUADS_DRAW_MODE) - rlEnableTexture(GetShapesTexture().id); - - rlBegin(RL_QUADS); - rlColor4ub(color.r, color.g, color.b, color.a); - - for (int i = 1; i < pointsCount - 1; i++) - { - rlVertex2f(points[0].x, points[0].y); - rlVertex2f(points[i].x, points[i].y); - rlVertex2f(points[i].x, points[i].y); - rlVertex2f(points[i + 1].x, points[i + 1].y); - } - rlEnd(); - rlDisableTexture(); -#else rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -516,7 +500,6 @@ void DrawPolyEx(Vector2 *points, int pointsCount, Color color) rlVertex2f(points[i + 1].x, points[i + 1].y); } rlEnd(); -#endif } } @@ -525,7 +508,7 @@ void DrawPolyExLines(Vector2 *points, int pointsCount, Color color) { if (pointsCount >= 2) { - if (rlCheckBufferLimit(RL_LINES, pointsCount)) rlglDraw(); + if (rlCheckBufferLimit(pointsCount)) rlglDraw(); rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); From 47358fe5ce50a7dfae2db6cd3c3d86ad93eec152 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 24 Dec 2018 17:09:46 +0100 Subject: [PATCH 03/58] Tweaks to support OpenGL ES 2.0 desktop --- src/core.c | 31 +++++++++++++++++++------------ src/rlgl.h | 14 +++----------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/core.c b/src/core.c index 79404abe..1b975214 100644 --- a/src/core.c +++ b/src/core.c @@ -2247,11 +2247,11 @@ static bool InitGraphicsDevice(int width, int height) //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers // Check some Window creation flags - if (configFlags & FLAG_WINDOW_RESIZABLE) glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // Resizable window - else glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); // Avoid window being resizable + if (configFlags & FLAG_WINDOW_RESIZABLE) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window + else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable - if (configFlags & FLAG_WINDOW_UNDECORATED) glfwWindowHint(GLFW_DECORATED, GL_FALSE); // Border and buttons on Window - else glfwWindowHint(GLFW_DECORATED, GL_TRUE); // Decorated window + if (configFlags & FLAG_WINDOW_UNDECORATED) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window + else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window // FLAG_WINDOW_TRANSPARENT not supported on HTML5 and not included in any released GLFW version yet #if defined(GLFW_TRANSPARENT_FRAMEBUFFER) if (configFlags & FLAG_WINDOW_TRANSPARENT) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer @@ -2267,21 +2267,28 @@ static bool InitGraphicsDevice(int width, int height) // Check selection OpenGL version if (rlGetVersion() == OPENGL_21) { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) } else if (rlGetVersion() == OPENGL_33) { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above! // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE #if defined(__APPLE__) - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // OSX Requires fordward compatibility + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires fordward compatibility #else - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE); // Fordward Compatibility Hint: Only 3.3 and above! + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Fordward Compatibility Hint: Only 3.3 and above! #endif - //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); // Request OpenGL DEBUG context + //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context + } + else if (rlGetVersion() == OPENGL_ES_20) // Request OpenGL ES 2.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); // Alternative: GLFW_EGL_CONTEXT_API (ANGLE) } if (fullscreen) @@ -3145,7 +3152,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i { if (key == exitKey && action == GLFW_PRESS) { - glfwSetWindowShouldClose(window, GL_TRUE); + glfwSetWindowShouldClose(window, GLFW_TRUE); // NOTE: Before closing window, while loop must be left! } diff --git a/src/rlgl.h b/src/rlgl.h index 8b229e9c..197ecb19 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -165,14 +165,6 @@ typedef unsigned char byte; unsigned char a; } Color; - // Rectangle type - typedef struct Rectangle { - int x; - int y; - int width; - int height; - } Rectangle; - // Texture2D type // NOTE: Data stored in GPU memory typedef struct Texture2D { @@ -703,9 +695,9 @@ typedef struct DrawCall { typedef struct VrStereoConfig { RenderTexture2D stereoFbo; // VR stereo rendering framebuffer Shader distortionShader; // VR stereo rendering distortion shader - Rectangle eyesViewport[2]; // VR stereo rendering eyes viewports Matrix eyesProjection[2]; // VR stereo rendering eyes projection matrices Matrix eyesViewOffset[2]; // VR stereo rendering eyes view offset matrices + int eyesViewport[2][4]; // VR stereo rendering eyes viewports [x, y, w, h] } VrStereoConfig; #endif @@ -4268,8 +4260,8 @@ static void SetStereoConfig(VrDeviceInfo hmd) vrConfig.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f); // Compute eyes Viewports - vrConfig.eyesViewport[0] = (Rectangle){ 0.0f, 0.0f, (float)hmd.hResolution/2, (float)hmd.vResolution }; - vrConfig.eyesViewport[1] = (Rectangle){ hmd.hResolution/2.0f, 0.0f, (float)hmd.hResolution/2, (float) hmd.vResolution }; + //vrConfig.eyesViewport[0] = { 0.0f, 0.0f, (float)hmd.hResolution/2, (float)hmd.vResolution }; + //vrConfig.eyesViewport[1] = { hmd.hResolution/2.0f, 0.0f, (float)hmd.hResolution/2, (float) hmd.vResolution }; } // Set internal projection and modelview matrix depending on eyes tracking data From 7fb2459916a36b8bc13774163005ddd9ccd3950b Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 24 Dec 2018 17:45:34 +0100 Subject: [PATCH 04/58] Added some TODO note OpenGL extensions loading could be improved... --- src/rlgl.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 197ecb19..5c14f9be 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1468,10 +1468,10 @@ void rlglInit(int width, int height) //for (int i = 0; i < numComp; i++) TraceLog(LOG_INFO, "Supported compressed format: 0x%x", format[i]); // NOTE: We don't need that much data on screen... right now... - -#if defined(GRAPHICS_API_OPENGL_11) - //TraceLog(LOG_INFO, "OpenGL 1.1 (or driver default) profile initialized"); -#endif + + // TODO: Automatize extensions loading using rlLoadExtensions() and GLAD + // Actually, when rlglInit() is called in InitWindow() in core.c, + // OpenGL required extensions have already been loaded (PLATFORM_DESKTOP) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Get supported extensions list @@ -1552,7 +1552,6 @@ void rlglInit(int width, int height) // Check texture float support if (strcmp(extList[i], (const char *)"GL_OES_texture_float") == 0) texFloatSupported = true; #endif - // DDS texture compression support if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) || (strcmp(extList[i], (const char *)"GL_WEBGL_compressed_texture_s3tc") == 0) || @@ -1608,6 +1607,8 @@ void rlglInit(int width, int height) if (debugMarkerSupported) TraceLog(LOG_INFO, "[EXTENSION] Debug Marker supported"); + + // Initialize buffers, default shaders and default textures //---------------------------------------------------------- From 1982eabe6eb1501362c62ee12052c24b0273eb13 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 24 Dec 2018 17:46:23 +0100 Subject: [PATCH 05/58] Some code tweaks --- games/transmission/screens/screen_ending.c | 64 ++++---- games/transmission/screens/screen_gameplay.c | 157 +++++++++---------- games/transmission/screens/screen_logo.c | 11 +- games/transmission/screens/screen_mission.c | 138 ++++++++-------- 4 files changed, 177 insertions(+), 193 deletions(-) diff --git a/games/transmission/screens/screen_ending.c b/games/transmission/screens/screen_ending.c index d0cf7d89..620fc30b 100644 --- a/games/transmission/screens/screen_ending.c +++ b/games/transmission/screens/screen_ending.c @@ -35,13 +35,13 @@ //---------------------------------------------------------------------------------- // Global Variables Definition (local to this module) //---------------------------------------------------------------------------------- -static char *codingWords[MAX_CODING_WORDS] = { - "pollo\0", - "conejo\0", +static char *codingWords[MAX_CODING_WORDS] = { + "pollo\0", + "conejo\0", "huevo\0", - "nido\0", - "aire\0", - "armario\0", + "nido\0", + "aire\0", + "armario\0", "agujero\0", "platano\0", "pastel\0", @@ -82,21 +82,21 @@ void InitEndingScreen(void) { framesCounter = 0; finishScreen = 0; - + rotation = 0.1f; scale = 0.05f; state = 0; - + texBackground = LoadTexture("resources/textures/ending_background.png"); texVignette = LoadTexture("resources/textures/message_vignette.png"); fxNews = LoadSound("resources/audio/fx_batman.ogg"); - + missions = LoadMissions("resources/missions.txt"); int wordsCount = missions[currentMission].wordsCount; strcpy(headline, missions[currentMission].msg); // Base headline int len = strlen(headline); - + // Remove @ from headline // TODO: Also remove additional spaces for (int i = 0; i < len; i++) @@ -110,7 +110,7 @@ void InitEndingScreen(void) { // WARNING: It fails if the last sentence word has a '.' after space char *title = StringReplace(headline, messageWords[i].text, codingWords[messageWords[i].id]); - + if (title != NULL) { strcpy(headline, title); // Base headline updated @@ -118,14 +118,14 @@ void InitEndingScreen(void) } } } - + TraceLog(LOG_WARNING, "Titular: %s", headline); - + // Generate newspaper with title and subtitle Image imNewspaper = LoadImage("resources/textures/ending_newspaper.png"); fontNews = LoadFontEx("resources/fonts/Lora-Bold.ttf", 32, 250, 0); ImageDrawTextEx(&imNewspaper, (Vector2){ 50, 220 }, fontNews, headline, fontNews.baseSize, 0, DARKGRAY); - + texNewspaper = LoadTextureFromImage(imNewspaper); //UnloadFont(fontNews); UnloadImage(imNewspaper); @@ -135,25 +135,25 @@ void InitEndingScreen(void) void UpdateEndingScreen(void) { framesCounter++; - + if (framesCounter == 10) PlaySound(fxNews); - + if (state == 0) { rotation += 18.0f; scale += 0.0096f; - - if (scale >= 1.0f) + + if (scale >= 1.0f) { scale = 1.0f; state = 1; } } - - if ((state == 1) && (IsKeyPressed(KEY_ENTER) || IsButtonPressed())) + + if ((state == 1) && (IsKeyPressed(KEY_ENTER) || IsButtonPressed())) { currentMission++; - + if (currentMission >= totalMissions) finishScreen = 2; else finishScreen = 1; } @@ -163,16 +163,16 @@ void UpdateEndingScreen(void) void DrawEndingScreen(void) { DrawTexture(texBackground, 0, 0, WHITE); - + DrawTexturePro(texNewspaper, (Rectangle){ 0, 0, texNewspaper.width, texNewspaper.height }, - (Rectangle){ GetScreenWidth()/2, GetScreenHeight()/2, texNewspaper.width*scale, texNewspaper.height*scale }, + (Rectangle){ GetScreenWidth()/2, GetScreenHeight()/2, texNewspaper.width*scale, texNewspaper.height*scale }, (Vector2){ (float)texNewspaper.width*scale/2, (float)texNewspaper.height*scale/2 }, rotation, WHITE); DrawTextureEx(texVignette, (Vector2){ 0, 0 }, 0.0f, 2.0f, WHITE); // Draw debug information DrawTextEx(fontNews, headline, (Vector2){ 10, 10 }, fontNews.baseSize, 0, RAYWHITE); - + for (int i = 0; i < missions[currentMission].wordsCount; i++) { DrawText(codingWords[messageWords[i].id], 10, 60 + 30*i, 20, (messageWords[i].id == missions[currentMission].sols[i]) ? GREEN : RED); @@ -187,9 +187,9 @@ void UnloadEndingScreen(void) UnloadTexture(texBackground); UnloadTexture(texNewspaper); UnloadTexture(texVignette); - + UnloadSound(fxNews); - free(missions); + free(missions); } // Ending Screen should finish? @@ -213,16 +213,16 @@ static char *StringReplace(char *orig, char *rep, char *with) // Sanity checks and initialization if (!orig || !rep) return NULL; - + len_rep = strlen(rep); if (len_rep == 0) return NULL; // Empty rep causes infinite loop during count - + if (!with) with = ""; // Replace with nothing if not provided len_with = strlen(with); // Count the number of replacements needed ins = orig; - for (count = 0; tmp = strstr(ins, rep); ++count) + for (count = 0; tmp = strstr(ins, rep); ++count) { ins = tmp + len_rep; } @@ -235,7 +235,7 @@ static char *StringReplace(char *orig, char *rep, char *with) // tmp points to the end of the result string // ins points to the next occurrence of rep in orig // orig points to the remainder of orig after "end of rep" - while (count--) + while (count--) { ins = strstr(orig, rep); len_front = ins - orig; @@ -243,8 +243,8 @@ static char *StringReplace(char *orig, char *rep, char *with) tmp = strcpy(tmp, with) + len_with; orig += len_front + len_rep; // move to next "end of rep" } - + strcpy(tmp, orig); - + return result; } diff --git a/games/transmission/screens/screen_gameplay.c b/games/transmission/screens/screen_gameplay.c index 0594e10f..aeec98ca 100644 --- a/games/transmission/screens/screen_gameplay.c +++ b/games/transmission/screens/screen_gameplay.c @@ -35,9 +35,9 @@ #define MAX_LINE_CHAR 30 /* -// NOTE: Coding words are generic and the same words -// are used for all missions, -typedef enum CodingWords { +// NOTE: Coding words are generic and the same words +// are used for all missions, +typedef enum CodingWords { POLLO = 0, CONEJO, HUEVO, @@ -50,13 +50,13 @@ typedef enum CodingWords { } CodingWords; */ -static char *codingWords[MAX_CODING_WORDS] = { - "pollo\0", - "conejo\0", +static char *codingWords[MAX_CODING_WORDS] = { + "pollo\0", + "conejo\0", "huevo\0", - "nido\0", - "aire\0", - "armario\0", + "nido\0", + "aire\0", + "armario\0", "agujero\0", "platano\0", "pastel\0", @@ -125,19 +125,19 @@ void InitGameplayScreen(void) { framesCounter = 0; finishScreen = 0; - + fontMessage = LoadFontEx("resources/fonts/traveling_typewriter.ttf", 30, 250, 0); - + texBackground = LoadTexture("resources/textures/message_background.png"); texVignette = LoadTexture("resources/textures/message_vignette.png"); - + fxGrab = LoadSound("resources/audio/fx_grab.ogg"); fxPlace = LoadSound("resources/audio/fx_place.ogg"); fxLeave = LoadSound("resources/audio/fx_leave.ogg"); - + musSpy = LoadMusicStream("resources/audio/s_p_y.xm"); PlayMusicStream(musSpy); - + #if defined(PLATFORM_WEB) #define WORD_ATLAS_FROM_FILE #endif @@ -147,20 +147,20 @@ void InitGameplayScreen(void) // Generate coding words atlas directly from text Image imWordsBase = LoadImage("resources/textures/words_base.png"); Image imWords = GenImageColor(imWordsBase.width, imWordsBase.height*MAX_CODING_WORDS, WHITE); - + for (int i = 0; i < MAX_CODING_WORDS; i++) { - ImageDraw(&imWords, imWordsBase, + ImageDraw(&imWords, imWordsBase, (Rectangle){ 0, 0, imWordsBase.width, imWordsBase.height }, (Rectangle){ 0, imWordsBase.height*i, imWordsBase.width, imWordsBase.height }); - - ImageDrawTextEx(&imWords,(Vector2){ imWordsBase.width/2 - MeasureTextEx(fontMessage, codingWords[i], - fontMessage.baseSize, 0).x/2, imWordsBase.height*i }, fontMessage, codingWords[i], - fontMessage.baseSize, 0, BLACK); + + ImageDrawTextEx(&imWords,(Vector2){ imWordsBase.width/2 - MeasureTextEx(fontMessage, codingWords[i], + fontMessage.baseSize, 0).x/2, imWordsBase.height*i }, fontMessage, codingWords[i], + fontMessage.baseSize, 0, BLACK); } - + texWordsAtlas = LoadTextureFromImage(imWords); - + UnloadImage(imWordsBase); UnloadImage(imWords); #endif @@ -183,13 +183,13 @@ void InitGameplayScreen(void) words[i].iniRec = words[i].rec; words[i].hover = false; // Mouse hover detected words[i].picked = false; // Mouse picked - + //words[i].text = ''; //codingWords[i]; // Fill text if required... } - + // Analize missions[currentMission].msg string for words! int msgLen = strlen(missions[currentMission].msg); - + // Add '/' each MAX_LINE_CHAR chars int currentLine = 1; int i = currentLine * MAX_LINE_CHAR; @@ -203,39 +203,35 @@ void InitGameplayScreen(void) i = currentLine*MAX_LINE_CHAR; } else i++; - } - + } + int currentWord = 0; int offsetX = 0; int offsetY = 0; bool foundWord = false; int wordInitPosX = 0; int wordInitPosY = 0; - - // TODO: messageWords should be reseted every mission - //memcpy(messageWords, 0, sizeof(Word)*MAX_MISSION_WORDS); for (int i = 0; i < msgLen; i++) - { + { char c = missions[currentMission].msg[i]; if (foundWord && (c == ' ' || c == '.')) { foundWord = false; - + messageWords[currentWord - 1].rec.width = (int)MeasureTextEx(fontMessage, SubText(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), 30, 0).x; messageWords[currentWord - 1].rec.height = fontMessage.baseSize; - - //TODO: Guardar en message + strncpy(messageWords[currentWord - 1].text, SubText(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), i - wordInitPosX); } - + if (c == '@') // One word to change { foundWord = true; missions[currentMission].msg[i] = ' '; offsetX = (int)MeasureTextEx(fontMessage, SubText(missions[currentMission].msg, wordInitPosY, (i + 1) - wordInitPosY), 30, 0).x; - + messageWords[currentWord].rec.x = offsetX; messageWords[currentWord].rec.y = offsetY; @@ -254,16 +250,16 @@ void InitGameplayScreen(void) for (int i = 0; i < missions[currentMission].wordsCount; i++) { messageWords[i].id = -1; // Not required for message words, id is the array position - + // Recalculate words rectangles considering text offset on screen messageWords[i].rec.x += msgOffset.x; messageWords[i].rec.y += msgOffset.y; - + // Recalculate words rectangle considering new width height messageWords[i].rec.x -= (texWordsAtlas.width - messageWords[i].rec.width)/2; messageWords[i].rec.y -= ((texWordsAtlas.height / MAX_CODING_WORDS) - messageWords[i].rec.height)/2; - - //Recalculate width height + + //Recalculate width height messageWords[i].rec.width = texWordsAtlas.width; messageWords[i].rec.height = texWordsAtlas.height / MAX_CODING_WORDS; @@ -276,13 +272,13 @@ void InitGameplayScreen(void) void UpdateGameplayScreen(void) { UpdateMusicStream(musSpy); - + for (int i = 0; i < MAX_CODING_WORDS; i++) { if (CheckCollisionPointRec(GetMousePosition(), words[i].rec)) { words[i].hover = true; - + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { words[i].picked = true; @@ -290,41 +286,41 @@ void UpdateGameplayScreen(void) } } else words[i].hover = false; - - + + if (words[i].picked) - { + { for (int j = 0; j < missions[currentMission].wordsCount; j++) - { + { if (CheckCollisionPointRec(GetMousePosition(), messageWords[j].rec)) messageWords[j].hover = true; else messageWords[j].hover = false; } - + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) - { + { words[i].picked = false; - + for (int j = 0; j < missions[currentMission].wordsCount; j++) - { + { messageWords[j].hover = false; - + if (CheckCollisionPointRec(GetMousePosition(), messageWords[j].rec)) { PlaySound(fxPlace); - + words[i].rec.x = messageWords[j].rec.x; words[i].rec.y = messageWords[j].rec.y; - + if (messageWords[j].id != -1) { int id = messageWords[j].id; words[id].rec = words[id].iniRec; } - + messageWords[j].id = i; for (int k = 0; k < missions[currentMission].wordsCount; k++) { - if (j != k && messageWords[j].id == messageWords[k].id) + if (j != k && messageWords[j].id == messageWords[k].id) { messageWords[k].id = -1; break; @@ -332,60 +328,53 @@ void UpdateGameplayScreen(void) } break; } - else + else { PlaySound(fxLeave); - + words[i].rec = words[i].iniRec; if (i == messageWords[j].id) messageWords[j].id = -1; } - } - } + } + } } - - // TODO: Move word picked with mouse + + // Move word picked with mouse if (words[i].picked) { words[i].rec.x = GetMouseX() - words[i].rec.width/2; words[i].rec.y = GetMouseY() - words[i].rec.height/2; - - // TODO: Check if label is placed in some mission word position - //if (CheckCollisionRecs(words[i].rec)) - } - else - { - //if (words[i].id != -1) } } - + canSend = true; - for(int j = 0; j < missions[currentMission].wordsCount; j++) - { - if(messageWords[j].id == -1) + for (int j = 0; j < missions[currentMission].wordsCount; j++) + { + if (messageWords[j].id == -1) { canSend = false; break; } } - - if (canSend && (IsKeyPressed(KEY_ENTER) || IsButtonPressed())) - { + + if (canSend && (IsKeyPressed(KEY_ENTER) || IsButtonPressed())) + { finishScreen = true; - } + } } // Gameplay Screen Draw logic void DrawGameplayScreen(void) { DrawTexture(texBackground, 0, 0, WHITE); - + DrawTextEx(fontMessage, missions[currentMission].msg, msgOffset, fontMessage.baseSize, 0, BLACK); - + for (int i = 0; i < missions[currentMission].wordsCount; i++) { Rectangle recLines = messageWords[i].rec; DrawRectangleLines(recLines.x, recLines.y, recLines.width, recLines.height, Fade(RED, 0.35f)); - if(messageWords[i].hover) DrawRectangleRec(messageWords[i].rec, Fade(RED, 0.30f)); + if (messageWords[i].hover) DrawRectangleRec(messageWords[i].rec, Fade(RED, 0.30f)); DrawText(FormatText("%i", messageWords[i].id), i*25, 0, 30, RED); } for (int i = 0; i < MAX_CODING_WORDS; i++) @@ -394,9 +383,9 @@ void DrawGameplayScreen(void) else if (words[i].hover) DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, RED); else DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, WHITE); } - + DrawTexturePro(texVignette, (Rectangle){0,0,texVignette.width, texVignette.height}, (Rectangle){0,0,GetScreenWidth(), GetScreenHeight()}, (Vector2){0,0}, 0, WHITE); - + if (canSend) DrawButton("enviar"); } @@ -406,13 +395,13 @@ void UnloadGameplayScreen(void) UnloadTexture(texBackground); UnloadTexture(texVignette); UnloadTexture(texWordsAtlas); - + UnloadSound(fxGrab); UnloadSound(fxLeave); UnloadSound(fxPlace); - + UnloadMusicStream(musSpy); - + free(missions); } diff --git a/games/transmission/screens/screen_logo.c b/games/transmission/screens/screen_logo.c index 30271ba6..37543302 100644 --- a/games/transmission/screens/screen_logo.c +++ b/games/transmission/screens/screen_logo.c @@ -61,15 +61,15 @@ void InitLogoScreen(void) // Logo Screen Update logic void UpdateLogoScreen(void) { - if(!fadeOut) + if (!fadeOut) { fadeValue += 0.02f; - if(fadeValue > 1.01f) + if (fadeValue > 1.01f) { fadeValue = 1.0f; framesCounter++; - if(framesCounter % showLogoFrames == 0) + if ((framesCounter%showLogoFrames) == 0) { fadeOut = true; finishScreen = true; @@ -77,10 +77,7 @@ void UpdateLogoScreen(void) } } - if(IsKeyPressed(KEY_ENTER)) - { - finishScreen = true; - } + if (IsKeyPressed(KEY_ENTER)) finishScreen = true; } // Logo Screen Draw logic diff --git a/games/transmission/screens/screen_mission.c b/games/transmission/screens/screen_mission.c index 0f996502..d62b7205 100644 --- a/games/transmission/screens/screen_mission.c +++ b/games/transmission/screens/screen_mission.c @@ -88,34 +88,33 @@ static Music musMission; // Mission Screen Functions Definition //---------------------------------------------------------------------------------- static void WriteMissionText(); -static void EndWritting(); -static void BlinkKeyword(); + // Mission Screen Initialization logic void InitMissionScreen(void) { framesCounter = 0; finishScreen = 0; - - fadeButton = 0.80f; - + + fadeButton = 0.80f; + texBackground = LoadTexture("resources/textures/mission_background.png"); - - texBackline = LoadTexture("resources/textures/mission_backline.png"); + + texBackline = LoadTexture("resources/textures/mission_backline.png"); sourceRecBackLine = (Rectangle){0,0,GetScreenWidth(), texBackline.height}; destRecBackLine = (Rectangle){0,0,sourceRecBackLine.width, sourceRecBackLine.height}; fadeBackLine = 0; - + fxTransmit = LoadSound("resources/audio/fx_message.ogg"); musMission = LoadMusicStream("resources/audio/music_mission.ogg"); - + PlayMusicStream(musMission); - + // Initialize missions missions = LoadMissions("resources/missions.txt"); - + missionMaxLength = strlen(missions[currentMission].brief); - + // Insert line breaks every MAX_LINE_CHAR int currentLine = 1; int i = currentLine * MAX_LINE_CHAR; @@ -129,35 +128,35 @@ void InitMissionScreen(void) i = currentLine*MAX_LINE_CHAR; } else i++; - } - - missionSize = 30; + } + + missionSize = 30; missionLenght = 0; missionSpeed = 1; - + numberColor = RAYWHITE; missionColor = LIGHTGRAY; keywordColor = (Color){198, 49, 60, 255}; //RED - + numberPosition = (Vector2){150, 185}; missionPosition = (Vector2){numberPosition.x, numberPosition.y + 60}; keywordPosition = (Vector2){missionPosition.x, missionPosition.y + MeasureTextEx(fontMission, missions[currentMission].brief, missionSize, 0).y + 60}; - + startWritting = false; writeNumber = false; writeMission = false; writeKeyword = false; writeEnd = false; - + writtingMission = false; - + showNumberWaitFrames = 30; - showMissionWaitFrames = 60; - showKeywordWaitFrames = 60; - + showMissionWaitFrames = 60; + showKeywordWaitFrames = 60; + blinkKeyWord = true; blinkFrames = 15; - + PlaySound(fxTransmit); } @@ -165,37 +164,52 @@ void InitMissionScreen(void) void UpdateMissionScreen(void) { UpdateMusicStream(musMission); - - if (!writeEnd) WriteMissionText(); - else BlinkKeyword(); - + + if (!writeEnd) WriteMissionText(); + else + { + framesCounter++; + + if ((framesCounter%blinkFrames) == 0) + { + framesCounter = 0; + blinkKeyWord = !blinkKeyWord; + } + } + if (showButton) { - if (IsKeyPressed(KEY_ENTER) || IsButtonPressed()) + if (IsKeyPressed(KEY_ENTER) || IsButtonPressed()) { - if (!writeEnd) EndWritting(); + if (!writeEnd) + { + writeEnd = true; + writeKeyword = true; + writeNumber = true; + missionLenght = missionMaxLength; + } else { finishScreen = true; showButton = false; } - } + } } } // Mission Screen Draw logic void DrawMissionScreen(void) { - // TODO: Draw MISSION screen here! + // Draw MISSION screen here! DrawTexture(texBackground, 0,0, WHITE); DrawTexturePro(texBackline, sourceRecBackLine, destRecBackLine, (Vector2){0,0},0, Fade(WHITE, fadeBackLine)); - - if (writeNumber) DrawTextEx(fontMission, FormatText("Filtración #%02i ", currentMission + 1), numberPosition, missionSize + 10, 0, numberColor); + + if (writeNumber) DrawTextEx(fontMission, FormatText("Filtración #%02i ", currentMission + 1), numberPosition, missionSize + 10, 0, numberColor); DrawTextEx(fontMission, SubText(missions[currentMission].brief, 0, missionLenght), missionPosition, missionSize, 0, missionColor); - if (writeKeyword && blinkKeyWord) DrawTextEx(fontMission, FormatText("Keyword: %s", missions[currentMission].key), keywordPosition, missionSize + 10, 0, keywordColor); - - if (showButton) - { + if (writeKeyword && blinkKeyWord) DrawTextEx(fontMission, FormatText("Keyword: %s", missions[currentMission].key), keywordPosition, missionSize + 10, 0, keywordColor); + + if (showButton) + { if (!writeEnd) DrawButton("saltar"); else DrawButton("codificar"); } @@ -204,8 +218,8 @@ void DrawMissionScreen(void) // Mission Screen Unload logic void UnloadMissionScreen(void) { - // TODO: Unload MISSION screen variables here! - UnloadTexture(texBackground); + // Unload MISSION screen variables here! + UnloadTexture(texBackground); UnloadTexture(texBackline); UnloadSound(fxTransmit); UnloadMusicStream(musMission); @@ -220,74 +234,58 @@ int FinishMissionScreen(void) static void WriteMissionText() { - if(!startWritting) + if (!startWritting) { framesCounter++; - if(framesCounter % 60 == 0) + if (framesCounter % 60 == 0) { framesCounter = 0; startWritting = true; } - } - else if(!writeNumber) + } + else if (!writeNumber) { framesCounter++; fadeBackLine += 0.020f; - if(framesCounter % showNumberWaitFrames == 0) + if (framesCounter % showNumberWaitFrames == 0) { framesCounter = 0; writeNumber = true; showButton = true; } } - else if(!writeMission) + else if (!writeMission) { framesCounter ++; - if(framesCounter % showMissionWaitFrames == 0) + if (framesCounter % showMissionWaitFrames == 0) { framesCounter = 0; writeMission = true; writtingMission = true; } } - else if(writeMission && writtingMission) + else if (writeMission && writtingMission) { framesCounter++; - if(framesCounter % missionSpeed == 0) + if (framesCounter % missionSpeed == 0) { framesCounter = 0; - missionLenght++; - - if(missionLenght == missionMaxLength) + missionLenght++; + + if (missionLenght == missionMaxLength) { writtingMission = false; } } } - else if(!writeKeyword) + else if (!writeKeyword) { framesCounter++; - if(framesCounter % showKeywordWaitFrames == 0) + if (framesCounter % showKeywordWaitFrames == 0) { framesCounter = 0; writeKeyword = true; writeEnd = true; } - } -} -static void EndWritting() -{ - writeEnd = true; - writeKeyword = true; - writeNumber = true; - missionLenght = missionMaxLength; -} -static void BlinkKeyword() -{ - framesCounter++; - if(framesCounter % blinkFrames == 0) - { - framesCounter = 0; - blinkKeyWord = !blinkKeyWord; } } From 96207a8a026a629fcc3026efab96cf18e1302618 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 25 Dec 2018 15:17:42 +0100 Subject: [PATCH 06/58] REVIEWED: LoadFontEx() Changed parameters order for consistency with LoadFontData() and other functions when an array is passed by parameter and array size is the following parameter. --- examples/textures/textures_image_text.c | 2 +- games/transmission/screens/screen_ending.c | 2 +- games/transmission/screens/screen_gameplay.c | 2 +- src/raylib.h | 2 +- src/text.c | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/textures/textures_image_text.c b/examples/textures/textures_image_text.c index fb99e827..ce91fbf2 100644 --- a/examples/textures/textures_image_text.c +++ b/examples/textures/textures_image_text.c @@ -21,7 +21,7 @@ int main() InitWindow(screenWidth, screenHeight, "raylib [texture] example - image text drawing"); // TTF Font loading with custom generation parameters - Font font = LoadFontEx("resources/KAISG.ttf", 64, 95, 0); + Font font = LoadFontEx("resources/KAISG.ttf", 64, 0, 0); Image parrots = LoadImage("resources/parrots.png"); // Load image in CPU memory (RAM) diff --git a/games/transmission/screens/screen_ending.c b/games/transmission/screens/screen_ending.c index 620fc30b..bb355b8b 100644 --- a/games/transmission/screens/screen_ending.c +++ b/games/transmission/screens/screen_ending.c @@ -123,7 +123,7 @@ void InitEndingScreen(void) // Generate newspaper with title and subtitle Image imNewspaper = LoadImage("resources/textures/ending_newspaper.png"); - fontNews = LoadFontEx("resources/fonts/Lora-Bold.ttf", 32, 250, 0); + fontNews = LoadFontEx("resources/fonts/Lora-Bold.ttf", 32, 0, 250); ImageDrawTextEx(&imNewspaper, (Vector2){ 50, 220 }, fontNews, headline, fontNews.baseSize, 0, DARKGRAY); texNewspaper = LoadTextureFromImage(imNewspaper); diff --git a/games/transmission/screens/screen_gameplay.c b/games/transmission/screens/screen_gameplay.c index aeec98ca..814db824 100644 --- a/games/transmission/screens/screen_gameplay.c +++ b/games/transmission/screens/screen_gameplay.c @@ -126,7 +126,7 @@ void InitGameplayScreen(void) framesCounter = 0; finishScreen = 0; - fontMessage = LoadFontEx("resources/fonts/traveling_typewriter.ttf", 30, 250, 0); + fontMessage = LoadFontEx("resources/fonts/traveling_typewriter.ttf", 30, 0, 250); texBackground = LoadTexture("resources/textures/message_background.png"); texVignette = LoadTexture("resources/textures/message_vignette.png"); diff --git a/src/raylib.h b/src/raylib.h index 023d35c7..d4b5bd20 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1095,7 +1095,7 @@ RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle // Font loading/unloading functions RLAPI Font GetFontDefault(void); // Get the default Font RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) -RLAPI Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load font from file with extended parameters +RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCount); // Load font from file with extended parameters RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style) RLAPI CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type); // Load font data for further use RLAPI Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info diff --git a/src/text.c b/src/text.c index 80b75f10..770c8ddb 100644 --- a/src/text.c +++ b/src/text.c @@ -275,7 +275,7 @@ Font LoadFont(const char *fileName) Font font = { 0 }; #if defined(SUPPORT_FILEFORMAT_TTF) - if (IsFileExtension(fileName, ".ttf") || IsFileExtension(fileName, ".otf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, DEFAULT_TTF_NUMCHARS, NULL); + if (IsFileExtension(fileName, ".ttf") || IsFileExtension(fileName, ".otf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, NULL, DEFAULT_TTF_NUMCHARS); else #endif #if defined(SUPPORT_FILEFORMAT_FNT) @@ -301,7 +301,7 @@ Font LoadFont(const char *fileName) // Load Font from TTF font file with generation parameters // NOTE: You can pass an array with desired characters, those characters should be available in the font // if array is NULL, default char set is selected 32..126 -Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars) +Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCount) { Font font = { 0 }; From 35a6e9a07476c06e239b2cf1879bdb97fdc04b09 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 25 Dec 2018 15:18:35 +0100 Subject: [PATCH 07/58] Corrected issue with MOD playing Despite issue is corrected, now module loop doesn't work... --- examples/audio/audio_module_playing.c | 6 +++--- src/audio.c | 20 +++++++++++++++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/examples/audio/audio_module_playing.c b/examples/audio/audio_module_playing.c index 671a119f..54bfa3d2 100644 --- a/examples/audio/audio_module_playing.c +++ b/examples/audio/audio_module_playing.c @@ -30,11 +30,11 @@ int main() int screenWidth = 800; int screenHeight = 450; - SetConfigFlags(FLAG_MSAA_4X_HINT); // NOTE: Try to enable MSAA 4X + SetConfigFlags(FLAG_MSAA_4X_HINT); // NOTE: Try to enable MSAA 4X InitWindow(screenWidth, screenHeight, "raylib [audio] example - module playing (streaming)"); - InitAudioDevice(); // Initialize audio device + InitAudioDevice(); // Initialize audio device Color colors[14] = { ORANGE, RED, GOLD, LIME, BLUE, VIOLET, BROWN, LIGHTGRAY, PINK, YELLOW, GREEN, SKYBLUE, PURPLE, BEIGE }; @@ -52,7 +52,7 @@ int main() circles[i].color = colors[GetRandomValue(0, 13)]; } - Music xm = LoadMusicStream("resources/mini1111.xm"); + Music xm = LoadMusicStream("resources/chiptun1.mod"); PlayMusicStream(xm); diff --git a/src/audio.c b/src/audio.c index 41aa1b61..b4eec113 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1176,14 +1176,15 @@ Music LoadMusicStream(const char *fileName) if (jar_mod_load_file(&music->ctxMod, fileName)) { + // NOTE: Only stereo is supported for MOD music->stream = InitAudioStream(48000, 16, 2); music->totalSamples = (unsigned int)jar_mod_max_samples(&music->ctxMod); music->samplesLeft = music->totalSamples; music->ctxType = MUSIC_MODULE_MOD; music->loopCount = -1; // Infinite loop by default - TraceLog(LOG_DEBUG, "[%s] MOD number of samples: %i", fileName, music->samplesLeft); - TraceLog(LOG_DEBUG, "[%s] MOD track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f); + TraceLog(LOG_INFO, "[%s] MOD number of samples: %i", fileName, music->samplesLeft); + TraceLog(LOG_INFO, "[%s] MOD track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f); } else musicLoaded = false; } @@ -1345,18 +1346,27 @@ void UpdateMusicStream(Music music) #if defined(SUPPORT_FILEFORMAT_XM) case MUSIC_MODULE_XM: { - // NOTE: Internally this function considers 2 channels generation, so samplesCount/2 --> WEIRD + // NOTE: Internally this function considers 2 channels generation, so samplesCount/2 jar_xm_generate_samples_16bit(music->ctxXm, (short *)pcm, samplesCount/2); } break; #endif #if defined(SUPPORT_FILEFORMAT_MOD) - case MUSIC_MODULE_MOD: jar_mod_fillbuffer(&music->ctxMod, pcm, samplesCount, 0); break; + case MUSIC_MODULE_MOD: + { + // NOTE: 3rd parameter (nbsample) specify the number of stereo 16bits samples you want, so sampleCount/2 + jar_mod_fillbuffer(&music->ctxMod, (short *)pcm, samplesCount/2, 0); + } break; #endif default: break; } + UpdateAudioStream(music->stream, pcm, samplesCount); - music->samplesLeft -= samplesCount; + if ((music->ctxType == MUSIC_MODULE_XM) || (music->ctxType == MUSIC_MODULE_MOD)) + { + music->samplesLeft -= samplesCount/2; + } + else music->samplesLeft -= samplesCount; if (music->samplesLeft <= 0) { From 7b8965eb38adcbc325533acc831e3331c3efba9c Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 25 Dec 2018 15:19:25 +0100 Subject: [PATCH 08/58] Support float texture data on OpenGL ES 2.0 --- examples/models/models_skybox.c | 2 +- examples/models/resources/shaders/cubemap.fs | 4 ++-- examples/models/resources/shaders/cubemap.vs | 4 ++-- examples/models/resources/shaders/skybox.fs | 4 ++-- examples/models/resources/shaders/skybox.vs | 4 ++-- games/transmission/transmission.c | 2 +- src/rlgl.h | 17 +++++++++++++---- 7 files changed, 23 insertions(+), 14 deletions(-) diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index 6f6002b8..a6b233e3 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -21,7 +21,7 @@ int main() InitWindow(screenWidth, screenHeight, "raylib [models] example - skybox loading and drawing"); // Define the camera to look into our 3d world - Camera camera = {{ 1.0f, 1.0f, 1.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; + Camera camera = {{ 1.0f, 1.0f, 1.0f }, { 4.0f, 1.0f, 4.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f, 0 }; // Load skybox model Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f); diff --git a/examples/models/resources/shaders/cubemap.fs b/examples/models/resources/shaders/cubemap.fs index 09ae62f5..e8e28536 100644 --- a/examples/models/resources/shaders/cubemap.fs +++ b/examples/models/resources/shaders/cubemap.fs @@ -9,7 +9,7 @@ #version 330 // Input vertex attributes (from vertex shader) -in vec3 fragPos; +in vec3 fragPosition; // Input uniform values uniform sampler2D equirectangularMap; @@ -28,7 +28,7 @@ vec2 SampleSphericalMap(vec3 v) void main() { // Normalize local position - vec2 uv = SampleSphericalMap(normalize(fragPos)); + vec2 uv = SampleSphericalMap(normalize(fragPosition)); // Fetch color from texture map vec3 color = texture(equirectangularMap, uv).rgb; diff --git a/examples/models/resources/shaders/cubemap.vs b/examples/models/resources/shaders/cubemap.vs index 6e0bf4e1..5721eaa2 100644 --- a/examples/models/resources/shaders/cubemap.vs +++ b/examples/models/resources/shaders/cubemap.vs @@ -16,12 +16,12 @@ uniform mat4 projection; uniform mat4 view; // Output vertex attributes (to fragment shader) -out vec3 fragPos; +out vec3 fragPosition; void main() { // Calculate fragment position based on model transformations - fragPos = vertexPosition; + fragPosition = vertexPosition; // Calculate final vertex position gl_Position = projection*view*vec4(vertexPosition, 1.0); diff --git a/examples/models/resources/shaders/skybox.fs b/examples/models/resources/shaders/skybox.fs index 0bd2f320..053a2517 100644 --- a/examples/models/resources/shaders/skybox.fs +++ b/examples/models/resources/shaders/skybox.fs @@ -9,7 +9,7 @@ #version 330 // Input vertex attributes (from vertex shader) -in vec3 fragPos; +in vec3 fragPosition; // Input uniform values uniform samplerCube environmentMap; @@ -20,7 +20,7 @@ out vec4 finalColor; void main() { // Fetch color from texture map - vec3 color = texture(environmentMap, fragPos).rgb; + vec3 color = texture(environmentMap, fragPosition).rgb; // Apply gamma correction color = color/(color + vec3(1.0)); diff --git a/examples/models/resources/shaders/skybox.vs b/examples/models/resources/shaders/skybox.vs index f40d615c..dcbe6c3d 100644 --- a/examples/models/resources/shaders/skybox.vs +++ b/examples/models/resources/shaders/skybox.vs @@ -16,12 +16,12 @@ uniform mat4 projection; uniform mat4 view; // Output vertex attributes (to fragment shader) -out vec3 fragPos; +out vec3 fragPosition; void main() { // Calculate fragment position based on model transformations - fragPos = vertexPosition; + fragPosition = vertexPosition; // Remove translation from the view matrix mat4 rotView = mat4(mat3(view)); diff --git a/games/transmission/transmission.c b/games/transmission/transmission.c index d4298e34..91ca28c1 100644 --- a/games/transmission/transmission.c +++ b/games/transmission/transmission.c @@ -70,7 +70,7 @@ int main(void) SetMusicVolume(music, 1.0f); PlayMusicStream(music); - fontMission = LoadFontEx("resources/fonts/traveling_typewriter.ttf", 64, 250, 0); + fontMission = LoadFontEx("resources/fonts/traveling_typewriter.ttf", 64, 0, 250); texButton = LoadTexture("resources/textures/title_ribbon.png"); // UI BUTTON diff --git a/src/rlgl.h b/src/rlgl.h index 5c14f9be..8bee37a3 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2968,7 +2968,7 @@ Matrix GetMatrixModelview() Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size) { Texture2D cubemap = { 0 }; -#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // NOTE: SetShaderDefaultLocations() already setups locations for projection and view Matrix in shader // Other locations should be setup externally in shader before calling the function @@ -2978,22 +2978,31 @@ Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size) glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); // Flag not supported on OpenGL ES 2.0 #endif - // Setup framebuffer unsigned int fbo, rbo; glGenFramebuffers(1, &fbo); glGenRenderbuffers(1, &rbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glBindRenderbuffer(GL_RENDERBUFFER, rbo); +#if defined(GRAPHICS_API_OPENGL_33) glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); +#elif defined(GRAPHICS_API_OPENGL_ES2) + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size, size); +#endif glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); // Set up cubemap to render and attach to framebuffer // NOTE: faces are stored with 16 bit floating point values glGenTextures(1, &cubemap.id); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); - for (unsigned int i = 0; i < 6; i++) - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL); + for (unsigned int i = 0; i < 6; i++) + { +#if defined(GRAPHICS_API_OPENGL_33) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB32F, size, size, 0, GL_RGB, GL_FLOAT, NULL); +#elif defined(GRAPHICS_API_OPENGL_ES2) + if (texFloatSupported) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL); +#endif + } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); #if defined(GRAPHICS_API_OPENGL_33) From af4a177af41064db5ebf9a28dcac9eb178cd7fa9 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 26 Dec 2018 10:34:45 +0100 Subject: [PATCH 09/58] Corrected issue with internal buffer Problem aligning provided vertex data to multiples of four, because main buffer is intended to bu used with indexed quads... but also shared with triangles and lines. --- src/rlgl.h | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 8bee37a3..632c21f0 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1102,7 +1102,8 @@ void rlEnd(void) // processed but are placed in a single point to not result in a fragment output... // TODO: System could be improved (a bit) just storing every draw alignment value // and adding it to vertexOffset on drawing... maybe in a future... - int vertexToAlign = draws[drawsCounter - 1].vertexCount%4; + int vertexCount = draws[drawsCounter - 1].vertexCount; + int vertexToAlign = (vertexCount >= 4) ? vertexCount%4 : (4 - vertexCount%4); for (int i = 0; i < vertexToAlign; i++) rlVertex3f(-1, -1, -1); // Make sure vertexCount is the same for vertices, texcoords, colors and normals @@ -1145,7 +1146,7 @@ void rlEnd(void) // Verify internal buffers limits // NOTE: This check is combined with usage of rlCheckBufferLimit() - if ((vertexData[currentBuffer].vCounter/4) >= (MAX_BATCH_ELEMENTS - 4)) + if ((vertexData[currentBuffer].vCounter) >= (MAX_BATCH_ELEMENTS*4 - 4)) { // WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlglDraw(), // we need to call rlPopMatrix() before to recover *currentMatrix (modelview) for the next forced draw call! @@ -1165,7 +1166,7 @@ void rlVertex3f(float x, float y, float z) if (useTransformMatrix) vec = Vector3Transform(vec, transformMatrix); // Verify that MAX_BATCH_ELEMENTS limit not reached - if (vertexData[currentBuffer].vCounter/4 < MAX_BATCH_ELEMENTS) + if (vertexData[currentBuffer].vCounter < (MAX_BATCH_ELEMENTS*4)) { vertexData[currentBuffer].vertices[3*vertexData[currentBuffer].vCounter] = vec.x; vertexData[currentBuffer].vertices[3*vertexData[currentBuffer].vCounter + 1] = vec.y; @@ -1262,7 +1263,7 @@ void rlDisableTexture(void) #else // NOTE: If quads batch limit is reached, // we force a draw call and next batch starts - if (vertexData[currentBuffer].vCounter/4 >= MAX_BATCH_ELEMENTS) rlglDraw(); + if (vertexData[currentBuffer].vCounter >= (MAX_BATCH_ELEMENTS*4)) rlglDraw(); #endif } @@ -1735,7 +1736,7 @@ bool rlCheckBufferLimit(int vCount) { bool overflow = false; #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - if ((vertexData[currentBuffer].vCounter + vCount)/4 >= MAX_BATCH_ELEMENTS) overflow = true; + if ((vertexData[currentBuffer].vCounter + vCount) >= (MAX_BATCH_ELEMENTS*4)) overflow = true; #endif return overflow; } @@ -3013,7 +3014,6 @@ Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size) // Create projection (transposed) and different views for each face Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); - //MatrixTranspose(&fboProjection); Matrix fboViews[6] = { MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), @@ -3086,7 +3086,6 @@ Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size) // Create projection (transposed) and different views for each face Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); - //MatrixTranspose(&fboProjection); Matrix fboViews[6] = { MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), @@ -3163,7 +3162,6 @@ Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size) // Create projection (transposed) and different views for each face Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); - //MatrixTranspose(&fboProjection); Matrix fboViews[6] = { MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), From 9a8320c52bf179633c33f16d4f0adbf57f820b1b Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 26 Dec 2018 10:51:13 +0100 Subject: [PATCH 10/58] REVIEWED: DrawRectanglePro() --- src/raylib.h | 2 +- src/shapes.c | 71 ++++++++++++++++++++++++++++++-------------------- src/textures.c | 12 ++++----- 3 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index d4b5bd20..baac5a8c 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -986,7 +986,7 @@ RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle -RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color colors[4]); // Draw a color-filled rectangle with pro parameters +RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a vertical-gradient-filled rectangle RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a horizontal-gradient-filled rectangle RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors diff --git a/src/shapes.c b/src/shapes.c index 7a6d1d49..3e0379a3 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -130,9 +130,9 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color) rlEnableTexture(GetShapesTexture().id); rlPushMatrix(); - rlTranslatef((float)startPos.x, (float)startPos.y, 0); - rlRotatef(RAD2DEG*angle, 0, 0, 1); - rlTranslatef(0, (thick > 1.0f) ? -thick/2.0f : -1.0f, 0); + rlTranslatef((float)startPos.x, (float)startPos.y, 0.0f); + rlRotatef(RAD2DEG*angle, 0.0f, 0.0f, 1.0f); + rlTranslatef(0, (thick > 1.0f) ? -thick/2.0f : -1.0f, 0.0f); rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); @@ -273,48 +273,40 @@ void DrawRectangle(int posX, int posY, int width, int height, Color color) // NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw) void DrawRectangleV(Vector2 position, Vector2 size, Color color) { - Color colors[4] = { color, color, color, color }; - - DrawRectanglePro((Rectangle){ position.x, position.y, size.x, size.y }, (Vector2){ 0.0f, 0.0f }, 0.0f, colors); + DrawRectanglePro((Rectangle){ position.x, position.y, size.x, size.y }, (Vector2){ 0.0f, 0.0f }, 0.0f, color); } // Draw a color-filled rectangle void DrawRectangleRec(Rectangle rec, Color color) { - Color colors[4] = { color, color, color, color }; - - DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, colors); + DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, color); } // Draw a color-filled rectangle with pro parameters -void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color colors[4]) +void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color) { rlEnableTexture(GetShapesTexture().id); rlPushMatrix(); - //rlTranslatef(rec.x, rec.y, 0); // Already considered on vertex position - rlRotatef(rotation, 0, 0, 1); - rlTranslatef(-origin.x, -origin.y, 0); + rlTranslatef(rec.x, rec.y, 0.0f); + rlRotatef(rotation, 0.0f, 0.0f, 1.0f); + rlTranslatef(-origin.x, -origin.y, 0.0f); rlBegin(RL_QUADS); rlNormal3f(0.0f, 0.0f, 1.0f); + rlColor4ub(color.r, color.g, color.b, color.a); - // NOTE: Default raylib font character 95 is a white square - rlColor4ub(colors[0].r, colors[0].g, colors[0].b, colors[0].a); rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); - rlVertex2f(rec.x, rec.y); - - rlColor4ub(colors[1].r, colors[1].g, colors[1].b, colors[1].a); + rlVertex2f(0.0f, 0.0f); + rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); - rlVertex2f(rec.x, rec.y + rec.height); - - rlColor4ub(colors[2].r, colors[2].g, colors[2].b, colors[2].a); + rlVertex2f(0.0f, rec.height); + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); - rlVertex2f(rec.x + rec.width, rec.y + rec.height); + rlVertex2f(rec.width, rec.height); - rlColor4ub(colors[3].r, colors[3].g, colors[3].b, colors[3].a); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); - rlVertex2f(rec.x + rec.width, rec.y); + rlVertex2f(rec.width, 0.0f); rlEnd(); rlPopMatrix(); @@ -339,9 +331,32 @@ void DrawRectangleGradientH(int posX, int posY, int width, int height, Color col // NOTE: Colors refer to corners, starting at top-lef corner and counter-clockwise void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4) { - Color colors[4] = { col1, col2, col3, col4 }; + rlEnableTexture(GetShapesTexture().id); - DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, colors); + rlPushMatrix(); + rlBegin(RL_QUADS); + rlNormal3f(0.0f, 0.0f, 1.0f); + + // NOTE: Default raylib font character 95 is a white square + rlColor4ub(col1.r, col1.g, col1.b, col1.a); + rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); + rlVertex2f(rec.x, rec.y); + + rlColor4ub(col2.r, col2.g, col2.b, col2.a); + rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(rec.x, rec.y + rec.height); + + rlColor4ub(col3.r, col3.g, col3.b, col3.a); + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(rec.x + rec.width, rec.y + rec.height); + + rlColor4ub(col4.r, col4.g, col4.b, col4.a); + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); + rlVertex2f(rec.x + rec.width, rec.y); + rlEnd(); + rlPopMatrix(); + + rlDisableTexture(); } // Draw rectangle outline @@ -443,8 +458,8 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col if (rlCheckBufferLimit(4*(360/sides))) rlglDraw(); rlPushMatrix(); - rlTranslatef(center.x, center.y, 0.0); - rlRotatef(rotation, 0, 0, 1); + rlTranslatef(center.x, center.y, 0.0f); + rlRotatef(rotation, 0.0f, 0.0f, 1.0f); #if defined(SUPPORT_QUADS_DRAW_MODE) rlEnableTexture(GetShapesTexture().id); diff --git a/src/textures.c b/src/textures.c index fa1d7f0e..a7120979 100644 --- a/src/textures.c +++ b/src/textures.c @@ -2434,9 +2434,9 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V rlEnableTexture(texture.id); rlPushMatrix(); - rlTranslatef(destRec.x, destRec.y, 0); - rlRotatef(rotation, 0, 0, 1); - rlTranslatef(-origin.x, -origin.y, 0); + rlTranslatef(destRec.x, destRec.y, 0.0f); + rlRotatef(rotation, 0.0f, 0.0f, 1.0f); + rlTranslatef(-origin.x, -origin.y, 0.0f); rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, tint.a); @@ -2524,9 +2524,9 @@ void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle destR rlEnableTexture(texture.id); rlPushMatrix(); - rlTranslatef(destRec.x, destRec.y, 0); - rlRotatef(rotation, 0, 0, 1); - rlTranslatef(-origin.x, -origin.y, 0); + rlTranslatef(destRec.x, destRec.y, 0.0f); + rlRotatef(rotation, 0.0f, 0.0f, 1.0f); + rlTranslatef(-origin.x, -origin.y, 0.0f); rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, tint.a); From 01338b0a14f053d281970a32595bc531e8c70885 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 26 Dec 2018 13:26:34 +0100 Subject: [PATCH 11/58] WARNING: BREAKING CHANGE Added a bunch of useful text management functions. Consequently, some already available functions like `FormatText()` and `SubText()` has been renamed for consistency. Created temporal fallbacks for old names. raylib version bumped to 2.3. --- examples/shapes/shapes_logo_raylib_anim.c | 4 +- examples/text/text_writing_anim.c | 4 +- games/transmission/screens/screen_gameplay.c | 6 +- games/transmission/screens/screen_mission.c | 2 +- games/transmission/screens/screen_title.c | 4 +- src/config.h | 2 +- src/core.c | 8 +- src/raylib.h | 26 +- src/text.c | 293 ++++++++++++++++--- 9 files changed, 288 insertions(+), 61 deletions(-) diff --git a/examples/shapes/shapes_logo_raylib_anim.c b/examples/shapes/shapes_logo_raylib_anim.c index c6d3796e..9be1d963 100644 --- a/examples/shapes/shapes_logo_raylib_anim.c +++ b/examples/shapes/shapes_logo_raylib_anim.c @@ -2,7 +2,7 @@ * * raylib [shapes] example - raylib logo animation * -* This example has been created using raylib 1.4 (www.raylib.com) +* This example has been created using raylib 2.3 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * * Copyright (c) 2014 Ramon Santamaria (@raysan5) @@ -140,7 +140,7 @@ int main() DrawRectangle(screenWidth/2 - 112, screenHeight/2 - 112, 224, 224, Fade(RAYWHITE, alpha)); - DrawText(SubText("raylib", 0, lettersCount), screenWidth/2 - 44, screenHeight/2 + 48, 50, Fade(BLACK, alpha)); + DrawText(TextSubtext("raylib", 0, lettersCount), screenWidth/2 - 44, screenHeight/2 + 48, 50, Fade(BLACK, alpha)); } else if (state == 4) { diff --git a/examples/text/text_writing_anim.c b/examples/text/text_writing_anim.c index 5563b561..b2aba697 100644 --- a/examples/text/text_writing_anim.c +++ b/examples/text/text_writing_anim.c @@ -2,7 +2,7 @@ * * raylib [text] example - Text Writing Animation * -* This example has been created using raylib 1.4 (www.raylib.com) +* This example has been created using raylib 2.3 (www.raylib.com) * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) * * Copyright (c) 2016 Ramon Santamaria (@raysan5) @@ -44,7 +44,7 @@ int main() ClearBackground(RAYWHITE); - DrawText(SubText(message, 0, framesCounter/10), 210, 160, 20, MAROON); + DrawText(TextSubtext(message, 0, framesCounter/10), 210, 160, 20, MAROON); DrawText("PRESS [ENTER] to RESTART!", 240, 260, 20, LIGHTGRAY); DrawText("PRESS [SPACE] to SPEED UP!", 239, 300, 20, LIGHTGRAY); diff --git a/games/transmission/screens/screen_gameplay.c b/games/transmission/screens/screen_gameplay.c index 814db824..ee70632a 100644 --- a/games/transmission/screens/screen_gameplay.c +++ b/games/transmission/screens/screen_gameplay.c @@ -219,10 +219,10 @@ void InitGameplayScreen(void) { foundWord = false; - messageWords[currentWord - 1].rec.width = (int)MeasureTextEx(fontMessage, SubText(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), 30, 0).x; + messageWords[currentWord - 1].rec.width = (int)MeasureTextEx(fontMessage, TextSubtext(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), 30, 0).x; messageWords[currentWord - 1].rec.height = fontMessage.baseSize; - strncpy(messageWords[currentWord - 1].text, SubText(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), i - wordInitPosX); + strncpy(messageWords[currentWord - 1].text, TextSubtext(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), i - wordInitPosX); } if (c == '@') // One word to change @@ -230,7 +230,7 @@ void InitGameplayScreen(void) foundWord = true; missions[currentMission].msg[i] = ' '; - offsetX = (int)MeasureTextEx(fontMessage, SubText(missions[currentMission].msg, wordInitPosY, (i + 1) - wordInitPosY), 30, 0).x; + offsetX = (int)MeasureTextEx(fontMessage, TextSubtext(missions[currentMission].msg, wordInitPosY, (i + 1) - wordInitPosY), 30, 0).x; messageWords[currentWord].rec.x = offsetX; messageWords[currentWord].rec.y = offsetY; diff --git a/games/transmission/screens/screen_mission.c b/games/transmission/screens/screen_mission.c index d62b7205..1cd2563b 100644 --- a/games/transmission/screens/screen_mission.c +++ b/games/transmission/screens/screen_mission.c @@ -205,7 +205,7 @@ void DrawMissionScreen(void) DrawTexturePro(texBackline, sourceRecBackLine, destRecBackLine, (Vector2){0,0},0, Fade(WHITE, fadeBackLine)); if (writeNumber) DrawTextEx(fontMission, FormatText("Filtración #%02i ", currentMission + 1), numberPosition, missionSize + 10, 0, numberColor); - DrawTextEx(fontMission, SubText(missions[currentMission].brief, 0, missionLenght), missionPosition, missionSize, 0, missionColor); + DrawTextEx(fontMission, TextSubtext(missions[currentMission].brief, 0, missionLenght), missionPosition, missionSize, 0, missionColor); if (writeKeyword && blinkKeyWord) DrawTextEx(fontMission, FormatText("Keyword: %s", missions[currentMission].key), keywordPosition, missionSize + 10, 0, keywordColor); if (showButton) diff --git a/games/transmission/screens/screen_title.c b/games/transmission/screens/screen_title.c index 22efadb1..2a30a6ba 100644 --- a/games/transmission/screens/screen_title.c +++ b/games/transmission/screens/screen_title.c @@ -137,8 +137,8 @@ void UpdateTitleScreen(void) void DrawTitleScreen(void) { DrawTexture(texBackground, 0,0, WHITE); - DrawTextEx(fontTitle, SubText(textTitle, 0, transmissionLenght), transmissionPosition, titleSize, 0, titleColor); - DrawTextEx(fontTitle, SubText(textTitle, 12, missionLenght), missionPositon, titleSize, 0, titleColor); + DrawTextEx(fontTitle, TextSubtext(textTitle, 0, transmissionLenght), transmissionPosition, titleSize, 0, titleColor); + DrawTextEx(fontTitle, TextSubtext(textTitle, 12, missionLenght), missionPositon, titleSize, 0, titleColor); DrawButton("start"); } diff --git a/src/config.h b/src/config.h index baafb934..6d16207c 100644 --- a/src/config.h +++ b/src/config.h @@ -25,7 +25,7 @@ * **********************************************************************************************/ -#define RAYLIB_VERSION "2.2-dev" +#define RAYLIB_VERSION "2.3-dev" // Edit to control what features Makefile'd raylib is compiled with #if defined(RAYLIB_CMAKE) diff --git a/src/core.c b/src/core.c index 1b975214..6c2713e1 100644 --- a/src/core.c +++ b/src/core.c @@ -3176,17 +3176,17 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // NOTE: delay represents the time between frames in the gif, if we capture a gif frame every // 10 game frames and each frame trakes 16.6ms (60fps), delay between gif frames should be ~16.6*10. - GifBegin(FormatText("screenrec%03i.gif", screenshotCounter), screenWidth, screenHeight, (int)(GetFrameTime()*10.0f), 8, false); + GifBegin(TextFormat("screenrec%03i.gif", screenshotCounter), screenWidth, screenHeight, (int)(GetFrameTime()*10.0f), 8, false); screenshotCounter++; - TraceLog(LOG_INFO, "Begin animated GIF recording: %s", FormatText("screenrec%03i.gif", screenshotCounter)); + TraceLog(LOG_INFO, "Begin animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); } } else #endif // SUPPORT_GIF_RECORDING #if defined(SUPPORT_SCREEN_CAPTURE) { - TakeScreenshot(FormatText("screenshot%03i.png", screenshotCounter)); + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); screenshotCounter++; } #endif // SUPPORT_SCREEN_CAPTURE @@ -4454,7 +4454,7 @@ static void LogoAnimation(void) DrawRectangle(screenWidth/2 - 112, screenHeight/2 - 112, 224, 224, Fade(RAYWHITE, alpha)); - DrawText(SubText("raylib", 0, lettersCount), screenWidth/2 - 44, screenHeight/2 + 48, 50, Fade(BLACK, alpha)); + DrawText(TextSubtext("raylib", 0, lettersCount), screenWidth/2 - 44, screenHeight/2 + 48, 50, Fade(BLACK, alpha)); } EndDrawing(); diff --git a/src/raylib.h b/src/raylib.h index baac5a8c..3cad19fb 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -135,6 +135,11 @@ #define MAGENTA CLITERAL{ 255, 0, 255, 255 } // Magenta #define RAYWHITE CLITERAL{ 245, 245, 245, 255 } // My own White (raylib logo) +// Temporal hack to avoid breaking old codebases using +// deprecated raylib implementation of these functions +#define FormatText TextFormat +#define SubText TextSubText + //---------------------------------------------------------------------------------- // Structures Definition //---------------------------------------------------------------------------------- @@ -1111,11 +1116,22 @@ RLAPI int MeasureText(const char *text, int fontSize); RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font -// Text string edition functions -RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed' -RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string -RLAPI char **SplitText(char *text, char delimiter, int *strCount); // Split text string into multiple strings (memory should be freed manually!) -RLAPI bool IsEqualText(const char *text1, const char *text2); // Check if two text string are equal +// Text strings management functions +// NOTE: Some strings allocate memory internally for returned strings, just be careful! +RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal +RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending +RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf) +RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string +RLAPI const char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (memory should be freed!) +RLAPI const char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (memory should be freed!) +RLAPI const char *TextJoin(const char **textList, int count, const char *delimiter); // Join text strings with delimiter +RLAPI char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings (memory should be freed!) +RLAPI void TextSplitEx(const char *text, char delimiter, int *count, const char **ptrs, int *lengths); // Get pointers to substrings separated by delimiter +RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! +RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string +RLAPI const char *TextToUpper(const char *text); // Get upper case version of provided string +RLAPI const char *TextToLower(const char *text); // Get lower case version of provided string +RLAPI const char *TextToPascal(const char *text); // Get Pascal case notation version of provided string //------------------------------------------------------------------------------------ // Basic 3d Shapes Drawing Functions (Module: models) diff --git a/src/text.c b/src/text.c index 770c8ddb..ebf2599a 100644 --- a/src/text.c +++ b/src/text.c @@ -47,6 +47,7 @@ #include // Required for: strlen() #include // Required for: va_list, va_start(), vfprintf(), va_end() #include // Required for: FILE, fopen(), fclose(), fscanf(), feof(), rewind(), fgets() +#include // Required for: toupper(), tolower() #include "utils.h" // Required for: fopen() Android mapping @@ -62,8 +63,7 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define MAX_FORMATTEXT_LENGTH 512 -#define MAX_SUBTEXT_LENGTH 512 +#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers of some Text*() functions //---------------------------------------------------------------------------------- // Types and Structures Definition @@ -700,7 +700,7 @@ void DrawFPS(int posX, int posY) } // NOTE: We have rounding errors every frame, so it oscillates a lot - DrawText(FormatText("%2i FPS", fps), posX, posY, 20, LIME); + DrawText(TextFormat("%2i FPS", fps), posX, posY, 20, LIME); } // Draw text (using default font) @@ -863,10 +863,33 @@ int GetGlyphIndex(Font font, int character) #endif } -// Formatting of text with variables to 'embed' -const char *FormatText(const char *text, ...) +// Text strings management functions +//---------------------------------------------------------------------------------- +// Check if two text string are equal +// REQUIRES: strcmp() +bool TextIsEqual(const char *text1, const char *text2) { - static char buffer[MAX_FORMATTEXT_LENGTH]; + bool result = false; + + if (strcmp(text1, text2) == 0) result = true; + + return result; +} + +// Get text length in bytes, check for \0 character +unsigned int TextLength(const char *text) +{ + unsigned int length = 0; + + while (*text++) length++; + + return length; +} + +// Formatting of text with variables to 'embed' +const char *TextFormat(const char *text, ...) +{ + static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; va_list args; va_start(args, text); @@ -877,9 +900,11 @@ const char *FormatText(const char *text, ...) } // Get a piece of a text string -const char *SubText(const char *text, int position, int length) +// REQUIRES: strlen() +const char *TextSubtext(const char *text, int position, int length) { - static char buffer[MAX_SUBTEXT_LENGTH] = { 0 }; + static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + int textLength = strlen(text); if (position >= textLength) @@ -901,52 +926,238 @@ const char *SubText(const char *text, int position, int length) return buffer; } -// Split string into multiple strings -// NOTE: Files count is returned by parameters pointer -// NOTE: Allocated memory should be manually freed -char **SplitText(char *text, char delimiter, int *strCount) +// Replace text string +// REQUIRES: strlen(), strstr(), strncpy(), strcpy() +// WARNING: Internally allocated memory must be freed by the user (if return != NULL) +const char *TextReplace(char *text, const char *replace, const char *by) { - #define MAX_SUBSTRING_LENGTH 128 + char *result; + + char *insertPoint; // Next insert point + char *temp; // Temp pointer + int replaceLen; // Replace string length of (the string to remove) + int byLen; // Replacement length (the string to replace replace by) + int lastReplacePos; // Distance between replace and end of last replace + int count; // Number of replacements - char **strings = NULL; - int len = strlen(text); - char *strDup = (char *)malloc(len + 1); - strcpy(strDup, text); - int counter = 1; + // Sanity checks and initialization + if (!text || !replace) return NULL; - // Count how many substrings we have on string - for (int i = 0; i < len; i++) if (text[i] == delimiter) counter++; + replaceLen = strlen(replace); + if (replaceLen == 0) return NULL; // Empty replace causes infinite loop during count - // Memory allocation for substrings - strings = (char **)malloc(sizeof(char *)*counter); - for (int i = 0; i < counter; i++) strings[i] = (char *)malloc(sizeof(char)*MAX_SUBSTRING_LENGTH); + if (!by) by = ""; // Replace by nothing if not provided + byLen = strlen(by); - char *substrPtr = NULL; - char delimiters[1] = { delimiter }; // Only caring for one delimiter - substrPtr = strtok(strDup, delimiters); + // Count the number of replacements needed + insertPoint = text; + for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen; - for (int i = 0; (i < counter) && (substrPtr != NULL); i++) + // Allocate returning string and point temp to it + temp = result = malloc(strlen(text) + (byLen - replaceLen)*count + 1); + + if (!result) return NULL; // Memory could not be allocated + + // First time through the loop, all the variable are set correctly from here on, + // temp points to the end of the result string + // insertPoint points to the next occurrence of replace in text + // text points to the remainder of text after "end of replace" + while (count--) { - strcpy(strings[i], substrPtr); - substrPtr = strtok(NULL, delimiters); + insertPoint = strstr(text, replace); + lastReplacePos = insertPoint - text; + temp = strncpy(temp, text, lastReplacePos) + lastReplacePos; + temp = strcpy(temp, by) + byLen; + text += lastReplacePos + replaceLen; // Move to next "end of replace" } - *strCount = counter; - free(strDup); - - return strings; -} - -// Check if two text string are equal -bool IsEqualText(const char *text1, const char *text2) -{ - bool result = false; - - if (strcmp(text1, text2) == 0) result = true; + // Copy remaind text part after replacement to result (pointed by moving temp) + strcpy(temp, text); return result; } +// Insert text in a specific position, moves all text forward +// REQUIRES: strlen(), strcpy(), strtok() +// WARNING: Allocated memory should be manually freed +const char *TextInsert(const char *text, const char *insert, int position) +{ + int textLen = strlen(text); + int insertLen = strlen(insert); + + char *result = (char *)malloc(textLen + insertLen + 1); + + for (int i = 0; i < position; i++) result[i] = text[i]; + for (int i = position; i < insertLen + position; i++) result[i] = insert[i]; + for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i]; + + result[textLen + insertLen] = '\0'; // Make sure text string is valid! + + return result; +} + +// Join text strings with delimiter +// REQUIRES: strcat() +const char *TextJoin(const char **textList, int count, const char *delimiter) +{ + static char text[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + memset(text, 0, MAX_TEXT_BUFFER_LENGTH); + + int delimiterLen = strlen(delimiter); + + for (int i = 0; i < count; i++) + { + strcat(text, textList[i]); + if ((delimiterLen > 0) && (i < (count - 1))) strcat(text, delimiter); + } + + return text; +} + +// Split string into multiple strings +// REQUIRES: strlen(), strcpy(), strtok() +// WARNING: Allocated memory should be manually freed +char **TextSplit(const char *text, char delimiter, int *count) +{ + #define MAX_SUBSTRING_LENGTH 128 + + // TODO: Allocate memory properly for every substring size + + char **result = NULL; + + int len = strlen(text); + char *textcopy = (char *)malloc(len + 1); + strcpy(textcopy, text); + int counter = 1; + + // Count how many substrings we have on text and init memory for each of them + for (int i = 0; i < len; i++) if (text[i] == delimiter) counter++; + + // Memory allocation for substrings + result = (char **)malloc(sizeof(char *)*counter); + for (int i = 0; i < counter; i++) result[i] = (char *)malloc(sizeof(char)*MAX_SUBSTRING_LENGTH); + + char *substrPtr = NULL; + char delimiters[1] = { delimiter }; // Only caring for one delimiter + substrPtr = strtok(textcopy, delimiters); + + for (int i = 0; (i < counter) && (substrPtr != NULL); i++) + { + strcpy(result[i], substrPtr); + substrPtr = strtok(NULL, delimiters); + } + + *count = counter; + free(textcopy); + + return result; +} + +// Get pointers to substrings separated by delimiter +void TextSplitEx(const char *text, char delimiter, int *count, const char **ptrs, int *lengths) +{ + int elementsCount = 0; + int charsCount = 0; + + ptrs[0] = text; + + for (int i = 0; text[i] != '\0'; i++) + { + charsCount++; + + if (text[i] == delimiter) + { + lengths[elementsCount] = charsCount - 1; + charsCount = 0; + elementsCount++; + + ptrs[elementsCount] = &text[i + 1]; + } + } + + lengths[elementsCount] = charsCount; + elementsCount++; + + *count = elementsCount; +} + +// Append text at specific position and move cursor! +// REQUIRES: strcpy() +void TextAppend(char *text, const char *append, int *position) +{ + strcpy(text + *position, append); + *position += strlen(append); +} + +// Find first text occurrence within a string +// REQUIRES: strstr() +int TextFindIndex(const char *text, const char *find) +{ + int position = -1; + + char *ptr = strstr(text, find); + + if (ptr != NULL) position = ptr - text; + + return position; +} + +// Get upper case version of provided string +// REQUIRES: toupper() +const char *TextToUpper(const char *text) +{ + static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + + for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) + { + if (text[i] != '\0') buffer[i] = (char)toupper(text[i]); + else { buffer[i] = '\0'; break; } + } + + return buffer; +} + +// Get lower case version of provided string +// REQUIRES: tolower() +const char *TextToLower(const char *text) +{ + static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + + for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) + { + if (text[i] != '\0') buffer[i] = (char)tolower(text[i]); + else { buffer[i] = '\0'; break; } + } + + return buffer; +} + +// Get Pascal case notation version of provided string +// REQUIRES: toupper() +const char *TextToPascal(const char *text) +{ + static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; + + buffer[0] = (char)toupper(text[0]); + + for (int i = 1, j = 1; i < MAX_TEXT_BUFFER_LENGTH; i++, j++) + { + if (text[j] != '\0') + { + if (text[j] != '_') buffer[i] = text[j]; + else + { + j++; + buffer[i] = (char)toupper(text[j]); + } + } + else { buffer[i] = '\0'; break; } + } + + return buffer; +} +//---------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- From 91a7b0e5ef7fbb3f58ebbf93e62512e96a5caf57 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 26 Dec 2018 13:57:10 +0100 Subject: [PATCH 12/58] Corrected typo --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 3cad19fb..a51c957f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -138,7 +138,7 @@ // Temporal hack to avoid breaking old codebases using // deprecated raylib implementation of these functions #define FormatText TextFormat -#define SubText TextSubText +#define SubText TextSubtext //---------------------------------------------------------------------------------- // Structures Definition From 414c3ee1f97c2c939d68b1b99a72feec1e29112a Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 26 Dec 2018 23:44:16 +0100 Subject: [PATCH 13/58] ADDED: DrawTextureQuad() Useful for tiling and offset parameters definition. --- src/raylib.h | 7 ++++--- src/textures.c | 12 ++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index a51c957f..8535f8d5 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1090,8 +1090,9 @@ RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2 RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters RLAPI void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle -RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters -RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle destRec, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely. +RLAPI void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint); // Draw texture quad with tiling and offset parameters +RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters +RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle destRec, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely //------------------------------------------------------------------------------------ // Font Loading and Text Drawing Functions (Module: text) @@ -1120,7 +1121,7 @@ RLAPI int GetGlyphIndex(Font font, int character); // NOTE: Some strings allocate memory internally for returned strings, just be careful! RLAPI bool TextIsEqual(const char *text1, const char *text2); // Check if two text string are equal RLAPI unsigned int TextLength(const char *text); // Get text length, checks for '\0' ending -RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf) +RLAPI const char *TextFormat(const char *text, ...); // Text formatting with variables (sprintf style) RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string RLAPI const char *TextReplace(char *text, const char *replace, const char *by); // Replace text string (memory should be freed!) RLAPI const char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (memory should be freed!) diff --git a/src/textures.c b/src/textures.c index a7120979..726a2a84 100644 --- a/src/textures.c +++ b/src/textures.c @@ -2418,6 +2418,17 @@ void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Co DrawTexturePro(texture, sourceRec, destRec, origin, 0.0f, tint); } +// Draw texture quad with tiling and offset parameters +// NOTE: Tiling and offset should be provided considering normalized texture values [0..1] +// i.e tiling = { 1.0f, 1.0f } refers to all texture, offset = { 0.5f, 0.5f } moves texture origin to center +void DrawTextureQuad(Texture2D texture, Vector2 tiling, Vector2 offset, Rectangle quad, Color tint) +{ + Rectangle source = { offset.x*texture.width, offset.y*texture.height, tiling.x*texture.width, tiling.y*texture.height }; + Vector2 origin = { 0.0f, 0.0f }; + + DrawTexturePro(texture, source, quad, origin, 0.0f, tint); +} + // Draw a part of a texture (defined by a rectangle) with 'pro' parameters // NOTE: origin is relative to destination rectangle size void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, float rotation, Color tint) @@ -2464,6 +2475,7 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V } } +// Draws a texture (or part of it) that stretches or shrinks nicely using n-patch info void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle destRec, Vector2 origin, float rotation, Color tint) { if (texture.id > 0) From a072385c6da533331661b25f6ed24fae0f8fd41f Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 29 Dec 2018 00:00:52 +0100 Subject: [PATCH 14/58] Corrected issue on draws resetting --- src/rlgl.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 632c21f0..7776879a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3961,7 +3961,7 @@ static void DrawBuffersDefault(void) if (eyesCount == 2) SetStereoView(eye, matProjection, matModelView); #endif - // Draw quads buffers + // Draw buffers if (vertexData[currentBuffer].vCounter > 0) { // Set current shader and upload current MVP matrix @@ -4047,10 +4047,14 @@ static void DrawBuffersDefault(void) projection = matProjection; modelview = matModelView; - // Reset draws counter - draws[0].mode = RL_QUADS; - draws[0].vertexCount = 0; - draws[0].textureId = defaultTextureId; + // Reset draws array + for (int i = 0; i < MAX_DRAWCALL_REGISTERED; i++) + { + draws[i].mode = RL_QUADS; + draws[i].vertexCount = 0; + draws[i].textureId = defaultTextureId; + } + drawsCounter = 1; // Change to next buffer in the list From 95d3f24c686c09c458bedeb6486ffc75dfda4170 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 29 Dec 2018 00:29:28 +0100 Subject: [PATCH 15/58] Use QUADS for DrawPolyEx() --- src/shapes.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/shapes.c b/src/shapes.c index 3e0379a3..83cdbe98 100644 --- a/src/shapes.c +++ b/src/shapes.c @@ -503,18 +503,28 @@ void DrawPolyEx(Vector2 *points, int pointsCount, Color color) { if (pointsCount >= 3) { - if (rlCheckBufferLimit(pointsCount)) rlglDraw(); + if (rlCheckBufferLimit((pointsCount - 2)*4)) rlglDraw(); - rlBegin(RL_TRIANGLES); + rlEnableTexture(GetShapesTexture().id); + rlBegin(RL_QUADS); rlColor4ub(color.r, color.g, color.b, color.a); for (int i = 1; i < pointsCount - 1; i++) { + rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); rlVertex2f(points[0].x, points[0].y); + + rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlVertex2f(points[i].x, points[i].y); + + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); + rlVertex2f(points[i + 1].x, points[i + 1].y); + + rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); rlVertex2f(points[i + 1].x, points[i + 1].y); } rlEnd(); + rlDisableTexture(); } } From 0619571149f1fde5500dec4b64a94541ef0981f2 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 29 Dec 2018 14:44:28 +0100 Subject: [PATCH 16/58] ADDED: DrawTextRec() and example --- examples/text/text_draw_inside_rectangle.c | 120 ++++++++++++++++++ examples/text/text_draw_inside_rectangle.png | Bin 0 -> 17844 bytes src/raylib.h | 3 +- src/text.c | 125 +++++++++++++++++++ 4 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 examples/text/text_draw_inside_rectangle.c create mode 100644 examples/text/text_draw_inside_rectangle.png diff --git a/examples/text/text_draw_inside_rectangle.c b/examples/text/text_draw_inside_rectangle.c new file mode 100644 index 00000000..e60fa5e5 --- /dev/null +++ b/examples/text/text_draw_inside_rectangle.c @@ -0,0 +1,120 @@ +/******************************************************************************************* +* +* raylib [text] example - Draw text inside a rectangle +* +* This example has been created using raylib 2.3 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2018 Vlad Adrian (@demizdor) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [text] example - draw text inside a rectangle"); + + char text[] = "Text cannot escape\tthis container\t...word wrap also works when active so here's\ + a long text for testing.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ + tempor incididunt ut labore et dolore magna aliqua. Nec ullamcorper sit amet risus nullam eget felis eget."; + + bool resizing = false; + bool wordWrap = true; + + Rectangle container = { 25, 25, screenWidth - 50, screenHeight - 250}; + Rectangle resizer = { container.x + container.width - 17, container.y + container.height - 17, 14, 14 }; + + // Minimum width and heigh for the container rectangle + const int minWidth = 60; + const int minHeight = 60; + const int maxWidth = screenWidth - 50; + const int maxHeight = screenHeight - 160; + + Vector2 lastMouse = { 0, 0 }; // Stores last mouse coordinates + + Color borderColor = MAROON; // Container border color + + Font font = GetFontDefault(); // Get default system font + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_SPACE)) wordWrap = !wordWrap; + + Vector2 mouse = GetMousePosition(); + + // Check if the mouse is inside the container and toggle border color + if (CheckCollisionPointRec(mouse, container)) borderColor = Fade(MAROON, 0.4f); + else if (!resizing) borderColor = MAROON; + + // Container resizing logic + if (resizing) + { + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) resizing = false; + + int width = container.width + (mouse.x - lastMouse.x); + container.width = (width > minWidth)? ((width < maxWidth)? width : maxWidth) : minWidth; + + int height = container.height + (mouse.y - lastMouse.y); + container.height = (height > minHeight)? ((height < maxHeight)? height : maxHeight) : minHeight; + } + else + { + // Check if we're resizing + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && CheckCollisionPointRec(mouse, resizer)) resizing = true; + } + + // Move resizer rectangle properly + resizer.x = container.x + container.width - 17; + resizer.y = container.y + container.height - 17; + + lastMouse = mouse; // Update mouse + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawRectangleLinesEx(container, 3, borderColor); // Draw container border + + // Draw text in container (add some padding) + DrawTextRec(font, text, + (Rectangle){ container.x + 4, container.y + 4, container.width - 4, container.height - 4 }, + 20.0f, 2.0f, wordWrap, GRAY); + + DrawRectangleRec(resizer, borderColor); // Draw the resize box + + // Draw info + DrawText("Word Wrap: ", 313, screenHeight-115, 20, BLACK); + if (wordWrap) DrawText("ON", 447, screenHeight - 115, 20, RED); + else DrawText("OFF", 447, screenHeight - 115, 20, BLACK); + DrawText("Press [SPACE] to toggle word wrap", 218, screenHeight - 91, 20, GRAY); + + DrawRectangle(0, screenHeight - 54, screenWidth, 54, GRAY); + DrawText("Click hold & drag the to resize the container", 155, screenHeight - 38, 20, RAYWHITE); + DrawRectangleRec((Rectangle){ 382, screenHeight - 34, 12, 12 }, MAROON); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/text/text_draw_inside_rectangle.png b/examples/text/text_draw_inside_rectangle.png new file mode 100644 index 0000000000000000000000000000000000000000..f46b10967d74f6f4e4b194ba9aab1b65c4822c49 GIT binary patch literal 17844 zcmeHPd00~iwhxOCkj)|r!31LzaAA>EkR42dmZc!bM1)ASh9F2aEM*e}16Gy*0)j=r z1SDDzXMzHXBB6?+l>h=o(6X9{>^ow3H;au$P5Vt}-Z$?J|Kz*(s`s9I?m55nJDX>m z9Z|B<5NQwyBx{eha{+>Or7g;r4dcdqcgxUYMU6JW2dY7a~fA zf98h(7Aayl!(?L^3d*1S5eYk8gOfaDP?AKf!GRM$@uQm^T9!8iCFbGQd$h{iDF^t2L zB{AxZgouJwNhlj08rsy*(bMBowPWpoMc6OKo=tCY z6|%|aHyXjvN!L=>+@+0=;4D-+H@(0uIIpoJccVOKlZef@JtrygHB6a$9PHCb0YFIp zey~iv47fD=G-Fi^NKdqS;o(2 zhE#)uNIm)S*EH14_?FZ?$Ghw`9UI>ndbH3xw?hKzhQ8fk3_J7WA9eS|3JBkCRZGpe zBUKq**1y)LS--HKft@lge=XtC@bcalFv@_w&ynePOoZ#!yylLVb(uP`n=Yb*bjlm{ zb5T-kohVscAU&tGxL?O}T5c*7zWU<1`D6LWPV|!yu?)6Uzs7m9!tNdI&R z1B+gIPQG?2$81=kWf_)zndubb+L8(5>#P%H6kE?J-O|7ZsQJpWk7I_jTe1jCIW-u904{?50eKUt&46<5Y_(9)6dNw`rXE$Cxh7i&mFFW zGob-}fZ}>jW4a}_$`)q~7aJaTyh<=_A76Q$mHlEavnBA7d&wy;uP=J&Z+3;~z-O9G z&8YqsR5rrUqWHkY#)hcLu=B0cU+A4IE3_rd^x$tc-ikP=yn+n3>gB1yM~E6k?qBo1 z%f4Lh4?xGS?&rOKF@{tFfBi>WP^4X`K^6b4qP^g{7{AX7Iu3EZ|BwN-_sroQ=hcgC4cuZu28Kh{}^Dn;Uxz z6hz5;%8<>hf1@Z|2)XiE#sNSGmrp$Ze=Y*)ya;P(=rE|8Ar6)j@u7ch(CO^@I8Z93 zU?7DNcsbyqVbk~&c5DP!xf3JCj|-qF8!SPp6^Dm>lB)3lxGa%4H7Xm^)6>IXLXmLL zg%#_P+8Bo%k`&e(Ts6!2xqf9oBT)589^a6yc5toF^`S6*TYX~%PU`DP(+I9hCYgFL zYmrybZcduP%)^#d=1jyfW;YsNA^$9}z#yvLVJ$5=#1nB<4C>UE=}wc=!N;$BoL4un z)292>#h<}H>69)cd0k9}sKp`QNLHB@2307kI#H@(z52m)&G(T7uFOT^hrz-?PIEe1 z0g@emT7rtGp}D7Q&8&xhwN3ror^xNO)1l6$+NyPNzp8E!{y3@D^uDh&7J2mJZD_5c50vqxVt$rUm$c37vxMYjB%ZT;;Y>)I% z=i@q6ww<>K>lY)0tdSpUo1$3)6&(_2{yE)%{=rLkJ?a!i5}CvrIX;XDD!L_M~i&VSmYQ{ItGwOf`t$XmAgKN)n&vQ zCXB?0v;Gs}#4zOQaXzX-99=j}a+5aMPnjwAn#^6EMxF`L$g&6!Q38-TE`Nw+EpX~5 z%Ubx)(SisodUASNFM1SbW^02rFmrYlE$rUzS*zHkMmNk&ELx1wlJZpoc)b@j_ZSPB zY-|1^u<|73+~u;SLo`pQ|3SHF9srVs0H6pbB+M#kYHP!ihzx}q0Q?Etb2Nb%&5Q>X z&YgeJUlXVmf^2AJG#xS!m)OTmX^ZNGm9YTYCsJ~Fqq-dyu`brZ9DbADx#!qSFJ#rF z7W2CKtoFT*_=q08tX9@33>)br$sCItmUlr-OZc(#TfD`TM(BkR8&c| z@(pgIpX_VFpZ;p= zMW)iv2C$VTBU~7xQqr9%u*2 z*iB~f`)|v+Ehf^&qbPOFxJr`_G3Cv?e6m$oJ_#iJXlwJmN5J(5=w>-jnX00(S{HHU zMUXXD$oc{8&1)-J*s{1+fl2!?uO#sqMu_s;YIs&a@v8~OG*I(wH09B!_CKIc63-#b zdpC+Q+spI&N>!lD@*+xyQ)$>_YH`fCarv8{4$DyyaWpwemkGt|>{C?ojgzblM_h)t zeURn#*OF%|X{AcO2Ve!a@?P{CRSz2Dvvm40HCl4ctS<+c`^p3K_OAadxGlk*VYFZ)6%E;$| zA&z+)|>0PVWC1SEMQ{ z(!t}mkA!71iNiqMfp@bdPU>NcCCJy~YV&vTT<)qc1L9P#mn+AARSVW8VolGVH-Ktm zH&sYp2**LSDf1ze)AHrDc`v$=zPIJ33EQ1nB+Rs&(Fx+7Ak;O2HPxKl&%`O7;O1}} zPuDQN_>nOwj5%XfWc_O4&YqrTkS~Ff%alTXNY$m5JB$e39I5w-5kDa;Ud2riSvX+{ z;(Gn{`Q@dQS(3+8U2dA;|PL9m3JUjqwfe|LK0(6r7D1J&@|NW3W} zW8o}ZhTUl4m-w!`k+#lt$XVvazk&=m3)%4iTrYv{XDN?1F8;tbY8-F=H zX7^f}wm0~hBy*f{-?IF%#*I6Oy#ew!CLKz{m66=$49K1bhu9^m6r2L(HN^btZ9Z*R zT(%k|97s3sY__bXN7BqxWMc}}iPAUSb$+f!?gt0CPFbCPg+2R#K`_L4ClW!CycaGM z3JsGo=j@#vqUNmZ?BFZ>dyh9|7A85YJGnlb39q@A0gXH=Pi`5|+=d_!Gw zEF|}b_8ym~vcAW#H?(=VOvr{+scsn)Jz-ql8{a;vT1<$!`a|?js)b`tcoVz9)PtN`N8jLm;#$EsU2??f5&-XT z#=kwzYK-}A1DX0AUnXJ@I5BgAEEQoOPDJrk-uLn2c=@Qvazl>e=l=pNz_;^Zm3c2^ zPed^ii2f?C*|5!HCK#-byvXFNTELw0z+lXHTcE5HzW*{~JpS*bb#GXH22t>_ofx^q z)|yyzaby-YsYsZE0Id|cK~71KqQ|wcI)F33Y%;1v(nDxyWodY{znj?KZ&7oTQ4>D_ z1VA?Co)XX2j|(jJ_eY#A1Awn_NDJ&84Few$eaWKay(bt`gPk%kq1OIzGu`}?2QgTI;2({aKaI`3Mu4P=OU;*tLcBwqd zrrYWbm}S-fj~F>`%md4+ISNs~1n&?AbY_S;)VT|-uUSxeT4|g8)KjaB=aXArp`O12 z!c_?`q1dy8s|Zek{L#8v@=#f!*lrXCJtcp1zLakl_xEe2{_fJgh{ zfB$_1<$unX%Abjr$uzt}AsSV8C0Qt*YN-B+zTkDPAjgZDaY1qmTk4lRBdKnf%jpG~ zPjOJoSS_}1#!HE+g@T|e)gSvMw^>k!EHH)7yCF{Gp>cq?2lD`eDhwbvm~5&i?dmj? zy7G9JwPt2Qkk0k%vJ=MIGig%7%nb1qZ7LFqbB4{h0_Ne9D4b=z_ejz<>N&QM}l4MQAqlM;+*85#WC~?y~$L zc*Y0RU6;*~Q+;06!v6&=)aXGtrH+pnJbkCQeNh z<1d7r@p49T_h+XQ0h^lSCy2tPyOv|?a%|#CBeD_nk0e&nYDFTb=a^%D#w2L{L$zbB}tk`q#Acg33<5 z7q5I*^l_0#P~YcF`oEt)6EOqkNKMwSMwgkZ(_Eacp&{GYyX2jGW=0#g{(zhbO058G0kwh6N>#*MP;cxF^IB7pKfaZsA`BxMpUL0ImiMSg58Kj;Qx{Ca zS@BxOc)|NDIpT~E1G@zu>=TLn-8Q=296>isKpn9`>ObY{kObqFPO0ArA?MtQ^baPl zHRVJwLkCs1%w*+1jZ0J)R6s>KdG#^VB^?*R>2JLk?&Iv@K&92vb;Aeqr@SQb7o4cT z$i-?(RbIkd9qdsgj;g-uSfzpvj88=7ZO&Ac+Q+oUpPT~VnH*4YH265_eh+mEKxYz|6yVRBmnkrZhIU=QRZ$UYU^EJJ;6jtsQv|x| z=8wM%orUH57zk=>IYgcs+-<_pi$b z3&AO8W{rQt*d5{txvSto$3WFBDeBs2uuP`8d7L9-gJJb5)w?Y>vRdavv+fo{E#jv4 z<~PV^jz_nIKFDB8US0ei<FN5qc>3zV>DQ$hS3jbX!pj| z&*I(`PRrPjF)RzhMj!ZpF$8_aNn` z=r)BDu@!c**Zp3i^m{qGTiI9dDLIkDO_6(&0XqxhNsbp<{^7P~fhO!hu)-}=Y)AO6 zVx@Do{{F_5!<*dI%8{LCy7X;_MlWYn(;V+sd{dh6#Jg@lk}v_zb{br;9-O_rZ@yNE zO_RIJ*v&Ka7*ObxZgw( z>ENkcS^)F{2;&}cU|n!Cfr$%_J}`E{#orw*7tc6s;=e=-+p4Rp$@kt=Bk5mNDa z*-DY!ZLOD5zD$K!QIbQ`ciJQmYvV82zDZD{b~}`e!dn-0gDQ~jRHXpj@C4bEnB5wa zk49Hs_9!-RE}UE0T>;fgve(o=$HpVGV-Q z-}pmaJg6X*ow~-V;+trR@EuCcIGxAXGmG8BcVncFxX?35VLqtyXL5a^TgEJ!nqOJX zouqjx``5`nBV`e(G^8 zqc#27_-iZfABGin+YTBw^bs}6^+XE-5p6IVHsmOxppIxe1{&#t8+mRH(x{5UpO&YN zS?q1npt2(KA1O_aUJeO^;rnZG>3#;{Se{DP2z0pQ2vBLFgM-5&iY}Oe0~v~A5ogJM zKcZakf5>Wq9aYXktZU1M7X>X9VBQdz@!_y~etMb;cQ$)GBCF!i`a6AdhJ=B9*k9Pb z9_KlVyj9Z#nHWYjlq_}fLyVA6=ZW${m?(d7H}|ii>%U+?|;#43OEs5axgn c0RjcvZ5LN^_iP6KHx`inE=Rjk8| [0xc2 0xbf](¿) + letter = (unsigned char)text[i + 1]; + index = GetGlyphIndex(font, (int)letter); + i++; + } + else if ((unsigned char)text[i] == 0xc3) // UTF-8 encoding identification HACK! + { + // Support UTF-8 encoded values from [0xc3 0x80](À) -> [0xc3 0xbf](ÿ) + letter = (unsigned char)text[i + 1]; + index = GetGlyphIndex(font, (int)letter + 64); + i++; + } + else index = GetGlyphIndex(font, (unsigned char)text[i]); + + glyphWidth = (font.chars[index].advanceX == 0)? + (int)(font.chars[index].rec.width*scaleFactor + spacing): + (int)(font.chars[index].advanceX*scaleFactor + spacing); + } + + // NOTE: When word wrap is active first we measure a `word`(measure until a ' ','\n','\t' is found) + // then set all the variables back to what they were before the measurement, change the state to + // draw that word then change the state again and repeat until the end of the string...when the word + // doesn't fit inside the rect we simple increase `textOffsetY` to draw it on the next line + if (state == MEASURE_WORD) + { + // Measuring state + if ((letter == ' ') || (letter == '\n') || (letter == '\t') || ((i + 1) == length)) + { + int t = textOffsetX + glyphWidth; + + if (textOffsetX+1>=rec.width) + { + textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor); + lastTextOffsetX = t - lastTextOffsetX; + textOffsetX = 0; + } + else + { + textOffsetX = lastTextOffsetX; + lastTextOffsetX = t; + } + + glyphWidth = 0; + state = !state; // Change state + t = i; + i = firstWord?-1:wordStart; + wordStart = t; + } + } + else + { + // Drawing state + int t = textOffsetX + glyphWidth; + + if (letter == '\n') + { + textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor); + lastTextOffsetX = t - lastTextOffsetX; + if (lastTextOffsetX < 0) lastTextOffsetX = 0; + textOffsetX = 0; + } + else if ((letter != ' ') && (letter != '\t')) + { + if ((t + 1) >= rec.width) + { + textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor); + textOffsetX = 0; + } + + if ((textOffsetY + (int)((font.baseSize + font.baseSize/2)*scaleFactor)) > rec.height) break; + + DrawTexturePro(font.texture, font.chars[index].rec, + (Rectangle){ rec.x + textOffsetX + font.chars[index].offsetX*scaleFactor, + rec.y + textOffsetY + font.chars[index].offsetY*scaleFactor, + font.chars[index].rec.width*scaleFactor, + font.chars[index].rec.height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f, tint); + } + + if (wordWrap) + { + if ((letter == ' ') || (letter == '\n') || (letter == '\t')) + { + // After drawing a word change the state + firstWord = false; + i = wordStart; + textOffsetX = lastTextOffsetX; + glyphWidth = 0; + state = !state; + } + } + } + + textOffsetX += glyphWidth; + } +} + // Measure string width for default font int MeasureText(const char *text, int fontSize) { From d0d99d35034a55b94e273749b4c549da83f35152 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 29 Dec 2018 15:07:03 +0100 Subject: [PATCH 17/58] Corrected issue with MOD/XM loop --- src/audio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/audio.c b/src/audio.c index b4eec113..00e28f2a 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1364,7 +1364,8 @@ void UpdateMusicStream(Music music) UpdateAudioStream(music->stream, pcm, samplesCount); if ((music->ctxType == MUSIC_MODULE_XM) || (music->ctxType == MUSIC_MODULE_MOD)) { - music->samplesLeft -= samplesCount/2; + if (samplesCount > 1) music->samplesLeft -= samplesCount/2; + else music->samplesLeft -= samplesCount; } else music->samplesLeft -= samplesCount; From 9a40529ae74507da20c5e414f8cd7073b83733e8 Mon Sep 17 00:00:00 2001 From: Basem Abughallya Date: Sat, 29 Dec 2018 17:41:41 +0100 Subject: [PATCH 18/58] utils: declare funopen only on Android MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit funopen is only needed on Android, so declare it only there. This fixes following MinGW build failure: utils.c:50:7: error: conflicting types for ‘funopen’ FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), ^~~~~~~ In file included from utils.c:45:0: /usr/include/stdio.h:586:7: note: previous declaration of ‘funopen’ was here FILE *funopen (const void *__cookie, ^~~~~~~ --- src/utils.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils.c b/src/utils.c index b2951040..e891ef72 100644 --- a/src/utils.c +++ b/src/utils.c @@ -46,11 +46,6 @@ #include // Required for: va_list, va_start(), vfprintf(), va_end() #include // Required for: strlen(), strrchr(), strcmp() -/* This should be in , but Travis doesn't find it... */ -FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), - int (*writefn)(void *, const char *, int), - fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *)); - //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -67,6 +62,11 @@ AAssetManager *assetManager; // Module specific Functions Declaration //---------------------------------------------------------------------------------- #if defined(PLATFORM_ANDROID) +/* This should be in , but Travis doesn't find it... */ +FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), + int (*writefn)(void *, const char *, int), + fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *)); + static int android_read(void *cookie, char *buf, int size); static int android_write(void *cookie, const char *buf, int size); static fpos_t android_seek(void *cookie, fpos_t offset, int whence); From 9740b1eb702cc6a7253891be2ebcc7bebb332973 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 1 Jan 2019 20:52:57 +0100 Subject: [PATCH 19/58] Update mini_al to latest version (Web Audio / AAudio backends) --- src/external/mini_al.c | 1 + src/external/mini_al.h | 2179 ++++++++++++++++++++++++++++++---------- 2 files changed, 1626 insertions(+), 554 deletions(-) diff --git a/src/external/mini_al.c b/src/external/mini_al.c index f2805513..d52789a7 100644 --- a/src/external/mini_al.c +++ b/src/external/mini_al.c @@ -3,5 +3,6 @@ #define MINI_AL_IMPLEMENTATION #define MAL_NO_JACK #define MAL_NO_OPENAL +#define MAL_NO_SDL //#define MAL_NO_NULL #include "mini_al.h" diff --git a/src/external/mini_al.h b/src/external/mini_al.h index 384ece56..e892b031 100644 --- a/src/external/mini_al.h +++ b/src/external/mini_al.h @@ -1,16 +1,12 @@ // Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file. -// mini_al - v0.8.11 - 2018-11-21 +// mini_al - v0.8.15 - 201x-xx-xx // // David Reid - davidreidsoftware@gmail.com // ABOUT // ===== -// mini_al is a small library for making it easy to connect to a playback or capture device and send -// or receive data from that device. -// -// mini_al uses an asynchronous API. Every device is created with it's own thread, with audio data -// being delivered to or from the device via a callback. Synchronous APIs are not supported in the -// interest of keeping the library as simple and light-weight as possible. +// mini_al is a single file library for audio playback and capture. It's written in C (compilable as +// C++) and released into the public domain. // // Supported Backends: // - WASAPI @@ -23,9 +19,9 @@ // - sndio (OpenBSD) // - audio(4) (NetBSD and OpenBSD) // - OSS (FreeBSD) +// - AAudio (Android 8.0+) // - OpenSL|ES (Android only) -// - OpenAL -// - SDL +// - Web Audio (Emscripten) // - Null (Silence) // // Supported Formats: @@ -44,42 +40,10 @@ // // You can then #include this file in other parts of the program as you would with any other header file. // -// If you want to disable a specific backend, #define the appropriate MAL_NO_* option before the implementation. -// -// Note that GCC and Clang requires "-msse2", "-mavx2", etc. for SIMD optimizations. -// -// -// Building for Windows -// -------------------- -// The Windows build should compile clean on all popular compilers without the need to configure any include paths -// nor link to any libraries. -// -// Building for macOS and iOS -// -------------------------- -// The macOS build should compile clean without the need to download any dependencies or link to any libraries or -// frameworks. The iOS build needs to be compiled as Objective-C (sorry) and will need to link the relevant frameworks -// but should Just Work with Xcode. -// -// Building for Linux -// ------------------ -// The Linux build only requires linking to -ldl, -lpthread and -lm. You do not need any development packages for any -// of the supported backends. -// -// Building for BSD -// ---------------- -// The BSD build only requires linking to -ldl, -lpthread and -lm. NetBSD uses audio(4), OpenBSD uses sndio and -// FreeBSD uses OSS. -// -// Building for Android -// -------------------- -// The Android build uses OpenSL|ES, and will require an appropriate API level that supports OpenSL|ES. mini_al has -// been tested against API levels 16 and 21. -// -// Building for Emscripten -// ----------------------- -// The Emscripten build currently uses SDL 1.2 for it's backend which means specifying "-s USE_SDL=2" is unecessary -// as of this version. -// +// mini_al uses an asynchronous, callback based API. You initialize a device with a configuration (sample rate, +// channel count, etc.) which includes the callback you want to use to handle data transmission to/from the +// device. In the callback you either read from a data pointer in the case of playback or write to it in the case +// of capture. // // Playback Example // ---------------- @@ -111,6 +75,42 @@ // // // +// If you want to disable a specific backend, #define the appropriate MAL_NO_* option before the implementation. +// +// Note that GCC and Clang requires "-msse2", "-mavx2", etc. for SIMD optimizations. +// +// +// Building for Windows +// -------------------- +// The Windows build should compile clean on all popular compilers without the need to configure any include paths +// nor link to any libraries. +// +// Building for macOS and iOS +// -------------------------- +// The macOS build should compile clean without the need to download any dependencies or link to any libraries or +// frameworks. The iOS build needs to be compiled as Objective-C (sorry) and will need to link the relevant frameworks +// but should Just Work with Xcode. +// +// Building for Linux +// ------------------ +// The Linux build only requires linking to -ldl, -lpthread and -lm. You do not need any development packages. +// +// Building for BSD +// ---------------- +// The BSD build only requires linking to -ldl, -lpthread and -lm. NetBSD uses audio(4), OpenBSD uses sndio and +// FreeBSD uses OSS. +// +// Building for Android +// -------------------- +// AAudio is the highest priority backend on Android. This should work out out of the box without needing any kind of +// compiler configuration. Support for AAudio starts with Android 8 which means older versions will fall back to +// OpenSL|ES which requires API level 16+. +// +// Building for Emscripten +// ----------------------- +// The Emscripten build emits Web Audio JavaScript directly and should Just Work without any configuration. +// +// // NOTES // ===== // - This library uses an asynchronous API for delivering and requesting audio data. Each device will have @@ -127,7 +127,6 @@ // mode. // // -// // BACKEND NUANCES // =============== // @@ -141,7 +140,12 @@ // ------- // - To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: // -// - Only a single mal_context can be active at any given time. This is due to a limitation with OpenSL|ES. +// - With OpenSL|ES, only a single mal_context can be active at any given time. This is due to a limitation with OpenSL|ES. +// - With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration API (devices are +// enumerated through Java). You can however perform your own device enumeration through Java and then set the ID in the +// mal_device_id structure (mal_device_id.aaudio) and pass it to mal_device_init(). +// - The backend API will perform resampling where possible. The reason for this as opposed to using mini_al's built-in +// resampler is to take advantage of any potential device-specific optimizations the driver may implement. // // UWP // --- @@ -154,6 +158,15 @@ // // // +// Web Audio / Emscripten +// ---------------------- +// - The first time a context is initialized it will create a global object called "mal" whose primary purpose is to act +// as a factory for device objects. +// - Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as they've been deprecated. +// - Google is implementing a policy in their browsers that prevent automatic media output without first receiving some kind +// of user input. See here for details: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting +// the device may fail if you try to start playback without first handling some kind of user input. +// // OpenAL // ------ // - Capture is not supported on iOS with OpenAL. Use the Core Audio backend instead. @@ -193,9 +206,15 @@ // #define MAL_NO_OSS // Disables the OSS backend. // +// #define MAL_NO_AAUDIO +// Disables the AAudio backend. +// // #define MAL_NO_OPENSL // Disables the OpenSL|ES backend. // +// #define MAL_NO_WEBAUDIO +// Disables the Web Audio backend. +// // #define MAL_NO_OPENAL // Disables the OpenAL backend. // @@ -576,8 +595,10 @@ typedef enum typedef enum { - mal_channel_mix_mode_planar_blend = 0, // Simple averaging based on the plane(s) the channel is sitting on. + mal_channel_mix_mode_rectangular = 0, // Simple averaging based on the plane(s) the channel is sitting on. mal_channel_mix_mode_simple, // Drop excess channels; zeroed out extra channels. + mal_channel_mix_mode_custom_weights, // Use custom weights specified in mal_channel_router_config. + mal_channel_mix_mode_planar_blend = mal_channel_mix_mode_rectangular, mal_channel_mix_mode_default = mal_channel_mix_mode_planar_blend } mal_channel_mix_mode; @@ -590,6 +611,7 @@ typedef enum mal_standard_channel_map_vorbis, mal_standard_channel_map_sound4, // FreeBSD's sound(4). mal_standard_channel_map_sndio, // www.sndio.org/tips.html + mal_standard_channel_map_webaudio = mal_standard_channel_map_flac, // https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions. mal_standard_channel_map_default = mal_standard_channel_map_microsoft } mal_standard_channel_map; @@ -645,6 +667,7 @@ typedef struct mal_channel channelMapIn[MAL_MAX_CHANNELS]; mal_channel channelMapOut[MAL_MAX_CHANNELS]; mal_channel_mix_mode mixingMode; + float weights[MAL_MAX_CHANNELS][MAL_MAX_CHANNELS]; // [in][out]. Only used when mixingMode is set to mal_channel_mix_mode_custom_weights. mal_bool32 noSSE2 : 1; mal_bool32 noAVX2 : 1; mal_bool32 noAVX512 : 1; @@ -663,7 +686,6 @@ struct mal_channel_router mal_bool32 useAVX512 : 1; mal_bool32 useNEON : 1; mal_uint8 shuffleTable[MAL_MAX_CHANNELS]; - float weights[MAL_MAX_CHANNELS][MAL_MAX_CHANNELS]; }; @@ -918,8 +940,8 @@ mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_f // 2) Planar Blending // Channels are blended based on a set of planes that each speaker emits audio from. // -// Planar Blending -// --------------- +// Rectangular / Planar Blending +// ----------------------------- // In this mode, channel positions are associated with a set of planes where the channel conceptually emits audio from. An example is the front/left speaker. // This speaker is positioned to the front of the listener, so you can think of it as emitting audio from the front plane. It is also positioned to the left // of the listener so you can think of it as also emitting audio from the left plane. Now consider the (unrealistic) situation where the input channel map @@ -949,7 +971,8 @@ mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_f // Note that input and output data is always deinterleaved 32-bit floating point. // // Initialize the channel router with mal_channel_router_init(). You will need to pass in a config object which specifies the input and output configuration, -// mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing. +// mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing. Note +// that the mixing mode is only used when a 1:1 mapping is unavailable. This includes the custom weights mode. // // Read data from the channel router with mal_channel_router_read_deinterleaved(). Output data is always 32-bit floating point. // @@ -1125,10 +1148,10 @@ void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dithe void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode); // Deinterleaves an interleaved buffer. -void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames); +void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames); // Interleaves a group of deinterleaved buffers. -void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames); +void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames); ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1162,6 +1185,7 @@ void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint3 #define MAL_SUPPORT_JACK #endif #if defined(MAL_ANDROID) + #define MAL_SUPPORT_AAUDIO #define MAL_SUPPORT_OPENSL #endif #if defined(__OpenBSD__) // <-- Change this to "#if defined(MAL_BSD)" to enable sndio on all BSD flavors. @@ -1177,13 +1201,15 @@ void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint3 #if defined(MAL_APPLE) #define MAL_SUPPORT_COREAUDIO #endif - -#define MAL_SUPPORT_SDL // All platforms support SDL. +#if defined(MAL_EMSCRIPTEN) + #define MAL_SUPPORT_WEBAUDIO +#endif // Explicitly disable OpenAL and Null backends for Emscripten because they both use a background thread which is not properly supported right now. #if !defined(MAL_EMSCRIPTEN) +#define MAL_SUPPORT_SDL #define MAL_SUPPORT_OPENAL -#define MAL_SUPPORT_NULL // All platforms support the null backend. +#define MAL_SUPPORT_NULL #endif @@ -1217,9 +1243,15 @@ void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint3 #if !defined(MAL_NO_OSS) && defined(MAL_SUPPORT_OSS) #define MAL_ENABLE_OSS #endif +#if !defined(MAL_NO_AAUDIO) && defined(MAL_SUPPORT_AAUDIO) + #define MAL_ENABLE_AAUDIO +#endif #if !defined(MAL_NO_OPENSL) && defined(MAL_SUPPORT_OPENSL) #define MAL_ENABLE_OPENSL #endif +#if !defined(MAL_NO_WEBAUDIO) && defined(MAL_SUPPORT_WEBAUDIO) + #define MAL_ENABLE_WEBAUDIO +#endif #if !defined(MAL_NO_OPENAL) && defined(MAL_SUPPORT_OPENAL) #define MAL_ENABLE_OPENAL #endif @@ -1254,7 +1286,9 @@ typedef enum mal_backend_sndio, mal_backend_audio4, mal_backend_oss, + mal_backend_aaudio, mal_backend_opensl, + mal_backend_webaudio, mal_backend_openal, mal_backend_sdl } mal_backend; @@ -1290,7 +1324,6 @@ typedef struct pthread_t thread; } posix; #endif - int _unused; }; } mal_thread; @@ -1313,7 +1346,6 @@ typedef struct pthread_mutex_t mutex; } posix; #endif - int _unused; }; } mal_mutex; @@ -1338,7 +1370,6 @@ typedef struct mal_uint32 value; } posix; #endif - int _unused; }; } mal_event; @@ -1396,9 +1427,15 @@ typedef union #ifdef MAL_SUPPORT_OSS char oss[64]; // "dev/dsp0", etc. "dev/dsp" for the default device. #endif +#ifdef MAL_SUPPORT_AAUDIO + mal_int32 aaudio; // AAudio uses a 32-bit integer for identification. +#endif #ifdef MAL_SUPPORT_OPENSL mal_uint32 opensl; // OpenSL|ES uses a 32-bit unsigned integer for identification. #endif +#ifdef MAL_SUPPORT_WEBAUDIO + char webaudio[32]; // Web Audio always uses default devices for now, but if this changes it'll be a GUID. +#endif #ifdef MAL_SUPPORT_OPENAL char openal[256]; // OpenAL seems to use human-readable device names as the ID. #endif @@ -1432,7 +1469,11 @@ typedef struct typedef struct { - mal_int64 counter; + union + { + mal_int64 counter; + double counterD; + }; } mal_timer; typedef struct @@ -1699,6 +1740,7 @@ struct mal_context mal_proc AudioOutputUnitStart; mal_proc AudioOutputUnitStop; mal_proc AudioUnitAddPropertyListener; + mal_proc AudioUnitGetPropertyInfo; mal_proc AudioUnitGetProperty; mal_proc AudioUnitSetProperty; mal_proc AudioUnitInitialize; @@ -1741,12 +1783,48 @@ struct mal_context int versionMinor; } oss; #endif +#ifdef MAL_SUPPORT_AAUDIO + struct + { + mal_handle hAAudio; /* libaaudio.so */ + mal_proc AAudio_createStreamBuilder; + mal_proc AAudioStreamBuilder_delete; + mal_proc AAudioStreamBuilder_setDeviceId; + mal_proc AAudioStreamBuilder_setDirection; + mal_proc AAudioStreamBuilder_setSharingMode; + mal_proc AAudioStreamBuilder_setFormat; + mal_proc AAudioStreamBuilder_setChannelCount; + mal_proc AAudioStreamBuilder_setSampleRate; + mal_proc AAudioStreamBuilder_setBufferCapacityInFrames; + mal_proc AAudioStreamBuilder_setFramesPerDataCallback; + mal_proc AAudioStreamBuilder_setDataCallback; + mal_proc AAudioStreamBuilder_setPerformanceMode; + mal_proc AAudioStreamBuilder_openStream; + mal_proc AAudioStream_close; + mal_proc AAudioStream_getState; + mal_proc AAudioStream_waitForStateChange; + mal_proc AAudioStream_getFormat; + mal_proc AAudioStream_getChannelCount; + mal_proc AAudioStream_getSampleRate; + mal_proc AAudioStream_getBufferCapacityInFrames; + mal_proc AAudioStream_getFramesPerDataCallback; + mal_proc AAudioStream_getFramesPerBurst; + mal_proc AAudioStream_requestStart; + mal_proc AAudioStream_requestStop; + } aaudio; +#endif #ifdef MAL_SUPPORT_OPENSL struct { int _unused; } opensl; #endif +#ifdef MAL_SUPPORT_WEBAUDIO + struct + { + int _unused; + } webaudio; +#endif #ifdef MAL_SUPPORT_OPENAL struct { @@ -1840,16 +1918,11 @@ struct mal_context mal_handle hSDL; // SDL mal_proc SDL_InitSubSystem; mal_proc SDL_QuitSubSystem; - mal_proc SDL_CloseAudio; - mal_proc SDL_OpenAudio; - mal_proc SDL_PauseAudio; mal_proc SDL_GetNumAudioDevices; mal_proc SDL_GetAudioDeviceName; mal_proc SDL_CloseAudioDevice; mal_proc SDL_OpenAudioDevice; mal_proc SDL_PauseAudioDevice; - - mal_bool32 usingSDL1; } sdl; #endif #ifdef MAL_SUPPORT_NULL @@ -2030,6 +2103,7 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device /*AudioComponent*/ mal_ptr component; // <-- Can this be per-context? /*AudioUnit*/ mal_ptr audioUnit; /*AudioBufferList**/ mal_ptr pAudioBufferList; // Only used for input devices. + mal_event stopEvent; mal_bool32 isSwitchingDevice; /* <-- Set to true when the default device has changed and mini_al is in the process of switching. */ } coreaudio; #endif @@ -2060,6 +2134,12 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device void* pIntermediaryBuffer; } oss; #endif +#ifdef MAL_SUPPORT_AAUDIO + struct + { + /*AAudioStream**/ mal_ptr pStream; + } aaudio; +#endif #ifdef MAL_SUPPORT_OPENSL struct { @@ -2075,6 +2155,12 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device mal_uint8* pBuffer; // This is malloc()'d and is used for storing audio data. Typed as mal_uint8 for easy offsetting. } opensl; #endif +#ifdef MAL_SUPPORT_WEBAUDIO + struct + { + int index; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */ + } webaudio; +#endif #ifdef MAL_SUPPORT_OPENAL struct { @@ -2130,9 +2216,9 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device // - PulseAudio // - ALSA // - JACK +// - AAudio // - OpenSL|ES -// - OpenAL -// - SDL +// - Web Audio / Emscripten // - Null // // is used to configure the context. Use the onLog config to set a callback for whenever a @@ -2704,6 +2790,10 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount #include #endif +#ifdef MAL_EMSCRIPTEN +#include +#endif + #if !defined(MAL_64BIT) && !defined(MAL_32BIT) #ifdef _WIN32 #ifdef _WIN64 @@ -3749,11 +3839,17 @@ mal_uint32 mal_get_standard_sample_rate_priority_index(mal_uint32 sampleRate) #define MAL_HAS_AUDIO4 // When enabled, always assume audio(4) is available. #endif #ifdef MAL_ENABLE_OSS - #define MAL_HAS_OSS // OSS is the only supported backend for Unix and BSD, so it must be present else this library is useless. + #define MAL_HAS_OSS +#endif +#ifdef MAL_ENABLE_AAUDIO + #define MAL_HAS_AAUDIO #endif #ifdef MAL_ENABLE_OPENSL #define MAL_HAS_OPENSL // OpenSL is the only supported backend for Android. It must be present. #endif +#ifdef MAL_ENABLE_WEBAUDIO + #define MAL_HAS_WEBAUDIO +#endif #ifdef MAL_ENABLE_OPENAL #define MAL_HAS_OPENAL #ifdef MAL_NO_RUNTIME_LINKING @@ -3797,7 +3893,9 @@ const mal_backend g_malDefaultBackends[] = { mal_backend_pulseaudio, mal_backend_alsa, mal_backend_jack, + mal_backend_aaudio, mal_backend_opensl, + mal_backend_webaudio, mal_backend_openal, mal_backend_sdl, mal_backend_null @@ -3818,7 +3916,9 @@ const char* mal_get_backend_name(mal_backend backend) case mal_backend_sndio: return "sndio"; case mal_backend_audio4: return "audio(4)"; case mal_backend_oss: return "OSS"; + case mal_backend_aaudio: return "AAudio"; case mal_backend_opensl: return "OpenSL|ES"; + case mal_backend_webaudio: return "Web Audio"; case mal_backend_openal: return "OpenAL"; case mal_backend_sdl: return "SDL"; default: return "Unknown"; @@ -3909,6 +4009,16 @@ double mal_timer_get_time_in_seconds(mal_timer* pTimer) return (newTimeCounter - oldTimeCounter) / g_mal_TimerFrequency; } +#elif defined(MAL_EMSCRIPTEN) +void mal_timer_init(mal_timer* pTimer) +{ + pTimer->counterD = emscripten_get_now(); +} + +double mal_timer_get_time_in_seconds(mal_timer* pTimer) +{ + return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */ +} #else #if defined(CLOCK_MONOTONIC) #define MAL_CLOCK_ID CLOCK_MONOTONIC @@ -4172,7 +4282,12 @@ void mal_thread_wait__posix(mal_thread* pThread) void mal_sleep__posix(mal_uint32 milliseconds) { - usleep(milliseconds * 1000); // <-- usleep is in microseconds. +#ifdef MAL_EMSCRIPTEN + (void)milliseconds; + mal_assert(MAL_FALSE); /* The Emscripten build should never sleep. */ +#else + usleep(milliseconds * 1000); /* <-- usleep is in microseconds. */ +#endif } @@ -4852,7 +4967,7 @@ mal_result mal_device_init__null(mal_context* pContext, mal_device_type type, co return MAL_SUCCESS; } -mal_result mal_device__start_backend__null(mal_device* pDevice) +mal_result mal_device_start__null(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -4862,7 +4977,7 @@ mal_result mal_device__start_backend__null(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__null(mal_device* pDevice) +mal_result mal_device_stop__null(mal_device* pDevice) { mal_assert(pDevice != NULL); (void)pDevice; @@ -4870,7 +4985,7 @@ mal_result mal_device__stop_backend__null(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__break_main_loop__null(mal_device* pDevice) +mal_result mal_device_break_main_loop__null(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -4948,7 +5063,7 @@ mal_uint32 mal_device__wait_for_frames__null(mal_device* pDevice) return mal_device__get_available_frames__null(pDevice); } -mal_result mal_device__main_loop__null(mal_device* pDevice) +mal_result mal_device_main_loop__null(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -5009,10 +5124,10 @@ mal_result mal_context_init__null(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__null; pContext->onDeviceInit = mal_device_init__null; pContext->onDeviceUninit = mal_device_uninit__null; - pContext->onDeviceStart = mal_device__start_backend__null; - pContext->onDeviceStop = mal_device__stop_backend__null; - pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__null; - pContext->onDeviceMainLoop = mal_device__main_loop__null; + pContext->onDeviceStart = mal_device_start__null; + pContext->onDeviceStop = mal_device_stop__null; + pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__null; + pContext->onDeviceMainLoop = mal_device_main_loop__null; // The null backend always works. return MAL_SUCCESS; @@ -6978,7 +7093,7 @@ done: } } -mal_result mal_device__start_backend__wasapi(mal_device* pDevice) +mal_result mal_device_start__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -7006,7 +7121,7 @@ mal_result mal_device__start_backend__wasapi(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__wasapi(mal_device* pDevice) +mal_result mal_device_stop__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -7028,7 +7143,7 @@ mal_result mal_device__stop_backend__wasapi(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__break_main_loop__wasapi(mal_device* pDevice) +mal_result mal_device_break_main_loop__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -7148,7 +7263,7 @@ mal_result mal_device__wait_for_frames__wasapi(mal_device* pDevice, mal_uint32* return mal_device__get_available_frames__wasapi(pDevice, pFrameCount); } -mal_result mal_device__main_loop__wasapi(mal_device* pDevice) +mal_result mal_device_main_loop__wasapi(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -7262,10 +7377,10 @@ mal_result mal_context_init__wasapi(mal_context* pContext) pContext->onDeviceInit = mal_device_init__wasapi; pContext->onDeviceUninit = mal_device_uninit__wasapi; pContext->onDeviceReinit = mal_device_reinit__wasapi; - pContext->onDeviceStart = mal_device__start_backend__wasapi; - pContext->onDeviceStop = mal_device__stop_backend__wasapi; - pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__wasapi; - pContext->onDeviceMainLoop = mal_device__main_loop__wasapi; + pContext->onDeviceStart = mal_device_start__wasapi; + pContext->onDeviceStop = mal_device_stop__wasapi; + pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__wasapi; + pContext->onDeviceMainLoop = mal_device_main_loop__wasapi; return result; } @@ -8366,7 +8481,7 @@ mal_result mal_device_init__dsound(mal_context* pContext, mal_device_type type, } -mal_result mal_device__start_backend__dsound(mal_device* pDevice) +mal_result mal_device_start__dsound(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -8400,7 +8515,7 @@ mal_result mal_device__start_backend__dsound(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__dsound(mal_device* pDevice) +mal_result mal_device_stop__dsound(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -8419,7 +8534,7 @@ mal_result mal_device__stop_backend__dsound(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__break_main_loop__dsound(mal_device* pDevice) +mal_result mal_device_break_main_loop__dsound(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -8522,7 +8637,7 @@ mal_uint32 mal_device__wait_for_frames__dsound(mal_device* pDevice) return mal_device__get_available_frames__dsound(pDevice); } -mal_result mal_device__main_loop__dsound(mal_device* pDevice) +mal_result mal_device_main_loop__dsound(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -8609,10 +8724,10 @@ mal_result mal_context_init__dsound(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__dsound; pContext->onDeviceInit = mal_device_init__dsound; pContext->onDeviceUninit = mal_device_uninit__dsound; - pContext->onDeviceStart = mal_device__start_backend__dsound; - pContext->onDeviceStop = mal_device__stop_backend__dsound; - pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__dsound; - pContext->onDeviceMainLoop = mal_device__main_loop__dsound; + pContext->onDeviceStart = mal_device_start__dsound; + pContext->onDeviceStop = mal_device_stop__dsound; + pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__dsound; + pContext->onDeviceMainLoop = mal_device_main_loop__dsound; return MAL_SUCCESS; } @@ -9204,7 +9319,7 @@ on_error: } -mal_result mal_device__start_backend__winmm(mal_device* pDevice) +mal_result mal_device_start__winmm(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -9272,7 +9387,7 @@ mal_result mal_device__start_backend__winmm(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__winmm(mal_device* pDevice) +mal_result mal_device_stop__winmm(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -9311,7 +9426,7 @@ mal_result mal_device__stop_backend__winmm(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__break_main_loop__winmm(mal_device* pDevice) +mal_result mal_device_break_main_loop__winmm(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -9321,7 +9436,7 @@ mal_result mal_device__break_main_loop__winmm(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__main_loop__winmm(mal_device* pDevice) +mal_result mal_device_main_loop__winmm(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -9473,10 +9588,10 @@ mal_result mal_context_init__winmm(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__winmm; pContext->onDeviceInit = mal_device_init__winmm; pContext->onDeviceUninit = mal_device_uninit__winmm; - pContext->onDeviceStart = mal_device__start_backend__winmm; - pContext->onDeviceStop = mal_device__stop_backend__winmm; - pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__winmm; - pContext->onDeviceMainLoop = mal_device__main_loop__winmm; + pContext->onDeviceStart = mal_device_start__winmm; + pContext->onDeviceStop = mal_device_stop__winmm; + pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__winmm; + pContext->onDeviceMainLoop = mal_device_main_loop__winmm; return MAL_SUCCESS; } @@ -11005,7 +11120,7 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, co } -mal_result mal_device__start_backend__alsa(mal_device* pDevice) +mal_result mal_device_start__alsa(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -11036,7 +11151,7 @@ mal_result mal_device__start_backend__alsa(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__alsa(mal_device* pDevice) +mal_result mal_device_stop__alsa(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -11044,7 +11159,7 @@ mal_result mal_device__stop_backend__alsa(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__break_main_loop__alsa(mal_device* pDevice) +mal_result mal_device_break_main_loop__alsa(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -11057,7 +11172,7 @@ mal_result mal_device__break_main_loop__alsa(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__main_loop__alsa(mal_device* pDevice) +mal_result mal_device_main_loop__alsa(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -11283,10 +11398,10 @@ mal_result mal_context_init__alsa(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__alsa; pContext->onDeviceInit = mal_device_init__alsa; pContext->onDeviceUninit = mal_device_uninit__alsa; - pContext->onDeviceStart = mal_device__start_backend__alsa; - pContext->onDeviceStop = mal_device__stop_backend__alsa; - pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__alsa; - pContext->onDeviceMainLoop = mal_device__main_loop__alsa; + pContext->onDeviceStart = mal_device_start__alsa; + pContext->onDeviceStop = mal_device_stop__alsa; + pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__alsa; + pContext->onDeviceMainLoop = mal_device_main_loop__alsa; return MAL_SUCCESS; } @@ -12794,7 +12909,7 @@ mal_result mal_device__cork_stream__pulse(mal_device* pDevice, int cork) return MAL_SUCCESS; } -mal_result mal_device__start_backend__pulse(mal_device* pDevice) +mal_result mal_device_start__pulse(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -12826,7 +12941,7 @@ mal_result mal_device__start_backend__pulse(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__pulse(mal_device* pDevice) +mal_result mal_device_stop__pulse(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -12865,7 +12980,7 @@ mal_result mal_device__stop_backend__pulse(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__break_main_loop__pulse(mal_device* pDevice) +mal_result mal_device_break_main_loop__pulse(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -12878,7 +12993,7 @@ mal_result mal_device__break_main_loop__pulse(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__main_loop__pulse(mal_device* pDevice) +mal_result mal_device_main_loop__pulse(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -13071,10 +13186,10 @@ mal_result mal_context_init__pulse(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__pulse; pContext->onDeviceInit = mal_device_init__pulse; pContext->onDeviceUninit = mal_device_uninit__pulse; - pContext->onDeviceStart = mal_device__start_backend__pulse; - pContext->onDeviceStop = mal_device__stop_backend__pulse; - pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__pulse; - pContext->onDeviceMainLoop = mal_device__main_loop__pulse; + pContext->onDeviceStart = mal_device_start__pulse; + pContext->onDeviceStop = mal_device_stop__pulse; + pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__pulse; + pContext->onDeviceMainLoop = mal_device_main_loop__pulse; // Although we have found the libpulse library, it doesn't necessarily mean PulseAudio is useable. We need to initialize @@ -13462,7 +13577,7 @@ mal_result mal_device_init__jack(mal_context* pContext, mal_device_type type, co } -mal_result mal_device__start_backend__jack(mal_device* pDevice) +mal_result mal_device_start__jack(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -13511,7 +13626,7 @@ mal_result mal_device__start_backend__jack(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__jack(mal_device* pDevice) +mal_result mal_device_stop__jack(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -13632,8 +13747,8 @@ mal_result mal_context_init__jack(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__jack; pContext->onDeviceInit = mal_device_init__jack; pContext->onDeviceUninit = mal_device_uninit__jack; - pContext->onDeviceStart = mal_device__start_backend__jack; - pContext->onDeviceStop = mal_device__stop_backend__jack; + pContext->onDeviceStart = mal_device_start__jack; + pContext->onDeviceStop = mal_device_stop__jack; // Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting @@ -13691,6 +13806,7 @@ typedef OSStatus (* mal_AudioComponentInstanceNew_proc)(AudioComponent inCompone typedef OSStatus (* mal_AudioOutputUnitStart_proc)(AudioUnit inUnit); typedef OSStatus (* mal_AudioOutputUnitStop_proc)(AudioUnit inUnit); typedef OSStatus (* mal_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); +typedef OSStatus (* mal_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable); typedef OSStatus (* mal_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); typedef OSStatus (* mal_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); typedef OSStatus (* mal_AudioUnitInitialize_proc)(AudioUnit inUnit); @@ -13943,6 +14059,80 @@ mal_result mal_format_from_AudioStreamBasicDescription(const AudioStreamBasicDes return MAL_FORMAT_NOT_SUPPORTED; } +mal_result mal_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + mal_assert(pChannelLayout != NULL); + + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { + for (UInt32 iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions; ++iChannel) { + channelMap[iChannel] = mal_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); + } + } else +#if 0 + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { + // This is the same kind of system that's used by Windows audio APIs. + UInt32 iChannel = 0; + AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; + for (UInt32 iBit = 0; iBit < 32; ++iBit) { + AudioChannelBitmap bit = bitmap & (1 << iBit); + if (bit != 0) { + channelMap[iChannel++] = mal_channel_from_AudioChannelBit(bit); + } + } + } else +#endif + { + // Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should + // be updated to determine the mapping based on the tag. + UInt32 channelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); + switch (pChannelLayout->mChannelLayoutTag) + { + case kAudioChannelLayoutTag_Mono: + case kAudioChannelLayoutTag_Stereo: + case kAudioChannelLayoutTag_StereoHeadphones: + case kAudioChannelLayoutTag_MatrixStereo: + case kAudioChannelLayoutTag_MidSide: + case kAudioChannelLayoutTag_XY: + case kAudioChannelLayoutTag_Binaural: + case kAudioChannelLayoutTag_Ambisonic_B_Format: + { + mal_get_standard_channel_map(mal_standard_channel_map_default, channelCount, channelMap); + } break; + + case kAudioChannelLayoutTag_Octagonal: + { + channelMap[7] = MAL_CHANNEL_SIDE_RIGHT; + channelMap[6] = MAL_CHANNEL_SIDE_LEFT; + } // Intentional fallthrough. + case kAudioChannelLayoutTag_Hexagonal: + { + channelMap[5] = MAL_CHANNEL_BACK_CENTER; + } // Intentional fallthrough. + case kAudioChannelLayoutTag_Pentagonal: + { + channelMap[4] = MAL_CHANNEL_FRONT_CENTER; + } // Intentional fallghrough. + case kAudioChannelLayoutTag_Quadraphonic: + { + channelMap[3] = MAL_CHANNEL_BACK_RIGHT; + channelMap[2] = MAL_CHANNEL_BACK_LEFT; + channelMap[1] = MAL_CHANNEL_RIGHT; + channelMap[0] = MAL_CHANNEL_LEFT; + } break; + + // TODO: Add support for more tags here. + + default: + { + mal_get_standard_channel_map(mal_standard_channel_map_default, channelCount, channelMap); + } break; + } + } + + return MAL_SUCCESS; +} + + #if defined(MAL_APPLE_DESKTOP) mal_result mal_get_device_object_ids__coreaudio(mal_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) // NOTE: Free the returned buffer with mal_free(). { @@ -14124,7 +14314,6 @@ mal_result mal_get_AudioObject_stream_descriptions(mal_context* pContext, AudioO } - mal_result mal_get_AudioObject_channel_layout(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, AudioChannelLayout** ppChannelLayout) // NOTE: Free the returned pointer with mal_free(). { mal_assert(pContext != NULL); @@ -14183,79 +14372,6 @@ mal_result mal_get_AudioObject_channel_count(mal_context* pContext, AudioObjectI return MAL_SUCCESS; } -mal_result mal_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, mal_channel channelMap[MAL_MAX_CHANNELS]) -{ - mal_assert(pChannelLayout != NULL); - - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { - for (UInt32 iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions; ++iChannel) { - channelMap[iChannel] = mal_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); - } - } else -#if 0 - if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { - // This is the same kind of system that's used by Windows audio APIs. - UInt32 iChannel = 0; - AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; - for (UInt32 iBit = 0; iBit < 32; ++iBit) { - AudioChannelBitmap bit = bitmap & (1 << iBit); - if (bit != 0) { - channelMap[iChannel++] = mal_channel_from_AudioChannelBit(bit); - } - } - } else -#endif - { - // Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should - // be updated to determine the mapping based on the tag. - UInt32 channelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); - switch (pChannelLayout->mChannelLayoutTag) - { - case kAudioChannelLayoutTag_Mono: - case kAudioChannelLayoutTag_Stereo: - case kAudioChannelLayoutTag_StereoHeadphones: - case kAudioChannelLayoutTag_MatrixStereo: - case kAudioChannelLayoutTag_MidSide: - case kAudioChannelLayoutTag_XY: - case kAudioChannelLayoutTag_Binaural: - case kAudioChannelLayoutTag_Ambisonic_B_Format: - { - mal_get_standard_channel_map(mal_standard_channel_map_default, channelCount, channelMap); - } break; - - case kAudioChannelLayoutTag_Octagonal: - { - channelMap[7] = MAL_CHANNEL_SIDE_RIGHT; - channelMap[6] = MAL_CHANNEL_SIDE_LEFT; - } // Intentional fallthrough. - case kAudioChannelLayoutTag_Hexagonal: - { - channelMap[5] = MAL_CHANNEL_BACK_CENTER; - } // Intentional fallthrough. - case kAudioChannelLayoutTag_Pentagonal: - { - channelMap[4] = MAL_CHANNEL_FRONT_CENTER; - } // Intentional fallghrough. - case kAudioChannelLayoutTag_Quadraphonic: - { - channelMap[3] = MAL_CHANNEL_BACK_RIGHT; - channelMap[2] = MAL_CHANNEL_BACK_LEFT; - channelMap[1] = MAL_CHANNEL_RIGHT; - channelMap[0] = MAL_CHANNEL_LEFT; - } break; - - // TODO: Add support for more tags here. - - default: - { - mal_get_standard_channel_map(mal_standard_channel_map_default, channelCount, channelMap); - } break; - } - } - - return MAL_SUCCESS; -} - mal_result mal_get_AudioObject_channel_map(mal_context* pContext, AudioObjectID deviceObjectID, mal_device_type deviceType, mal_channel channelMap[MAL_MAX_CHANNELS]) { mal_assert(pContext != NULL); @@ -14268,9 +14384,11 @@ mal_result mal_get_AudioObject_channel_map(mal_context* pContext, AudioObjectID result = mal_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap); if (result != MAL_SUCCESS) { + mal_free(pChannelLayout); return result; } + mal_free(pChannelLayout); return result; } @@ -14688,7 +14806,46 @@ mal_result mal_find_best_format__coreaudio(mal_context* pContext, AudioObjectID } #endif +mal_result mal_get_AudioUnit_channel_map(mal_context* pContext, AudioUnit audioUnit, mal_device_type deviceType, mal_channel channelMap[MAL_MAX_CHANNELS]) +{ + mal_assert(pContext != NULL); + + AudioUnitScope deviceScope; + AudioUnitElement deviceBus; + if (deviceType == mal_device_type_playback) { + deviceScope = kAudioUnitScope_Output; + deviceBus = MAL_COREAUDIO_OUTPUT_BUS; + } else { + deviceScope = kAudioUnitScope_Input; + deviceBus = MAL_COREAUDIO_INPUT_BUS; + } + + UInt32 channelLayoutSize; + OSStatus status = ((mal_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); + if (status != noErr) { + return mal_result_from_OSStatus(status); + } + + AudioChannelLayout* pChannelLayout = (AudioChannelLayout*)mal_malloc(channelLayoutSize); + if (pChannelLayout == NULL) { + return MAL_OUT_OF_MEMORY; + } + + status = ((mal_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); + if (status != noErr) { + mal_free(pChannelLayout); + return mal_result_from_OSStatus(status); + } + + mal_result result = mal_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap); + if (result != MAL_SUCCESS) { + mal_free(pChannelLayout); + return result; + } + mal_free(pChannelLayout); + return MAL_SUCCESS; +} mal_bool32 mal_context_is_device_id_equal__coreaudio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) { @@ -15060,7 +15217,7 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA } #if defined(MAL_DEBUG_OUTPUT) - printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderBufferList->mBuffers[iBuffer].mNumberChannels, pRenderBufferList->mBuffers[iBuffer].mDataByteSize); + printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, pRenderedBufferList->mBuffers[iBuffer].mDataByteSize); #endif } } @@ -15108,6 +15265,8 @@ void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPro if (onStop) { onStop(pDevice); } + + mal_event_signal(&pDevice->coreaudio.stopEvent); } else { UInt32 isRunning; UInt32 isRunningSize = sizeof(isRunning); @@ -15397,11 +15556,24 @@ mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device } - // Internal channel map. + // Internal channel map. This is weird in my testing. If I use the AudioObject to get the + // channel map, the channel descriptions are set to "Unknown" for some reason. To work around + // this it looks like retrieving it from the AudioUnit will work. However, and this is where + // it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore + // I'm going to fall back to a default assumption in these cases. #if defined(MAL_APPLE_DESKTOP) - result = mal_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut); + result = mal_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut); if (result != MAL_SUCCESS) { - return result; + #if 0 + // Try falling back to the channel map from the AudioObject. + result = mal_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut); + if (result != MAL_SUCCESS) { + return result; + } + #else + // Fall back to default assumptions. + mal_get_standard_channel_map(mal_standard_channel_map_default, pData->channelsOut, pData->channelMapOut); + #endif } #else // TODO: Figure out how to get the channel map using AVAudioSession. @@ -15530,6 +15702,12 @@ mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device // Grab the name. #if defined(MAL_APPLE_DESKTOP) mal_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); +#else + if (deviceType == mal_device_type_playback) { + mal_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MAL_DEFAULT_PLAYBACK_DEVICE_NAME); + } else { + mal_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MAL_DEFAULT_CAPTURE_DEVICE_NAME); + } #endif return result; @@ -15608,7 +15786,7 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev data.usingDefaultChannelMap = pDevice->usingDefaultChannelMap; data.shareMode = pDevice->initConfig.shareMode; - mal_result result = mal_device_init_internal__coreaudio(pDevice->pContext, pDevice->type, NULL, &data, (void*)pDevice); + mal_result result = mal_device_init_internal__coreaudio(pDevice->pContext, pDevice->type, pDeviceID, &data, (void*)pDevice); if (result != MAL_SUCCESS) { return result; } @@ -15646,11 +15824,17 @@ mal_result mal_device_init__coreaudio(mal_context* pContext, mal_device_type dev ((mal_AudioObjectAddPropertyListener_proc)pDevice->pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &mal_default_output_device_changed__coreaudio, pDevice); #endif + /* + When stopping the device, a callback is called on another thread. We need to wait for this callback + before returning from mal_device_stop(). This event is used for this. + */ + mal_event_init(pContext, &pDevice->coreaudio.stopEvent); + return MAL_SUCCESS; } -mal_result mal_device__start_backend__coreaudio(mal_device* pDevice) +mal_result mal_device_start__coreaudio(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -15662,7 +15846,7 @@ mal_result mal_device__start_backend__coreaudio(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__coreaudio(mal_device* pDevice) +mal_result mal_device_stop__coreaudio(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -15671,6 +15855,8 @@ mal_result mal_device__stop_backend__coreaudio(mal_device* pDevice) return mal_result_from_OSStatus(status); } + /* We need to wait for the callback to finish before returning. */ + mal_event_wait(&pDevice->coreaudio.stopEvent); return MAL_SUCCESS; } @@ -15759,6 +15945,7 @@ mal_result mal_context_init__coreaudio(mal_context* pContext) pContext->coreaudio.AudioOutputUnitStart = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); pContext->coreaudio.AudioOutputUnitStop = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); pContext->coreaudio.AudioUnitAddPropertyListener = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); + pContext->coreaudio.AudioUnitGetPropertyInfo = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); pContext->coreaudio.AudioUnitGetProperty = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); pContext->coreaudio.AudioUnitSetProperty = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); pContext->coreaudio.AudioUnitInitialize = mal_dlsym(pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); @@ -15779,6 +15966,7 @@ mal_result mal_context_init__coreaudio(mal_context* pContext) pContext->coreaudio.AudioOutputUnitStart = (mal_proc)AudioOutputUnitStart; pContext->coreaudio.AudioOutputUnitStop = (mal_proc)AudioOutputUnitStop; pContext->coreaudio.AudioUnitAddPropertyListener = (mal_proc)AudioUnitAddPropertyListener; + pContext->coreaudio.AudioUnitGetPropertyInfo = (mal_proc)AudioUnitGetPropertyInfo; pContext->coreaudio.AudioUnitGetProperty = (mal_proc)AudioUnitGetProperty; pContext->coreaudio.AudioUnitSetProperty = (mal_proc)AudioUnitSetProperty; pContext->coreaudio.AudioUnitInitialize = (mal_proc)AudioUnitInitialize; @@ -15793,8 +15981,8 @@ mal_result mal_context_init__coreaudio(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__coreaudio; pContext->onDeviceInit = mal_device_init__coreaudio; pContext->onDeviceUninit = mal_device_uninit__coreaudio; - pContext->onDeviceStart = mal_device__start_backend__coreaudio; - pContext->onDeviceStop = mal_device__stop_backend__coreaudio; + pContext->onDeviceStart = mal_device_start__coreaudio; + pContext->onDeviceStop = mal_device_stop__coreaudio; return MAL_SUCCESS; } @@ -16422,7 +16610,7 @@ mal_result mal_device_init__sndio(mal_context* pContext, mal_device_type deviceT return MAL_SUCCESS; } -mal_result mal_device__start_backend__sndio(mal_device* pDevice) +mal_result mal_device_start__sndio(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -16451,7 +16639,7 @@ mal_result mal_device__start_backend__sndio(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__sndio(mal_device* pDevice) +mal_result mal_device_stop__sndio(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -16459,7 +16647,7 @@ mal_result mal_device__stop_backend__sndio(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__break_main_loop__sndio(mal_device* pDevice) +mal_result mal_device_break_main_loop__sndio(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -16467,7 +16655,7 @@ mal_result mal_device__break_main_loop__sndio(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__main_loop__sndio(mal_device* pDevice) +mal_result mal_device_main_loop__sndio(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -16561,10 +16749,10 @@ mal_result mal_context_init__sndio(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__sndio; pContext->onDeviceInit = mal_device_init__sndio; pContext->onDeviceUninit = mal_device_uninit__sndio; - pContext->onDeviceStart = mal_device__start_backend__sndio; - pContext->onDeviceStop = mal_device__stop_backend__sndio; - pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__sndio; - pContext->onDeviceMainLoop = mal_device__main_loop__sndio; + pContext->onDeviceStart = mal_device_start__sndio; + pContext->onDeviceStop = mal_device_stop__sndio; + pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__sndio; + pContext->onDeviceMainLoop = mal_device_main_loop__sndio; return MAL_SUCCESS; } @@ -17090,7 +17278,7 @@ mal_result mal_device_init__audio4(mal_context* pContext, mal_device_type device return MAL_SUCCESS; } -mal_result mal_device__start_backend__audio4(mal_device* pDevice) +mal_result mal_device_start__audio4(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -17119,7 +17307,7 @@ mal_result mal_device__start_backend__audio4(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__audio4(mal_device* pDevice) +mal_result mal_device_stop__audio4(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -17140,7 +17328,7 @@ mal_result mal_device__stop_backend__audio4(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__break_main_loop__audio4(mal_device* pDevice) +mal_result mal_device_break_main_loop__audio4(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -17186,7 +17374,7 @@ mal_result mal_device__wait__audio4(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__main_loop__audio4(mal_device* pDevice) +mal_result mal_device_main_loop__audio4(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -17275,10 +17463,10 @@ mal_result mal_context_init__audio4(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__audio4; pContext->onDeviceInit = mal_device_init__audio4; pContext->onDeviceUninit = mal_device_uninit__audio4; - pContext->onDeviceStart = mal_device__start_backend__audio4; - pContext->onDeviceStop = mal_device__stop_backend__audio4; - pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__audio4; - pContext->onDeviceMainLoop = mal_device__main_loop__audio4; + pContext->onDeviceStart = mal_device_start__audio4; + pContext->onDeviceStop = mal_device_stop__audio4; + pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__audio4; + pContext->onDeviceMainLoop = mal_device_main_loop__audio4; return MAL_SUCCESS; } @@ -17629,7 +17817,7 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, con } -mal_result mal_device__start_backend__oss(mal_device* pDevice) +mal_result mal_device_start__oss(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -17652,7 +17840,7 @@ mal_result mal_device__start_backend__oss(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__oss(mal_device* pDevice) +mal_result mal_device_stop__oss(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -17675,7 +17863,7 @@ mal_result mal_device__stop_backend__oss(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__break_main_loop__oss(mal_device* pDevice) +mal_result mal_device_break_main_loop__oss(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -17683,7 +17871,7 @@ mal_result mal_device__break_main_loop__oss(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__main_loop__oss(mal_device* pDevice) +mal_result mal_device_main_loop__oss(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -17754,10 +17942,10 @@ mal_result mal_context_init__oss(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__oss; pContext->onDeviceInit = mal_device_init__oss; pContext->onDeviceUninit = mal_device_uninit__oss; - pContext->onDeviceStart = mal_device__start_backend__oss; - pContext->onDeviceStop = mal_device__stop_backend__oss; - pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__oss; - pContext->onDeviceMainLoop = mal_device__main_loop__oss; + pContext->onDeviceStart = mal_device_start__oss; + pContext->onDeviceStop = mal_device_stop__oss; + pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__oss; + pContext->onDeviceMainLoop = mal_device_main_loop__oss; close(fd); return MAL_SUCCESS; @@ -17765,6 +17953,483 @@ mal_result mal_context_init__oss(mal_context* pContext) #endif // OSS +/////////////////////////////////////////////////////////////////////////////// +// +// AAudio Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_AAUDIO +//#include + +#define MAL_AAUDIO_UNSPECIFIED 0 + +typedef int32_t mal_aaudio_result_t; +typedef int32_t mal_aaudio_direction_t; +typedef int32_t mal_aaudio_sharing_mode_t; +typedef int32_t mal_aaudio_format_t; +typedef int32_t mal_aaudio_stream_state_t; +typedef int32_t mal_aaudio_performance_mode_t; +typedef int32_t mal_aaudio_data_callback_result_t; + +/* Result codes. mini_al only cares about the success code. */ +#define MAL_AAUDIO_OK 0 + +/* Directions. */ +#define MAL_AAUDIO_DIRECTION_OUTPUT 0 +#define MAL_AAUDIO_DIRECTION_INPUT 1 + +/* Sharing modes. */ +#define MAL_AAUDIO_SHARING_MODE_EXCLUSIVE 0 +#define MAL_AAUDIO_SHARING_MODE_SHARED 1 + +/* Formats. */ +#define MAL_AAUDIO_FORMAT_PCM_I16 1 +#define MAL_AAUDIO_FORMAT_PCM_FLOAT 2 + +/* Stream states. */ +#define MAL_AAUDIO_STREAM_STATE_UNINITIALIZED 0 +#define MAL_AAUDIO_STREAM_STATE_UNKNOWN 1 +#define MAL_AAUDIO_STREAM_STATE_OPEN 2 +#define MAL_AAUDIO_STREAM_STATE_STARTING 3 +#define MAL_AAUDIO_STREAM_STATE_STARTED 4 +#define MAL_AAUDIO_STREAM_STATE_PAUSING 5 +#define MAL_AAUDIO_STREAM_STATE_PAUSED 6 +#define MAL_AAUDIO_STREAM_STATE_FLUSHING 7 +#define MAL_AAUDIO_STREAM_STATE_FLUSHED 8 +#define MAL_AAUDIO_STREAM_STATE_STOPPING 9 +#define MAL_AAUDIO_STREAM_STATE_STOPPED 10 +#define MAL_AAUDIO_STREAM_STATE_CLOSING 11 +#define MAL_AAUDIO_STREAM_STATE_CLOSED 12 +#define MAL_AAUDIO_STREAM_STATE_DISCONNECTED 13 + +/* Performance modes. */ +#define MAL_AAUDIO_PERFORMANCE_MODE_NONE 10 +#define MAL_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11 +#define MAL_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12 + +/* Callback results. */ +#define MAL_AAUDIO_CALLBACK_RESULT_CONTINUE 0 +#define MAL_AAUDIO_CALLBACK_RESULT_STOP 1 + +/* Objects. */ +typedef struct mal_AAudioStreamBuilder_t* mal_AAudioStreamBuilder; +typedef struct mal_AAudioStream_t* mal_AAudioStream; + +typedef mal_aaudio_data_callback_result_t (*mal_AAudioStream_dataCallback)(mal_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); + +typedef mal_aaudio_result_t (* MAL_PFN_AAudio_createStreamBuilder) (mal_AAudioStreamBuilder** ppBuilder); +typedef mal_aaudio_result_t (* MAL_PFN_AAudioStreamBuilder_delete) (mal_AAudioStreamBuilder* pBuilder); +typedef void (* MAL_PFN_AAudioStreamBuilder_setDeviceId) (mal_AAudioStreamBuilder* pBuilder, int32_t deviceId); +typedef void (* MAL_PFN_AAudioStreamBuilder_setDirection) (mal_AAudioStreamBuilder* pBuilder, mal_aaudio_direction_t direction); +typedef void (* MAL_PFN_AAudioStreamBuilder_setSharingMode) (mal_AAudioStreamBuilder* pBuilder, mal_aaudio_sharing_mode_t sharingMode); +typedef void (* MAL_PFN_AAudioStreamBuilder_setFormat) (mal_AAudioStreamBuilder* pBuilder, mal_aaudio_format_t format); +typedef void (* MAL_PFN_AAudioStreamBuilder_setChannelCount) (mal_AAudioStreamBuilder* pBuilder, int32_t channelCount); +typedef void (* MAL_PFN_AAudioStreamBuilder_setSampleRate) (mal_AAudioStreamBuilder* pBuilder, int32_t sampleRate); +typedef void (* MAL_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(mal_AAudioStreamBuilder* pBuilder, int32_t numFrames); +typedef void (* MAL_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (mal_AAudioStreamBuilder* pBuilder, int32_t numFrames); +typedef void (* MAL_PFN_AAudioStreamBuilder_setDataCallback) (mal_AAudioStreamBuilder* pBuilder, mal_AAudioStream_dataCallback callback, void* pUserData); +typedef void (* MAL_PFN_AAudioStreamBuilder_setPerformanceMode) (mal_AAudioStreamBuilder* pBuilder, mal_aaudio_performance_mode_t mode); +typedef mal_aaudio_result_t (* MAL_PFN_AAudioStreamBuilder_openStream) (mal_AAudioStreamBuilder* pBuilder, mal_AAudioStream** ppStream); +typedef mal_aaudio_result_t (* MAL_PFN_AAudioStream_close) (mal_AAudioStream* pStream); +typedef mal_aaudio_stream_state_t (* MAL_PFN_AAudioStream_getState) (mal_AAudioStream* pStream); +typedef mal_aaudio_result_t (* MAL_PFN_AAudioStream_waitForStateChange) (mal_AAudioStream* pStream, mal_aaudio_stream_state_t inputState, mal_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds); +typedef mal_aaudio_format_t (* MAL_PFN_AAudioStream_getFormat) (mal_AAudioStream* pStream); +typedef int32_t (* MAL_PFN_AAudioStream_getChannelCount) (mal_AAudioStream* pStream); +typedef int32_t (* MAL_PFN_AAudioStream_getSampleRate) (mal_AAudioStream* pStream); +typedef int32_t (* MAL_PFN_AAudioStream_getBufferCapacityInFrames) (mal_AAudioStream* pStream); +typedef int32_t (* MAL_PFN_AAudioStream_getFramesPerDataCallback) (mal_AAudioStream* pStream); +typedef int32_t (* MAL_PFN_AAudioStream_getFramesPerBurst) (mal_AAudioStream* pStream); +typedef mal_aaudio_result_t (* MAL_PFN_AAudioStream_requestStart) (mal_AAudioStream* pStream); +typedef mal_aaudio_result_t (* MAL_PFN_AAudioStream_requestStop) (mal_AAudioStream* pStream); + +mal_result mal_result_from_aaudio(mal_aaudio_result_t resultAA) +{ + switch (resultAA) + { + case MAL_AAUDIO_OK: return MAL_SUCCESS; + default: break; + } + + return MAL_ERROR; +} + +mal_aaudio_data_callback_result_t mal_stream_data_callback__aaudio(mal_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) +{ + mal_device* pDevice = (mal_device*)pUserData; + mal_assert(pDevice != NULL); + + (void)pStream; + + if (pDevice->type == mal_device_type_playback) { + mal_device__read_frames_from_client(pDevice, frameCount, pAudioData); + } else { + mal_device__send_frames_to_client(pDevice, frameCount, pAudioData); + } + + return MAL_AAUDIO_CALLBACK_RESULT_CONTINUE; +} + +mal_result mal_open_stream__aaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, const mal_device_config* pConfig, const mal_device* pDevice, mal_AAudioStream** ppStream) +{ + mal_AAudioStreamBuilder* pBuilder; + mal_aaudio_result_t resultAA; + + (void)pContext; + *ppStream = NULL; + + resultAA = ((MAL_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder); + if (resultAA != MAL_AAUDIO_OK) { + return mal_result_from_aaudio(resultAA); + } + + if (pDeviceID != NULL) { + ((MAL_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio); + } + + ((MAL_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == mal_device_type_playback) ? MAL_AAUDIO_DIRECTION_OUTPUT : MAL_AAUDIO_DIRECTION_INPUT); + ((MAL_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == mal_share_mode_shared) ? MAL_AAUDIO_SHARING_MODE_SHARED : MAL_AAUDIO_SHARING_MODE_EXCLUSIVE); + + if (pConfig != NULL) { + if (pDevice == NULL || !pDevice->usingDefaultSampleRate) { + ((MAL_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pConfig->sampleRate); + } + if (pDevice == NULL || !pDevice->usingDefaultChannels) { + ((MAL_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pConfig->channels); + } + if (pDevice == NULL || !pDevice->usingDefaultFormat) { + ((MAL_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pConfig->format == mal_format_s16) ? MAL_AAUDIO_FORMAT_PCM_I16 : MAL_AAUDIO_FORMAT_PCM_FLOAT); + } + + ((MAL_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, pConfig->bufferSizeInFrames); + + /* TODO: Don't set the data callback when synchronous reading and writing is being used. */ + ((MAL_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, pConfig->bufferSizeInFrames / pConfig->periods); + ((MAL_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, mal_stream_data_callback__aaudio, (void*)pDevice); + + /* Not sure how this affects things, but since there's a mapping between mini_al's performance profiles and AAudio's performance modes, let go ahead and set it. */ + ((MAL_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == mal_performance_profile_low_latency) ? MAL_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MAL_AAUDIO_PERFORMANCE_MODE_NONE); + } + + resultAA = ((MAL_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream); + if (resultAA != MAL_AAUDIO_OK) { + *ppStream = NULL; + ((MAL_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder); + return mal_result_from_aaudio(resultAA); + } + + ((MAL_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder); + return MAL_SUCCESS; +} + +mal_result mal_close_stream__aaudio(mal_context* pContext, mal_AAudioStream* pStream) +{ + return mal_result_from_aaudio(((MAL_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); +} + +mal_bool32 mal_has_default_device__aaudio(mal_context* pContext, mal_device_type deviceType) +{ + /* The only way to know this is to try creating a stream. */ + mal_AAudioStream* pStream; + mal_result result = mal_open_stream__aaudio(pContext, deviceType, NULL, mal_share_mode_shared, NULL, NULL, &pStream); + if (result != MAL_SUCCESS) { + return MAL_FALSE; + } + + mal_close_stream__aaudio(pContext, pStream); + return MAL_TRUE; +} + +mal_result mal_wait_for_simple_state_transition__aaudio(mal_context* pContext, mal_AAudioStream* pStream, mal_aaudio_stream_state_t oldState, mal_aaudio_stream_state_t newState) +{ + mal_aaudio_stream_state_t actualNewState; + mal_aaudio_result_t resultAA = ((MAL_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */ + if (resultAA != MAL_AAUDIO_OK) { + return mal_result_from_aaudio(resultAA); + } + + if (newState != actualNewState) { + return MAL_ERROR; /* Failed to transition into the expected state. */ + } + + return MAL_SUCCESS; +} + + +mal_bool32 mal_context_is_device_id_equal__aaudio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return pID0->aaudio == pID1->aaudio; +} + +mal_result mal_context_enumerate_devices__aaudio(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_bool32 cbResult = MAL_TRUE; + + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */ + + /* Playback. */ + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + deviceInfo.id.aaudio = MAL_AAUDIO_UNSPECIFIED; + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + + if (mal_has_default_device__aaudio(pContext, mal_device_type_playback)) { + cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + } + } + + /* Capture. */ + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + deviceInfo.id.aaudio = MAL_AAUDIO_UNSPECIFIED; + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + + if (mal_has_default_device__aaudio(pContext, mal_device_type_capture)) { + cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info__aaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_AAudioStream* pStream; + mal_result result; + + mal_assert(pContext != NULL); + + /* ID */ + if (pDeviceID != NULL) { + pDeviceInfo->id.aaudio = pDeviceID->aaudio; + } else { + pDeviceInfo->id.aaudio = MAL_AAUDIO_UNSPECIFIED; + } + + /* Name */ + if (deviceType == mal_device_type_playback) { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + + /* We'll need to open the device to get accurate sample rate and channel count information. */ + result = mal_open_stream__aaudio(pContext, deviceType, pDeviceID, shareMode, NULL, NULL, &pStream); + if (result != MAL_SUCCESS) { + return result; + } + + pDeviceInfo->minChannels = ((MAL_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream); + pDeviceInfo->maxChannels = pDeviceInfo->minChannels; + pDeviceInfo->minSampleRate = ((MAL_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream); + pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate; + + mal_close_stream__aaudio(pContext, pStream); + pStream = NULL; + + + /* AAudio supports s16 and f32. */ + pDeviceInfo->formatCount = 2; + pDeviceInfo->formats[0] = mal_format_s16; + pDeviceInfo->formats[1] = mal_format_f32; + + return MAL_SUCCESS; +} + + +void mal_device_uninit__aaudio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + mal_close_stream__aaudio(pDevice->pContext, (mal_AAudioStream*)pDevice->aaudio.pStream); + pDevice->aaudio.pStream = NULL; +} + +mal_result mal_device_init__aaudio(mal_context* pContext, mal_device_type type, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + mal_result result; + + mal_assert(pDevice != NULL); + + /* We need to make a copy of the config so we can make an adjustment to the buffer size. */ + mal_device_config config = *pConfig; + config.bufferSizeInFrames = pDevice->bufferSizeInFrames; + if (config.bufferSizeInFrames == 0) { + config.bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->sampleRate); + } + + /* We first need to try opening the stream. */ + result = mal_open_stream__aaudio(pContext, type, pDeviceID, pConfig->shareMode, &config, pDevice, (mal_AAudioStream**)&pDevice->aaudio.pStream); + if (result != MAL_SUCCESS) { + return result; /* Failed to open the AAudio stream. */ + } + + pDevice->internalFormat = (((MAL_PFN_AAudioStream_getFormat)pContext->aaudio.AAudioStream_getFormat)((mal_AAudioStream*)pDevice->aaudio.pStream) == MAL_AAUDIO_FORMAT_PCM_I16) ? mal_format_s16 : mal_format_f32; + pDevice->internalChannels = ((MAL_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)((mal_AAudioStream*)pDevice->aaudio.pStream); + pDevice->internalSampleRate = ((MAL_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)((mal_AAudioStream*)pDevice->aaudio.pStream); + mal_get_standard_channel_map(mal_standard_channel_map_default, pDevice->internalChannels, pDevice->internalChannelMap); /* <-- Cannot find info on channel order, so assuming a default. */ + pDevice->bufferSizeInFrames = ((MAL_PFN_AAudioStream_getBufferCapacityInFrames)pContext->aaudio.AAudioStream_getBufferCapacityInFrames)((mal_AAudioStream*)pDevice->aaudio.pStream); + + /* TODO: When synchronous reading and writing is supported, use AAudioStream_getFramesPerBurst() instead of AAudioStream_getFramesPerDataCallback(). Keep + * using AAudioStream_getFramesPerDataCallback() for asynchronous mode, though. */ + int32_t framesPerPeriod = ((MAL_PFN_AAudioStream_getFramesPerDataCallback)pContext->aaudio.AAudioStream_getFramesPerDataCallback)((mal_AAudioStream*)pDevice->aaudio.pStream); + if (framesPerPeriod > 0) { + pDevice->periods = 1; + } else { + pDevice->periods = pDevice->bufferSizeInFrames / framesPerPeriod; + } + + return MAL_SUCCESS; +} + +mal_result mal_device_start__aaudio(mal_device* pDevice) +{ + mal_aaudio_result_t resultAA; + + mal_assert(pDevice != NULL); + + resultAA = ((MAL_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)((mal_AAudioStream*)pDevice->aaudio.pStream); + if (resultAA != MAL_AAUDIO_OK) { + return mal_result_from_aaudio(resultAA); + } + + /* Do we actually need to wait for the device to transition into it's started state? */ + + /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ + mal_aaudio_stream_state_t currentState = ((MAL_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)((mal_AAudioStream*)pDevice->aaudio.pStream); + if (currentState != MAL_AAUDIO_STREAM_STATE_STARTED) { + mal_result result; + + if (currentState != MAL_AAUDIO_STREAM_STATE_STARTING) { + return MAL_ERROR; /* Expecting the stream to be a starting or started state. */ + } + + result = mal_wait_for_simple_state_transition__aaudio(pDevice->pContext, (mal_AAudioStream*)pDevice->aaudio.pStream, currentState, MAL_AAUDIO_STREAM_STATE_STARTED); + if (result != MAL_SUCCESS) { + return result; + } + } + + return MAL_SUCCESS; +} + +mal_result mal_device_stop__aaudio(mal_device* pDevice) +{ + mal_aaudio_result_t resultAA; + + mal_assert(pDevice != NULL); + + resultAA = ((MAL_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)((mal_AAudioStream*)pDevice->aaudio.pStream); + if (resultAA != MAL_AAUDIO_OK) { + return mal_result_from_aaudio(resultAA); + } + + /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */ + mal_aaudio_stream_state_t currentState = ((MAL_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)((mal_AAudioStream*)pDevice->aaudio.pStream); + if (currentState != MAL_AAUDIO_STREAM_STATE_STOPPED) { + mal_result result; + + if (currentState != MAL_AAUDIO_STREAM_STATE_STOPPING) { + return MAL_ERROR; /* Expecting the stream to be a stopping or stopped state. */ + } + + result = mal_wait_for_simple_state_transition__aaudio(pDevice->pContext, (mal_AAudioStream*)pDevice->aaudio.pStream, currentState, MAL_AAUDIO_STREAM_STATE_STOPPED); + if (result != MAL_SUCCESS) { + return result; + } + } + + mal_stop_proc onStop = pDevice->onStop; + if (onStop) { + onStop(pDevice); + } + + return MAL_SUCCESS; +} + + +mal_result mal_context_uninit__aaudio(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_aaudio); + + mal_dlclose(pContext->aaudio.hAAudio); + pContext->aaudio.hAAudio = NULL; + + return MAL_SUCCESS; +} + +mal_result mal_context_init__aaudio(mal_context* pContext) +{ + mal_assert(pContext != NULL); + (void)pContext; + + const char* libNames[] = { + "libaaudio.so" + }; + + for (size_t i = 0; i < mal_countof(libNames); ++i) { + pContext->aaudio.hAAudio = mal_dlopen(libNames[i]); + if (pContext->aaudio.hAAudio != NULL) { + break; + } + } + + if (pContext->aaudio.hAAudio == NULL) { + return MAL_FAILED_TO_INIT_BACKEND; + } + + pContext->aaudio.AAudio_createStreamBuilder = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); + pContext->aaudio.AAudioStreamBuilder_delete = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); + pContext->aaudio.AAudioStreamBuilder_setDeviceId = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); + pContext->aaudio.AAudioStreamBuilder_setDirection = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); + pContext->aaudio.AAudioStreamBuilder_setSharingMode = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); + pContext->aaudio.AAudioStreamBuilder_setFormat = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); + pContext->aaudio.AAudioStreamBuilder_setChannelCount = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); + pContext->aaudio.AAudioStreamBuilder_setSampleRate = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); + pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); + pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setDataCallback = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); + pContext->aaudio.AAudioStreamBuilder_openStream = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); + pContext->aaudio.AAudioStream_close = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_close"); + pContext->aaudio.AAudioStream_getState = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getState"); + pContext->aaudio.AAudioStream_waitForStateChange = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); + pContext->aaudio.AAudioStream_getFormat = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getFormat"); + pContext->aaudio.AAudioStream_getChannelCount = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); + pContext->aaudio.AAudioStream_getSampleRate = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); + pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); + pContext->aaudio.AAudioStream_getFramesPerDataCallback = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); + pContext->aaudio.AAudioStream_getFramesPerBurst = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); + pContext->aaudio.AAudioStream_requestStart = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_requestStart"); + pContext->aaudio.AAudioStream_requestStop = (mal_proc)mal_dlsym(pContext->aaudio.hAAudio, "AAudioStream_requestStop"); + + pContext->isBackendAsynchronous = MAL_TRUE; + + pContext->onUninit = mal_context_uninit__aaudio; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__aaudio; + pContext->onEnumDevices = mal_context_enumerate_devices__aaudio; + pContext->onGetDeviceInfo = mal_context_get_device_info__aaudio; + pContext->onDeviceInit = mal_device_init__aaudio; + pContext->onDeviceUninit = mal_device_uninit__aaudio; + pContext->onDeviceStart = mal_device_start__aaudio; + pContext->onDeviceStop = mal_device_stop__aaudio; + + return MAL_SUCCESS; +} +#endif // AAudio + + /////////////////////////////////////////////////////////////////////////////// // // OpenSL|ES Backend @@ -17949,6 +18614,11 @@ mal_result mal_context_enumerate_devices__opensl(mal_context* pContext, mal_enum mal_assert(pContext != NULL); mal_assert(callback != NULL); + mal_assert(g_malOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */ + if (g_malOpenSLInitCounter == 0) { + return MAL_INVALID_OPERATION; + } + // TODO: Test Me. // // This is currently untested, so for now we are just returning default devices. @@ -18049,6 +18719,11 @@ mal_result mal_context_get_device_info__opensl(mal_context* pContext, mal_device mal_assert(pContext != NULL); (void)shareMode; + mal_assert(g_malOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */ + if (g_malOpenSLInitCounter == 0) { + return MAL_INVALID_OPERATION; + } + // TODO: Test Me. // // This is currently untested, so for now we are just returning default devices. @@ -18176,12 +18851,23 @@ void mal_device_uninit__opensl(mal_device* pDevice) { mal_assert(pDevice != NULL); + mal_assert(g_malOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */ + if (g_malOpenSLInitCounter == 0) { + return; + } + // Uninit device. if (pDevice->type == mal_device_type_playback) { - if (pDevice->opensl.pAudioPlayerObj) MAL_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); - if (pDevice->opensl.pOutputMixObj) MAL_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); + if (pDevice->opensl.pAudioPlayerObj) { + MAL_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); + } + if (pDevice->opensl.pOutputMixObj) { + MAL_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); + } } else { - if (pDevice->opensl.pAudioRecorderObj) MAL_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); + if (pDevice->opensl.pAudioRecorderObj) { + MAL_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); + } } mal_free(pDevice->opensl.pBuffer); @@ -18191,6 +18877,11 @@ mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type, { (void)pContext; + mal_assert(g_malOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */ + if (g_malOpenSLInitCounter == 0) { + return MAL_INVALID_OPERATION; + } + // For now, only supporting Android implementations of OpenSL|ES since that's the only one I've // been able to test with and I currently depend on Android-specific extensions (simple buffer // queues). @@ -18451,10 +19142,15 @@ mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type, return MAL_SUCCESS; } -mal_result mal_device__start_backend__opensl(mal_device* pDevice) +mal_result mal_device_start__opensl(mal_device* pDevice) { mal_assert(pDevice != NULL); + mal_assert(g_malOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */ + if (g_malOpenSLInitCounter == 0) { + return MAL_INVALID_OPERATION; + } + if (pDevice->type == mal_device_type_playback) { SLresult resultSL = MAL_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); if (resultSL != SL_RESULT_SUCCESS) { @@ -18491,10 +19187,15 @@ mal_result mal_device__start_backend__opensl(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__opensl(mal_device* pDevice) +mal_result mal_device_stop__opensl(mal_device* pDevice) { mal_assert(pDevice != NULL); + mal_assert(g_malOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */ + if (g_malOpenSLInitCounter == 0) { + return MAL_INVALID_OPERATION; + } + if (pDevice->type == mal_device_type_playback) { SLresult resultSL = MAL_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); if (resultSL != SL_RESULT_SUCCESS) { @@ -18511,7 +19212,6 @@ mal_result mal_device__stop_backend__opensl(mal_device* pDevice) MAL_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueue)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueue); // Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. - mal_device__set_state(pDevice, MAL_STATE_STOPPED); mal_stop_proc onStop = pDevice->onStop; if (onStop) { onStop(pDevice); @@ -18568,13 +19268,481 @@ mal_result mal_context_init__opensl(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__opensl; pContext->onDeviceInit = mal_device_init__opensl; pContext->onDeviceUninit = mal_device_uninit__opensl; - pContext->onDeviceStart = mal_device__start_backend__opensl; - pContext->onDeviceStop = mal_device__stop_backend__opensl; + pContext->onDeviceStart = mal_device_start__opensl; + pContext->onDeviceStop = mal_device_stop__opensl; return MAL_SUCCESS; } #endif // OpenSL|ES + +/////////////////////////////////////////////////////////////////////////////// +// +// Web Audio Backend +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef MAL_HAS_WEBAUDIO +#include + +mal_bool32 mal_is_capture_supported__webaudio() +{ + return EM_ASM_INT({ + return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined); + }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */ +} + +#ifdef __cplusplus +extern "C" { +#endif +EMSCRIPTEN_KEEPALIVE void mal_device_process_pcm_frames__webaudio(mal_device* pDevice, int frameCount, float* pFrames) +{ + if (pDevice->type == mal_device_type_playback) { + /* Playback. Write to pFrames. */ + mal_device__read_frames_from_client(pDevice, (mal_uint32)frameCount, pFrames); + } else { + /* Capture. Read from pFrames. */ + mal_device__send_frames_to_client(pDevice, (mal_uint32)frameCount, pFrames); + } +} +#ifdef __cplusplus +} +#endif + +mal_bool32 mal_context_is_device_id_equal__webaudio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1) +{ + mal_assert(pContext != NULL); + mal_assert(pID0 != NULL); + mal_assert(pID1 != NULL); + (void)pContext; + + return mal_strcmp(pID0->webaudio, pID1->webaudio) == 0; +} + +mal_result mal_context_enumerate_devices__webaudio(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData) +{ + mal_assert(pContext != NULL); + mal_assert(callback != NULL); + + // Only supporting default devices for now. + mal_bool32 cbResult = MAL_TRUE; + + // Playback. + if (cbResult) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + } + + // Capture. + if (cbResult) { + if (mal_is_capture_supported__webaudio()) { + mal_device_info deviceInfo; + mal_zero_object(&deviceInfo); + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + } + } + + return MAL_SUCCESS; +} + +mal_result mal_context_get_device_info__webaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo) +{ + mal_assert(pContext != NULL); + (void)shareMode; + + if (deviceType == mal_device_type_capture && !mal_is_capture_supported__webaudio()) { + return MAL_NO_DEVICE; + } + + + mal_zero_memory(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio)); + + /* Only supporting default devices for now. */ + if (deviceType == mal_device_type_playback) { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */ + pDeviceInfo->minChannels = 1; + pDeviceInfo->maxChannels = MAL_MAX_CHANNELS; + if (pDeviceInfo->maxChannels > 32) { + pDeviceInfo->maxChannels = 32; /* Maximum output channel count is 32 for createScriptProcessor() (JavaScript). */ + } + + /* We can query the sample rate by just using a temporary audio context. */ + pDeviceInfo->minSampleRate = EM_ASM_INT({ + try { + var temp = new (window.AudioContext || window.webkitAudioContext)(); + var sampleRate = temp.sampleRate; + temp.close(); + return sampleRate; + } catch(e) { + return 0; + } + }, 0); /* Must pass in a dummy argument for C99 compatibility. */ + pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate; + if (pDeviceInfo->minSampleRate == 0) { + return MAL_NO_DEVICE; + } + + /* Web Audio only supports f32. */ + pDeviceInfo->formatCount = 1; + pDeviceInfo->formats[0] = mal_format_f32; + + return MAL_SUCCESS; +} + + +void mal_device_uninit__webaudio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + EM_ASM({ + var device = mal.get_device_by_index($0); + + /* Make sure all nodes are disconnected and marked for collection. */ + if (device.scriptNode !== undefined) { + device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ + device.scriptNode.disconnect(); + device.scriptNode = undefined; + } + if (device.streamNode !== undefined) { + device.streamNode.disconnect(); + device.streamNode = undefined; + } + + /* + Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want + to clear the callback before closing. + */ + device.webaudio.close(); + device.webaudio = undefined; + + /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */ + if (device.intermediaryBuffer !== undefined) { + Module._free(device.intermediaryBuffer); + device.intermediaryBuffer = undefined; + device.intermediaryBufferView = undefined; + device.intermediaryBufferSizeInBytes = undefined; + } + + /* Make sure the device is untracked so the slot can be reused later. */ + mal.untrack_device_by_index($0); + }, pDevice->webaudio.index, pDevice->type == mal_device_type_playback); +} + +mal_result mal_device_init__webaudio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice) +{ + if (deviceType == mal_device_type_capture && !mal_is_capture_supported__webaudio()) { + return MAL_NO_DEVICE; + } + + /* Try calculating an appropriate default buffer size. */ + if (pDevice->bufferSizeInFrames == 0) { + pDevice->bufferSizeInFrames = mal_calculate_buffer_size_in_frames_from_milliseconds(pDevice->bufferSizeInMilliseconds, pDevice->sampleRate); + if (pDevice->usingDefaultBufferSize) { + float bufferSizeScaleFactor = 1; + pDevice->bufferSizeInFrames = mal_scale_buffer_size(pDevice->bufferSizeInFrames, bufferSizeScaleFactor); + } + } + + /* The size of the buffer must be a power of 2 and between 256 and 16384. */ + if (pDevice->bufferSizeInFrames < 256) { + pDevice->bufferSizeInFrames = 256; + } else if (pDevice->bufferSizeInFrames > 16384) { + pDevice->bufferSizeInFrames = 16384; + } else { + pDevice->bufferSizeInFrames = mal_next_power_of_2(pDevice->bufferSizeInFrames); + } + + /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */ + pDevice->webaudio.index = EM_ASM_INT({ + var channels = $0; + var sampleRate = $1; + var bufferSize = $2; /* In PCM frames. */ + var isPlayback = $3; + var pDevice = $4; + + if (typeof(mal) === 'undefined') { + return -1; /* Context not initialized. */ + } + + var device = {}; + + /* The AudioContext must be created in a suspended state. */ + device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate}); + device.webaudio.suspend(); + + /* + We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. Because it's passed between + JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free(). + */ + device.intermediaryBufferSizeInBytes = channels * bufferSize * 4; + device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes); + device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); + + /* + Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations. + + ScriptProcessorNode is actually deprecated so this is likely to be temporary. The way this works for playback is very simple. You just set a callback + that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of + something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to + work for years to come, but this may need to change to use AudioSourceBufferNode instead, which I think is what Emscripten uses for it's built-in SDL + implementation. I'll be avoiding that insane AudioWorklet API like the plague... + + For capture it is a bit unintuitive. We use the ScriptProccessorNode _only_ to get the raw PCM data. It is connected to an AudioContext just like the + playback case, however we just output silence to the AudioContext instead of passing any real data. It would make more sense to me to use the + MediaRecorder API, but unfortunately you need to specify a MIME time (Opus, Vorbis, etc.) for the binary blob that's returned to the client, but I've + been unable to figure out how to get this as raw PCM. The closes I can think is to use the MIME type for WAV files and just parse it, but I don't know + how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like + this for now. If anything knows how I could get raw PCM data using the MediaRecorder API please let me know! + */ + device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channels, channels); + + if (isPlayback) { + device.scriptNode.onaudioprocess = function(e) { + if (device.intermediaryBuffer === undefined) { + return; /* This means the device has been uninitialized. */ + } + + var outputSilence = false; + + /* Sanity check. This will never happen, right? */ + if (e.outputBuffer.numberOfChannels != channels) { + console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence."); + outputSilence = true; + return; + } + + /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */ + var totalFramesProcessed = 0; + while (totalFramesProcessed < e.outputBuffer.length) { + var framesRemaining = e.outputBuffer.length - totalFramesProcessed; + var framesToProcess = framesRemaining; + if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) { + framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4); + } + + /* Read data from the client into our intermediary buffer. */ + ccall("mal_device_process_pcm_frames__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]); + + /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */ + if (outputSilence) { + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + e.outputBuffer.getChannelData(iChannel).fill(0.0); + } + } else { + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { + e.outputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame] = device.intermediaryBufferView[iFrame*channels + iChannel]; + } + } + } + + totalFramesProcessed += framesToProcess; + } + }; + + device.scriptNode.connect(device.webaudio.destination); + } else { + device.scriptNode.onaudioprocess = function(e) { + if (device.intermediaryBuffer === undefined) { + return; /* This means the device has been uninitialized. */ + } + + /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */ + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + e.outputBuffer.getChannelData(iChannel).fill(0.0); + } + + /* There are some situations where we may want to send silence to the client. */ + var sendSilence = false; + if (device.streamNode === undefined) { + sendSilence = true; + } + + /* Sanity check. This will never happen, right? */ + if (e.inputBuffer.numberOfChannels != channels) { + console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence."); + sendSilence = true; + } + + /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */ + var totalFramesProcessed = 0; + while (totalFramesProcessed < e.inputBuffer.length) { + var framesRemaining = e.inputBuffer.length - totalFramesProcessed; + var framesToProcess = framesRemaining; + if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) { + framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4); + } + + /* We need to do the reverse of the playback case. We need to interleave the input data and copy it into the intermediary buffer. Then we send it to the client. */ + if (sendSilence) { + device.intermediaryBufferView.fill(0.0); + } else { + for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { + for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) { + device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame]; + } + } + } + + /* Send data to the client from our intermediary buffer. */ + ccall("mal_device_process_pcm_frames__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]); + + totalFramesProcessed += framesToProcess; + } + }; + + navigator.mediaDevices.getUserMedia({audio:true, video:false}) + .then(function(stream) { + device.streamNode = device.webaudio.createMediaStreamSource(stream); + device.streamNode.connect(device.scriptNode); + device.scriptNode.connect(device.webaudio.destination); + }) + .catch(function(error) { + /* I think this should output silence... */ + device.scriptNode.connect(device.webaudio.destination); + }); + } + + return mal.track_device(device); + }, pConfig->channels, pConfig->sampleRate, pDevice->bufferSizeInFrames, deviceType == mal_device_type_playback, pDevice); + + if (pDevice->webaudio.index < 0) { + return MAL_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + pDevice->internalFormat = mal_format_f32; + pDevice->internalChannels = pConfig->channels; + pDevice->internalSampleRate = EM_ASM_INT({ return mal.get_device_by_index($0).webaudio.sampleRate; }, pDevice->webaudio.index); + mal_get_standard_channel_map(mal_standard_channel_map_webaudio, pDevice->internalChannels, pDevice->internalChannelMap); + pDevice->periods = 1; + + return MAL_SUCCESS; +} + +mal_result mal_device_start__webaudio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + EM_ASM({ + mal.get_device_by_index($0).webaudio.resume(); + }, pDevice->webaudio.index); + + return MAL_SUCCESS; +} + +mal_result mal_device_stop__webaudio(mal_device* pDevice) +{ + mal_assert(pDevice != NULL); + + EM_ASM({ + mal.get_device_by_index($0).webaudio.suspend(); + }, pDevice->webaudio.index); + + mal_stop_proc onStop = pDevice->onStop; + if (onStop) { + onStop(pDevice); + } + + return MAL_SUCCESS; +} + +mal_result mal_context_uninit__webaudio(mal_context* pContext) +{ + mal_assert(pContext != NULL); + mal_assert(pContext->backend == mal_backend_webaudio); + + /* Nothing needs to be done here. */ + (void)pContext; + + return MAL_SUCCESS; +} + +mal_result mal_context_init__webaudio(mal_context* pContext) +{ + mal_assert(pContext != NULL); + + /* Here is where our global JavaScript object is initialized. */ + int resultFromJS = EM_ASM_INT({ + if ((window.AudioContext || window.webkitAudioContext) === undefined) { + return 0; /* Web Audio not supported. */ + } + + if (typeof(mal) === 'undefined') { + mal = {}; + mal.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */ + + mal.track_device = function(device) { + /* Try inserting into a free slot first. */ + for (var iDevice = 0; iDevice < mal.devices.length; ++iDevice) { + if (mal.devices[iDevice] == null) { + mal.devices[iDevice] = device; + return iDevice; + } + } + + /* Getting here means there is no empty slots in the array so we just push to the end. */ + mal.devices.push(device); + return mal.devices.length - 1; + }; + + mal.untrack_device_by_index = function(deviceIndex) { + /* We just set the device's slot to null. The slot will get reused in the next call to mal_track_device. */ + mal.devices[deviceIndex] = null; + + /* Trim the array if possible. */ + while (mal.devices.length > 0) { + if (mal.devices[mal.devices.length-1] == null) { + mal.devices.pop(); + } else { + break; + } + } + }; + + mal.untrack_device = function(device) { + for (var iDevice = 0; iDevice < mal.devices.length; ++iDevice) { + if (mal.devices[iDevice] == device) { + return mal.untrack_device_by_index(iDevice); + } + } + }; + + mal.get_device_by_index = function(deviceIndex) { + return mal.devices[deviceIndex]; + }; + } + + return 1; + }, 0); /* Must pass in a dummy argument for C99 compatibility. */ + + if (resultFromJS != 1) { + return MAL_FAILED_TO_INIT_BACKEND; + } + + + pContext->isBackendAsynchronous = MAL_TRUE; + + pContext->onUninit = mal_context_uninit__webaudio; + pContext->onDeviceIDEqual = mal_context_is_device_id_equal__webaudio; + pContext->onEnumDevices = mal_context_enumerate_devices__webaudio; + pContext->onGetDeviceInfo = mal_context_get_device_info__webaudio; + pContext->onDeviceInit = mal_device_init__webaudio; + pContext->onDeviceUninit = mal_device_uninit__webaudio; + pContext->onDeviceStart = mal_device_start__webaudio; + pContext->onDeviceStop = mal_device_stop__webaudio; + + return MAL_SUCCESS; +} +#endif // Web Audio + + /////////////////////////////////////////////////////////////////////////////// // // OpenAL Backend @@ -19180,7 +20348,7 @@ mal_result mal_device_init__openal(mal_context* pContext, mal_device_type type, return MAL_SUCCESS; } -mal_result mal_device__start_backend__openal(mal_device* pDevice) +mal_result mal_device_start__openal(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -19210,7 +20378,7 @@ mal_result mal_device__start_backend__openal(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__stop_backend__openal(mal_device* pDevice) +mal_result mal_device_stop__openal(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -19224,7 +20392,7 @@ mal_result mal_device__stop_backend__openal(mal_device* pDevice) return MAL_SUCCESS; } -mal_result mal_device__break_main_loop__openal(mal_device* pDevice) +mal_result mal_device_break_main_loop__openal(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -19272,7 +20440,7 @@ mal_uint32 mal_device__wait_for_frames__openal(mal_device* pDevice) } } -mal_result mal_device__main_loop__openal(mal_device* pDevice) +mal_result mal_device_main_loop__openal(mal_device* pDevice) { mal_assert(pDevice != NULL); @@ -19540,10 +20708,10 @@ mal_result mal_context_init__openal(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__openal; pContext->onDeviceInit = mal_device_init__openal; pContext->onDeviceUninit = mal_device_uninit__openal; - pContext->onDeviceStart = mal_device__start_backend__openal; - pContext->onDeviceStop = mal_device__stop_backend__openal; - pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__openal; - pContext->onDeviceMainLoop = mal_device__main_loop__openal; + pContext->onDeviceStart = mal_device_start__openal; + pContext->onDeviceStop = mal_device_stop__openal; + pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__openal; + pContext->onDeviceMainLoop = mal_device_main_loop__openal; return MAL_SUCCESS; } @@ -19558,8 +20726,6 @@ mal_result mal_context_init__openal(mal_context* pContext) /////////////////////////////////////////////////////////////////////////////// #ifdef MAL_HAS_SDL -//#define MAL_USE_SDL_1 - #define MAL_SDL_INIT_AUDIO 0x00000010 #define MAL_AUDIO_U8 0x0008 #define MAL_AUDIO_S16 0x8010 @@ -19576,11 +20742,6 @@ mal_result mal_context_init__openal(mal_context* pContext) #define SDL_MAIN_HANDLED #ifdef MAL_EMSCRIPTEN #include - - // For now just use SDL 1.2 with Emscripten. This avoids the need for "-s USE_SDL=2" at compile time. - #ifndef MAL_USE_SDL_1 - #define MAL_USE_SDL_1 - #endif #else #include #endif @@ -19612,11 +20773,8 @@ typedef int (* MAL_PFN_SDL_InitSubSystem)(mal_uint32 flags); typedef void (* MAL_PFN_SDL_QuitSubSystem)(mal_uint32 flags); typedef int (* MAL_PFN_SDL_GetNumAudioDevices)(int iscapture); typedef const char* (* MAL_PFN_SDL_GetAudioDeviceName)(int index, int iscapture); -typedef void (* MAL_PFN_SDL_CloseAudio)(void); typedef void (* MAL_PFN_SDL_CloseAudioDevice)(MAL_SDL_AudioDeviceID dev); -typedef int (* MAL_PFN_SDL_OpenAudio)(MAL_SDL_AudioSpec* desired, MAL_SDL_AudioSpec* obtained); typedef MAL_SDL_AudioDeviceID (* MAL_PFN_SDL_OpenAudioDevice)(const char* device, int iscapture, const MAL_SDL_AudioSpec* desired, MAL_SDL_AudioSpec* obtained, int allowed_changes); -typedef void (* MAL_PFN_SDL_PauseAudio)(int pause_on); typedef void (* MAL_PFN_SDL_PauseAudioDevice)(MAL_SDL_AudioDeviceID dev, int pause_on); MAL_SDL_AudioFormat mal_format_to_sdl(mal_format format) @@ -19659,68 +20817,42 @@ mal_result mal_context_enumerate_devices__sdl(mal_context* pContext, mal_enum_de mal_assert(pContext != NULL); mal_assert(callback != NULL); -#ifndef MAL_USE_SDL_1 - if (!pContext->sdl.usingSDL1) { - mal_bool32 isTerminated = MAL_FALSE; + mal_bool32 isTerminated = MAL_FALSE; - // Playback - if (!isTerminated) { - int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(0); - for (int i = 0; i < deviceCount; ++i) { - mal_device_info deviceInfo; - mal_zero_object(&deviceInfo); - - deviceInfo.id.sdl = i; - mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 0), (size_t)-1); - - mal_bool32 cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); - if (cbResult == MAL_FALSE) { - isTerminated = MAL_TRUE; - break; - } - } - } - - // Capture - if (!isTerminated) { - int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(1); - for (int i = 0; i < deviceCount; ++i) { - mal_device_info deviceInfo; - mal_zero_object(&deviceInfo); - - deviceInfo.id.sdl = i; - mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 1), (size_t)-1); - - mal_bool32 cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); - if (cbResult == MAL_FALSE) { - isTerminated = MAL_TRUE; - break; - } - } - } - } else -#endif - { - // SDL1 only uses default devices, and does not support capture. - mal_bool32 cbResult = MAL_TRUE; - - // Playback. - if (cbResult) { + // Playback + if (!isTerminated) { + int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(0); + for (int i = 0; i < deviceCount; ++i) { mal_device_info deviceInfo; mal_zero_object(&deviceInfo); - mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); - } -#if 0 // No capture with SDL1. - // Capture. - if (cbResult) { + deviceInfo.id.sdl = i; + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 0), (size_t)-1); + + mal_bool32 cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData); + if (cbResult == MAL_FALSE) { + isTerminated = MAL_TRUE; + break; + } + } + } + + // Capture + if (!isTerminated) { + int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(1); + for (int i = 0; i < deviceCount; ++i) { mal_device_info deviceInfo; mal_zero_object(&deviceInfo); - mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + + deviceInfo.id.sdl = i; + mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 1), (size_t)-1); + + mal_bool32 cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); + if (cbResult == MAL_FALSE) { + isTerminated = MAL_TRUE; + break; + } } -#endif } return MAL_SUCCESS; @@ -19731,24 +20863,7 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty mal_assert(pContext != NULL); (void)shareMode; -#ifndef MAL_USE_SDL_1 - if (!pContext->sdl.usingSDL1) { - if (pDeviceID == NULL) { - if (deviceType == mal_device_type_playback) { - pDeviceInfo->id.sdl = 0; - mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); - } else { - pDeviceInfo->id.sdl = 0; - mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); - } - } else { - pDeviceInfo->id.sdl = pDeviceID->sdl; - mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, (deviceType == mal_device_type_playback) ? 0 : 1), (size_t)-1); - } - } else -#endif - { - // SDL1 uses default devices. + if (pDeviceID == NULL) { if (deviceType == mal_device_type_playback) { pDeviceInfo->id.sdl = 0; mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); @@ -19756,6 +20871,9 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty pDeviceInfo->id.sdl = 0; mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } + } else { + pDeviceInfo->id.sdl = pDeviceID->sdl; + mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, (deviceType == mal_device_type_playback) ? 0 : 1), (size_t)-1); } // To get an accurate idea on the backend's native format we need to open the device. Not ideal, but it's the only way. An @@ -19779,40 +20897,20 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty MAL_SDL_AudioSpec desiredSpec, obtainedSpec; mal_zero_memory(&desiredSpec, sizeof(desiredSpec)); -#ifndef MAL_USE_SDL_1 - if (!pContext->sdl.usingSDL1) { - int isCapture = (deviceType == mal_device_type_playback) ? 0 : 1; + int isCapture = (deviceType == mal_device_type_playback) ? 0 : 1; - const char* pDeviceName = NULL; - if (pDeviceID != NULL) { - pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture); - } - - MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudioDevice)pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE); - if (tempDeviceID == 0) { - return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); - } - - ((MAL_PFN_SDL_CloseAudioDevice)pContext->sdl.SDL_CloseAudioDevice)(tempDeviceID); - } else -#endif - { - // SDL1 uses default devices. - (void)pDeviceID; - - // SDL1 only supports playback as far as I can tell. - if (deviceType != mal_device_type_playback) { - return MAL_NO_DEVICE; - } - - MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudio)pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec); - if (tempDeviceID != 0) { - return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); - } - - ((MAL_PFN_SDL_CloseAudio)pContext->sdl.SDL_CloseAudio)(); + const char* pDeviceName = NULL; + if (pDeviceID != NULL) { + pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture); } + MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudioDevice)pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE); + if (tempDeviceID == 0) { + return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); + } + + ((MAL_PFN_SDL_CloseAudioDevice)pContext->sdl.SDL_CloseAudioDevice)(tempDeviceID); + pDeviceInfo->minChannels = obtainedSpec.channels; pDeviceInfo->maxChannels = obtainedSpec.channels; pDeviceInfo->minSampleRate = obtainedSpec.freq; @@ -19839,14 +20937,7 @@ void mal_device_uninit__sdl(mal_device* pDevice) { mal_assert(pDevice != NULL); -#ifndef MAL_USE_SDL_1 - if (!pDevice->pContext->sdl.usingSDL1) { - ((MAL_PFN_SDL_CloseAudioDevice)pDevice->pContext->sdl.SDL_CloseAudioDevice)(pDevice->sdl.deviceID); - } else -#endif - { - ((MAL_PFN_SDL_CloseAudio)pDevice->pContext->sdl.SDL_CloseAudio)(); - } + ((MAL_PFN_SDL_CloseAudioDevice)pDevice->pContext->sdl.SDL_CloseAudioDevice)(pDevice->sdl.deviceID); } @@ -19858,7 +20949,7 @@ void mal_audio_callback__sdl(void* pUserData, mal_uint8* pBuffer, int bufferSize mal_uint32 bufferSizeInFrames = (mal_uint32)bufferSizeInBytes / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); #ifdef MAL_DEBUG_OUTPUT - printf("[SDL] Callback: bufferSizeInBytes=%d, bufferSizeInFrames=%d\n", bufferSizeInBytes, bufferSizeInFrames); + printf("[SDL] Callback: bufferSizeInBytes=%d, bufferSizeInFrames=%d\n", bufferSizeInBytes, bufferSizeInFrames); #endif if (pDevice->type == mal_device_type_playback) { @@ -19906,41 +20997,16 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con desiredSpec.format = MAL_AUDIO_F32; } -#ifndef MAL_USE_SDL_1 - if (!pDevice->pContext->sdl.usingSDL1) { - int isCapture = (type == mal_device_type_playback) ? 0 : 1; + int isCapture = (type == mal_device_type_playback) ? 0 : 1; - const char* pDeviceName = NULL; - if (pDeviceID != NULL) { - pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pDevice->pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture); - } + const char* pDeviceName = NULL; + if (pDeviceID != NULL) { + pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pDevice->pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture); + } - pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE); - if (pDevice->sdl.deviceID == 0) { - return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL2 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); - } - } else -#endif - { - // SDL1 uses default devices. - (void)pDeviceID; - - // SDL1 only supports playback as far as I can tell. - if (type != mal_device_type_playback) { - return MAL_NO_DEVICE; - } - - // SDL1 does not support floating point formats. - if (desiredSpec.format == MAL_AUDIO_F32) { - desiredSpec.format = MAL_AUDIO_S16; - } - - int deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec); - if (deviceID < 0) { - return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL1 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); - } - - pDevice->sdl.deviceID = (mal_uint32)deviceID; + pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE); + if (pDevice->sdl.deviceID == 0) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL2 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); } pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format); @@ -19952,7 +21018,6 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con #ifdef MAL_DEBUG_OUTPUT printf("=== SDL CONFIG ===\n"); - printf(" SDL VERSION: %s\n", pDevice->pContext->sdl.usingSDL1 ? "1" : "2"); printf(" FORMAT: %s -> %s\n", mal_get_format_name(pConfig->format), mal_get_format_name(pDevice->internalFormat)); printf(" CHANNELS: %d -> %d\n", desiredSpec.channels, obtainedSpec.channels); printf(" SAMPLE RATE: %d -> %d\n", desiredSpec.freq, obtainedSpec.freq); @@ -19962,34 +21027,19 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con return MAL_SUCCESS; } -mal_result mal_device__start_backend__sdl(mal_device* pDevice) +mal_result mal_device_start__sdl(mal_device* pDevice) { mal_assert(pDevice != NULL); -#ifndef MAL_USE_SDL_1 - if (!pDevice->pContext->sdl.usingSDL1) { - ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 0); - } else -#endif - { - ((MAL_PFN_SDL_PauseAudio)pDevice->pContext->sdl.SDL_PauseAudio)(0); - } - + ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 0); return MAL_SUCCESS; } -mal_result mal_device__stop_backend__sdl(mal_device* pDevice) +mal_result mal_device_stop__sdl(mal_device* pDevice) { mal_assert(pDevice != NULL); -#ifndef MAL_USE_SDL_1 - if (!pDevice->pContext->sdl.usingSDL1) { - ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 1); - } else -#endif - { - ((MAL_PFN_SDL_PauseAudio)pDevice->pContext->sdl.SDL_PauseAudio)(1); - } + ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 1); mal_device__set_state(pDevice, MAL_STATE_STOPPED); mal_stop_proc onStop = pDevice->onStop; @@ -20018,14 +21068,11 @@ mal_result mal_context_init__sdl(mal_context* pContext) // Run-time linking. const char* libNames[] = { #if defined(MAL_WIN32) - "SDL2.dll", - "SDL.dll" + "SDL2.dll" #elif defined(MAL_APPLE) - "SDL2.framework/SDL2", - "SDL.framework/SDL" + "SDL2.framework/SDL2" #else - "libSDL2-2.0.so.0", - "libSDL-1.2.so.0" + "libSDL2-2.0.so.0" #endif }; @@ -20042,41 +21089,21 @@ mal_result mal_context_init__sdl(mal_context* pContext) pContext->sdl.SDL_InitSubSystem = mal_dlsym(pContext->sdl.hSDL, "SDL_InitSubSystem"); pContext->sdl.SDL_QuitSubSystem = mal_dlsym(pContext->sdl.hSDL, "SDL_QuitSubSystem"); - pContext->sdl.SDL_CloseAudio = mal_dlsym(pContext->sdl.hSDL, "SDL_CloseAudio"); - pContext->sdl.SDL_OpenAudio = mal_dlsym(pContext->sdl.hSDL, "SDL_OpenAudio"); - pContext->sdl.SDL_PauseAudio = mal_dlsym(pContext->sdl.hSDL, "SDL_PauseAudio"); -#ifndef MAL_USE_SDL_1 pContext->sdl.SDL_GetNumAudioDevices = mal_dlsym(pContext->sdl.hSDL, "SDL_GetNumAudioDevices"); pContext->sdl.SDL_GetAudioDeviceName = mal_dlsym(pContext->sdl.hSDL, "SDL_GetAudioDeviceName"); pContext->sdl.SDL_CloseAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_CloseAudioDevice"); pContext->sdl.SDL_OpenAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_OpenAudioDevice"); pContext->sdl.SDL_PauseAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_PauseAudioDevice"); -#endif #else // Compile-time linking. pContext->sdl.SDL_InitSubSystem = (mal_proc)SDL_InitSubSystem; pContext->sdl.SDL_QuitSubSystem = (mal_proc)SDL_QuitSubSystem; - pContext->sdl.SDL_CloseAudio = (mal_proc)SDL_CloseAudio; - pContext->sdl.SDL_OpenAudio = (mal_proc)SDL_OpenAudio; - pContext->sdl.SDL_PauseAudio = (mal_proc)SDL_PauseAudio; -#ifndef MAL_USE_SDL_1 pContext->sdl.SDL_GetNumAudioDevices = (mal_proc)SDL_GetNumAudioDevices; pContext->sdl.SDL_GetAudioDeviceName = (mal_proc)SDL_GetAudioDeviceName; pContext->sdl.SDL_CloseAudioDevice = (mal_proc)SDL_CloseAudioDevice; pContext->sdl.SDL_OpenAudioDevice = (mal_proc)SDL_OpenAudioDevice; pContext->sdl.SDL_PauseAudioDevice = (mal_proc)SDL_PauseAudioDevice; #endif -#endif - - // We need to determine whether or not we are using SDL2 or SDL1. We can know this by looking at whether or not certain - // function pointers are NULL. - if (pContext->sdl.SDL_GetNumAudioDevices == NULL || - pContext->sdl.SDL_GetAudioDeviceName == NULL || - pContext->sdl.SDL_CloseAudioDevice == NULL || - pContext->sdl.SDL_OpenAudioDevice == NULL || - pContext->sdl.SDL_PauseAudioDevice == NULL) { - pContext->sdl.usingSDL1 = MAL_TRUE; - } int resultSDL = ((MAL_PFN_SDL_InitSubSystem)pContext->sdl.SDL_InitSubSystem)(MAL_SDL_INIT_AUDIO); if (resultSDL != 0) { @@ -20091,8 +21118,8 @@ mal_result mal_context_init__sdl(mal_context* pContext) pContext->onGetDeviceInfo = mal_context_get_device_info__sdl; pContext->onDeviceInit = mal_device_init__sdl; pContext->onDeviceUninit = mal_device_uninit__sdl; - pContext->onDeviceStart = mal_device__start_backend__sdl; - pContext->onDeviceStop = mal_device__stop_backend__sdl; + pContext->onDeviceStart = mal_device_start__sdl; + pContext->onDeviceStop = mal_device_stop__sdl; return MAL_SUCCESS; } @@ -20549,12 +21576,24 @@ mal_result mal_context_init(const mal_backend backends[], mal_uint32 backendCoun result = mal_context_init__oss(pContext); } break; #endif + #ifdef MAL_HAS_AAUDIO + case mal_backend_aaudio: + { + result = mal_context_init__aaudio(pContext); + } break; + #endif #ifdef MAL_HAS_OPENSL case mal_backend_opensl: { result = mal_context_init__opensl(pContext); } break; #endif + #ifdef MAL_HAS_WEBAUDIO + case mal_backend_webaudio: + { + result = mal_context_init__webaudio(pContext); + } break; + #endif #ifdef MAL_HAS_OPENAL case mal_backend_openal: { @@ -20991,14 +22030,14 @@ mal_result mal_device_init_ex(const mal_backend backends[], mal_uint32 backendCo void mal_device_uninit(mal_device* pDevice) { - if (!mal_device__is_initialized(pDevice)) return; + if (!mal_device__is_initialized(pDevice)) { + return; + } // Make sure the device is stopped first. The backends will probably handle this naturally, // but I like to do it explicitly for my own sanity. if (mal_device_is_started(pDevice)) { - while (mal_device_stop(pDevice) == MAL_DEVICE_BUSY) { - mal_sleep(1); - } + mal_device_stop(pDevice); } // Putting the device into an uninitialized state will make the worker thread return. @@ -21045,28 +22084,19 @@ void mal_device_set_stop_callback(mal_device* pDevice, mal_stop_proc proc) mal_result mal_device_start(mal_device* pDevice) { - if (pDevice == NULL) return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); - if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); + if (pDevice == NULL) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); + } + + if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); + } mal_result result = MAL_ERROR; mal_mutex_lock(&pDevice->lock); { - // Be a bit more descriptive if the device is already started or is already in the process of starting. This is likely - // a bug with the application. - if (mal_device__get_state(pDevice) == MAL_STATE_STARTING) { - mal_mutex_unlock(&pDevice->lock); - return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called while another thread is already starting it.", MAL_DEVICE_ALREADY_STARTING); - } - if (mal_device__get_state(pDevice) == MAL_STATE_STARTED) { - mal_mutex_unlock(&pDevice->lock); - return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called for a device that's already started.", MAL_DEVICE_ALREADY_STARTED); - } - - // The device needs to be in a stopped state. If it's not, we just let the caller know the device is busy. - if (mal_device__get_state(pDevice) != MAL_STATE_STOPPED) { - mal_mutex_unlock(&pDevice->lock); - return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called while another thread is in the process of stopping it.", MAL_DEVICE_BUSY); - } + // Starting, stopping and pausing are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. + mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STOPPED /*|| mal_device__get_state(pDevice) == MAL_STATE_PAUSED*/); mal_device__set_state(pDevice, MAL_STATE_STARTING); @@ -21094,28 +22124,19 @@ mal_result mal_device_start(mal_device* pDevice) mal_result mal_device_stop(mal_device* pDevice) { - if (pDevice == NULL) return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); - if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); + if (pDevice == NULL) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); + } + + if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) { + return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED); + } mal_result result = MAL_ERROR; mal_mutex_lock(&pDevice->lock); { - // Be a bit more descriptive if the device is already stopped or is already in the process of stopping. This is likely - // a bug with the application. - if (mal_device__get_state(pDevice) == MAL_STATE_STOPPING) { - mal_mutex_unlock(&pDevice->lock); - return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called while another thread is already stopping it.", MAL_DEVICE_ALREADY_STOPPING); - } - if (mal_device__get_state(pDevice) == MAL_STATE_STOPPED) { - mal_mutex_unlock(&pDevice->lock); - return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called for a device that's already stopped.", MAL_DEVICE_ALREADY_STOPPED); - } - - // The device needs to be in a started state. If it's not, we just let the caller know the device is busy. - if (mal_device__get_state(pDevice) != MAL_STATE_STARTED) { - mal_mutex_unlock(&pDevice->lock); - return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called while another thread is in the process of starting it.", MAL_DEVICE_BUSY); - } + // Starting, stopping and pausing are wrapped in a mutex which means we can assert that the device is in a started or paused state. + mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STARTED /*|| mal_device__get_state(pDevice) == MAL_STATE_PAUSED*/); mal_device__set_state(pDevice, MAL_STATE_STOPPING); @@ -21124,6 +22145,7 @@ mal_result mal_device_stop(mal_device* pDevice) // Asynchronous backends need to be handled differently. if (mal_context_is_backend_asynchronous(pDevice->pContext)) { result = pDevice->pContext->onDeviceStop(pDevice); + mal_device__set_state(pDevice, MAL_STATE_STOPPED); } else { // Synchronous backends. @@ -24759,8 +25781,8 @@ float g_malChannelPlaneRatios[MAL_CHANNEL_POSITION_COUNT][6] = { { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_RIGHT { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_CENTER { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_LFE - { 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f}, // MAL_CHANNEL_BACK_LEFT - { 0.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.0f}, // MAL_CHANNEL_BACK_RIGHT + { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, // MAL_CHANNEL_BACK_LEFT + { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, // MAL_CHANNEL_BACK_RIGHT { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_LEFT_CENTER { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_RIGHT_CENTER { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, // MAL_CHANNEL_BACK_CENTER @@ -24971,7 +25993,7 @@ mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; if (channelPosIn == channelPosOut) { - pRouter->weights[iChannelIn][iChannelOut] = 1; + pRouter->config.weights[iChannelIn][iChannelOut] = 1; } } } @@ -24986,7 +26008,7 @@ mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; if (channelPosOut != MAL_CHANNEL_NONE && channelPosOut != MAL_CHANNEL_MONO && channelPosOut != MAL_CHANNEL_LFE) { - pRouter->weights[iChannelIn][iChannelOut] = 1; + pRouter->config.weights[iChannelIn][iChannelOut] = 1; } } } @@ -25014,7 +26036,7 @@ mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; if (channelPosIn != MAL_CHANNEL_NONE && channelPosIn != MAL_CHANNEL_MONO && channelPosIn != MAL_CHANNEL_LFE) { - pRouter->weights[iChannelIn][iChannelOut] += monoWeight; + pRouter->config.weights[iChannelIn][iChannelOut] += monoWeight; } } } @@ -25024,56 +26046,67 @@ mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal // Input and output channels that are not present on the other side need to be blended in based on spatial locality. - if (pRouter->config.mixingMode != mal_channel_mix_mode_simple) { - // Unmapped input channels. - for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { - mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; + switch (pRouter->config.mixingMode) + { + case mal_channel_mix_mode_rectangular: + { + // Unmapped input channels. + for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { + mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; - if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) { - if (!mal_channel_map_contains_channel_position(pRouter->config.channelsOut, pRouter->config.channelMapOut, channelPosIn)) { - for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { - mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; + if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) { + if (!mal_channel_map_contains_channel_position(pRouter->config.channelsOut, pRouter->config.channelMapOut, channelPosIn)) { + for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { + mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; - if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) { - float weight = 0; - if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) { - weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut); - } + if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) { + float weight = 0; + if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) { + weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut); + } - // Only apply the weight if we haven't already got some contribution from the respective channels. - if (pRouter->weights[iChannelIn][iChannelOut] == 0) { - pRouter->weights[iChannelIn][iChannelOut] = weight; + // Only apply the weight if we haven't already got some contribution from the respective channels. + if (pRouter->config.weights[iChannelIn][iChannelOut] == 0) { + pRouter->config.weights[iChannelIn][iChannelOut] = weight; + } } } } } } - } - // Unmapped output channels. - for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { - mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; + // Unmapped output channels. + for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) { + mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; - if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) { - if (!mal_channel_map_contains_channel_position(pRouter->config.channelsIn, pRouter->config.channelMapIn, channelPosOut)) { - for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { - mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; + if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) { + if (!mal_channel_map_contains_channel_position(pRouter->config.channelsIn, pRouter->config.channelMapIn, channelPosOut)) { + for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) { + mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn]; - if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) { - float weight = 0; - if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) { - weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut); - } + if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) { + float weight = 0; + if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) { + weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut); + } - // Only apply the weight if we haven't already got some contribution from the respective channels. - if (pRouter->weights[iChannelIn][iChannelOut] == 0) { - pRouter->weights[iChannelIn][iChannelOut] = weight; + // Only apply the weight if we haven't already got some contribution from the respective channels. + if (pRouter->config.weights[iChannelIn][iChannelOut] == 0) { + pRouter->config.weights[iChannelIn][iChannelOut] = weight; + } } } } } } - } + } break; + + case mal_channel_mix_mode_custom_weights: + case mal_channel_mix_mode_simple: + default: + { + /* Fallthrough. */ + } break; } return MAL_SUCCESS; @@ -25125,7 +26158,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram mal_uint64 iFrame = 0; #if defined(MAL_SUPPORT_NEON) if (mal_channel_router__can_use_neon(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) { - float32x4_t weight = vmovq_n_f32(pRouter->weights[iChannelIn][iChannelOut]); + float32x4_t weight = vmovq_n_f32(pRouter->config.weights[iChannelIn][iChannelOut]); mal_uint64 frameCount4 = frameCount/4; for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) { @@ -25140,7 +26173,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram #endif #if defined(MAL_SUPPORT_AVX512) if (mal_channel_router__can_use_avx512(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) { - __m512 weight = _mm512_set1_ps(pRouter->weights[iChannelIn][iChannelOut]); + __m512 weight = _mm512_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]); mal_uint64 frameCount16 = frameCount/16; for (mal_uint64 iFrame16 = 0; iFrame16 < frameCount16; iFrame16 += 1) { @@ -25155,7 +26188,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram #endif #if defined(MAL_SUPPORT_AVX2) if (mal_channel_router__can_use_avx2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) { - __m256 weight = _mm256_set1_ps(pRouter->weights[iChannelIn][iChannelOut]); + __m256 weight = _mm256_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]); mal_uint64 frameCount8 = frameCount/8; for (mal_uint64 iFrame8 = 0; iFrame8 < frameCount8; iFrame8 += 1) { @@ -25170,7 +26203,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram #endif #if defined(MAL_SUPPORT_SSE2) if (mal_channel_router__can_use_sse2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) { - __m128 weight = _mm_set1_ps(pRouter->weights[iChannelIn][iChannelOut]); + __m128 weight = _mm_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]); mal_uint64 frameCount4 = frameCount/4; for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) { @@ -25183,10 +26216,10 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram } else #endif { // Reference. - float weight0 = pRouter->weights[iChannelIn][iChannelOut]; - float weight1 = pRouter->weights[iChannelIn][iChannelOut]; - float weight2 = pRouter->weights[iChannelIn][iChannelOut]; - float weight3 = pRouter->weights[iChannelIn][iChannelOut]; + float weight0 = pRouter->config.weights[iChannelIn][iChannelOut]; + float weight1 = pRouter->config.weights[iChannelIn][iChannelOut]; + float weight2 = pRouter->config.weights[iChannelIn][iChannelOut]; + float weight3 = pRouter->config.weights[iChannelIn][iChannelOut]; mal_uint64 frameCount4 = frameCount/4; for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) { @@ -25200,7 +26233,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram // Leftover. for (; iFrame < frameCount; ++iFrame) { - ppSamplesOut[iChannelOut][iFrame] += ppSamplesIn[iChannelIn][iFrame] * pRouter->weights[iChannelIn][iChannelOut]; + ppSamplesOut[iChannelOut][iFrame] += ppSamplesIn[iChannelIn][iFrame] * pRouter->config.weights[iChannelIn][iChannelOut]; } } } @@ -26309,7 +27342,7 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form } } -void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames) +void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames) { if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) { return; // Invalid args. @@ -26320,7 +27353,7 @@ void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uin case mal_format_s16: { const mal_int16* pSrcS16 = (const mal_int16*)pInterleavedPCMFrames; - for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + for (mal_uint64 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { mal_int16* pDstS16 = (mal_int16*)ppDeinterleavedPCMFrames[iChannel]; pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel]; @@ -26331,7 +27364,7 @@ void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uin case mal_format_f32: { const float* pSrcF32 = (const float*)pInterleavedPCMFrames; - for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + for (mal_uint64 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel]; pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel]; @@ -26343,7 +27376,7 @@ void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uin { mal_uint32 sampleSizeInBytes = mal_get_bytes_per_sample(format); - for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + for (mal_uint64 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { void* pDst = mal_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); const void* pSrc = mal_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); @@ -26354,14 +27387,14 @@ void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uin } } -void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames) +void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames) { switch (format) { case mal_format_s16: { mal_int16* pDstS16 = (mal_int16*)pInterleavedPCMFrames; - for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + for (mal_uint64 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { const mal_int16* pSrcS16 = (const mal_int16*)ppDeinterleavedPCMFrames[iChannel]; pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame]; @@ -26372,7 +27405,7 @@ void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint3 case mal_format_f32: { float* pDstF32 = (float*)pInterleavedPCMFrames; - for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + for (mal_uint64 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel]; pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame]; @@ -26384,7 +27417,7 @@ void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint3 { mal_uint32 sampleSizeInBytes = mal_get_bytes_per_sample(format); - for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + for (mal_uint64 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) { void* pDst = mal_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); const void* pSrc = mal_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); @@ -26790,7 +27823,7 @@ mal_uint64 mal_dsp_read(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, } } - // Slower path. The real is done here. To do this all we need to do is read from the last stage in the pipeline. + // Slower path. The real work is done here. To do this all we need to do is read from the last stage in the pipeline. mal_assert(pDSP->isPostFormatConversionRequired == MAL_TRUE); mal_dsp_callback_data data; @@ -27155,7 +28188,7 @@ mal_result mal_decoder_internal_on_seek_to_frame__wav(mal_decoder* pDecoder, mal drwav* pWav = (drwav*)pDecoder->pInternalDecoder; mal_assert(pWav != NULL); - drwav_bool32 result = drwav_seek_to_sample(pWav, frameIndex*pWav->channels); + drwav_bool32 result = drwav_seek_to_pcm_frame(pWav, frameIndex); if (result) { return MAL_SUCCESS; } else { @@ -27180,9 +28213,9 @@ mal_uint32 mal_decoder_internal_on_read_frames__wav(mal_dsp* pDSP, mal_uint32 fr mal_assert(pWav != NULL); switch (pDecoder->internalFormat) { - case mal_format_s16: return (mal_uint32)drwav_read_s16(pWav, frameCount*pDecoder->internalChannels, (drwav_int16*)pSamplesOut) / pDecoder->internalChannels; - case mal_format_s32: return (mal_uint32)drwav_read_s32(pWav, frameCount*pDecoder->internalChannels, (drwav_int32*)pSamplesOut) / pDecoder->internalChannels; - case mal_format_f32: return (mal_uint32)drwav_read_f32(pWav, frameCount*pDecoder->internalChannels, (float*)pSamplesOut) / pDecoder->internalChannels; + case mal_format_s16: return (mal_uint32)drwav_read_pcm_frames_s16(pWav, frameCount, (drwav_int16*)pSamplesOut); + case mal_format_s32: return (mal_uint32)drwav_read_pcm_frames_s32(pWav, frameCount, (drwav_int32*)pSamplesOut); + case mal_format_f32: return (mal_uint32)drwav_read_pcm_frames_f32(pWav, frameCount, (float*)pSamplesOut); default: break; } @@ -27282,7 +28315,7 @@ mal_result mal_decoder_internal_on_seek_to_frame__flac(mal_decoder* pDecoder, ma drflac* pFlac = (drflac*)pDecoder->pInternalDecoder; mal_assert(pFlac != NULL); - drflac_bool32 result = drflac_seek_to_sample(pFlac, frameIndex*pFlac->channels); + drflac_bool32 result = drflac_seek_to_pcm_frame(pFlac, frameIndex); if (result) { return MAL_SUCCESS; } else { @@ -27307,7 +28340,16 @@ mal_uint32 mal_decoder_internal_on_read_frames__flac(mal_dsp* pDSP, mal_uint32 f drflac* pFlac = (drflac*)pDecoder->pInternalDecoder; mal_assert(pFlac != NULL); - return (mal_uint32)drflac_read_s32(pFlac, frameCount*pDecoder->internalChannels, (drflac_int32*)pSamplesOut) / pDecoder->internalChannels; + switch (pDecoder->internalFormat) { + case mal_format_s16: return (mal_uint32)drflac_read_pcm_frames_s16(pFlac, frameCount, (drflac_int16*)pSamplesOut); + case mal_format_s32: return (mal_uint32)drflac_read_pcm_frames_s32(pFlac, frameCount, (drflac_int32*)pSamplesOut); + case mal_format_f32: return (mal_uint32)drflac_read_pcm_frames_f32(pFlac, frameCount, (float*)pSamplesOut); + default: break; + } + + // Should never get here. If we do, it means the internal format was not set correctly at initialization time. + mal_assert(MAL_FALSE); + return 0; } mal_result mal_decoder_init_flac__internal(const mal_decoder_config* pConfig, mal_decoder* pDecoder) @@ -27326,8 +28368,15 @@ mal_result mal_decoder_init_flac__internal(const mal_decoder_config* pConfig, ma pDecoder->onUninit = mal_decoder_internal_on_uninit__flac; pDecoder->pInternalDecoder = pFlac; - // The internal format is always s32. + // dr_flac supports reading as s32, s16 and f32. Try to do a one-to-one mapping if possible, but fall back to s32 if not. s32 is the "native" FLAC format + // since it's the only one that's truly lossless. pDecoder->internalFormat = mal_format_s32; + if (pConfig->format == mal_format_s16) { + pDecoder->internalFormat = mal_format_s16; + } else if (pConfig->format == mal_format_f32) { + pDecoder->internalFormat = mal_format_f32; + } + pDecoder->internalChannels = pFlac->channels; pDecoder->internalSampleRate = pFlac->sampleRate; mal_get_standard_channel_map(mal_standard_channel_map_flac, pDecoder->internalChannels, pDecoder->internalChannelMap); @@ -27646,7 +28695,7 @@ mal_result mal_decoder_internal_on_seek_to_frame__mp3(mal_decoder* pDecoder, mal drmp3* pMP3 = (drmp3*)pDecoder->pInternalDecoder; mal_assert(pMP3 != NULL); - drmp3_bool32 result = drmp3_seek_to_frame(pMP3, frameIndex); + drmp3_bool32 result = drmp3_seek_to_pcm_frame(pMP3, frameIndex); if (result) { return MAL_SUCCESS; } else { @@ -27672,7 +28721,7 @@ mal_uint32 mal_decoder_internal_on_read_frames__mp3(mal_dsp* pDSP, mal_uint32 fr drmp3* pMP3 = (drmp3*)pDecoder->pInternalDecoder; mal_assert(pMP3 != NULL); - return (mal_uint32)drmp3_read_f32(pMP3, frameCount, (float*)pSamplesOut); + return (mal_uint32)drmp3_read_pcm_frames_f32(pMP3, frameCount, (float*)pSamplesOut); } mal_result mal_decoder_init_mp3__internal(const mal_decoder_config* pConfig, mal_decoder* pDecoder) @@ -28556,6 +29605,28 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount // REVISION HISTORY // ================ // +// v0.8.15 - 201x-xx-xx +// - Add Web Audio backend. This is used when compiling with Emscripten. The SDL backend, which was previously +// used for web support, will be removed in a future version. +// - Add AAudio backend (Android Audio). This is the new priority backend for Android. Support for AAudio starts +// with Android 8. OpenSL|ES is used as a fallback for older versions of Android. +// - Deprecate the OpenAL backend. +// - Deprecate the SDL backend. +// +// v0.8.14 - 2018-12-16 +// - Core Audio: Fix a bug where the device state is not set correctly after stopping. +// - Add support for custom weights to the channel router. +// - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav. +// +// v0.8.13 - 2018-12-04 +// - Core Audio: Fix a bug with channel mapping. +// - Fix a bug with channel routing where the back/left and back/right channels have the wrong weight. +// +// v0.8.12 - 2018-11-27 +// - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2". +// - Fix a linking error with ALSA. +// - Fix a bug on iOS where the device name is not set correctly. +// // v0.8.11 - 2018-11-21 // - iOS bug fixes. // - Minor tweaks to PulseAudio. From d5735720b0cd8893320774e074694b48eb7b7d01 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 1 Jan 2019 20:53:30 +0100 Subject: [PATCH 20/58] Update Makefile for Emscripten --- examples/Makefile | 18 +++++++++--------- src/Makefile | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 53e8555d..976cfbc1 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -118,8 +118,8 @@ endif ifeq ($(PLATFORM),PLATFORM_WEB) # Emscripten required variables EMSDK_PATH = C:/emsdk - EMSCRIPTEN_VERSION = 1.38.20 - CLANG_VERSION = e1.38.20_64bit + EMSCRIPTEN_VERSION = 1.38.21 + CLANG_VERSION = e1.38.21_64bit PYTHON_VERSION = 2.7.13.1_64bit\python-2.7.13.amd64 NODE_VERSION = 8.9.1_64bit export PATH = $(EMSDK_PATH);$(EMSDK_PATH)\clang\$(CLANG_VERSION);$(EMSDK_PATH)\node\$(NODE_VERSION)\bin;$(EMSDK_PATH)\python\$(PYTHON_VERSION);$(EMSDK_PATH)\emscripten\$(EMSCRIPTEN_VERSION);C:\raylib\MinGW\bin:$$(PATH) @@ -130,23 +130,23 @@ endif # RAYLIB_RELEASE_PATH points to provided binaries or your freshly built version. ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),WINDOWS) - RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/win32/mingw32 + RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/src endif ifeq ($(PLATFORM_OS),LINUX) - RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/linux + RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/src endif ifeq ($(PLATFORM_OS),OSX) - RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/osx + RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/src endif ifeq ($(PLATFORM_OS),BSD) - RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/bsd + RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/src endif endif ifeq ($(PLATFORM),PLATFORM_RPI) - RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/rpi + RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/src endif ifeq ($(PLATFORM),PLATFORM_WEB) - RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/release/libs/html5 + RAYLIB_RELEASE_PATH = $(RAYLIB_PATH)/src endif # EXAMPLE_RUNTIME_PATH embeds a custom runtime location of libraylib.so or other desired libraries @@ -249,7 +249,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # -s EMTERPRETIFY_ASYNC=1 # support synchronous loops by emterpreter # --profiling # include information for code profiling # --preload-file resources # specify a resources folder for data compilation - CFLAGS += -Os -s USE_GLFW=3 -s USE_SDL=2 -s ASSERTIONS=1 -s WASM=1 -s EMTERPRETIFY=1 -s EMTERPRETIFY_ASYNC=1 + CFLAGS += -Os -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s EMTERPRETIFY=1 -s EMTERPRETIFY_ASYNC=1 # NOTE: Simple raylib examples are compiled to be interpreter by emterpreter, that way, # we can compile same code for ALL platforms with no change required, but, working on bigger diff --git a/src/Makefile b/src/Makefile index e3e9d31b..2c2bfb27 100644 --- a/src/Makefile +++ b/src/Makefile @@ -149,8 +149,8 @@ endif ifeq ($(PLATFORM),PLATFORM_WEB) # Emscripten required variables EMSDK_PATH = C:/emsdk - EMSCRIPTEN_VERSION = 1.38.20 - CLANG_VERSION = e1.38.20_64bit + EMSCRIPTEN_VERSION ?= 1.38.21 + CLANG_VERSION = e$(EMSCRIPTEN_VERSION)_64bit PYTHON_VERSION = 2.7.13.1_64bit\python-2.7.13.amd64 NODE_VERSION = 8.9.1_64bit export PATH = $(EMSDK_PATH);$(EMSDK_PATH)\clang\$(CLANG_VERSION);$(EMSDK_PATH)\node\$(NODE_VERSION)\bin;$(EMSDK_PATH)\python\$(PYTHON_VERSION);$(EMSDK_PATH)\emscripten\$(EMSCRIPTEN_VERSION);C:\raylib\MinGW\bin:$$(PATH) @@ -300,7 +300,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # -s ALLOW_MEMORY_GROWTH=1 # to allow memory resizing # -s TOTAL_MEMORY=16777216 # to specify heap memory size (default = 16MB) # -s USE_PTHREADS=1 # multithreading support - CFLAGS += -s USE_GLFW=3 -s USE_SDL=2 -s ASSERTIONS=1 --profiling + CFLAGS += -s USE_GLFW=3 -s ASSERTIONS=1 --profiling endif ifeq ($(PLATFORM),PLATFORM_ANDROID) # Compiler flags for arquitecture From 893329886451706033d8af4901cf7c251903a136 Mon Sep 17 00:00:00 2001 From: ChrisDill Date: Wed, 2 Jan 2019 10:14:55 +0000 Subject: [PATCH 21/58] Added SetMouseOffset - Changed mouseScale to Vector2. - Added SetMouseOffset to change XY of mouseScale. --- src/core.c | 23 ++++++++++++++++------- src/raylib.h | 1 + 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/core.c b/src/core.c index 6c2713e1..c4f410e4 100644 --- a/src/core.c +++ b/src/core.c @@ -333,7 +333,7 @@ static int defaultKeyboardMode; // Used to store default keyboar // Mouse states static Vector2 mousePosition; // Mouse position on screen -static float mouseScale = 1.0f; // Mouse default scale +static Vector2 mouseScale = { 1.0f }; // Mouse default scale static bool cursorHidden = false; // Track if cursor is hidden static bool cursorOnScreen = false; // Tracks if cursor is inside client area static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen @@ -2075,7 +2075,7 @@ int GetMouseX(void) #if defined(PLATFORM_ANDROID) return (int)touchPosition[0].x; #else - return (int)(mousePosition.x*mouseScale); + return (int)(mousePosition.x*mouseScale.x); #endif } @@ -2085,7 +2085,7 @@ int GetMouseY(void) #if defined(PLATFORM_ANDROID) return (int)touchPosition[0].x; #else - return (int)(mousePosition.y*mouseScale); + return (int)(mousePosition.y*mouseScale.y); #endif } @@ -2095,7 +2095,7 @@ Vector2 GetMousePosition(void) #if defined(PLATFORM_ANDROID) return GetTouchPosition(0); #else - return (Vector2){ mousePosition.x*mouseScale, mousePosition.y*mouseScale }; + return (Vector2){ mousePosition.x*mouseScale.x, mousePosition.y*mouseScale.y }; #endif } @@ -2114,7 +2114,16 @@ void SetMousePosition(Vector2 position) void SetMouseScale(float scale) { #if !defined(PLATFORM_ANDROID) - mouseScale = scale; + mouseScale = (Vector2){ scale }; +#endif +} + +// Set mouse scaling +// NOTE: Useful when rendering to different size targets +void SetMouseOffset(Vector2 offset) +{ +#if !defined(PLATFORM_ANDROID) + mouseScale = offset; #endif } @@ -4225,10 +4234,10 @@ static void *EventThread(void *arg) // Screen confinement if (mousePosition.x < 0) mousePosition.x = 0; - if (mousePosition.x > screenWidth/mouseScale) mousePosition.x = screenWidth/mouseScale; + if (mousePosition.x > screenWidth/mouseScale.x) mousePosition.x = screenWidth/mouseScale.x; if (mousePosition.y < 0) mousePosition.y = 0; - if (mousePosition.y > screenHeight/mouseScale) mousePosition.y = screenHeight/mouseScale; + if (mousePosition.y > screenHeight/mouseScale) mousePosition.y = screenHeight/mouseScale.y; // Gesture update if (GestureNeedsUpdate) diff --git a/src/raylib.h b/src/raylib.h index 0e9e0155..9deb8f42 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -942,6 +942,7 @@ RLAPI int GetMouseY(void); // Returns mouse p RLAPI Vector2 GetMousePosition(void); // Returns mouse position XY RLAPI void SetMousePosition(Vector2 position); // Set mouse position XY RLAPI void SetMouseScale(float scale); // Set mouse scaling +RLAPI void SetMouseOffset(Vector2 scale); // Set mouse scaling XY RLAPI int GetMouseWheelMove(void); // Returns mouse wheel movement Y // Input-related functions: touch From a707574f33467f81e8e300e09db23269bf2c1b98 Mon Sep 17 00:00:00 2001 From: ChrisDill Date: Wed, 2 Jan 2019 10:46:19 +0000 Subject: [PATCH 22/58] Default mouseScale fixed - Didn't set X and Y values correctly. --- src/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core.c b/src/core.c index c4f410e4..6c886024 100644 --- a/src/core.c +++ b/src/core.c @@ -333,7 +333,7 @@ static int defaultKeyboardMode; // Used to store default keyboar // Mouse states static Vector2 mousePosition; // Mouse position on screen -static Vector2 mouseScale = { 1.0f }; // Mouse default scale +static Vector2 mouseScale = { 1.0f, 1.0f }; // Mouse default scale static bool cursorHidden = false; // Track if cursor is hidden static bool cursorOnScreen = false; // Tracks if cursor is inside client area static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen @@ -2114,7 +2114,7 @@ void SetMousePosition(Vector2 position) void SetMouseScale(float scale) { #if !defined(PLATFORM_ANDROID) - mouseScale = (Vector2){ scale }; + mouseScale = (Vector2){ scale, scale }; #endif } From 63f9f91ea88e74b622ea583df252899b2da40fd0 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 2 Jan 2019 14:05:20 +0100 Subject: [PATCH 23/58] Avoid crashing if music file not loaded --- src/audio.c | 63 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/src/audio.c b/src/audio.c index 00e28f2a..a76481b4 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1219,6 +1219,8 @@ Music LoadMusicStream(const char *fileName) // Unload music stream void UnloadMusicStream(Music music) { + if (music == NULL) return; + CloseAudioStream(music->stream); if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg); @@ -1241,41 +1243,46 @@ void UnloadMusicStream(Music music) // Start music playing (open stream) void PlayMusicStream(Music music) { - AudioBuffer *audioBuffer = (AudioBuffer *)music->stream.audioBuffer; - - if (audioBuffer == NULL) + if (music != NULL) { - TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); - return; + AudioBuffer *audioBuffer = (AudioBuffer *)music->stream.audioBuffer; + + if (audioBuffer == NULL) + { + TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); + return; + } + + // For music streams, we need to make sure we maintain the frame cursor position. This is hack for this section of code in UpdateMusicStream() + // // NOTE: In case window is minimized, music stream is stopped, + // // just make sure to play again on window restore + // if (IsMusicPlaying(music)) PlayMusicStream(music); + mal_uint32 frameCursorPos = audioBuffer->frameCursorPos; + + PlayAudioStream(music->stream); // <-- This resets the cursor position. + + audioBuffer->frameCursorPos = frameCursorPos; } - - // For music streams, we need to make sure we maintain the frame cursor position. This is hack for this section of code in UpdateMusicStream() - // // NOTE: In case window is minimized, music stream is stopped, - // // just make sure to play again on window restore - // if (IsMusicPlaying(music)) PlayMusicStream(music); - mal_uint32 frameCursorPos = audioBuffer->frameCursorPos; - - PlayAudioStream(music->stream); // <-- This resets the cursor position. - - audioBuffer->frameCursorPos = frameCursorPos; } // Pause music playing void PauseMusicStream(Music music) { - PauseAudioStream(music->stream); + if (music != NULL) PauseAudioStream(music->stream); } // Resume music playing void ResumeMusicStream(Music music) { - ResumeAudioStream(music->stream); + if (music != NULL) ResumeAudioStream(music->stream); } // Stop music playing (close stream) // TODO: To clear a buffer, make sure they have been already processed! void StopMusicStream(Music music) { + if (music == NULL) return; + StopAudioStream(music->stream); // Restart music context @@ -1304,6 +1311,8 @@ void StopMusicStream(Music music) // TODO: Make sure buffers are ready for update... check music state void UpdateMusicStream(Music music) { + if (music == NULL) return; + bool streamEnding = false; unsigned int subBufferSizeInFrames = ((AudioBuffer *)music->stream.audioBuffer)->bufferSizeInFrames/2; @@ -1406,32 +1415,35 @@ void UpdateMusicStream(Music music) // Check if any music is playing bool IsMusicPlaying(Music music) { - return IsAudioStreamPlaying(music->stream); + if (music == NULL) return false; + else return IsAudioStreamPlaying(music->stream); } // Set volume for music void SetMusicVolume(Music music, float volume) { - SetAudioStreamVolume(music->stream, volume); + if (music != NULL) SetAudioStreamVolume(music->stream, volume); } // Set pitch for music void SetMusicPitch(Music music, float pitch) { - SetAudioStreamPitch(music->stream, pitch); + if (music != NULL) SetAudioStreamPitch(music->stream, pitch); } // Set music loop count (loop repeats) // NOTE: If set to -1, means infinite loop void SetMusicLoopCount(Music music, int count) { - music->loopCount = count; + if (music != NULL) music->loopCount = count; } // Get music time length (in seconds) float GetMusicTimeLength(Music music) { - float totalSeconds = (float)music->totalSamples/(music->stream.sampleRate*music->stream.channels); + float totalSeconds = 0.0f; + + if (music != NULL) totalSeconds = (float)music->totalSamples/(music->stream.sampleRate*music->stream.channels); return totalSeconds; } @@ -1441,8 +1453,11 @@ float GetMusicTimePlayed(Music music) { float secondsPlayed = 0.0f; - unsigned int samplesPlayed = music->totalSamples - music->samplesLeft; - secondsPlayed = (float)samplesPlayed/(music->stream.sampleRate*music->stream.channels); + if (music != NULL) + { + unsigned int samplesPlayed = music->totalSamples - music->samplesLeft; + secondsPlayed = (float)samplesPlayed/(music->stream.sampleRate*music->stream.channels); + } return secondsPlayed; } From d2e861fdce806294e569f1afef28e06c30509365 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 2 Jan 2019 18:00:22 +0100 Subject: [PATCH 24/58] Test Windows compilation on Travis CI --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c27b6582..74ad8e8f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ matrix: env: ARCH=amd64 INSTALL_GLFW=YES USE_EXTERNAL_GLFW=OFF sudo: required - os: linux - env: ARCH=amd64 INSTALL_GLFW=YES OPENAL=ON STATIC=OFF RELEASE=NO + env: ARCH=amd64 INSTALL_GLFW=YES STATIC=OFF RELEASE=NO sudo: required - os: linux env: WAYLAND=ON ARCH=amd64 RELEASE=NO @@ -42,6 +42,9 @@ matrix: - os: linux env: ARCH=html5 RELEASE=NO sudo: required + - os: windows + compiler: gcc + env: ARCH=i386 SHARED=OFF RELEASE=NO before_script: @@ -49,7 +52,6 @@ before_install: - if [ -z "$USE_EXTERNAL_GLFW" ]; then export USE_EXTERNAL_GLFW=IF_POSSIBLE; fi - if [ -z "$SHARED" ]; then export SHARED=ON ; fi - if [ -z "$STATIC" ]; then export STATIC=ON ; fi - - if [ -z "$OPENAL" ]; then export OPENAL=OFF; fi - if [[ "$INSTALL_GLFW" == "YES" && "$USE_EXTERNAL_GLFW" != "OFF" ]]; then export DONT_TEST=1; fi From f9c43dc379165927e847981ec9e14eb5d3b14534 Mon Sep 17 00:00:00 2001 From: ChrisDill Date: Wed, 2 Jan 2019 19:09:34 +0000 Subject: [PATCH 25/58] Mouse functions changed - SetMouseScale changed to take in a Vector2. - Added mouseOffset global which is used in mouse read functions. --- src/core.c | 15 ++++++++------- src/raylib.h | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/core.c b/src/core.c index 6c886024..c013a288 100644 --- a/src/core.c +++ b/src/core.c @@ -334,6 +334,7 @@ static int defaultKeyboardMode; // Used to store default keyboar // Mouse states static Vector2 mousePosition; // Mouse position on screen static Vector2 mouseScale = { 1.0f, 1.0f }; // Mouse default scale +static Vector2 mouseOffset = { 0.0f, 0.0f }; // Mouse default scale static bool cursorHidden = false; // Track if cursor is hidden static bool cursorOnScreen = false; // Tracks if cursor is inside client area static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen @@ -2075,7 +2076,7 @@ int GetMouseX(void) #if defined(PLATFORM_ANDROID) return (int)touchPosition[0].x; #else - return (int)(mousePosition.x*mouseScale.x); + return (int)((mousePosition.x+mouseOffset.x)*mouseScale.x); #endif } @@ -2085,7 +2086,7 @@ int GetMouseY(void) #if defined(PLATFORM_ANDROID) return (int)touchPosition[0].x; #else - return (int)(mousePosition.y*mouseScale.y); + return (int)((mousePosition.y+mouseOffset.y)*mouseScale.y); #endif } @@ -2095,7 +2096,7 @@ Vector2 GetMousePosition(void) #if defined(PLATFORM_ANDROID) return GetTouchPosition(0); #else - return (Vector2){ mousePosition.x*mouseScale.x, mousePosition.y*mouseScale.y }; + return (Vector2){ (mousePosition.x+mouseOffset.x)*mouseScale.x, (mousePosition.y+mouseOffset.y)*mouseScale.y }; #endif } @@ -2111,14 +2112,14 @@ void SetMousePosition(Vector2 position) // Set mouse scaling // NOTE: Useful when rendering to different size targets -void SetMouseScale(float scale) +void SetMouseScale(Vector2 scale) { #if !defined(PLATFORM_ANDROID) - mouseScale = (Vector2){ scale, scale }; + mouseScale = scale; #endif } -// Set mouse scaling +// Set mouse offset // NOTE: Useful when rendering to different size targets void SetMouseOffset(Vector2 offset) { @@ -4237,7 +4238,7 @@ static void *EventThread(void *arg) if (mousePosition.x > screenWidth/mouseScale.x) mousePosition.x = screenWidth/mouseScale.x; if (mousePosition.y < 0) mousePosition.y = 0; - if (mousePosition.y > screenHeight/mouseScale) mousePosition.y = screenHeight/mouseScale.y; + if (mousePosition.y > screenHeight/mouseScale.y) mousePosition.y = screenHeight/mouseScale.y; // Gesture update if (GestureNeedsUpdate) diff --git a/src/raylib.h b/src/raylib.h index 9deb8f42..2fef4fc8 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -941,8 +941,8 @@ RLAPI int GetMouseX(void); // Returns mouse p RLAPI int GetMouseY(void); // Returns mouse position Y RLAPI Vector2 GetMousePosition(void); // Returns mouse position XY RLAPI void SetMousePosition(Vector2 position); // Set mouse position XY -RLAPI void SetMouseScale(float scale); // Set mouse scaling -RLAPI void SetMouseOffset(Vector2 scale); // Set mouse scaling XY +RLAPI void SetMouseScale(Vector2 scale); // Set mouse scaling +RLAPI void SetMouseOffset(Vector2 scale); // Set mouse offset RLAPI int GetMouseWheelMove(void); // Returns mouse wheel movement Y // Input-related functions: touch From d427f17210170b4ae0f185208db504264056d3c1 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Thu, 3 Jan 2019 13:53:20 +0100 Subject: [PATCH 26/58] REVIEWED some functions parameters Decided to allow user to provide values directly instead of requiring a Vector2 struct, probably more confortable to use. - SetMousePosition() - SetMouseOffset() - SetMouseScale() --- src/core.c | 40 ++++++++++++++++++---------------------- src/raylib.h | 6 +++--- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/core.c b/src/core.c index c013a288..f2edc1b6 100644 --- a/src/core.c +++ b/src/core.c @@ -332,9 +332,9 @@ static int defaultKeyboardMode; // Used to store default keyboar #endif // Mouse states -static Vector2 mousePosition; // Mouse position on screen -static Vector2 mouseScale = { 1.0f, 1.0f }; // Mouse default scale -static Vector2 mouseOffset = { 0.0f, 0.0f }; // Mouse default scale +static Vector2 mousePosition = { 0.0f, 0.0f }; // Mouse position on screen +static Vector2 mouseScale = { 1.0f, 1.0f }; // Mouse scaling +static Vector2 mouseOffset = { 0.0f, 0.0f }; // Mouse offset static bool cursorHidden = false; // Track if cursor is hidden static bool cursorOnScreen = false; // Tracks if cursor is inside client area static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen @@ -2076,7 +2076,7 @@ int GetMouseX(void) #if defined(PLATFORM_ANDROID) return (int)touchPosition[0].x; #else - return (int)((mousePosition.x+mouseOffset.x)*mouseScale.x); + return (int)((mousePosition.x + mouseOffset.x)*mouseScale.x); #endif } @@ -2086,7 +2086,7 @@ int GetMouseY(void) #if defined(PLATFORM_ANDROID) return (int)touchPosition[0].x; #else - return (int)((mousePosition.y+mouseOffset.y)*mouseScale.y); + return (int)((mousePosition.y + mouseOffset.y)*mouseScale.y); #endif } @@ -2096,36 +2096,32 @@ Vector2 GetMousePosition(void) #if defined(PLATFORM_ANDROID) return GetTouchPosition(0); #else - return (Vector2){ (mousePosition.x+mouseOffset.x)*mouseScale.x, (mousePosition.y+mouseOffset.y)*mouseScale.y }; + return (Vector2){ (mousePosition.x + mouseOffset.x)*mouseScale.x, (mousePosition.y + mouseOffset.y)*mouseScale.y }; #endif } // Set mouse position XY -void SetMousePosition(Vector2 position) +void SetMousePosition(int x, int y) { - mousePosition = position; + mousePosition = (Vector2){ (float)x, (float)y }; #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // NOTE: emscripten not implemented - glfwSetCursorPos(window, position.x, position.y); -#endif -} - -// Set mouse scaling -// NOTE: Useful when rendering to different size targets -void SetMouseScale(Vector2 scale) -{ -#if !defined(PLATFORM_ANDROID) - mouseScale = scale; + glfwSetCursorPos(window, mousePosition.x, mousePosition.y); #endif } // Set mouse offset // NOTE: Useful when rendering to different size targets -void SetMouseOffset(Vector2 offset) +void SetMouseOffset(int offsetX, int offsetY) { -#if !defined(PLATFORM_ANDROID) - mouseScale = offset; -#endif + mouseOffset = (Vector2){ (float)offsetX, (float)offsetY }; +} + +// Set mouse scaling +// NOTE: Useful when rendering to different size targets +void SetMouseScale(float scaleX, float scaleY) +{ + mouseScale = (Vector2){ scaleX, scaleY }; } // Returns mouse wheel movement Y diff --git a/src/raylib.h b/src/raylib.h index 2fef4fc8..c6156c10 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -940,9 +940,9 @@ RLAPI bool IsMouseButtonUp(int button); // Detect if a mou RLAPI int GetMouseX(void); // Returns mouse position X RLAPI int GetMouseY(void); // Returns mouse position Y RLAPI Vector2 GetMousePosition(void); // Returns mouse position XY -RLAPI void SetMousePosition(Vector2 position); // Set mouse position XY -RLAPI void SetMouseScale(Vector2 scale); // Set mouse scaling -RLAPI void SetMouseOffset(Vector2 scale); // Set mouse offset +RLAPI void SetMousePosition(int x, int y); // Set mouse position XY +RLAPI void SetMouseOffset(int offsetX, int offsetY); // Set mouse offset +RLAPI void SetMouseScale(float scaleX, float scaleY); // Set mouse scaling RLAPI int GetMouseWheelMove(void); // Returns mouse wheel movement Y // Input-related functions: touch From 644eddda11acefe38329cd8fea5b25c1a63ef778 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Thu, 3 Jan 2019 13:55:59 +0100 Subject: [PATCH 27/58] REMOVED: ShowLogo() Same functionality could be achieved using FLAGS... but also considering removing raylib logo animation... --- games/drturtle/06_drturtle_final.c | 1 - games/koala_seasons/koala_seasons.c | 1 - src/core.c | 6 ------ src/raylib.h | 1 - 4 files changed, 9 deletions(-) diff --git a/games/drturtle/06_drturtle_final.c b/games/drturtle/06_drturtle_final.c index a74d4f7e..15dcf09c 100644 --- a/games/drturtle/06_drturtle_final.c +++ b/games/drturtle/06_drturtle_final.c @@ -35,7 +35,6 @@ int main() const int screenHeight = 720; // Init window - ShowLogo(); InitWindow(screenWidth, screenHeight, "Dr. Turtle & Mr. GAMERA"); // Initialize audio device diff --git a/games/koala_seasons/koala_seasons.c b/games/koala_seasons/koala_seasons.c index 479fab85..214fda4c 100644 --- a/games/koala_seasons/koala_seasons.c +++ b/games/koala_seasons/koala_seasons.c @@ -50,7 +50,6 @@ int main(void) { const int screenHeight = 720; const char windowTitle[30] = "KOALA SEASONS"; - //ShowLogo(); //SetConfigFlags(FLAG_FULLSCREEN_MODE); // Note that windowTitle is ignored on Android diff --git a/src/core.c b/src/core.c index f2edc1b6..2d607c3a 100644 --- a/src/core.c +++ b/src/core.c @@ -1461,12 +1461,6 @@ Color Fade(Color color, float alpha) return (Color){color.r, color.g, color.b, (unsigned char)(255.0f*alpha)}; } -// Activate raylib logo at startup (can be done with flags) -void ShowLogo(void) -{ - showLogo = true; -} - // Setup window configuration flags (view FLAGS) void SetConfigFlags(unsigned char flags) { diff --git a/src/raylib.h b/src/raylib.h index c6156c10..e422e6bc 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -878,7 +878,6 @@ RLAPI Color GetColor(int hexValue); // Returns a C RLAPI Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f // Misc. functions -RLAPI void ShowLogo(void); // Activate raylib logo at startup (can be done with flags) RLAPI void SetConfigFlags(unsigned char flags); // Setup window configuration flags (view FLAGS) RLAPI void SetTraceLog(unsigned char types); // Enable trace log message types (bit flags based) RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set a trace log callback to enable custom logging bypassing raylib's one From 34d110bb3e327f86def2fc6801e8b17a33188114 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Thu, 3 Jan 2019 15:15:54 +0100 Subject: [PATCH 28/58] Trying to get Windows build on Travis CI --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 74ad8e8f..4b75e19f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -103,6 +103,11 @@ before_install: export RAYLIB_PACKAGE_SUFFIX="-macOS"; if [ "$INSTALL_GLFW" == "YES" ]; then brew update; brew install glfw; fi; fi + - if [ "$TRAVIS_OS_NAME" == "windows" ]; then + export DONT_TEST=1; + export RAYLIB_PACKAGE_SUFFIX="-windows"; + export CMAKE_ARCH_ARGS='-DPLATFORM=Desktop'; + fi - mkdir build - $RUNNER $CC --version From 214aa0c1b0d5c766d22bbb1069ecfb3a78373577 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Thu, 3 Jan 2019 16:07:51 +0100 Subject: [PATCH 29/58] Update .travis.yml --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4b75e19f..3d176523 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,6 +56,7 @@ before_install: export DONT_TEST=1; fi - if [ "$TRAVIS_OS_NAME" == "linux" ]; then + MAKE=make; if [[ "$ARCH" == *-android ]]; then export DONT_TEST=1; export RAYLIB_PACKAGE_SUFFIX="-Android-arm64"; @@ -100,10 +101,12 @@ before_install: fi; fi - if [ "$TRAVIS_OS_NAME" == "osx" ]; then + MAKE=make; export RAYLIB_PACKAGE_SUFFIX="-macOS"; if [ "$INSTALL_GLFW" == "YES" ]; then brew update; brew install glfw; fi; fi - if [ "$TRAVIS_OS_NAME" == "windows" ]; then + MAKE=mingw32-make; export DONT_TEST=1; export RAYLIB_PACKAGE_SUFFIX="-windows"; export CMAKE_ARCH_ARGS='-DPLATFORM=Desktop'; @@ -129,9 +132,9 @@ script: -DUSE_WAYLAND=$WAYLAND -DINCLUDE_EVERYTHING=ON .. - - $RUNNER make VERBOSE=1 + - $RUNNER $MAKE VERBOSE=1 - if [ "$RELEASE" != "NO" ]; then $RUNNER make package; fi - - sudo $RUNNER make install + - sudo $RUNNER $MAKE install - if [ ! "$DONT_TEST" ]; then pkg-config --static --libs raylib; nm -g release/libraylib.a | grep glfwGetProcAddress || (echo "libraylib.a doesn't contain GLFW symbols! Aborting..." && false); From 0bacc978c39a234da4a2b73ce3cb5b43e072a103 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Fri, 4 Jan 2019 15:48:25 +0100 Subject: [PATCH 30/58] Corrected issue on web --- src/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core.c b/src/core.c index 2d607c3a..8844b1d3 100644 --- a/src/core.c +++ b/src/core.c @@ -2381,7 +2381,9 @@ static bool InitGraphicsDevice(int width, int height) // Try to disable GPU V-Sync by default, set framerate using SetTargetFPS() // NOTE: V-Sync can be enabled by graphic driver configuration +#if !defined(PLATFORM_WEB) glfwSwapInterval(0); +#endif #if defined(PLATFORM_DESKTOP) // Load OpenGL 3.3 extensions From 1038e79b364d75a037e2b71e1a7dbc5937f88f1b Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 5 Jan 2019 15:04:18 +0100 Subject: [PATCH 31/58] Corrected issue with text measure on Latin-1 chars Considering chars inputs come in UTF8 form! --- src/text.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/text.c b/src/text.c index 0b343af3..7b2b67bb 100644 --- a/src/text.c +++ b/src/text.c @@ -935,13 +935,30 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing float textHeight = (float)font.baseSize; float scaleFactor = fontSize/(float)font.baseSize; + unsigned char letter = 0; // Current character + int index = 0; // Index position in sprite font + for (int i = 0; i < len; i++) { lenCounter++; - + if (text[i] != '\n') { - int index = GetGlyphIndex(font, (int)text[i]); + if ((unsigned char)text[i] == 0xc2) // UTF-8 encoding identification + { + // Support UTF-8 encoded values from [0xc2 0x80] -> [0xc2 0xbf](¿) + letter = (unsigned char)text[i + 1]; + index = GetGlyphIndex(font, (int)letter); + i++; + } + else if ((unsigned char)text[i] == 0xc3) // UTF-8 encoding identification + { + // Support UTF-8 encoded values from [0xc3 0x80](À) -> [0xc3 0xbf](ÿ) + letter = (unsigned char)text[i + 1]; + index = GetGlyphIndex(font, (int)letter + 64); + i++; + } + else index = GetGlyphIndex(font, (unsigned char)text[i]); if (font.chars[index].advanceX != 0) textWidth += font.chars[index].advanceX; else textWidth += (font.chars[index].rec.width + font.chars[index].offsetX); From 490e930665c7a2853f4a678aa397a7ed3551d8df Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 5 Jan 2019 17:25:35 +0100 Subject: [PATCH 32/58] Reviewed example --- examples/textures/textures_raw_data.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/textures/textures_raw_data.c b/examples/textures/textures_raw_data.c index b038792b..481bd66a 100644 --- a/examples/textures/textures_raw_data.c +++ b/examples/textures/textures_raw_data.c @@ -32,8 +32,8 @@ int main() UnloadImage(fudesumiRaw); // Unload CPU (RAM) image data // Generate a checked texture by code (1024x1024 pixels) - int width = 1024; - int height = 1024; + int width = 960; + int height = 480; // Dynamic memory allocation to store pixels data (Color type) Color *pixels = (Color *)malloc(width*height*sizeof(Color)); @@ -42,8 +42,8 @@ int main() { for (int x = 0; x < width; x++) { - if (((x/32+y/32)/1)%2 == 0) pixels[y*height + x] = ORANGE; - else pixels[y*height + x] = GOLD; + if (((x/32+y/32)/1)%2 == 0) pixels[y*width + x] = ORANGE; + else pixels[y*width + x] = GOLD; } } @@ -73,9 +73,9 @@ int main() DrawTexture(checked, screenWidth/2 - checked.width/2, screenHeight/2 - checked.height/2, Fade(WHITE, 0.5f)); DrawTexture(fudesumi, 430, -30, WHITE); - DrawText("CHECKED TEXTURE ", 84, 100, 30, BROWN); - DrawText("GENERATED by CODE", 72, 164, 30, BROWN); - DrawText("and RAW IMAGE LOADING", 46, 226, 30, BROWN); + DrawText("CHECKED TEXTURE ", 84, 85, 30, BROWN); + DrawText("GENERATED by CODE", 72, 148, 30, BROWN); + DrawText("and RAW IMAGE LOADING", 46, 210, 30, BROWN); DrawText("(c) Fudesumi sprite by Eiden Marsal", 310, screenHeight - 20, 10, BROWN); From a41cc08f9b30531a41921100d5a0d257053bd056 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 5 Jan 2019 18:03:09 +0100 Subject: [PATCH 33/58] Support screenshots and GIF capturing on Android and Web --- src/core.c | 78 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/src/core.c b/src/core.c index 8844b1d3..a7ea150b 100644 --- a/src/core.c +++ b/src/core.c @@ -311,7 +311,7 @@ extern EGLNativeWindowType uwpWindow; // Native EGL window handler for static struct android_app *androidApp; // Android activity static struct android_poll_source *source; // Android events polling source static int ident, events; // Android ALooper_pollAll() variables -static const char *internalDataPath; // Android internal data path to write data (/data/data//files) +static const char *internalDataPath = NULL; // Android internal data path to write data (/data/data//files) static bool appEnabled = true; // Used to detec if app is active static bool contextRebindRequired = false; // Used to know context rebind required @@ -1473,17 +1473,32 @@ void SetConfigFlags(unsigned char flags) // NOTE TraceLog() function is located in [utils.h] // Takes a screenshot of current screen (saved a .png) +// NOTE: This function could work in any platform but some platforms: PLATFORM_ANDROID and PLATFORM_WEB +// have their own internal file-systems, to dowload image to user file-system some additional mechanism is required void TakeScreenshot(const char *fileName) { -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) unsigned char *imgData = rlReadScreenPixels(renderWidth, renderHeight); - Image image = { imgData, renderWidth, renderHeight, 1, UNCOMPRESSED_R8G8B8A8 }; - ExportImage(image, fileName); - free(imgData); - - TraceLog(LOG_INFO, "Screenshot taken: %s", fileName); + + char path[512] = { 0 }; +#if defined(PLATFORM_ANDROID) + strcpy(path, internalDataPath); + strcat(path, "/"); + strcat(path, fileName); +#else + strcpy(path, fileName); #endif + + ExportImage(image, path); + free(imgData); + +#if defined(PLATFORM_WEB) + // Download file from MEMFS (emscripten memory filesystem) + // SaveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html + emscripten_run_script(TextFormat("SaveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path))); +#endif + + TraceLog(LOG_INFO, "Screenshot taken: %s", path); } // Check if the file exists @@ -1492,11 +1507,10 @@ bool FileExists(const char *fileName) bool result = false; #if defined(_WIN32) - if (_access(fileName, 0) != -1) + if (_access(fileName, 0) != -1) result = true; #else - if (access(fileName, F_OK) != -1) + if (access(fileName, F_OK) != -1) result = true; #endif - result = true; return result; } @@ -1509,7 +1523,7 @@ bool IsFileExtension(const char *fileName, const char *ext) if ((fileExt = strrchr(fileName, '.')) != NULL) { - #if defined(_WIN32) +#if defined(_WIN32) result = true; int extLen = strlen(ext); @@ -1525,9 +1539,9 @@ bool IsFileExtension(const char *fileName, const char *ext) } } else result = false; - #else +#else if (strcmp(fileExt, ext) == 0) result = true; - #endif +#endif } return result; @@ -1729,7 +1743,7 @@ void StorageSaveValue(int position, int value) { FILE *storageFile = NULL; - char path[128]; + char path[512] = { 0 }; #if defined(PLATFORM_ANDROID) strcpy(path, internalDataPath); strcat(path, "/"); @@ -1769,7 +1783,7 @@ int StorageLoadValue(int position) { int value = 0; - char path[128]; + char path[512] = { 0 }; #if defined(PLATFORM_ANDROID) strcpy(path, internalDataPath); strcat(path, "/"); @@ -1814,7 +1828,9 @@ void OpenURL(const char *url) if (strchr(url, '\'') != NULL) { TraceLog(LOG_WARNING, "Provided URL does not seem to be valid."); - } else { + } + else + { char *cmd = calloc(strlen(url) + 10, sizeof(char)); #if defined(_WIN32) @@ -3158,16 +3174,21 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // NOTE: Before closing window, while loop must be left! } -#if defined(PLATFORM_DESKTOP) else if (key == GLFW_KEY_F12 && action == GLFW_PRESS) { - #if defined(SUPPORT_GIF_RECORDING) +#if defined(SUPPORT_GIF_RECORDING) if (mods == GLFW_MOD_CONTROL) { if (gifRecording) { GifEnd(); gifRecording = false; + + #if defined(PLATFORM_WEB) + // Download file from MEMFS (emscripten memory filesystem) + // SaveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html + emscripten_run_script(TextFormat("SaveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1))); + #endif TraceLog(LOG_INFO, "End animated GIF recording"); } @@ -3175,25 +3196,32 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i { gifRecording = true; gifFramesCounter = 0; + + char path[512] = { 0 }; + #if defined(PLATFORM_ANDROID) + strcpy(path, internalDataPath); + strcat(path, TextFormat("/screenrec%03i.gif", screenshotCounter)); + #else + strcpy(path, TextFormat("/screenrec%03i.gif", screenshotCounter)); + #endif // NOTE: delay represents the time between frames in the gif, if we capture a gif frame every // 10 game frames and each frame trakes 16.6ms (60fps), delay between gif frames should be ~16.6*10. - GifBegin(TextFormat("screenrec%03i.gif", screenshotCounter), screenWidth, screenHeight, (int)(GetFrameTime()*10.0f), 8, false); + GifBegin(path, screenWidth, screenHeight, (int)(GetFrameTime()*10.0f), 8, false); screenshotCounter++; TraceLog(LOG_INFO, "Begin animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); } } else - #endif // SUPPORT_GIF_RECORDING - #if defined(SUPPORT_SCREEN_CAPTURE) +#endif // SUPPORT_GIF_RECORDING +#if defined(SUPPORT_SCREEN_CAPTURE) { TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); screenshotCounter++; } - #endif // SUPPORT_SCREEN_CAPTURE +#endif // SUPPORT_SCREEN_CAPTURE } -#endif // PLATFORM_DESKTOP else { currentKeyState[key] = action; @@ -3375,11 +3403,11 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // Init hi-res timer InitTimer(); - #if defined(SUPPORT_DEFAULT_FONT) + #if defined(SUPPORT_DEFAULT_FONT) // Load default font // NOTE: External function (defined in module: text) LoadDefaultFont(); - #endif + #endif // TODO: GPU assets reload in case of lost focus (lost context) // NOTE: This problem has been solved just unbinding and rebinding context from display From 5c614f69755623e346105d17c71697005bd2900c Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 5 Jan 2019 19:24:42 +0100 Subject: [PATCH 34/58] Some code tweaks --- examples/models/models_material_pbr.c | 2 +- src/raylib.h | 2 +- src/rlgl.h | 33 ++++++++++++++++++++------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/examples/models/models_material_pbr.c b/examples/models/models_material_pbr.c index a4a10d60..6885f753 100644 --- a/examples/models/models_material_pbr.c +++ b/examples/models/models_material_pbr.c @@ -156,7 +156,7 @@ static Material LoadMaterialPBR(Color albedo, float metalness, float roughness) Texture2D cubemap = GenTextureCubemap(shdrCubemap, texHDR, CUBEMAP_SIZE); mat.maps[MAP_IRRADIANCE].texture = GenTextureIrradiance(shdrIrradiance, cubemap, IRRADIANCE_SIZE); mat.maps[MAP_PREFILTER].texture = GenTexturePrefilter(shdrPrefilter, cubemap, PREFILTERED_SIZE); - mat.maps[MAP_BRDF].texture = GenTextureBRDF(shdrBRDF, cubemap, BRDF_SIZE); + mat.maps[MAP_BRDF].texture = GenTextureBRDF(shdrBRDF, BRDF_SIZE); UnloadTexture(cubemap); UnloadTexture(texHDR); diff --git a/src/raylib.h b/src/raylib.h index e422e6bc..6d4b08d0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1241,7 +1241,7 @@ RLAPI Matrix GetMatrixModelview(); // Get RLAPI Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size); // Generate cubemap texture from HDR texture RLAPI Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size); // Generate irradiance texture using cubemap data RLAPI Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size); // Generate prefilter texture using cubemap data -RLAPI Texture2D GenTextureBRDF(Shader shader, Texture2D cubemap, int size); // Generate BRDF texture using cubemap data +RLAPI Texture2D GenTextureBRDF(Shader shader, int size); // Generate BRDF texture using cubemap data // Shading begin/end functions RLAPI void BeginShaderMode(Shader shader); // Begin custom shader drawing diff --git a/src/rlgl.h b/src/rlgl.h index 7776879a..2b575614 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -480,7 +480,7 @@ Matrix GetMatrixModelview(); // Get inter Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size); // Generate cubemap texture from HDR texture Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size); // Generate irradiance texture using cubemap data Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size); // Generate prefilter texture using cubemap data -Texture2D GenTextureBRDF(Shader shader, Texture2D cubemap, int size); // Generate BRDF texture using cubemap data +Texture2D GenTextureBRDF(Shader shader, int size); // Generate BRDF texture using cubemap data // Shading begin/end functions void BeginShaderMode(Shader shader); // Begin custom shader drawing @@ -849,7 +849,7 @@ static PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArrays; #if defined(SUPPORT_VR_SIMULATOR) // VR global variables -static VrStereoConfig vrConfig; // VR stereo configuration for simulator +static VrStereoConfig vrConfig = { 0 }; // VR stereo configuration for simulator static bool vrSimulatorReady = false; // VR simulator ready flag static bool vrStereoRender = false; // VR stereo rendering enabled/disabled flag // NOTE: This flag is useful to render data over stereo image (i.e. FPS) @@ -2993,7 +2993,7 @@ Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo); // Set up cubemap to render and attach to framebuffer - // NOTE: faces are stored with 16 bit floating point values + // NOTE: Faces are stored as 32 bit floating point values glGenTextures(1, &cubemap.id); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap.id); for (unsigned int i = 0; i < 6; i++) @@ -3012,7 +3012,7 @@ Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size) glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - // Create projection (transposed) and different views for each face + // Create projection and different views for each face Matrix fboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, 0.01, 1000.0); Matrix fboViews[6] = { MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 1.0f, 0.0f, 0.0f }, (Vector3){ 0.0f, -1.0f, 0.0f }), @@ -3050,6 +3050,10 @@ Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size) cubemap.width = size; cubemap.height = size; + cubemap.mipmaps = 1; + cubemap.format = UNCOMPRESSED_R32G32B32; + + // TODO: Texture2D is a GL_TEXTURE_CUBE_MAP, not a GL_TEXTURE_2D! Only cubemap.id makes some sense... #endif return cubemap; } @@ -3216,15 +3220,20 @@ Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size) } // Generate BRDF texture using cubemap data -// TODO: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 -Texture2D GenTextureBRDF(Shader shader, Texture2D cubemap, int size) +// NOTE: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +Texture2D GenTextureBRDF(Shader shader, int size) { Texture2D brdf = { 0 }; -#if defined(GRAPHICS_API_OPENGL_33) // || defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Generate BRDF convolution texture glGenTextures(1, &brdf.id); glBindTexture(GL_TEXTURE_2D, brdf.id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, size, size, 0, GL_RG, GL_FLOAT, 0); +#if defined(GRAPHICS_API_OPENGL_33) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, size, size, 0, GL_RG, GL_FLOAT, NULL); +#elif defined(GRAPHICS_API_OPENGL_ES2) + if (texFloatSupported) glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, size, size, 0, GL_RG, GL_FLOAT, NULL); +#endif + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -3236,7 +3245,11 @@ Texture2D GenTextureBRDF(Shader shader, Texture2D cubemap, int size) glGenRenderbuffers(1, &rbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glBindRenderbuffer(GL_RENDERBUFFER, rbo); +#if defined(GRAPHICS_API_OPENGL_33) glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, size, size); +#elif defined(GRAPHICS_API_OPENGL_ES2) + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size, size); +#endif glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, brdf.id, 0); glViewport(0, 0, size, size); @@ -3246,6 +3259,10 @@ Texture2D GenTextureBRDF(Shader shader, Texture2D cubemap, int size) // Unbind framebuffer and textures glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Unload framebuffer but keep color texture + glDeleteRenderbuffers(1, &rbo); + glDeleteFramebuffers(1, &fbo); // Reset viewport dimensions to default glViewport(0, 0, screenWidth, screenHeight); From f4fe7f4d4c6d25e4bcc1ea4b48af07ef49b82720 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 6 Jan 2019 15:49:29 +0100 Subject: [PATCH 35/58] Review BRDF texture generation Actually, that function should be redesigned... --- examples/models/resources/shaders/brdf.fs | 73 ++++++++++------------- src/rlgl.h | 7 ++- 2 files changed, 38 insertions(+), 42 deletions(-) diff --git a/examples/models/resources/shaders/brdf.fs b/examples/models/resources/shaders/brdf.fs index 59ae384a..3e8777d2 100644 --- a/examples/models/resources/shaders/brdf.fs +++ b/examples/models/resources/shaders/brdf.fs @@ -1,12 +1,15 @@ /******************************************************************************************* * -* rPBR [shader] - Bidirectional reflectance distribution function fragment shader +* BRDF LUT Generation - Bidirectional reflectance distribution function fragment shader +* +* REF: https://github.com/HectorMF/BRDFGenerator * * Copyright (c) 2017 Victor Fisac * **********************************************************************************************/ #version 330 + #define MAX_SAMPLES 1024u // Input vertex attributes (from vertex shader) @@ -18,43 +21,30 @@ const float PI = 3.14159265359; // Output fragment color out vec4 finalColor; -float DistributionGGX(vec3 N, vec3 H, float roughness); -float RadicalInverse_VdC(uint bits); vec2 Hammersley(uint i, uint N); -vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness); +float RadicalInverseVdC(uint bits); float GeometrySchlickGGX(float NdotV, float roughness); float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness); +vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness); vec2 IntegrateBRDF(float NdotV, float roughness); -float DistributionGGX(vec3 N, vec3 H, float roughness) +float RadicalInverseVdC(uint bits) { - float a = roughness*roughness; - float a2 = a*a; - float NdotH = max(dot(N, H), 0.0); - float NdotH2 = NdotH*NdotH; - - float nom = a2; - float denom = (NdotH2*(a2 - 1.0) + 1.0); - denom = PI*denom*denom; - - return nom/denom; -} - -float RadicalInverse_VdC(uint bits) -{ - bits = (bits << 16u) | (bits >> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - return float(bits) * 2.3283064365386963e-10; // / 0x100000000 + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 } +// Compute Hammersley coordinates vec2 Hammersley(uint i, uint N) { - return vec2(float(i)/float(N), RadicalInverse_VdC(i)); + return vec2(float(i)/float(N), RadicalInverseVdC(i)); } +// Integrate number of importance samples for (roughness and NoV) vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) { float a = roughness*roughness; @@ -85,6 +75,7 @@ float GeometrySchlickGGX(float NdotV, float roughness) return nom/denom; } +// Compute the geometry term for the BRDF given roughness squared, NoV, NoL float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { float NdotV = max(dot(N, V), 0.0); @@ -97,29 +88,31 @@ float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) vec2 IntegrateBRDF(float NdotV, float roughness) { - vec3 V = vec3(sqrt(1.0 - NdotV*NdotV), 0.0, NdotV); float A = 0.0; - float B = 0.0; + float B = 0.0; + vec3 V = vec3(sqrt(1.0 - NdotV*NdotV), 0.0, NdotV); vec3 N = vec3(0.0, 0.0, 1.0); - for(uint i = 0u; i < MAX_SAMPLES; i++) + for (int i = 0; i < MAX_SAMPLES; i++) { // Generate a sample vector that's biased towards the preferred alignment direction (importance sampling) - vec2 Xi = Hammersley(i, MAX_SAMPLES); - vec3 H = ImportanceSampleGGX(Xi, N, roughness); - vec3 L = normalize(2.0*dot(V, H)*H - V); - float NdotL = max(L.z, 0.0); - float NdotH = max(H.z, 0.0); - float VdotH = max(dot(V, H), 0.0); + + vec2 Xi = Hammersley(i, MAX_SAMPLES); // Compute a Hammersely coordinate + vec3 H = ImportanceSampleGGX(Xi, N, roughness); // Integrate number of importance samples for (roughness and NoV) + vec3 L = normalize(2.0*dot(V, H)*H - V); // Compute reflection vector L + + float NdotL = max(L.z, 0.0); // Compute normal dot light + float NdotH = max(H.z, 0.0); // Compute normal dot half + float VdotH = max(dot(V, H), 0.0); // Compute view dot half if (NdotL > 0.0) { - float G = GeometrySmith(N, V, L, roughness); - float G_Vis = (G*VdotH)/(NdotH*NdotV); - float Fc = pow(1.0 - VdotH, 5.0); + float G = GeometrySmith(N, V, L, roughness); // Compute the geometry term for the BRDF given roughness squared, NoV, NoL + float GVis = (G*VdotH)/(NdotH*NdotV); // Compute the visibility term given G, VoH, NoH, NoV, NoL + float Fc = pow(1.0 - VdotH, 5.0); // Compute the fresnel term given VoH - A += (1.0 - Fc)*G_Vis; - B += Fc*G_Vis; + A += (1.0 - Fc)*GVis; // Sum the result given fresnel, geometry, visibility + B += Fc*GVis; } } diff --git a/src/rlgl.h b/src/rlgl.h index 2b575614..8c7526fb 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3221,6 +3221,7 @@ Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size) // Generate BRDF texture using cubemap data // NOTE: OpenGL ES 2.0 does not support GL_RGB16F texture format, neither GL_DEPTH_COMPONENT24 +// TODO: Review implementation: https://github.com/HectorMF/BRDFGenerator Texture2D GenTextureBRDF(Shader shader, int size) { Texture2D brdf = { 0 }; @@ -3229,9 +3230,9 @@ Texture2D GenTextureBRDF(Shader shader, int size) glGenTextures(1, &brdf.id); glBindTexture(GL_TEXTURE_2D, brdf.id); #if defined(GRAPHICS_API_OPENGL_33) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, size, size, 0, GL_RG, GL_FLOAT, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, size, size, 0, GL_RGB, GL_FLOAT, NULL); #elif defined(GRAPHICS_API_OPENGL_ES2) - if (texFloatSupported) glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, size, size, 0, GL_RG, GL_FLOAT, NULL); + if (texFloatSupported) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL); #endif glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -3269,6 +3270,8 @@ Texture2D GenTextureBRDF(Shader shader, int size) brdf.width = size; brdf.height = size; + brdf.mipmaps = 1; + brdf.format = UNCOMPRESSED_R32G32B32; #endif return brdf; } From 4fb3527aa85f95e60b9da01ffa78a3fa9197817e Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sun, 6 Jan 2019 15:55:39 +0100 Subject: [PATCH 36/58] README.md: Change Travis Badge Branch to master It was develop before, which is no longer used. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7dbaaa6b..2967ffb3 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames progra no fancy interface, no visual helpers, no auto-debugging... just coding in the most pure spartan-programmers way. Are you ready to learn? Jump to [code examples!](http://www.raylib.com/examples.html) -[![Build Status](https://travis-ci.org/raysan5/raylib.svg?branch=develop)](https://travis-ci.org/raysan5/raylib) +[![Build Status](https://travis-ci.org/raysan5/raylib.svg?branch=master)](https://travis-ci.org/raysan5/raylib) [![https://ci.appveyor.com/api/projects/status/github/raysan5/raylib?svg=true](https://ci.appveyor.com/api/projects/status/github/raysan5/raylib?svg=true)](https://ci.appveyor.com/project/raysan5/raylib) [![Chat on Discord](https://img.shields.io/discord/308323056592486420.svg?logo=discord)](https://discord.gg/VkzNHUE) [![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE.md) From 127c44dafef16b4ee588fa348e96931c764ecf30 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sun, 6 Jan 2019 19:53:17 +0100 Subject: [PATCH 37/58] Revert "Update .travis.yml" This reverts commit 214aa0c1b0d5c766d22bbb1069ecfb3a78373577. --- .travis.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3d176523..4b75e19f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,7 +56,6 @@ before_install: export DONT_TEST=1; fi - if [ "$TRAVIS_OS_NAME" == "linux" ]; then - MAKE=make; if [[ "$ARCH" == *-android ]]; then export DONT_TEST=1; export RAYLIB_PACKAGE_SUFFIX="-Android-arm64"; @@ -101,12 +100,10 @@ before_install: fi; fi - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - MAKE=make; export RAYLIB_PACKAGE_SUFFIX="-macOS"; if [ "$INSTALL_GLFW" == "YES" ]; then brew update; brew install glfw; fi; fi - if [ "$TRAVIS_OS_NAME" == "windows" ]; then - MAKE=mingw32-make; export DONT_TEST=1; export RAYLIB_PACKAGE_SUFFIX="-windows"; export CMAKE_ARCH_ARGS='-DPLATFORM=Desktop'; @@ -132,9 +129,9 @@ script: -DUSE_WAYLAND=$WAYLAND -DINCLUDE_EVERYTHING=ON .. - - $RUNNER $MAKE VERBOSE=1 + - $RUNNER make VERBOSE=1 - if [ "$RELEASE" != "NO" ]; then $RUNNER make package; fi - - sudo $RUNNER $MAKE install + - sudo $RUNNER make install - if [ ! "$DONT_TEST" ]; then pkg-config --static --libs raylib; nm -g release/libraylib.a | grep glfwGetProcAddress || (echo "libraylib.a doesn't contain GLFW symbols! Aborting..." && false); From d3a46cc9d447b161f52e63ab81a7f10841792a0e Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sun, 6 Jan 2019 19:50:35 +0100 Subject: [PATCH 38/58] Travis CI: s/make/cmake --build/ for msvc compatibility CMake defaults to generating a Visual Studio build on Windows, which doesn't involve make. Thus use cmake --build to abstract that detail away. We already do that in the appveyor.yml. The install line is a bit hacky: cmake might not be in the search path when using sudo, thus use "$(which cmake)" to get the path, sudo doesn't seem to be available on the Travis CI windows environment, so use $(which sudo) without quotes to expand to nothing if that's the case... --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4b75e19f..12f7ac27 100644 --- a/.travis.yml +++ b/.travis.yml @@ -128,10 +128,11 @@ script: -DUSE_EXTERNAL_GLFW=$USE_EXTERNAL_GLFW -DUSE_WAYLAND=$WAYLAND -DINCLUDE_EVERYTHING=ON + -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON .. - - $RUNNER make VERBOSE=1 - - if [ "$RELEASE" != "NO" ]; then $RUNNER make package; fi - - sudo $RUNNER make install + - $RUNNER cmake --build . --target + - if [ "$RELEASE" != "NO" ]; then $RUNNER cmake --build . --target package; fi + - $RUNNER $(which sudo) "$(which cmake)" --build . --target install - if [ ! "$DONT_TEST" ]; then pkg-config --static --libs raylib; nm -g release/libraylib.a | grep glfwGetProcAddress || (echo "libraylib.a doesn't contain GLFW symbols! Aborting..." && false); From 1036de389abb9557cef7f801166a4550e0e90fee Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 6 Jan 2019 21:07:09 +0100 Subject: [PATCH 39/58] Some minor tweaks analyzing code Specific textures generation code is quite redundant and not flexible for the user, I'm trying to figure out some easy way to allow raylib users to do the same without needing those functions (very specific and shader dependant). RenderTexture loading and Cubemap textures support must be improved. --- src/rlgl.h | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 8c7526fb..384f00f7 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3004,6 +3004,7 @@ Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size) if (texFloatSupported) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL); #endif } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); #if defined(GRAPHICS_API_OPENGL_33) @@ -3033,7 +3034,7 @@ Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size) glViewport(0, 0, size, size); glBindFramebuffer(GL_FRAMEBUFFER, fbo); - for (unsigned int i = 0; i < 6; i++) + for (int i = 0; i < 6; i++) { SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_VIEW], fboViews[i]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemap.id, 0); @@ -3048,12 +3049,11 @@ Texture2D GenTextureCubemap(Shader shader, Texture2D skyHDR, int size) glViewport(0, 0, screenWidth, screenHeight); //glEnable(GL_CULL_FACE); + // NOTE: Texture2D is a GL_TEXTURE_CUBE_MAP, not a GL_TEXTURE_2D! cubemap.width = size; cubemap.height = size; cubemap.mipmaps = 1; cubemap.format = UNCOMPRESSED_R32G32B32; - - // TODO: Texture2D is a GL_TEXTURE_CUBE_MAP, not a GL_TEXTURE_2D! Only cubemap.id makes some sense... #endif return cubemap; } @@ -3081,7 +3081,10 @@ Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size) glGenTextures(1, &irradiance.id); glBindTexture(GL_TEXTURE_CUBE_MAP, irradiance.id); for (unsigned int i = 0; i < 6; i++) + { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL); + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); @@ -3109,7 +3112,7 @@ Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size) glViewport(0, 0, size, size); glBindFramebuffer(GL_FRAMEBUFFER, fbo); - for (unsigned int i = 0; i < 6; i++) + for (int i = 0; i < 6; i++) { SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_VIEW], fboViews[i]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradiance.id, 0); @@ -3125,6 +3128,8 @@ Texture2D GenTextureIrradiance(Shader shader, Texture2D cubemap, int size) irradiance.width = size; irradiance.height = size; + irradiance.mipmaps = 1; + //irradiance.format = UNCOMPRESSED_R16G16B16; #endif return irradiance; } @@ -3154,7 +3159,10 @@ Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size) glGenTextures(1, &prefilter.id); glBindTexture(GL_TEXTURE_CUBE_MAP, prefilter.id); for (unsigned int i = 0; i < 6; i++) + { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, size, size, 0, GL_RGB, GL_FLOAT, NULL); + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); @@ -3185,11 +3193,11 @@ Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size) #define MAX_MIPMAP_LEVELS 5 // Max number of prefilter texture mipmaps - for (unsigned int mip = 0; mip < MAX_MIPMAP_LEVELS; mip++) + for (int mip = 0; mip < MAX_MIPMAP_LEVELS; mip++) { // Resize framebuffer according to mip-level size. - unsigned int mipWidth = size*(int) powf(0.5f, (float) mip); - unsigned int mipHeight = size* (int) powf(0.5f, (float) mip); + unsigned int mipWidth = size*(int)powf(0.5f, (float)mip); + unsigned int mipHeight = size*(int)powf(0.5f, (float)mip); glBindRenderbuffer(GL_RENDERBUFFER, rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight); @@ -3198,7 +3206,7 @@ Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size) float roughness = (float)mip/(float)(MAX_MIPMAP_LEVELS - 1); glUniform1f(roughnessLoc, roughness); - for (unsigned int i = 0; i < 6; ++i) + for (int i = 0; i < 6; i++) { SetShaderValueMatrix(shader, shader.locs[LOC_MATRIX_VIEW], fboViews[i]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilter.id, mip); @@ -3215,6 +3223,8 @@ Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size) prefilter.width = size; prefilter.height = size; + //prefilter.mipmaps = 1 + (int)floor(log(size)/log(2)); + //prefilter.format = UNCOMPRESSED_R16G16B16; #endif return prefilter; } From dc415b9a5edb59153ebcd4dee164853cb542a6eb Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sun, 6 Jan 2019 21:13:19 +0100 Subject: [PATCH 40/58] Travis CI: emscripten: fix failing CMake install stage --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 12f7ac27..4c9274db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -132,7 +132,7 @@ script: .. - $RUNNER cmake --build . --target - if [ "$RELEASE" != "NO" ]; then $RUNNER cmake --build . --target package; fi - - $RUNNER $(which sudo) "$(which cmake)" --build . --target install + - $(which sudo) $RUNNER "$(which cmake)" --build . --target install - if [ ! "$DONT_TEST" ]; then pkg-config --static --libs raylib; nm -g release/libraylib.a | grep glfwGetProcAddress || (echo "libraylib.a doesn't contain GLFW symbols! Aborting..." && false); From b0345abd76fea12a45aafa58c6097bc632de32d7 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 6 Jan 2019 21:22:23 +0100 Subject: [PATCH 41/58] Added raylib-chaiscript and node-raylib --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 58f9cd55..c2344b6b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,7 +74,9 @@ Some people ported raylib to other languages in form of bindings or wrappers to - [raylib-py](https://github.com/overdev/raylib-py) : raylib **Python** binding - [raylib-haskell](https://github.com/DevJac/raylib-haskell) : raylib **Haskell** binding - [raylib-java](https://github.com/XoanaIO/raylib-java) : raylib **Java** binding - - *[raylib flat-assembler Usage example](http://forum.raylib.com/index.php?p=/discussion/comment/425/#Comment_425)* + - [raylib-chaiscript](https://github.com/RobLoach/raylib-chaiscript) : raylib **ChaiScript** binding + - [node-raylib](https://github.com/RobLoach/node-raylib) : **Node.js** raylib binding + - *[raylib flat-assembler Usage example]()* - *[raylib COBOL Usage example](https://github.com/Martinfx/Cobol/tree/master/OpenCobol/Games/raylib)* Usually, raylib bindings follow the convention: `raylib-{language}` From 85a2cda36dc528feae38e718683825e5ae56f8d8 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 6 Jan 2019 21:27:05 +0100 Subject: [PATCH 42/58] Added raylib-lua-ffi --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c2344b6b..2cad76fc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,6 +61,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to - [raylib](https://github.com/raysan5/raylib) : raylib **C/C++** version (default) - [raylib-lua](https://github.com/raysan5/raylib-lua) : raylib **Lua** binding + - [raylib-lua-ffi](https://github.com/raysan5/raylib/issues/693) : raylib **Lua** ffi binding - [raylib-go](https://github.com/gen2brain/raylib-go) : raylib **Go** binding - [raylib-Nim](https://gitlab.com/define-private-public/raylib-Nim) : raylib **Nim** binding - [cray](https://gitlab.com/Zatherz/cray) - raylib **Crystal** binding From 285de106d45b47d50de678ad82bc3b36ebfd49a6 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sun, 6 Jan 2019 21:13:19 +0100 Subject: [PATCH 43/58] Travis CI: emscripten: properly fix failing CMake install stage The .travis.yml is getting increasingly ugly. We should maybe consider creating separate shell scripts for each target.. --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4c9274db..b7183ede 100644 --- a/.travis.yml +++ b/.travis.yml @@ -132,7 +132,11 @@ script: .. - $RUNNER cmake --build . --target - if [ "$RELEASE" != "NO" ]; then $RUNNER cmake --build . --target package; fi - - $(which sudo) $RUNNER "$(which cmake)" --build . --target install + - if [ -n "$RUNNER" ]; then + sudo $RUNNER cmake --build . --target install; + else + $(which sudo) $RUNNER "$(which cmake)" --build . --target install; + fi - if [ ! "$DONT_TEST" ]; then pkg-config --static --libs raylib; nm -g release/libraylib.a | grep glfwGetProcAddress || (echo "libraylib.a doesn't contain GLFW symbols! Aborting..." && false); From 182bdd2e0a75a2b0405d50e3b16d504d0ab6da96 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 7 Jan 2019 23:27:41 +0100 Subject: [PATCH 44/58] Travis CI: bump installed wayland-protocols to v1.15 Move away from v1.12 as GLFW now requires >= 1.15. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b7183ede..e61ae1e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -118,7 +118,7 @@ script: sudo dpkg -i extra-cmake-modules_5.38.0a-0ubuntu1_amd64.deb; git clone git://anongit.freedesktop.org/wayland/wayland-protocols; pushd wayland-protocols; - git checkout 1.12 && ./autogen.sh --prefix=/usr && make && sudo make install; + git checkout 1.15 && ./autogen.sh --prefix=/usr && make && sudo make install; popd; fi - $RUNNER cmake $CMAKE_ARCH_ARGS From f719de4bf8cd460a06f6fb93c9e4610dc9664de4 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sat, 27 Oct 2018 11:01:04 +0200 Subject: [PATCH 45/58] external: glfw: Ignore {docs/examples/tests}/ directories --- src/external/.gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/external/.gitignore diff --git a/src/external/.gitignore b/src/external/.gitignore new file mode 100644 index 00000000..b6d20344 --- /dev/null +++ b/src/external/.gitignore @@ -0,0 +1,3 @@ +glfw/docs/ +glfw/examples/ +glfw/tests/ From c3d2b1b18451d66e247450fe06d2d95e6a9a8674 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sat, 27 Oct 2018 11:02:30 +0200 Subject: [PATCH 46/58] glfw: Update GLFW to current master glfw/glfw@5595fa3ae6 implements a proper fix for the macOS Mojave problem of OpenGL windows not being rendered until moved or manually updated. Pull in the current master and rebase the three patches we have on top: - two commits we have for reuse of the GLFW CMake build system for Wayland It hasn't yet to be acknowledged upstream. - one commit removing #include in glfw3native.h to avoid duplicate declarations. Fixes #655 and #665. --- src/external/glfw/.travis.yml | 2 +- src/external/glfw/CMake/modules/FindMir.cmake | 34 - src/external/glfw/CMakeLists.txt | 35 +- src/external/glfw/README.md | 25 +- src/external/glfw/deps/nuklear.h | 17420 +++++++++------- src/external/glfw/deps/vulkan/vulkan.h | 6 - src/external/glfw/include/GLFW/glfw3.h | 70 +- src/external/glfw/include/GLFW/glfw3native.h | 59 +- src/external/glfw/src/CMakeLists.txt | 23 +- src/external/glfw/src/cocoa_init.m | 49 +- src/external/glfw/src/cocoa_monitor.m | 3 +- src/external/glfw/src/cocoa_platform.h | 8 +- src/external/glfw/src/cocoa_window.m | 188 +- src/external/glfw/src/egl_context.h | 4 - src/external/glfw/src/glfw3.pc.in | 6 +- src/external/glfw/src/glfw_config.h.in | 4 +- src/external/glfw/src/init.c | 24 + src/external/glfw/src/input.c | 2 +- src/external/glfw/src/internal.h | 10 +- src/external/glfw/src/linux_joystick.c | 2 +- src/external/glfw/src/mir_init.c | 240 - src/external/glfw/src/mir_monitor.c | 218 - src/external/glfw/src/mir_platform.h | 133 - src/external/glfw/src/mir_window.c | 975 - src/external/glfw/src/monitor.c | 33 +- src/external/glfw/src/nsgl_context.m | 9 +- src/external/glfw/src/null_monitor.c | 3 +- src/external/glfw/src/osmesa_context.c | 2 +- src/external/glfw/src/vulkan.c | 3 - src/external/glfw/src/wgl_context.c | 60 +- src/external/glfw/src/win32_init.c | 68 +- src/external/glfw/src/win32_joystick.c | 2 + src/external/glfw/src/win32_monitor.c | 8 +- src/external/glfw/src/win32_platform.h | 69 +- src/external/glfw/src/win32_window.c | 299 +- src/external/glfw/src/window.c | 3 + src/external/glfw/src/wl_init.c | 183 +- src/external/glfw/src/wl_monitor.c | 60 +- src/external/glfw/src/wl_platform.h | 20 +- src/external/glfw/src/wl_window.c | 541 +- src/external/glfw/src/x11_init.c | 5 +- src/external/glfw/src/x11_monitor.c | 15 +- src/external/glfw/src/x11_window.c | 95 +- 43 files changed, 10989 insertions(+), 10029 deletions(-) delete mode 100644 src/external/glfw/CMake/modules/FindMir.cmake delete mode 100644 src/external/glfw/src/mir_init.c delete mode 100644 src/external/glfw/src/mir_monitor.c delete mode 100644 src/external/glfw/src/mir_platform.h delete mode 100644 src/external/glfw/src/mir_window.c diff --git a/src/external/glfw/.travis.yml b/src/external/glfw/.travis.yml index f7ce408c..804b8647 100644 --- a/src/external/glfw/.travis.yml +++ b/src/external/glfw/.travis.yml @@ -65,7 +65,7 @@ script: sudo dpkg -i extra-cmake-modules_5.38.0a-0ubuntu1_amd64.deb; git clone git://anongit.freedesktop.org/wayland/wayland-protocols; pushd wayland-protocols; - git checkout 1.12 && ./autogen.sh --prefix=/usr && make && sudo make install; + git checkout 1.15 && ./autogen.sh --prefix=/usr && make && sudo make install; popd; fi - cmake -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} -DGLFW_USE_WAYLAND=${USE_WAYLAND} .. diff --git a/src/external/glfw/CMake/modules/FindMir.cmake b/src/external/glfw/CMake/modules/FindMir.cmake deleted file mode 100644 index 2104df8a..00000000 --- a/src/external/glfw/CMake/modules/FindMir.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# FindMir -# ------- -# Finds the Mir library -# -# This will will define the following variables:: -# -# MIR_FOUND - the system has Mir -# MIR_INCLUDE_DIRS - the Mir include directory -# MIR_LIBRARIES - the Mir libraries -# MIR_DEFINITIONS - the Mir definitions - - -find_package (PkgConfig) -if(PKG_CONFIG_FOUND) - pkg_check_modules (PC_MIR mirclient>=0.26.2 QUIET) - - find_path(MIR_INCLUDE_DIR NAMES mir_toolkit/mir_client_library.h - PATHS ${PC_MIR_INCLUDE_DIRS}) - - find_library(MIR_LIBRARY NAMES mirclient - PATHS ${PC_MIR_LIBRARIES} ${PC_MIR_LIBRARY_DIRS}) - - include (FindPackageHandleStandardArgs) - find_package_handle_standard_args (MIR - REQUIRED_VARS MIR_LIBRARY MIR_INCLUDE_DIR) - - if (MIR_FOUND) - set(MIR_LIBRARIES ${MIR_LIBRARY}) - set(MIR_INCLUDE_DIRS ${PC_MIR_INCLUDE_DIRS}) - set(MIR_DEFINITIONS -DHAVE_MIR=1) - endif() - - mark_as_advanced (MIR_LIBRARY MIR_INCLUDE_DIR) -endif() diff --git a/src/external/glfw/CMakeLists.txt b/src/external/glfw/CMakeLists.txt index 83fe954e..8393f448 100644 --- a/src/external/glfw/CMakeLists.txt +++ b/src/external/glfw/CMakeLists.txt @@ -4,7 +4,7 @@ project(GLFW C) set(CMAKE_LEGACY_CYGWIN_WIN32 OFF) -if (NOT CMAKE_VERSION VERSION_LESS "3.1") +if (POLICY CMP0054) cmake_policy(SET CMP0054 NEW) endif() @@ -14,7 +14,6 @@ set(GLFW_VERSION_PATCH "0") set(GLFW_VERSION_EXTRA "") set(GLFW_VERSION "${GLFW_VERSION_MAJOR}.${GLFW_VERSION_MINOR}") set(GLFW_VERSION_FULL "${GLFW_VERSION}.${GLFW_VERSION_PATCH}${GLFW_VERSION_EXTRA}") -set(LIB_SUFFIX "" CACHE STRING "Takes an empty string or 64. Directory where lib will be installed: lib or lib64") set_property(GLOBAL PROPERTY USE_FOLDERS ON) @@ -25,6 +24,8 @@ option(GLFW_BUILD_DOCS "Build the GLFW documentation" ON) option(GLFW_INSTALL "Generate installation target" ON) option(GLFW_VULKAN_STATIC "Use the Vulkan loader statically linked into application" OFF) +include(GNUInstallDirs) + if (UNIX) option(GLFW_USE_OSMESA "Use OSMesa for offscreen context creation" OFF) endif() @@ -35,7 +36,6 @@ endif() if (UNIX AND NOT APPLE) option(GLFW_USE_WAYLAND "Use Wayland for window creation" OFF) - option(GLFW_USE_MIR "Use Mir for window creation" OFF) endif() if (MSVC) @@ -141,9 +141,6 @@ endif() if (GLFW_USE_WAYLAND) set(_GLFW_WAYLAND 1) message(STATUS "Using Wayland for window creation") -elseif (GLFW_USE_MIR) - set(_GLFW_MIR 1) - message(STATUS "Using Mir for window creation") elseif (GLFW_USE_OSMESA) set(_GLFW_OSMESA 1) message(STATUS "Using OSMesa for headless context creation") @@ -262,7 +259,7 @@ if (_GLFW_WAYLAND) find_package(Wayland REQUIRED Client Cursor Egl) find_package(WaylandScanner REQUIRED) - find_package(WaylandProtocols 1.12 REQUIRED) + find_package(WaylandProtocols 1.15 REQUIRED) list(APPEND glfw_PKG_DEPS "wayland-egl") @@ -273,7 +270,9 @@ if (_GLFW_WAYLAND) list(APPEND glfw_INCLUDE_DIRS "${XKBCOMMON_INCLUDE_DIRS}") include(CheckIncludeFiles) + include(CheckFunctionExists) check_include_files(xkbcommon/xkbcommon-compose.h HAVE_XKBCOMMON_COMPOSE_H) + check_function_exists(memfd_create HAVE_MEMFD_CREATE) if (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")) find_package(EpollShim) @@ -284,22 +283,6 @@ if (_GLFW_WAYLAND) endif() endif() -#-------------------------------------------------------------------- -# Use Mir for window creation -#-------------------------------------------------------------------- -if (_GLFW_MIR) - find_package(Mir REQUIRED) - list(APPEND glfw_PKG_DEPS "mirclient") - - list(APPEND glfw_INCLUDE_DIRS "${MIR_INCLUDE_DIRS}") - list(APPEND glfw_LIBRARIES "${MIR_LIBRARIES}" "${CMAKE_THREAD_LIBS_INIT}") - - find_package(XKBCommon REQUIRED) - list(APPEND glfw_PKG_DEPS "xkbcommon") - list(APPEND glfw_INCLUDE_DIRS "${XKBCOMMON_INCLUDE_DIRS}") - list(APPEND glfw_LIBRARIES "${XKBCOMMON_LIBRARY}") -endif() - #-------------------------------------------------------------------- # Use OSMesa for offscreen context creation #-------------------------------------------------------------------- @@ -340,7 +323,7 @@ endforeach() #-------------------------------------------------------------------- include(CMakePackageConfigHelpers) -set(GLFW_CONFIG_PATH "lib${LIB_SUFFIX}/cmake/glfw3") +set(GLFW_CONFIG_PATH "${CMAKE_INSTALL_LIBDIR}/cmake/glfw3") configure_package_config_file(src/glfw3Config.cmake.in src/glfw3Config.cmake @@ -377,7 +360,7 @@ endif() # The library is installed by src/CMakeLists.txt #-------------------------------------------------------------------- if (GLFW_INSTALL) - install(DIRECTORY include/GLFW DESTINATION include + install(DIRECTORY include/GLFW DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN glfw3.h PATTERN glfw3native.h) install(FILES "${GLFW_BINARY_DIR}/src/glfw3Config.cmake" @@ -388,7 +371,7 @@ if (GLFW_INSTALL) EXPORT_LINK_INTERFACE_LIBRARIES DESTINATION "${GLFW_CONFIG_PATH}") install(FILES "${GLFW_BINARY_DIR}/src/glfw3.pc" - DESTINATION "lib${LIB_SUFFIX}/pkgconfig") + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") # Only generate this target if no higher-level project already has if (NOT TARGET uninstall) diff --git a/src/external/glfw/README.md b/src/external/glfw/README.md index 82d4806a..86024bd3 100644 --- a/src/external/glfw/README.md +++ b/src/external/glfw/README.md @@ -11,8 +11,8 @@ application development. It provides a simple, platform-independent API for creating windows, contexts and surfaces, reading input, handling events, etc. GLFW natively supports Windows, macOS and Linux and other Unix-like systems. -Experimental implementations for the Wayland protocol and the Mir display server -are available but not yet officially supported. +An experimental implementation for the Wayland protocol is available but not +yet officially supported. GLFW is licensed under the [zlib/libpng license](http://www.glfw.org/license.html). @@ -167,6 +167,8 @@ information on what to include when reporting a bug. (#749,#842) - Added `GLFW_FOCUS_ON_SHOW` window hint and attribute to control input focus on calling show window (#1189) +- Added `GLFW_SCALE_TO_MONITOR` window hint for automatic window resizing + (#676,#1115) - Added `GLFW_JOYSTICK_HAT_BUTTONS` init hint (#889) - Added `GLFW_LOCK_KEY_MODS` input mode and `GLFW_MOD_*_LOCK` mod bits (#946) - Added macOS specific `GLFW_COCOA_RETINA_FRAMEBUFFER` window hint @@ -180,6 +182,10 @@ information on what to include when reporting a bug. - Added `GLFW_OSMESA_CONTEXT_API` for creating OpenGL contexts with [OSMesa](https://www.mesa3d.org/osmesa.html) (#281) - Added `GenerateMappings.cmake` script for updating gamepad mappings +- Export CMake `GLFW_PKG_DEPS` and `GLFW_PKG_LIBS` to parent scope for use + in client pkg-configs (#1307) +- Added a `glfw_objlib` CMake OBJECT library target for embedding into static + libraries (#1307) - Made `glfwCreateWindowSurface` emit an error when the window has a context (#1194,#1205) - Deprecated window parameter of clipboard string functions @@ -195,6 +201,8 @@ information on what to include when reporting a bug. - Bugfix: Invalid library paths were used in test and example CMake files (#930) - Bugfix: The scancode for synthetic key release events was always zero - Bugfix: The generated Doxyfile did not handle paths with spaces (#1081) +- Bugfix: The gamma ramp generated by `glfwSetGamma` did not use the monitor + ramp size (#1387,#1388) - [Win32] Added system error strings to relevant GLFW error descriptions (#733) - [Win32] Moved to `WM_INPUT` for disabled cursor mode motion input (#125) - [Win32] Removed XInput circular deadzone from joystick axis data (#1045) @@ -221,6 +229,10 @@ information on what to include when reporting a bug. - [Win32] Bugfix: The HID device notification was not unregistered (#1170) - [Win32] Bugfix: `glfwCreateWindow` activated window even with `GLFW_FOCUSED` hint set to false (#1179,#1180) +- [Win32] Bugfix: The keypad equals key was reported as `GLFW_KEY_UNKNOWN` + (#1315,#1316) +- [Win32] Bugfix: A title bar would be drawn over undecorated windows in some + circumstances (#1383) - [X11] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125) - [X11] Replaced `_GLFW_HAS_XF86VM` compile-time option with dynamic loading - [X11] Bugfix: `glfwGetVideoMode` would segfault on Cygwin/X @@ -235,6 +247,8 @@ information on what to include when reporting a bug. - [X11] Bugfix: Selection I/O reported but did not support `COMPOUND_TEXT` - [X11] Bugfix: Latin-1 text read from selections was not converted to UTF-8 - [X11] Bugfix: NVidia EGL would segfault if unloaded before closing the display +- [X11] Bugfix: Checking window maximized attrib could crash some WMs (#1356) +- [X11] Bugfix: Update cursor position on enter event (#1366) - [Linux] Added workaround for missing `SYN_DROPPED` in pre-2.6.39 kernel headers (#1196) - [Linux] Moved to evdev for joystick input (#906,#1005) @@ -267,6 +281,9 @@ information on what to include when reporting a bug. - [Cocoa] Bugfix: Window was resized twice when entering full screen (#1085) - [Cocoa] Bugfix: Duplicate size events were not filtered (#1085) - [Cocoa] Bugfix: Event polling did not initialize AppKit if necessary (#1218) +- [Cocoa] Bugfix: OpenGL rendering was not initially visible on 10.14 + (#1334,#1346) +- [Cocoa] Bugfix: Caps Lock did not generate any key events (#1368,#1373) - [WGL] Added support for `WGL_EXT_colorspace` for OpenGL ES contexts - [WGL] Added support for `WGL_ARB_create_context_no_error` - [GLX] Added support for `GLX_ARB_create_context_no_error` @@ -304,7 +321,9 @@ skills. - David Avedissian - Keith Bauer - John Bartholomew + - Coşku Baş - Niklas Behrens + - Andrew Belt - Niklas Bergström - Denis Bernard - Doug Binks @@ -371,6 +390,7 @@ skills. - Glenn Lewis - Shane Liesegang - Eyal Lotem + - Aaron Loucks - Tristam MacDonald - Hans Mackowiak - Дмитри Малышев @@ -387,6 +407,7 @@ skills. - Bruce Mitchener - Jack Moffitt - Jeff Molofee + - Alexander Monakov - Pierre Morel - Jon Morton - Pierre Moulon diff --git a/src/external/glfw/deps/nuklear.h b/src/external/glfw/deps/nuklear.h index 9b304a22..6c873535 100644 --- a/src/external/glfw/deps/nuklear.h +++ b/src/external/glfw/deps/nuklear.h @@ -1,242 +1,219 @@ /* - Nuklear - 2.00.4 - public domain - no warranty implied; use at your own risk. - authored from 2015-2017 by Micha Mettke - -ABOUT: - This is a minimal state graphical user interface single header toolkit - written in ANSI C and licensed under public domain. - It was designed as a simple embeddable user interface for application and does - not have any dependencies, a default renderbackend or OS window and input handling - but instead provides a very modular library approach by using simple input state - for input and draw commands describing primitive shapes as output. - So instead of providing a layered library that tries to abstract over a number - of platform and render backends it only focuses on the actual UI. - -VALUES: - - Graphical user interface toolkit - - Single header library - - Written in C89 (a.k.a. ANSI C or ISO C90) - - Small codebase (~18kLOC) - - Focus on portability, efficiency and simplicity - - No dependencies (not even the standard library if not wanted) - - Fully skinnable and customizable - - Low memory footprint with total memory control if needed or wanted - - UTF-8 support - - No global or hidden state - - Customizable library modules (you can compile and use only what you need) - - Optional font baker and vertex buffer output - -USAGE: - This library is self contained in one single header file and can be used either - in header only mode or in implementation mode. The header only mode is used - by default when included and allows including this header in other headers - and does not contain the actual implementation. - - The implementation mode requires to define the preprocessor macro - NK_IMPLEMENTATION in *one* .c/.cpp file before #includeing this file, e.g.: - - #define NK_IMPLEMENTATION - #include "nuklear.h" - - Also optionally define the symbols listed in the section "OPTIONAL DEFINES" - below in header and implementation mode if you want to use additional functionality - or need more control over the library. - IMPORTANT: Every time you include "nuklear.h" you have to define the same flags. - This is very important not doing it either leads to compiler errors - or even worse stack corruptions. - -FEATURES: - - Absolutely no platform dependent code - - Memory management control ranging from/to - - Ease of use by allocating everything from standard library - - Control every byte of memory inside the library - - Font handling control ranging from/to - - Use your own font implementation for everything - - Use this libraries internal font baking and handling API - - Drawing output control ranging from/to - - Simple shapes for more high level APIs which already have drawing capabilities - - Hardware accessible anti-aliased vertex buffer output - - Customizable colors and properties ranging from/to - - Simple changes to color by filling a simple color table - - Complete control with ability to use skinning to decorate widgets - - Bendable UI library with widget ranging from/to - - Basic widgets like buttons, checkboxes, slider, ... - - Advanced widget like abstract comboboxes, contextual menus,... - - Compile time configuration to only compile what you need - - Subset which can be used if you do not want to link or use the standard library - - Can be easily modified to only update on user input instead of frame updates - -OPTIONAL DEFINES: - NK_PRIVATE - If defined declares all functions as static, so they can only be accessed - inside the file that contains the implementation - - NK_INCLUDE_FIXED_TYPES - If defined it will include header for fixed sized types - otherwise nuklear tries to select the correct type. If that fails it will - throw a compiler error and you have to select the correct types yourself. - If used needs to be defined for implementation and header - - NK_INCLUDE_DEFAULT_ALLOCATOR - if defined it will include header and provide additional functions - to use this library without caring for memory allocation control and therefore - ease memory management. - Adds the standard library with malloc and free so don't define if you - don't want to link to the standard library - If used needs to be defined for implementation and header - - NK_INCLUDE_STANDARD_IO - if defined it will include header and provide - additional functions depending on file loading. - Adds the standard library with fopen, fclose,... so don't define this - if you don't want to link to the standard library - If used needs to be defined for implementation and header - - NK_INCLUDE_STANDARD_VARARGS - if defined it will include header and provide - additional functions depending on variable arguments - Adds the standard library with va_list and so don't define this if - you don't want to link to the standard library - If used needs to be defined for implementation and header - - NK_INCLUDE_VERTEX_BUFFER_OUTPUT - Defining this adds a vertex draw command list backend to this - library, which allows you to convert queue commands into vertex draw commands. - This is mainly if you need a hardware accessible format for OpenGL, DirectX, - Vulkan, Metal,... - If used needs to be defined for implementation and header - - NK_INCLUDE_FONT_BAKING - Defining this adds `stb_truetype` and `stb_rect_pack` implementation - to this library and provides font baking and rendering. - If you already have font handling or do not want to use this font handler - you don't have to define it. - If used needs to be defined for implementation and header - - NK_INCLUDE_DEFAULT_FONT - Defining this adds the default font: ProggyClean.ttf into this library - which can be loaded into a font atlas and allows using this library without - having a truetype font - Enabling this adds ~12kb to global stack memory - If used needs to be defined for implementation and header - - NK_INCLUDE_COMMAND_USERDATA - Defining this adds a userdata pointer into each command. Can be useful for - example if you want to provide custom shaders depending on the used widget. - Can be combined with the style structures. - If used needs to be defined for implementation and header - - NK_BUTTON_TRIGGER_ON_RELEASE - Different platforms require button clicks occurring either on buttons being - pressed (up to down) or released (down to up). - By default this library will react on buttons being pressed, but if you - define this it will only trigger if a button is released. - If used it is only required to be defined for the implementation part - - NK_ZERO_COMMAND_MEMORY - Defining this will zero out memory for each drawing command added to a - drawing queue (inside nk_command_buffer_push). Zeroing command memory - is very useful for fast checking (using memcmp) if command buffers are - equal and avoid drawing frames when nothing on screen has changed since - previous frame. - - NK_ASSERT - If you don't define this, nuklear will use with assert(). - Adds the standard library so define to nothing of not wanted - If used needs to be defined for implementation and header - - NK_BUFFER_DEFAULT_INITIAL_SIZE - Initial buffer size allocated by all buffers while using the default allocator - functions included by defining NK_INCLUDE_DEFAULT_ALLOCATOR. If you don't - want to allocate the default 4k memory then redefine it. - If used needs to be defined for implementation and header - - NK_MAX_NUMBER_BUFFER - Maximum buffer size for the conversion buffer between float and string - Under normal circumstances this should be more than sufficient. - If used needs to be defined for implementation and header - - NK_INPUT_MAX - Defines the max number of bytes which can be added as text input in one frame. - Under normal circumstances this should be more than sufficient. - If used it is only required to be defined for the implementation part - - NK_MEMSET - You can define this to 'memset' or your own memset implementation - replacement. If not nuklear will use its own version. - If used it is only required to be defined for the implementation part - - NK_MEMCPY - You can define this to 'memcpy' or your own memcpy implementation - replacement. If not nuklear will use its own version. - If used it is only required to be defined for the implementation part - - NK_SQRT - You can define this to 'sqrt' or your own sqrt implementation - replacement. If not nuklear will use its own slow and not highly - accurate version. - If used it is only required to be defined for the implementation part - - NK_SIN - You can define this to 'sinf' or your own sine implementation - replacement. If not nuklear will use its own approximation implementation. - If used it is only required to be defined for the implementation part - - NK_COS - You can define this to 'cosf' or your own cosine implementation - replacement. If not nuklear will use its own approximation implementation. - If used it is only required to be defined for the implementation part - - NK_STRTOD - You can define this to `strtod` or your own string to double conversion - implementation replacement. If not defined nuklear will use its own - imprecise and possibly unsafe version (does not handle nan or infinity!). - If used it is only required to be defined for the implementation part - - NK_DTOA - You can define this to `dtoa` or your own double to string conversion - implementation replacement. If not defined nuklear will use its own - imprecise and possibly unsafe version (does not handle nan or infinity!). - If used it is only required to be defined for the implementation part - - NK_VSNPRINTF - If you define `NK_INCLUDE_STANDARD_VARARGS` as well as `NK_INCLUDE_STANDARD_IO` - and want to be safe define this to `vsnprintf` on compilers supporting - later versions of C or C++. By default nuklear will check for your stdlib version - in C as well as compiler version in C++. if `vsnprintf` is available - it will define it to `vsnprintf` directly. If not defined and if you have - older versions of C or C++ it will be defined to `vsprintf` which is unsafe. - If used it is only required to be defined for the implementation part - - NK_BYTE - NK_INT16 - NK_UINT16 - NK_INT32 - NK_UINT32 - NK_SIZE_TYPE - NK_POINTER_TYPE - If you compile without NK_USE_FIXED_TYPE then a number of standard types - will be selected and compile time validated. If they are incorrect you can - define the correct types by overloading these type defines. - -CREDITS: - Developed by Micha Mettke and every direct or indirect contributor. - - Embeds stb_texedit, stb_truetype and stb_rectpack by Sean Barret (public domain) - Embeds ProggyClean.ttf font by Tristan Grimmer (MIT license). - - Big thank you to Omar Cornut (ocornut@github) for his imgui library and - giving me the inspiration for this library, Casey Muratori for handmade hero - and his original immediate mode graphical user interface idea and Sean - Barret for his amazing single header libraries which restored my faith - in libraries and brought me to create some of my own. - -LICENSE: - This software is dual-licensed to the public domain and under the following - license: you are granted a perpetual, irrevocable license to copy, modify, - publish and distribute this file as you see fit. +/// # Nuklear +/// ![](https://cloud.githubusercontent.com/assets/8057201/11761525/ae06f0ca-a0c6-11e5-819d-5610b25f6ef4.gif) +/// +/// ## Contents +/// 1. About section +/// 2. Highlights section +/// 3. Features section +/// 4. Usage section +/// 1. Flags section +/// 2. Constants section +/// 3. Dependencies section +/// 5. Example section +/// 6. API section +/// 1. Context section +/// 2. Input section +/// 3. Drawing section +/// 4. Window section +/// 5. Layouting section +/// 6. Groups section +/// 7. Tree section +/// 8. Properties section +/// 7. License section +/// 8. Changelog section +/// 9. Gallery section +/// 10. Credits section +/// +/// ## About +/// This is a minimal state immediate mode graphical user interface toolkit +/// written in ANSI C and licensed under public domain. It was designed as a simple +/// embeddable user interface for application and does not have any dependencies, +/// a default renderbackend or OS window and input handling but instead provides a very modular +/// library approach by using simple input state for input and draw +/// commands describing primitive shapes as output. So instead of providing a +/// layered library that tries to abstract over a number of platform and +/// render backends it only focuses on the actual UI. +/// +/// ## Highlights +/// - Graphical user interface toolkit +/// - Single header library +/// - Written in C89 (a.k.a. ANSI C or ISO C90) +/// - Small codebase (~18kLOC) +/// - Focus on portability, efficiency and simplicity +/// - No dependencies (not even the standard library if not wanted) +/// - Fully skinnable and customizable +/// - Low memory footprint with total memory control if needed or wanted +/// - UTF-8 support +/// - No global or hidden state +/// - Customizable library modules (you can compile and use only what you need) +/// - Optional font baker and vertex buffer output +/// +/// ## Features +/// - Absolutely no platform dependent code +/// - Memory management control ranging from/to +/// - Ease of use by allocating everything from standard library +/// - Control every byte of memory inside the library +/// - Font handling control ranging from/to +/// - Use your own font implementation for everything +/// - Use this libraries internal font baking and handling API +/// - Drawing output control ranging from/to +/// - Simple shapes for more high level APIs which already have drawing capabilities +/// - Hardware accessible anti-aliased vertex buffer output +/// - Customizable colors and properties ranging from/to +/// - Simple changes to color by filling a simple color table +/// - Complete control with ability to use skinning to decorate widgets +/// - Bendable UI library with widget ranging from/to +/// - Basic widgets like buttons, checkboxes, slider, ... +/// - Advanced widget like abstract comboboxes, contextual menus,... +/// - Compile time configuration to only compile what you need +/// - Subset which can be used if you do not want to link or use the standard library +/// - Can be easily modified to only update on user input instead of frame updates +/// +/// ## Usage +/// This library is self contained in one single header file and can be used either +/// in header only mode or in implementation mode. The header only mode is used +/// by default when included and allows including this header in other headers +/// and does not contain the actual implementation.

+/// +/// The implementation mode requires to define the preprocessor macro +/// NK_IMPLEMENTATION in *one* .c/.cpp file before #includeing this file, e.g.: +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~C +/// #define NK_IMPLEMENTATION +/// #include "nuklear.h" +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Also optionally define the symbols listed in the section "OPTIONAL DEFINES" +/// below in header and implementation mode if you want to use additional functionality +/// or need more control over the library. +/// +/// !!! WARNING +/// Every time nuklear is included define the same compiler flags. This very important not doing so could lead to compiler errors or even worse stack corruptions. +/// +/// ### Flags +/// Flag | Description +/// --------------------------------|------------------------------------------ +/// NK_PRIVATE | If defined declares all functions as static, so they can only be accessed inside the file that contains the implementation +/// NK_INCLUDE_FIXED_TYPES | If defined it will include header `` for fixed sized types otherwise nuklear tries to select the correct type. If that fails it will throw a compiler error and you have to select the correct types yourself. +/// NK_INCLUDE_DEFAULT_ALLOCATOR | If defined it will include header `` and provide additional functions to use this library without caring for memory allocation control and therefore ease memory management. +/// NK_INCLUDE_STANDARD_IO | If defined it will include header `` and provide additional functions depending on file loading. +/// NK_INCLUDE_STANDARD_VARARGS | If defined it will include header and provide additional functions depending on file loading. +/// NK_INCLUDE_VERTEX_BUFFER_OUTPUT | Defining this adds a vertex draw command list backend to this library, which allows you to convert queue commands into vertex draw commands. This is mainly if you need a hardware accessible format for OpenGL, DirectX, Vulkan, Metal,... +/// NK_INCLUDE_FONT_BAKING | Defining this adds `stb_truetype` and `stb_rect_pack` implementation to this library and provides font baking and rendering. If you already have font handling or do not want to use this font handler you don't have to define it. +/// NK_INCLUDE_DEFAULT_FONT | Defining this adds the default font: ProggyClean.ttf into this library which can be loaded into a font atlas and allows using this library without having a truetype font +/// NK_INCLUDE_COMMAND_USERDATA | Defining this adds a userdata pointer into each command. Can be useful for example if you want to provide custom shaders depending on the used widget. Can be combined with the style structures. +/// NK_BUTTON_TRIGGER_ON_RELEASE | Different platforms require button clicks occurring either on buttons being pressed (up to down) or released (down to up). By default this library will react on buttons being pressed, but if you define this it will only trigger if a button is released. +/// NK_ZERO_COMMAND_MEMORY | Defining this will zero out memory for each drawing command added to a drawing queue (inside nk_command_buffer_push). Zeroing command memory is very useful for fast checking (using memcmp) if command buffers are equal and avoid drawing frames when nothing on screen has changed since previous frame. +/// +/// !!! WARNING +/// The following flags will pull in the standard C library: +/// - NK_INCLUDE_DEFAULT_ALLOCATOR +/// - NK_INCLUDE_STANDARD_IO +/// - NK_INCLUDE_STANDARD_VARARGS +/// +/// !!! WARNING +/// The following flags if defined need to be defined for both header and implementation: +/// - NK_INCLUDE_FIXED_TYPES +/// - NK_INCLUDE_DEFAULT_ALLOCATOR +/// - NK_INCLUDE_STANDARD_VARARGS +/// - NK_INCLUDE_VERTEX_BUFFER_OUTPUT +/// - NK_INCLUDE_FONT_BAKING +/// - NK_INCLUDE_DEFAULT_FONT +/// - NK_INCLUDE_STANDARD_VARARGS +/// - NK_INCLUDE_COMMAND_USERDATA +/// +/// ### Constants +/// Define | Description +/// --------------------------------|--------------------------------------- +/// NK_BUFFER_DEFAULT_INITIAL_SIZE | Initial buffer size allocated by all buffers while using the default allocator functions included by defining NK_INCLUDE_DEFAULT_ALLOCATOR. If you don't want to allocate the default 4k memory then redefine it. +/// NK_MAX_NUMBER_BUFFER | Maximum buffer size for the conversion buffer between float and string Under normal circumstances this should be more than sufficient. +/// NK_INPUT_MAX | Defines the max number of bytes which can be added as text input in one frame. Under normal circumstances this should be more than sufficient. +/// +/// !!! WARNING +/// The following constants if defined need to be defined for both header and implementation: +/// - NK_MAX_NUMBER_BUFFER +/// - NK_BUFFER_DEFAULT_INITIAL_SIZE +/// - NK_INPUT_MAX +/// +/// ### Dependencies +/// Function | Description +/// ------------|--------------------------------------------------------------- +/// NK_ASSERT | If you don't define this, nuklear will use with assert(). +/// NK_MEMSET | You can define this to 'memset' or your own memset implementation replacement. If not nuklear will use its own version. +/// NK_MEMCPY | You can define this to 'memcpy' or your own memcpy implementation replacement. If not nuklear will use its own version. +/// NK_SQRT | You can define this to 'sqrt' or your own sqrt implementation replacement. If not nuklear will use its own slow and not highly accurate version. +/// NK_SIN | You can define this to 'sinf' or your own sine implementation replacement. If not nuklear will use its own approximation implementation. +/// NK_COS | You can define this to 'cosf' or your own cosine implementation replacement. If not nuklear will use its own approximation implementation. +/// NK_STRTOD | You can define this to `strtod` or your own string to double conversion implementation replacement. If not defined nuklear will use its own imprecise and possibly unsafe version (does not handle nan or infinity!). +/// NK_DTOA | You can define this to `dtoa` or your own double to string conversion implementation replacement. If not defined nuklear will use its own imprecise and possibly unsafe version (does not handle nan or infinity!). +/// NK_VSNPRINTF| If you define `NK_INCLUDE_STANDARD_VARARGS` as well as `NK_INCLUDE_STANDARD_IO` and want to be safe define this to `vsnprintf` on compilers supporting later versions of C or C++. By default nuklear will check for your stdlib version in C as well as compiler version in C++. if `vsnprintf` is available it will define it to `vsnprintf` directly. If not defined and if you have older versions of C or C++ it will be defined to `vsprintf` which is unsafe. +/// +/// !!! WARNING +/// The following dependencies will pull in the standard C library if not redefined: +/// - NK_ASSERT +/// +/// !!! WARNING +/// The following dependencies if defined need to be defined for both header and implementation: +/// - NK_ASSERT +/// +/// !!! WARNING +/// The following dependencies if defined need to be defined only for the implementation part: +/// - NK_MEMSET +/// - NK_MEMCPY +/// - NK_SQRT +/// - NK_SIN +/// - NK_COS +/// - NK_STRTOD +/// - NK_DTOA +/// - NK_VSNPRINTF +/// +/// ## Example +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// // init gui state +/// enum {EASY, HARD}; +/// static int op = EASY; +/// static float value = 0.6f; +/// static int i = 20; +/// struct nk_context ctx; +/// +/// nk_init_fixed(&ctx, calloc(1, MAX_MEMORY), MAX_MEMORY, &font); +/// if (nk_begin(&ctx, "Show", nk_rect(50, 50, 220, 220), +/// NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_CLOSABLE)) { +/// // fixed widget pixel width +/// nk_layout_row_static(&ctx, 30, 80, 1); +/// if (nk_button_label(&ctx, "button")) { +/// // event handling +/// } +/// +/// // fixed widget window ratio width +/// nk_layout_row_dynamic(&ctx, 30, 2); +/// if (nk_option_label(&ctx, "easy", op == EASY)) op = EASY; +/// if (nk_option_label(&ctx, "hard", op == HARD)) op = HARD; +/// +/// // custom widget pixel width +/// nk_layout_row_begin(&ctx, NK_STATIC, 30, 2); +/// { +/// nk_layout_row_push(&ctx, 50); +/// nk_label(&ctx, "Volume:", NK_TEXT_LEFT); +/// nk_layout_row_push(&ctx, 110); +/// nk_slider_float(&ctx, 0, &value, 1.0f, 0.1f); +/// } +/// nk_layout_row_end(&ctx); +/// } +/// nk_end(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// ![](https://cloud.githubusercontent.com/assets/8057201/10187981/584ecd68-675c-11e5-897c-822ef534a876.png) +/// +/// ## API +/// */ +#ifndef NK_SINGLE_FILE + #define NK_SINGLE_FILE +#endif + #ifndef NK_NUKLEAR_H_ #define NK_NUKLEAR_H_ @@ -254,13 +231,13 @@ extern "C" { #define NK_UTF_INVALID 0xFFFD /* internal invalid utf8 rune */ #define NK_UTF_SIZE 4 /* describes the number of bytes a glyph consists of*/ #ifndef NK_INPUT_MAX -#define NK_INPUT_MAX 16 + #define NK_INPUT_MAX 16 #endif #ifndef NK_MAX_NUMBER_BUFFER -#define NK_MAX_NUMBER_BUFFER 64 + #define NK_MAX_NUMBER_BUFFER 64 #endif #ifndef NK_SCROLLBAR_HIDING_TIMEOUT -#define NK_SCROLLBAR_HIDING_TIMEOUT 4.0f + #define NK_SCROLLBAR_HIDING_TIMEOUT 4.0f #endif /* * ============================================================== @@ -282,6 +259,13 @@ extern "C" { #define NK_API extern #endif #endif +#ifndef NK_LIB + #ifdef NK_SINGLE_FILE + #define NK_LIB static + #else + #define NK_LIB extern + #endif +#endif #define NK_INTERN static #define NK_STORAGE static @@ -295,26 +279,44 @@ extern "C" { #define NK_STRING_JOIN(arg1, arg2) NK_STRING_JOIN_DELAY(arg1, arg2) #ifdef _MSC_VER -#define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__COUNTER__) + #define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__COUNTER__) #else -#define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__LINE__) + #define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__LINE__) #endif #ifndef NK_STATIC_ASSERT -#define NK_STATIC_ASSERT(exp) typedef char NK_UNIQUE_NAME(_dummy_array)[(exp)?1:-1] + #define NK_STATIC_ASSERT(exp) typedef char NK_UNIQUE_NAME(_dummy_array)[(exp)?1:-1] #endif #ifndef NK_FILE_LINE #ifdef _MSC_VER -#define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__COUNTER__) + #define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__COUNTER__) #else -#define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__LINE__) + #define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__LINE__) #endif #endif #define NK_MIN(a,b) ((a) < (b) ? (a) : (b)) #define NK_MAX(a,b) ((a) < (b) ? (b) : (a)) #define NK_CLAMP(i,v,x) (NK_MAX(NK_MIN(v,x), i)) + +#ifdef NK_INCLUDE_STANDARD_VARARGS + #if defined(_MSC_VER) && (_MSC_VER >= 1600) /* VS 2010 and above */ + #include + #define NK_PRINTF_FORMAT_STRING _Printf_format_string_ + #else + #define NK_PRINTF_FORMAT_STRING + #endif + #if defined(__GNUC__) + #define NK_PRINTF_VARARG_FUNC(fmtargnumber) __attribute__((format(__printf__, fmtargnumber, fmtargnumber+1))) + #define NK_PRINTF_VALIST_FUNC(fmtargnumber) __attribute__((format(__printf__, fmtargnumber, 0))) + #else + #define NK_PRINTF_VARARG_FUNC(fmtargnumber) + #define NK_PRINTF_VALIST_FUNC(fmtargnumber) + #endif + #include /* valist, va_start, va_end, ... */ +#endif + /* * =============================================================== * @@ -507,101 +509,159 @@ enum nk_symbol_type { * CONTEXT * * =============================================================================*/ -/* Contexts are the main entry point and the majestro of nuklear and contain all required state. - * They are used for window, memory, input, style, stack, commands and time management and need - * to be passed into all nuklear GUI specific functions. - * - * Usage - * ------------------- - * To use a context it first has to be initialized which can be achieved by calling - * one of either `nk_init_default`, `nk_init_fixed`, `nk_init`, `nk_init_custom`. - * Each takes in a font handle and a specific way of handling memory. Memory control - * hereby ranges from standard library to just specifying a fixed sized block of memory - * which nuklear has to manage itself from. - * - * struct nk_context ctx; - * nk_init_xxx(&ctx, ...); - * while (1) { - * [...] - * nk_clear(&ctx); - * } - * nk_free(&ctx); - * - * Reference - * ------------------- - * nk_init_default - Initializes context with standard library memory allocation (malloc,free) - * nk_init_fixed - Initializes context from single fixed size memory block - * nk_init - Initializes context with memory allocator callbacks for alloc and free - * nk_init_custom - Initializes context from two buffers. One for draw commands the other for window/panel/table allocations - * nk_clear - Called at the end of the frame to reset and prepare the context for the next frame - * nk_free - Shutdown and free all memory allocated inside the context - * nk_set_user_data - Utility function to pass user data to draw command +/*/// ### Context +/// Contexts are the main entry point and the majestro of nuklear and contain all required state. +/// They are used for window, memory, input, style, stack, commands and time management and need +/// to be passed into all nuklear GUI specific functions. +/// +/// #### Usage +/// To use a context it first has to be initialized which can be achieved by calling +/// one of either `nk_init_default`, `nk_init_fixed`, `nk_init`, `nk_init_custom`. +/// Each takes in a font handle and a specific way of handling memory. Memory control +/// hereby ranges from standard library to just specifying a fixed sized block of memory +/// which nuklear has to manage itself from. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// // [...] +/// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// --------------------|------------------------------------------------------- +/// __nk_init_default__ | Initializes context with standard library memory allocation (malloc,free) +/// __nk_init_fixed__ | Initializes context from single fixed size memory block +/// __nk_init__ | Initializes context with memory allocator callbacks for alloc and free +/// __nk_init_custom__ | Initializes context from two buffers. One for draw commands the other for window/panel/table allocations +/// __nk_clear__ | Called at the end of the frame to reset and prepare the context for the next frame +/// __nk_free__ | Shutdown and free all memory allocated inside the context +/// __nk_set_user_data__| Utility function to pass user data to draw command */ #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR -/* nk_init_default - Initializes a `nk_context` struct with a default standard library allocator. - * Should be used if you don't want to be bothered with memory management in nuklear. - * Parameters: - * @ctx must point to an either stack or heap allocated `nk_context` struct - * @font must point to a previously initialized font handle for more info look at font documentation - * Return values: - * true(1) on success - * false(0) on failure */ +/*/// #### nk_init_default +/// Initializes a `nk_context` struct with a default standard library allocator. +/// Should be used if you don't want to be bothered with memory management in nuklear. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_init_default(struct nk_context *ctx, const struct nk_user_font *font); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|--------------------------------------------------------------- +/// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct +/// __font__ | Must point to a previously initialized font handle for more info look at font documentation +/// +/// Returns either `false(0)` on failure or `true(1)` on success. +/// +*/ NK_API int nk_init_default(struct nk_context*, const struct nk_user_font*); #endif -/* nk_init_fixed - Initializes a `nk_context` struct from a single fixed size memory block - * Should be used if you want complete control over nuklear's memory management. - * Especially recommended for system with little memory or systems with virtual memory. - * For the later case you can just allocate for example 16MB of virtual memory - * and only the required amount of memory will actually be committed. - * IMPORTANT: make sure the passed memory block is aligned correctly for `nk_draw_commands` - * Parameters: - * @ctx must point to an either stack or heap allocated `nk_context` struct - * @memory must point to a previously allocated memory block - * @size must contain the total size of @memory - * @font must point to a previously initialized font handle for more info look at font documentation - * Return values: - * true(1) on success - * false(0) on failure */ +/*/// #### nk_init_fixed +/// Initializes a `nk_context` struct from single fixed size memory block +/// Should be used if you want complete control over nuklear's memory management. +/// Especially recommended for system with little memory or systems with virtual memory. +/// For the later case you can just allocate for example 16MB of virtual memory +/// and only the required amount of memory will actually be committed. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_init_fixed(struct nk_context *ctx, void *memory, nk_size size, const struct nk_user_font *font); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// !!! Warning +/// make sure the passed memory block is aligned correctly for `nk_draw_commands`. +/// +/// Parameter | Description +/// ------------|-------------------------------------------------------------- +/// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct +/// __memory__ | Must point to a previously allocated memory block +/// __size__ | Must contain the total size of __memory__ +/// __font__ | Must point to a previously initialized font handle for more info look at font documentation +/// +/// Returns either `false(0)` on failure or `true(1)` on success. +*/ NK_API int nk_init_fixed(struct nk_context*, void *memory, nk_size size, const struct nk_user_font*); -/* nk_init - Initializes a `nk_context` struct with memory allocation callbacks for nuklear to allocate - * memory from. Used internally for `nk_init_default` and provides a kitchen sink allocation - * interface to nuklear. Can be useful for cases like monitoring memory consumption. - * Parameters: - * @ctx must point to an either stack or heap allocated `nk_context` struct - * @alloc must point to a previously allocated memory allocator - * @font must point to a previously initialized font handle for more info look at font documentation - * Return values: - * true(1) on success - * false(0) on failure */ +/*/// #### nk_init +/// Initializes a `nk_context` struct with memory allocation callbacks for nuklear to allocate +/// memory from. Used internally for `nk_init_default` and provides a kitchen sink allocation +/// interface to nuklear. Can be useful for cases like monitoring memory consumption. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_init(struct nk_context *ctx, struct nk_allocator *alloc, const struct nk_user_font *font); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|--------------------------------------------------------------- +/// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct +/// __alloc__ | Must point to a previously allocated memory allocator +/// __font__ | Must point to a previously initialized font handle for more info look at font documentation +/// +/// Returns either `false(0)` on failure or `true(1)` on success. +*/ NK_API int nk_init(struct nk_context*, struct nk_allocator*, const struct nk_user_font*); -/* nk_init_custom - Initializes a `nk_context` struct from two different either fixed or growing - * buffers. The first buffer is for allocating draw commands while the second buffer is - * used for allocating windows, panels and state tables. - * Parameters: - * @ctx must point to an either stack or heap allocated `nk_context` struct - * @cmds must point to a previously initialized memory buffer either fixed or dynamic to store draw commands into - * @pool must point to a previously initialized memory buffer either fixed or dynamic to store windows, panels and tables - * @font must point to a previously initialized font handle for more info look at font documentation - * Return values: - * true(1) on success - * false(0) on failure */ +/*/// #### nk_init_custom +/// Initializes a `nk_context` struct from two different either fixed or growing +/// buffers. The first buffer is for allocating draw commands while the second buffer is +/// used for allocating windows, panels and state tables. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_init_custom(struct nk_context *ctx, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font *font); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|--------------------------------------------------------------- +/// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct +/// __cmds__ | Must point to a previously initialized memory buffer either fixed or dynamic to store draw commands into +/// __pool__ | Must point to a previously initialized memory buffer either fixed or dynamic to store windows, panels and tables +/// __font__ | Must point to a previously initialized font handle for more info look at font documentation +/// +/// Returns either `false(0)` on failure or `true(1)` on success. +*/ NK_API int nk_init_custom(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font*); -/* nk_clear - Resets the context state at the end of the frame. This includes mostly - * garbage collector tasks like removing windows or table not called and therefore - * used anymore. - * Parameters: - * @ctx must point to a previously initialized `nk_context` struct */ +/*/// #### nk_clear +/// Resets the context state at the end of the frame. This includes mostly +/// garbage collector tasks like removing windows or table not called and therefore +/// used anymore. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_clear(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +*/ NK_API void nk_clear(struct nk_context*); -/* nk_free - Frees all memory allocated by nuklear. Not needed if context was - * initialized with `nk_init_fixed`. - * Parameters: - * @ctx must point to a previously initialized `nk_context` struct */ +/*/// #### nk_free +/// Frees all memory allocated by nuklear. Not needed if context was +/// initialized with `nk_init_fixed`. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_free(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +*/ NK_API void nk_free(struct nk_context*); #ifdef NK_INCLUDE_COMMAND_USERDATA -/* nk_set_user_data - Sets the currently passed userdata passed down into each draw command. - * Parameters: - * @ctx must point to a previously initialized `nk_context` struct - * @data handle with either pointer or index to be passed into every draw commands */ +/*/// #### nk_set_user_data +/// Sets the currently passed userdata passed down into each draw command. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_set_user_data(struct nk_context *ctx, nk_handle data); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|-------------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __data__ | Handle with either pointer or index to be passed into every draw commands +*/ NK_API void nk_set_user_data(struct nk_context*, nk_handle handle); #endif /* ============================================================================= @@ -609,54 +669,67 @@ NK_API void nk_set_user_data(struct nk_context*, nk_handle handle); * INPUT * * =============================================================================*/ -/* The input API is responsible for holding the current input state composed of - * mouse, key and text input states. - * It is worth noting that no direct os or window handling is done in nuklear. - * Instead all input state has to be provided by platform specific code. This in one hand - * expects more work from the user and complicates usage but on the other hand - * provides simple abstraction over a big number of platforms, libraries and other - * already provided functionality. - * - * Usage - * ------------------- - * Input state needs to be provided to nuklear by first calling `nk_input_begin` - * which resets internal state like delta mouse position and button transistions. - * After `nk_input_begin` all current input state needs to be provided. This includes - * mouse motion, button and key pressed and released, text input and scrolling. - * Both event- or state-based input handling are supported by this API - * and should work without problems. Finally after all input state has been - * mirrored `nk_input_end` needs to be called to finish input process. - * - * struct nk_context ctx; - * nk_init_xxx(&ctx, ...); - * while (1) { - * Event evt; - * nk_input_begin(&ctx); - * while (GetEvent(&evt)) { - * if (evt.type == MOUSE_MOVE) - * nk_input_motion(&ctx, evt.motion.x, evt.motion.y); - * else if (evt.type == ...) { - * ... - * } - * } - * nk_input_end(&ctx); - * [...] - * nk_clear(&ctx); - * } - * nk_free(&ctx); - * - * Reference - * ------------------- - * nk_input_begin - Begins the input mirroring process. Needs to be called before all other `nk_input_xxx` calls - * nk_input_motion - Mirrors mouse cursor position - * nk_input_key - Mirrors key state with either pressed or released - * nk_input_button - Mirrors mouse button state with either pressed or released - * nk_input_scroll - Mirrors mouse scroll values - * nk_input_char - Adds a single ASCII text character into an internal text buffer - * nk_input_glyph - Adds a single multi-byte UTF-8 character into an internal text buffer - * nk_input_unicode - Adds a single unicode rune into an internal text buffer - * nk_input_end - Ends the input mirroring process by calculating state changes. Don't call any `nk_input_xxx` function referenced above after this call - */ +/*/// ### Input +/// The input API is responsible for holding the current input state composed of +/// mouse, key and text input states. +/// It is worth noting that no direct OS or window handling is done in nuklear. +/// Instead all input state has to be provided by platform specific code. This on one hand +/// expects more work from the user and complicates usage but on the other hand +/// provides simple abstraction over a big number of platforms, libraries and other +/// already provided functionality. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// nk_input_begin(&ctx); +/// while (GetEvent(&evt)) { +/// if (evt.type == MOUSE_MOVE) +/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); +/// else if (evt.type == [...]) { +/// // [...] +/// } +/// } nk_input_end(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Usage +/// Input state needs to be provided to nuklear by first calling `nk_input_begin` +/// which resets internal state like delta mouse position and button transistions. +/// After `nk_input_begin` all current input state needs to be provided. This includes +/// mouse motion, button and key pressed and released, text input and scrolling. +/// Both event- or state-based input handling are supported by this API +/// and should work without problems. Finally after all input state has been +/// mirrored `nk_input_end` needs to be called to finish input process. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// Event evt; +/// nk_input_begin(&ctx); +/// while (GetEvent(&evt)) { +/// if (evt.type == MOUSE_MOVE) +/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); +/// else if (evt.type == [...]) { +/// // [...] +/// } +/// } +/// nk_input_end(&ctx); +/// // [...] +/// nk_clear(&ctx); +/// } nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// --------------------|------------------------------------------------------- +/// __nk_input_begin__ | Begins the input mirroring process. Needs to be called before all other `nk_input_xxx` calls +/// __nk_input_motion__ | Mirrors mouse cursor position +/// __nk_input_key__ | Mirrors key state with either pressed or released +/// __nk_input_button__ | Mirrors mouse button state with either pressed or released +/// __nk_input_scroll__ | Mirrors mouse scroll values +/// __nk_input_char__ | Adds a single ASCII text character into an internal text buffer +/// __nk_input_glyph__ | Adds a single multi-byte UTF-8 character into an internal text buffer +/// __nk_input_unicode__| Adds a single unicode rune into an internal text buffer +/// __nk_input_end__ | Ends the input mirroring process by calculating state changes. Don't call any `nk_input_xxx` function referenced above after this call +*/ enum nk_keys { NK_KEY_NONE, NK_KEY_SHIFT, @@ -699,273 +772,365 @@ enum nk_buttons { NK_BUTTON_DOUBLE, NK_BUTTON_MAX }; -/* nk_input_begin - Begins the input mirroring process by resetting text, scroll - * mouse previous mouse position and movement as well as key state transitions, - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct */ +/*/// #### nk_input_begin +/// Begins the input mirroring process by resetting text, scroll +/// mouse, previous mouse position and movement as well as key state transitions, +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_begin(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +*/ NK_API void nk_input_begin(struct nk_context*); -/* nk_input_motion - Mirrors current mouse position to nuklear - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @x must contain an integer describing the current mouse cursor x-position - * @y must contain an integer describing the current mouse cursor y-position */ +/*/// #### nk_input_motion +/// Mirrors current mouse position to nuklear +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_motion(struct nk_context *ctx, int x, int y); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __x__ | Must hold an integer describing the current mouse cursor x-position +/// __y__ | Must hold an integer describing the current mouse cursor y-position +*/ NK_API void nk_input_motion(struct nk_context*, int x, int y); -/* nk_input_key - Mirrors state of a specific key to nuklear - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @key must be any value specified in enum `nk_keys` that needs to be mirrored - * @down must be 0 for key is up and 1 for key is down */ +/*/// #### nk_input_key +/// Mirrors the state of a specific key to nuklear +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_key(struct nk_context*, enum nk_keys key, int down); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __key__ | Must be any value specified in enum `nk_keys` that needs to be mirrored +/// __down__ | Must be 0 for key is up and 1 for key is down +*/ NK_API void nk_input_key(struct nk_context*, enum nk_keys, int down); -/* nk_input_button - Mirrors the state of a specific mouse button to nuklear - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @nk_buttons must be any value specified in enum `nk_buttons` that needs to be mirrored - * @x must contain an integer describing mouse cursor x-position on click up/down - * @y must contain an integer describing mouse cursor y-position on click up/down - * @down must be 0 for key is up and 1 for key is down */ +/*/// #### nk_input_button +/// Mirrors the state of a specific mouse button to nuklear +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_button(struct nk_context *ctx, enum nk_buttons btn, int x, int y, int down); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __btn__ | Must be any value specified in enum `nk_buttons` that needs to be mirrored +/// __x__ | Must contain an integer describing mouse cursor x-position on click up/down +/// __y__ | Must contain an integer describing mouse cursor y-position on click up/down +/// __down__ | Must be 0 for key is up and 1 for key is down +*/ NK_API void nk_input_button(struct nk_context*, enum nk_buttons, int x, int y, int down); -/* nk_input_scroll - Copies the last mouse scroll value to nuklear. Is generally - * a scroll value. So does not have to come from mouse and could also originate - * from touch for example. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @val vector with both X- as well as Y-scroll value */ +/*/// #### nk_input_scroll +/// Copies the last mouse scroll value to nuklear. Is generally +/// a scroll value. So does not have to come from mouse and could also originate +/// TODO finish this sentence +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_scroll(struct nk_context *ctx, struct nk_vec2 val); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __val__ | vector with both X- as well as Y-scroll value +*/ NK_API void nk_input_scroll(struct nk_context*, struct nk_vec2 val); -/* nk_input_char - Copies a single ASCII character into an internal text buffer - * This is basically a helper function to quickly push ASCII characters into - * nuklear. Note that you can only push up to NK_INPUT_MAX bytes into - * struct `nk_input` between `nk_input_begin` and `nk_input_end`. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @c must be a single ASCII character preferable one that can be printed */ +/*/// #### nk_input_char +/// Copies a single ASCII character into an internal text buffer +/// This is basically a helper function to quickly push ASCII characters into +/// nuklear. +/// +/// !!! Note +/// Stores up to NK_INPUT_MAX bytes between `nk_input_begin` and `nk_input_end`. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_char(struct nk_context *ctx, char c); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __c__ | Must be a single ASCII character preferable one that can be printed +*/ NK_API void nk_input_char(struct nk_context*, char); -/* nk_input_unicode - Converts a encoded unicode rune into UTF-8 and copies the result - * into an internal text buffer. - * Note that you can only push up to NK_INPUT_MAX bytes into - * struct `nk_input` between `nk_input_begin` and `nk_input_end`. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @glyph UTF-32 unicode codepoint */ +/*/// #### nk_input_glyph +/// Converts an encoded unicode rune into UTF-8 and copies the result into an +/// internal text buffer. +/// +/// !!! Note +/// Stores up to NK_INPUT_MAX bytes between `nk_input_begin` and `nk_input_end`. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_glyph(struct nk_context *ctx, const nk_glyph g); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __g__ | UTF-32 unicode codepoint +*/ NK_API void nk_input_glyph(struct nk_context*, const nk_glyph); -/* nk_input_unicode - Converts a unicode rune into UTF-8 and copies the result - * into an internal text buffer. - * Note that you can only push up to NK_INPUT_MAX bytes into - * struct `nk_input` between `nk_input_begin` and `nk_input_end`. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @glyph UTF-32 unicode codepoint */ +/*/// #### nk_input_unicode +/// Converts a unicode rune into UTF-8 and copies the result +/// into an internal text buffer. +/// !!! Note +/// Stores up to NK_INPUT_MAX bytes between `nk_input_begin` and `nk_input_end`. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_unicode(struct nk_context*, nk_rune rune); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +/// __rune__ | UTF-32 unicode codepoint +*/ NK_API void nk_input_unicode(struct nk_context*, nk_rune); -/* nk_input_end - End the input mirroring process by resetting mouse grabbing - * state to ensure the mouse cursor is not grabbed indefinitely. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct */ +/*/// #### nk_input_end +/// End the input mirroring process by resetting mouse grabbing +/// state to ensure the mouse cursor is not grabbed indefinitely. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_input_end(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to a previously initialized `nk_context` struct +*/ NK_API void nk_input_end(struct nk_context*); /* ============================================================================= * * DRAWING * * =============================================================================*/ -/* This library was designed to be render backend agnostic so it does - * not draw anything to screen directly. Instead all drawn shapes, widgets - * are made of, are buffered into memory and make up a command queue. - * Each frame therefore fills the command buffer with draw commands - * that then need to be executed by the user and his own render backend. - * After that the command buffer needs to be cleared and a new frame can be - * started. It is probably important to note that the command buffer is the main - * drawing API and the optional vertex buffer API only takes this format and - * converts it into a hardware accessible format. - * - * Usage - * ------------------- - * To draw all draw commands accumulated over a frame you need your own render - * backend able to draw a number of 2D primitives. This includes at least - * filled and stroked rectangles, circles, text, lines, triangles and scissors. - * As soon as this criterion is met you can iterate over each draw command - * and execute each draw command in a interpreter like fashion: - * - * const struct nk_command *cmd = 0; - * nk_foreach(cmd, &ctx) { - * switch (cmd->type) { - * case NK_COMMAND_LINE: - * your_draw_line_function(...) - * break; - * case NK_COMMAND_RECT - * your_draw_rect_function(...) - * break; - * case ...: - * [...] - * } - * - * In program flow context draw commands need to be executed after input has been - * gathered and the complete UI with windows and their contained widgets have - * been executed and before calling `nk_clear` which frees all previously - * allocated draw commands. - * - * struct nk_context ctx; - * nk_init_xxx(&ctx, ...); - * while (1) { - * Event evt; - * nk_input_begin(&ctx); - * while (GetEvent(&evt)) { - * if (evt.type == MOUSE_MOVE) - * nk_input_motion(&ctx, evt.motion.x, evt.motion.y); - * else if (evt.type == [...]) { - * [...] - * } - * } - * nk_input_end(&ctx); - * - * [...] - * - * const struct nk_command *cmd = 0; - * nk_foreach(cmd, &ctx) { - * switch (cmd->type) { - * case NK_COMMAND_LINE: - * your_draw_line_function(...) - * break; - * case NK_COMMAND_RECT - * your_draw_rect_function(...) - * break; - * case ...: - * [...] - * } - * nk_clear(&ctx); - * } - * nk_free(&ctx); - * - * You probably noticed that you have to draw all of the UI each frame which is - * quite wasteful. While the actual UI updating loop is quite fast rendering - * without actually needing it is not. So there are multiple things you could do. - * - * First is only update on input. This of course is only an option if your - * application only depends on the UI and does not require any outside calculations. - * If you actually only update on input make sure to update the UI two times each - * frame and call `nk_clear` directly after the first pass and only draw in - * the second pass. In addition it is recommended to also add additional timers - * to make sure the UI is not drawn more than a fixed number of frames per second. - * - * struct nk_context ctx; - * nk_init_xxx(&ctx, ...); - * while (1) { - * [...wait for input ] - * - * [...do two UI passes ...] - * do_ui(...) - * nk_clear(&ctx); - * do_ui(...) - * - * const struct nk_command *cmd = 0; - * nk_foreach(cmd, &ctx) { - * switch (cmd->type) { - * case NK_COMMAND_LINE: - * your_draw_line_function(...) - * break; - * case NK_COMMAND_RECT - * your_draw_rect_function(...) - * break; - * case ...: - * [...] - * } - * nk_clear(&ctx); - * } - * nk_free(&ctx); - * - * The second probably more applicable trick is to only draw if anything changed. - * It is not really useful for applications with continuous draw loop but - * quite useful for desktop applications. To actually get nuklear to only - * draw on changes you first have to define `NK_ZERO_COMMAND_MEMORY` and - * allocate a memory buffer that will store each unique drawing output. - * After each frame you compare the draw command memory inside the library - * with your allocated buffer by memcmp. If memcmp detects differences - * you have to copy the command buffer into the allocated buffer - * and then draw like usual (this example uses fixed memory but you could - * use dynamically allocated memory). - * - * [... other defines ...] - * #define NK_ZERO_COMMAND_MEMORY - * #include "nuklear.h" - * - * struct nk_context ctx; - * void *last = calloc(1,64*1024); - * void *buf = calloc(1,64*1024); - * nk_init_fixed(&ctx, buf, 64*1024); - * while (1) { - * [...input...] - * [...ui...] - * - * void *cmds = nk_buffer_memory(&ctx.memory); - * if (memcmp(cmds, last, ctx.memory.allocated)) { - * memcpy(last,cmds,ctx.memory.allocated); - * const struct nk_command *cmd = 0; - * nk_foreach(cmd, &ctx) { - * switch (cmd->type) { - * case NK_COMMAND_LINE: - * your_draw_line_function(...) - * break; - * case NK_COMMAND_RECT - * your_draw_rect_function(...) - * break; - * case ...: - * [...] - * } - * } - * } - * nk_clear(&ctx); - * } - * nk_free(&ctx); - * - * Finally while using draw commands makes sense for higher abstracted platforms like - * X11 and Win32 or drawing libraries it is often desirable to use graphics - * hardware directly. Therefore it is possible to just define - * `NK_INCLUDE_VERTEX_BUFFER_OUTPUT` which includes optional vertex output. - * To access the vertex output you first have to convert all draw commands into - * vertexes by calling `nk_convert` which takes in your preferred vertex format. - * After successfully converting all draw commands just iterate over and execute all - * vertex draw commands: - * - * struct nk_convert_config cfg = {}; - * static const struct nk_draw_vertex_layout_element vertex_layout[] = { - * {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct your_vertex, pos)}, - * {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct your_vertex, uv)}, - * {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct your_vertex, col)}, - * {NK_VERTEX_LAYOUT_END} - * }; - * cfg.shape_AA = NK_ANTI_ALIASING_ON; - * cfg.line_AA = NK_ANTI_ALIASING_ON; - * cfg.vertex_layout = vertex_layout; - * cfg.vertex_size = sizeof(struct your_vertex); - * cfg.vertex_alignment = NK_ALIGNOF(struct your_vertex); - * cfg.circle_segment_count = 22; - * cfg.curve_segment_count = 22; - * cfg.arc_segment_count = 22; - * cfg.global_alpha = 1.0f; - * cfg.null = dev->null; - * - * struct nk_buffer cmds, verts, idx; - * nk_buffer_init_default(&cmds); - * nk_buffer_init_default(&verts); - * nk_buffer_init_default(&idx); - * nk_convert(&ctx, &cmds, &verts, &idx, &cfg); - * nk_draw_foreach(cmd, &ctx, &cmds) { - * if (!cmd->elem_count) continue; - * [...] - * } - * nk_buffer_free(&cms); - * nk_buffer_free(&verts); - * nk_buffer_free(&idx); - * - * Reference - * ------------------- - * nk__begin - Returns the first draw command in the context draw command list to be drawn - * nk__next - Increments the draw command iterator to the next command inside the context draw command list - * nk_foreach - Iterates over each draw command inside the context draw command list - * - * nk_convert - Converts from the abstract draw commands list into a hardware accessible vertex format - * nk__draw_begin - Returns the first vertex command in the context vertex draw list to be executed - * nk__draw_next - Increments the vertex command iterator to the next command inside the context vertex command list - * nk__draw_end - Returns the end of the vertex draw list - * nk_draw_foreach - Iterates over each vertex draw command inside the vertex draw list - */ +/*/// ### Drawing +/// This library was designed to be render backend agnostic so it does +/// not draw anything to screen directly. Instead all drawn shapes, widgets +/// are made of, are buffered into memory and make up a command queue. +/// Each frame therefore fills the command buffer with draw commands +/// that then need to be executed by the user and his own render backend. +/// After that the command buffer needs to be cleared and a new frame can be +/// started. It is probably important to note that the command buffer is the main +/// drawing API and the optional vertex buffer API only takes this format and +/// converts it into a hardware accessible format. +/// +/// #### Usage +/// To draw all draw commands accumulated over a frame you need your own render +/// backend able to draw a number of 2D primitives. This includes at least +/// filled and stroked rectangles, circles, text, lines, triangles and scissors. +/// As soon as this criterion is met you can iterate over each draw command +/// and execute each draw command in a interpreter like fashion: +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// switch (cmd->type) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case //...: +/// //[...] +/// } +/// } +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// In program flow context draw commands need to be executed after input has been +/// gathered and the complete UI with windows and their contained widgets have +/// been executed and before calling `nk_clear` which frees all previously +/// allocated draw commands. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// Event evt; +/// nk_input_begin(&ctx); +/// while (GetEvent(&evt)) { +/// if (evt.type == MOUSE_MOVE) +/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); +/// else if (evt.type == [...]) { +/// [...] +/// } +/// } +/// nk_input_end(&ctx); +/// // +/// // [...] +/// // +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// switch (cmd->type) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case ...: +/// // [...] +/// } +/// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// You probably noticed that you have to draw all of the UI each frame which is +/// quite wasteful. While the actual UI updating loop is quite fast rendering +/// without actually needing it is not. So there are multiple things you could do. +/// +/// First is only update on input. This of course is only an option if your +/// application only depends on the UI and does not require any outside calculations. +/// If you actually only update on input make sure to update the UI two times each +/// frame and call `nk_clear` directly after the first pass and only draw in +/// the second pass. In addition it is recommended to also add additional timers +/// to make sure the UI is not drawn more than a fixed number of frames per second. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// // [...wait for input ] +/// // [...do two UI passes ...] +/// do_ui(...) +/// nk_clear(&ctx); +/// do_ui(...) +/// // +/// // draw +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// switch (cmd->type) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case ...: +/// //[...] +/// } +/// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// The second probably more applicable trick is to only draw if anything changed. +/// It is not really useful for applications with continuous draw loop but +/// quite useful for desktop applications. To actually get nuklear to only +/// draw on changes you first have to define `NK_ZERO_COMMAND_MEMORY` and +/// allocate a memory buffer that will store each unique drawing output. +/// After each frame you compare the draw command memory inside the library +/// with your allocated buffer by memcmp. If memcmp detects differences +/// you have to copy the command buffer into the allocated buffer +/// and then draw like usual (this example uses fixed memory but you could +/// use dynamically allocated memory). +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// //[... other defines ...] +/// #define NK_ZERO_COMMAND_MEMORY +/// #include "nuklear.h" +/// // +/// // setup context +/// struct nk_context ctx; +/// void *last = calloc(1,64*1024); +/// void *buf = calloc(1,64*1024); +/// nk_init_fixed(&ctx, buf, 64*1024); +/// // +/// // loop +/// while (1) { +/// // [...input...] +/// // [...ui...] +/// void *cmds = nk_buffer_memory(&ctx.memory); +/// if (memcmp(cmds, last, ctx.memory.allocated)) { +/// memcpy(last,cmds,ctx.memory.allocated); +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// switch (cmd->type) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case ...: +/// // [...] +/// } +/// } +/// } +/// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Finally while using draw commands makes sense for higher abstracted platforms like +/// X11 and Win32 or drawing libraries it is often desirable to use graphics +/// hardware directly. Therefore it is possible to just define +/// `NK_INCLUDE_VERTEX_BUFFER_OUTPUT` which includes optional vertex output. +/// To access the vertex output you first have to convert all draw commands into +/// vertexes by calling `nk_convert` which takes in your preferred vertex format. +/// After successfully converting all draw commands just iterate over and execute all +/// vertex draw commands: +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// // fill configuration +/// struct nk_convert_config cfg = {}; +/// static const struct nk_draw_vertex_layout_element vertex_layout[] = { +/// {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct your_vertex, pos)}, +/// {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct your_vertex, uv)}, +/// {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct your_vertex, col)}, +/// {NK_VERTEX_LAYOUT_END} +/// }; +/// cfg.shape_AA = NK_ANTI_ALIASING_ON; +/// cfg.line_AA = NK_ANTI_ALIASING_ON; +/// cfg.vertex_layout = vertex_layout; +/// cfg.vertex_size = sizeof(struct your_vertex); +/// cfg.vertex_alignment = NK_ALIGNOF(struct your_vertex); +/// cfg.circle_segment_count = 22; +/// cfg.curve_segment_count = 22; +/// cfg.arc_segment_count = 22; +/// cfg.global_alpha = 1.0f; +/// cfg.null = dev->null; +/// // +/// // setup buffers and convert +/// struct nk_buffer cmds, verts, idx; +/// nk_buffer_init_default(&cmds); +/// nk_buffer_init_default(&verts); +/// nk_buffer_init_default(&idx); +/// nk_convert(&ctx, &cmds, &verts, &idx, &cfg); +/// // +/// // draw +/// nk_draw_foreach(cmd, &ctx, &cmds) { +/// if (!cmd->elem_count) continue; +/// //[...] +/// } +/// nk_buffer_free(&cms); +/// nk_buffer_free(&verts); +/// nk_buffer_free(&idx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// --------------------|------------------------------------------------------- +/// __nk__begin__ | Returns the first draw command in the context draw command list to be drawn +/// __nk__next__ | Increments the draw command iterator to the next command inside the context draw command list +/// __nk_foreach__ | Iterates over each draw command inside the context draw command list +/// __nk_convert__ | Converts from the abstract draw commands list into a hardware accessible vertex format +/// __nk_draw_begin__ | Returns the first vertex command in the context vertex draw list to be executed +/// __nk__draw_next__ | Increments the vertex command iterator to the next command inside the context vertex command list +/// __nk__draw_end__ | Returns the end of the vertex draw list +/// __nk_draw_foreach__ | Iterates over each vertex draw command inside the vertex draw list +*/ enum nk_anti_aliasing {NK_ANTI_ALIASING_OFF, NK_ANTI_ALIASING_ON}; enum nk_convert_result { NK_CONVERT_SUCCESS = 0, @@ -990,66 +1155,141 @@ struct nk_convert_config { nk_size vertex_size; /* sizeof one vertex for vertex packing */ nk_size vertex_alignment; /* vertex alignment: Can be obtained by NK_ALIGNOF */ }; -/* nk__begin - Returns a draw command list iterator to iterate all draw - * commands accumulated over one frame. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct at the end of a frame - * Return values: - * draw command pointer pointing to the first command inside the draw command list */ +/*/// #### nk__begin +/// Returns a draw command list iterator to iterate all draw +/// commands accumulated over one frame. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// const struct nk_command* nk__begin(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | must point to an previously initialized `nk_context` struct at the end of a frame +/// +/// Returns draw command pointer pointing to the first command inside the draw command list +*/ NK_API const struct nk_command* nk__begin(struct nk_context*); -/* nk__next - Returns a draw command list iterator to iterate all draw - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct at the end of a frame - * @cmd must point to an previously a draw command either returned by `nk__begin` or `nk__next` - * Return values: - * draw command pointer pointing to the next command inside the draw command list */ +/*/// #### nk__next +/// Returns draw command pointer pointing to the next command inside the draw command list +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// const struct nk_command* nk__next(struct nk_context*, const struct nk_command*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +/// __cmd__ | Must point to an previously a draw command either returned by `nk__begin` or `nk__next` +/// +/// Returns draw command pointer pointing to the next command inside the draw command list +*/ NK_API const struct nk_command* nk__next(struct nk_context*, const struct nk_command*); -/* nk_foreach - Iterates over each draw command inside the context draw command list - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct at the end of a frame - * @cmd pointer initialized to NULL */ +/*/// #### nk_foreach +/// Iterates over each draw command inside the context draw command list +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// #define nk_foreach(c, ctx) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +/// __cmd__ | Command pointer initialized to NULL +/// +/// Iterates over each draw command inside the context draw command list +*/ #define nk_foreach(c, ctx) for((c) = nk__begin(ctx); (c) != 0; (c) = nk__next(ctx,c)) #ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT -/* nk_convert - converts all internal draw command into vertex draw commands and fills - * three buffers with vertexes, vertex draw commands and vertex indices. The vertex format - * as well as some other configuration values have to be configured by filling out a - * `nk_convert_config` struct. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct at the end of a frame - * @cmds must point to a previously initialized buffer to hold converted vertex draw commands - * @vertices must point to a previously initialized buffer to hold all produced vertices - * @elements must point to a previously initialized buffer to hold all produced vertex indices - * @config must point to a filled out `nk_config` struct to configure the conversion process - * Returns: - * returns NK_CONVERT_SUCCESS on success and a enum nk_convert_result error values if not */ +/*/// #### nk_convert +/// Converts all internal draw commands into vertex draw commands and fills +/// three buffers with vertexes, vertex draw commands and vertex indices. The vertex format +/// as well as some other configuration values have to be configured by filling out a +/// `nk_convert_config` struct. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// nk_flags nk_convert(struct nk_context *ctx, struct nk_buffer *cmds, +// struct nk_buffer *vertices, struct nk_buffer *elements, const struct nk_convert_config*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +/// __cmds__ | Must point to a previously initialized buffer to hold converted vertex draw commands +/// __vertices__| Must point to a previously initialized buffer to hold all produced vertices +/// __elements__| Must point to a previously initialized buffer to hold all produced vertex indices +/// __config__ | Must point to a filled out `nk_config` struct to configure the conversion process +/// +/// Returns one of enum nk_convert_result error codes +/// +/// Parameter | Description +/// --------------------------------|----------------------------------------------------------- +/// NK_CONVERT_SUCCESS | Signals a successful draw command to vertex buffer conversion +/// NK_CONVERT_INVALID_PARAM | An invalid argument was passed in the function call +/// NK_CONVERT_COMMAND_BUFFER_FULL | The provided buffer for storing draw commands is full or failed to allocate more memory +/// NK_CONVERT_VERTEX_BUFFER_FULL | The provided buffer for storing vertices is full or failed to allocate more memory +/// NK_CONVERT_ELEMENT_BUFFER_FULL | The provided buffer for storing indicies is full or failed to allocate more memory +*/ NK_API nk_flags nk_convert(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, const struct nk_convert_config*); -/* nk__draw_begin - Returns a draw vertex command buffer iterator to iterate each the vertex draw command buffer - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct at the end of a frame - * @buf must point to an previously by `nk_convert` filled out vertex draw command buffer - * Return values: - * vertex draw command pointer pointing to the first command inside the vertex draw command buffer */ +/*/// #### nk__draw_begin +/// Returns a draw vertex command buffer iterator to iterate over the vertex draw command buffer +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// const struct nk_draw_command* nk__draw_begin(const struct nk_context*, const struct nk_buffer*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +/// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer +/// +/// Returns vertex draw command pointer pointing to the first command inside the vertex draw command buffer +*/ NK_API const struct nk_draw_command* nk__draw_begin(const struct nk_context*, const struct nk_buffer*); -/* nk__draw_end - Returns the vertex draw command at the end of the vertex draw command buffer - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct at the end of a frame - * @buf must point to an previously by `nk_convert` filled out vertex draw command buffer - * Return values: - * vertex draw command pointer pointing to the end of the last vertex draw command inside the vertex draw command buffer */ +/*/// #### nk__draw_end +/// Returns the vertex draw command at the end of the vertex draw command buffer +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// const struct nk_draw_command* nk__draw_end(const struct nk_context *ctx, const struct nk_buffer *buf); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +/// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer +/// +/// Returns vertex draw command pointer pointing to the end of the last vertex draw command inside the vertex draw command buffer +*/ NK_API const struct nk_draw_command* nk__draw_end(const struct nk_context*, const struct nk_buffer*); -/* nk__draw_next - Increments the vertex draw command buffer iterator - * Parameters: - * @cmd must point to an previously either by `nk__draw_begin` or `nk__draw_next` returned vertex draw command - * @buf must point to an previously by `nk_convert` filled out vertex draw command buffer - * @ctx must point to an previously initialized `nk_context` struct at the end of a frame - * Return values: - * vertex draw command pointer pointing to the end of the last vertex draw command inside the vertex draw command buffer */ +/*/// #### nk__draw_next +/// Increments the vertex draw command buffer iterator +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// const struct nk_draw_command* nk__draw_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __cmd__ | Must point to an previously either by `nk__draw_begin` or `nk__draw_next` returned vertex draw command +/// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +/// +/// Returns vertex draw command pointer pointing to the end of the last vertex draw command inside the vertex draw command buffer +*/ NK_API const struct nk_draw_command* nk__draw_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_context*); -/* nk_draw_foreach - Iterates over each vertex draw command inside a vertex draw command buffer - * Parameters: - * @cmd nk_draw_command pointer set to NULL - * @buf must point to an previously by `nk_convert` filled out vertex draw command buffer - * @ctx must point to an previously initialized `nk_context` struct at the end of a frame */ +/*/// #### nk_draw_foreach +/// Iterates over each vertex draw command inside a vertex draw command buffer +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// #define nk_draw_foreach(cmd,ctx, b) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __cmd__ | `nk_draw_command`iterator set to NULL +/// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer +/// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame +*/ #define nk_draw_foreach(cmd,ctx, b) for((cmd)=nk__draw_begin(ctx, b); (cmd)!=0; (cmd)=nk__draw_next(cmd, b, ctx)) #endif /* ============================================================================= @@ -1057,743 +1297,1654 @@ NK_API const struct nk_draw_command* nk__draw_next(const struct nk_draw_command* * WINDOW * * ============================================================================= - * Windows are the main persistent state used inside nuklear and are life time - * controlled by simply "retouching" (i.e. calling) each window each frame. - * All widgets inside nuklear can only be added inside function pair `nk_begin_xxx` - * and `nk_end`. Calling any widgets outside these two functions will result in an - * assert in debug or no state change in release mode. - * - * Each window holds frame persistent state like position, size, flags, state tables, - * and some garbage collected internal persistent widget state. Each window - * is linked into a window stack list which determines the drawing and overlapping - * order. The topmost window thereby is the currently active window. - * - * To change window position inside the stack occurs either automatically by - * user input by being clicked on or programmatically by calling `nk_window_focus`. - * Windows by default are visible unless explicitly being defined with flag - * `NK_WINDOW_HIDDEN`, the user clicked the close button on windows with flag - * `NK_WINDOW_CLOSABLE` or if a window was explicitly hidden by calling - * `nk_window_show`. To explicitly close and destroy a window call `nk_window_close`. - * - * Usage - * ------------------- - * To create and keep a window you have to call one of the two `nk_begin_xxx` - * functions to start window declarations and `nk_end` at the end. Furthermore it - * is recommended to check the return value of `nk_begin_xxx` and only process - * widgets inside the window if the value is not 0. Either way you have to call - * `nk_end` at the end of window declarations. Furthermore, do not attempt to - * nest `nk_begin_xxx` calls which will hopefully result in an assert or if not - * in a segmentation fault. - * - * if (nk_begin_xxx(...) { - * [... widgets ...] - * } - * nk_end(ctx); - * - * In the grand concept window and widget declarations need to occur after input - * handling and before drawing to screen. Not doing so can result in higher - * latency or at worst invalid behavior. Furthermore make sure that `nk_clear` - * is called at the end of the frame. While nuklear's default platform backends - * already call `nk_clear` for you if you write your own backend not calling - * `nk_clear` can cause asserts or even worse undefined behavior. - * - * struct nk_context ctx; - * nk_init_xxx(&ctx, ...); - * while (1) { - * Event evt; - * nk_input_begin(&ctx); - * while (GetEvent(&evt)) { - * if (evt.type == MOUSE_MOVE) - * nk_input_motion(&ctx, evt.motion.x, evt.motion.y); - * else if (evt.type == [...]) { - * nk_input_xxx(...); - * } - * } - * nk_input_end(&ctx); - * - * if (nk_begin_xxx(...) { - * [...] - * } - * nk_end(ctx); - * - * const struct nk_command *cmd = 0; - * nk_foreach(cmd, &ctx) { - * case NK_COMMAND_LINE: - * your_draw_line_function(...) - * break; - * case NK_COMMAND_RECT - * your_draw_rect_function(...) - * break; - * case ...: - * [...] - * } - * nk_clear(&ctx); - * } - * nk_free(&ctx); - * - * Reference - * ------------------- - * nk_begin - starts a new window; needs to be called every frame for every window (unless hidden) or otherwise the window gets removed - * nk_begin_titled - extended window start with separated title and identifier to allow multiple windows with same name but not title - * nk_end - needs to be called at the end of the window building process to process scaling, scrollbars and general cleanup - * - * nk_window_find - finds and returns the window with give name - * nk_window_get_bounds - returns a rectangle with screen position and size of the currently processed window. - * nk_window_get_position - returns the position of the currently processed window - * nk_window_get_size - returns the size with width and height of the currently processed window - * nk_window_get_width - returns the width of the currently processed window - * nk_window_get_height - returns the height of the currently processed window - * nk_window_get_panel - returns the underlying panel which contains all processing state of the current window - * nk_window_get_content_region - returns the position and size of the currently visible and non-clipped space inside the currently processed window - * nk_window_get_content_region_min - returns the upper rectangle position of the currently visible and non-clipped space inside the currently processed window - * nk_window_get_content_region_max - returns the upper rectangle position of the currently visible and non-clipped space inside the currently processed window - * nk_window_get_content_region_size - returns the size of the currently visible and non-clipped space inside the currently processed window - * nk_window_get_canvas - returns the draw command buffer. Can be used to draw custom widgets - * - * nk_window_has_focus - returns if the currently processed window is currently active - * nk_window_is_collapsed - returns if the window with given name is currently minimized/collapsed - * nk_window_is_closed - returns if the currently processed window was closed - * nk_window_is_hidden - returns if the currently processed window was hidden - * nk_window_is_active - same as nk_window_has_focus for some reason - * nk_window_is_hovered - returns if the currently processed window is currently being hovered by mouse - * nk_window_is_any_hovered - return if any window currently hovered - * nk_item_is_any_active - returns if any window or widgets is currently hovered or active - * - * nk_window_set_bounds - updates position and size of the currently processed window - * nk_window_set_position - updates position of the currently process window - * nk_window_set_size - updates the size of the currently processed window - * nk_window_set_focus - set the currently processed window as active window - * - * nk_window_close - closes the window with given window name which deletes the window at the end of the frame - * nk_window_collapse - collapses the window with given window name - * nk_window_collapse_if - collapses the window with given window name if the given condition was met - * nk_window_show - hides a visible or reshows a hidden window - * nk_window_show_if - hides/shows a window depending on condition - */ +/// ### Window +/// Windows are the main persistent state used inside nuklear and are life time +/// controlled by simply "retouching" (i.e. calling) each window each frame. +/// All widgets inside nuklear can only be added inside the function pair `nk_begin_xxx` +/// and `nk_end`. Calling any widgets outside these two functions will result in an +/// assert in debug or no state change in release mode.

+/// +/// Each window holds frame persistent state like position, size, flags, state tables, +/// and some garbage collected internal persistent widget state. Each window +/// is linked into a window stack list which determines the drawing and overlapping +/// order. The topmost window thereby is the currently active window.

+/// +/// To change window position inside the stack occurs either automatically by +/// user input by being clicked on or programmatically by calling `nk_window_focus`. +/// Windows by default are visible unless explicitly being defined with flag +/// `NK_WINDOW_HIDDEN`, the user clicked the close button on windows with flag +/// `NK_WINDOW_CLOSABLE` or if a window was explicitly hidden by calling +/// `nk_window_show`. To explicitly close and destroy a window call `nk_window_close`.

+/// +/// #### Usage +/// To create and keep a window you have to call one of the two `nk_begin_xxx` +/// functions to start window declarations and `nk_end` at the end. Furthermore it +/// is recommended to check the return value of `nk_begin_xxx` and only process +/// widgets inside the window if the value is not 0. Either way you have to call +/// `nk_end` at the end of window declarations. Furthermore, do not attempt to +/// nest `nk_begin_xxx` calls which will hopefully result in an assert or if not +/// in a segmentation fault. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // [... widgets ...] +/// } +/// nk_end(ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// In the grand concept window and widget declarations need to occur after input +/// handling and before drawing to screen. Not doing so can result in higher +/// latency or at worst invalid behavior. Furthermore make sure that `nk_clear` +/// is called at the end of the frame. While nuklear's default platform backends +/// already call `nk_clear` for you if you write your own backend not calling +/// `nk_clear` can cause asserts or even worse undefined behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// Event evt; +/// nk_input_begin(&ctx); +/// while (GetEvent(&evt)) { +/// if (evt.type == MOUSE_MOVE) +/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); +/// else if (evt.type == [...]) { +/// nk_input_xxx(...); +/// } +/// } +/// nk_input_end(&ctx); +/// +/// if (nk_begin_xxx(...) { +/// //[...] +/// } +/// nk_end(ctx); +/// +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case //...: +/// //[...] +/// } +/// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// ------------------------------------|---------------------------------------- +/// nk_begin | Starts a new window; needs to be called every frame for every window (unless hidden) or otherwise the window gets removed +/// nk_begin_titled | Extended window start with separated title and identifier to allow multiple windows with same name but not title +/// nk_end | Needs to be called at the end of the window building process to process scaling, scrollbars and general cleanup +// +/// nk_window_find | Finds and returns the window with give name +/// nk_window_get_bounds | Returns a rectangle with screen position and size of the currently processed window. +/// nk_window_get_position | Returns the position of the currently processed window +/// nk_window_get_size | Returns the size with width and height of the currently processed window +/// nk_window_get_width | Returns the width of the currently processed window +/// nk_window_get_height | Returns the height of the currently processed window +/// nk_window_get_panel | Returns the underlying panel which contains all processing state of the current window +/// nk_window_get_content_region | Returns the position and size of the currently visible and non-clipped space inside the currently processed window +/// nk_window_get_content_region_min | Returns the upper rectangle position of the currently visible and non-clipped space inside the currently processed window +/// nk_window_get_content_region_max | Returns the upper rectangle position of the currently visible and non-clipped space inside the currently processed window +/// nk_window_get_content_region_size | Returns the size of the currently visible and non-clipped space inside the currently processed window +/// nk_window_get_canvas | Returns the draw command buffer. Can be used to draw custom widgets +/// nk_window_has_focus | Returns if the currently processed window is currently active +/// nk_window_is_collapsed | Returns if the window with given name is currently minimized/collapsed +/// nk_window_is_closed | Returns if the currently processed window was closed +/// nk_window_is_hidden | Returns if the currently processed window was hidden +/// nk_window_is_active | Same as nk_window_has_focus for some reason +/// nk_window_is_hovered | Returns if the currently processed window is currently being hovered by mouse +/// nk_window_is_any_hovered | Return if any window currently hovered +/// nk_item_is_any_active | Returns if any window or widgets is currently hovered or active +// +/// nk_window_set_bounds | Updates position and size of the currently processed window +/// nk_window_set_position | Updates position of the currently process window +/// nk_window_set_size | Updates the size of the currently processed window +/// nk_window_set_focus | Set the currently processed window as active window +// +/// nk_window_close | Closes the window with given window name which deletes the window at the end of the frame +/// nk_window_collapse | Collapses the window with given window name +/// nk_window_collapse_if | Collapses the window with given window name if the given condition was met +/// nk_window_show | Hides a visible or reshows a hidden window +/// nk_window_show_if | Hides/shows a window depending on condition +*/ +/* +/// #### nk_panel_flags +/// Flag | Description +/// ----------------------------|---------------------------------------- +/// NK_WINDOW_BORDER | Draws a border around the window to visually separate window from the background +/// NK_WINDOW_MOVABLE | The movable flag indicates that a window can be moved by user input or by dragging the window header +/// NK_WINDOW_SCALABLE | The scalable flag indicates that a window can be scaled by user input by dragging a scaler icon at the button of the window +/// NK_WINDOW_CLOSABLE | Adds a closable icon into the header +/// NK_WINDOW_MINIMIZABLE | Adds a minimize icon into the header +/// NK_WINDOW_NO_SCROLLBAR | Removes the scrollbar from the window +/// NK_WINDOW_TITLE | Forces a header at the top at the window showing the title +/// NK_WINDOW_SCROLL_AUTO_HIDE | Automatically hides the window scrollbar if no user interaction: also requires delta time in `nk_context` to be set each frame +/// NK_WINDOW_BACKGROUND | Always keep window in the background +/// NK_WINDOW_SCALE_LEFT | Puts window scaler in the left-ottom corner instead right-bottom +/// NK_WINDOW_NO_INPUT | Prevents window of scaling, moving or getting focus +/// +/// #### nk_collapse_states +/// State | Description +/// ----------------|----------------------------------------------------------- +/// __NK_MINIMIZED__| UI section is collased and not visibile until maximized +/// __NK_MAXIMIZED__| UI section is extended and visibile until minimized +///

+*/ enum nk_panel_flags { - NK_WINDOW_BORDER = NK_FLAG(0), /* Draws a border around the window to visually separate window from the background */ - NK_WINDOW_MOVABLE = NK_FLAG(1), /* The movable flag indicates that a window can be moved by user input or by dragging the window header */ - NK_WINDOW_SCALABLE = NK_FLAG(2), /* The scalable flag indicates that a window can be scaled by user input by dragging a scaler icon at the button of the window */ - NK_WINDOW_CLOSABLE = NK_FLAG(3), /* adds a closable icon into the header */ - NK_WINDOW_MINIMIZABLE = NK_FLAG(4), /* adds a minimize icon into the header */ - NK_WINDOW_NO_SCROLLBAR = NK_FLAG(5), /* Removes the scrollbar from the window */ - NK_WINDOW_TITLE = NK_FLAG(6), /* Forces a header at the top at the window showing the title */ - NK_WINDOW_SCROLL_AUTO_HIDE = NK_FLAG(7), /* Automatically hides the window scrollbar if no user interaction: also requires delta time in `nk_context` to be set each frame */ - NK_WINDOW_BACKGROUND = NK_FLAG(8), /* Always keep window in the background */ - NK_WINDOW_SCALE_LEFT = NK_FLAG(9), /* Puts window scaler in the left-ottom corner instead right-bottom*/ - NK_WINDOW_NO_INPUT = NK_FLAG(10) /* Prevents window of scaling, moving or getting focus */ + NK_WINDOW_BORDER = NK_FLAG(0), + NK_WINDOW_MOVABLE = NK_FLAG(1), + NK_WINDOW_SCALABLE = NK_FLAG(2), + NK_WINDOW_CLOSABLE = NK_FLAG(3), + NK_WINDOW_MINIMIZABLE = NK_FLAG(4), + NK_WINDOW_NO_SCROLLBAR = NK_FLAG(5), + NK_WINDOW_TITLE = NK_FLAG(6), + NK_WINDOW_SCROLL_AUTO_HIDE = NK_FLAG(7), + NK_WINDOW_BACKGROUND = NK_FLAG(8), + NK_WINDOW_SCALE_LEFT = NK_FLAG(9), + NK_WINDOW_NO_INPUT = NK_FLAG(10) }; -/* nk_begin - starts a new window; needs to be called every frame for every window (unless hidden) or otherwise the window gets removed - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @title window title and identifier. Needs to be persistent over frames to identify the window - * @bounds initial position and window size. However if you do not define `NK_WINDOW_SCALABLE` or `NK_WINDOW_MOVABLE` you can set window position and size every frame - * @flags window flags defined in `enum nk_panel_flags` with a number of different window behaviors - * Return values: - * returns 1 if the window can be filled up with widgets from this point until `nk_end or 0 otherwise for example if minimized `*/ +/*/// #### nk_begin +/// Starts a new window; needs to be called every frame for every +/// window (unless hidden) or otherwise the window gets removed +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_begin(struct nk_context *ctx, const char *title, struct nk_rect bounds, nk_flags flags); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __title__ | Window title and identifier. Needs to be persistent over frames to identify the window +/// __bounds__ | Initial position and window size. However if you do not define `NK_WINDOW_SCALABLE` or `NK_WINDOW_MOVABLE` you can set window position and size every frame +/// __flags__ | Window flags defined in the nk_panel_flags section with a number of different window behaviors +/// +/// Returns `true(1)` if the window can be filled up with widgets from this point +/// until `nk_end` or `false(0)` otherwise for example if minimized +*/ NK_API int nk_begin(struct nk_context *ctx, const char *title, struct nk_rect bounds, nk_flags flags); -/* nk_begin_titled - extended window start with separated title and identifier to allow multiple windows with same name but not title - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name window identifier. Needs to be persistent over frames to identify the window - * @title window title displayed inside header if flag `NK_WINDOW_TITLE` or either `NK_WINDOW_CLOSABLE` or `NK_WINDOW_MINIMIZED` was set - * @bounds initial position and window size. However if you do not define `NK_WINDOW_SCALABLE` or `NK_WINDOW_MOVABLE` you can set window position and size every frame - * @flags window flags defined in `enum nk_panel_flags` with a number of different window behaviors - * Return values: - * returns 1 if the window can be filled up with widgets from this point until `nk_end or 0 otherwise `*/ +/*/// #### nk_begin_titled +/// Extended window start with separated title and identifier to allow multiple +/// windows with same title but not name +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, struct nk_rect bounds, nk_flags flags); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Window identifier. Needs to be persistent over frames to identify the window +/// __title__ | Window title displayed inside header if flag `NK_WINDOW_TITLE` or either `NK_WINDOW_CLOSABLE` or `NK_WINDOW_MINIMIZED` was set +/// __bounds__ | Initial position and window size. However if you do not define `NK_WINDOW_SCALABLE` or `NK_WINDOW_MOVABLE` you can set window position and size every frame +/// __flags__ | Window flags defined in the nk_panel_flags section with a number of different window behaviors +/// +/// Returns `true(1)` if the window can be filled up with widgets from this point +/// until `nk_end` or `false(0)` otherwise for example if minimized +*/ NK_API int nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, struct nk_rect bounds, nk_flags flags); -/* nk_end - needs to be called at the end of the window building process to process scaling, scrollbars and general cleanup. - * All widget calls after this functions will result in asserts or no state changes - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct */ +/*/// #### nk_end +/// Needs to be called at the end of the window building process to process scaling, scrollbars and general cleanup. +/// All widget calls after this functions will result in asserts or no state changes +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_end(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +*/ NK_API void nk_end(struct nk_context *ctx); -/* nk_window_find - finds and returns the window with give name - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name window identifier - * Return values: - * returns a `nk_window` struct pointing to the identified window or 0 if no window with given name was found */ +/*/// #### nk_window_find +/// Finds and returns a window from passed name +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_end(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Window identifier +/// +/// Returns a `nk_window` struct pointing to the identified window or NULL if +/// no window with the given name was found +*/ NK_API struct nk_window *nk_window_find(struct nk_context *ctx, const char *name); -/* nk_window_get_bounds - returns a rectangle with screen position and size of the currently processed window. - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns a `nk_rect` struct with window upper left position and size */ +/*/// #### nk_window_get_bounds +/// Returns a rectangle with screen position and size of the currently processed window +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_rect nk_window_get_bounds(const struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns a `nk_rect` struct with window upper left window position and size +*/ NK_API struct nk_rect nk_window_get_bounds(const struct nk_context *ctx); -/* nk_window_get_position - returns the position of the currently processed window. - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns a `nk_vec2` struct with window upper left position */ +/*/// #### nk_window_get_position +/// Returns the position of the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_window_get_position(const struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns a `nk_vec2` struct with window upper left position +*/ NK_API struct nk_vec2 nk_window_get_position(const struct nk_context *ctx); -/* nk_window_get_size - returns the size with width and height of the currently processed window. - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns a `nk_vec2` struct with window size */ +/*/// #### nk_window_get_size +/// Returns the size with width and height of the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_window_get_size(const struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns a `nk_vec2` struct with window width and height +*/ NK_API struct nk_vec2 nk_window_get_size(const struct nk_context*); -/* nk_window_get_width - returns the width of the currently processed window. - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns the window width */ +/*/// #### nk_window_get_width +/// Returns the width of the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// float nk_window_get_width(const struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns the current window width +*/ NK_API float nk_window_get_width(const struct nk_context*); -/* nk_window_get_height - returns the height of the currently processed window. - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns the window height */ +/*/// #### nk_window_get_height +/// Returns the height of the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// float nk_window_get_height(const struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns the current window height +*/ NK_API float nk_window_get_height(const struct nk_context*); -/* nk_window_get_panel - returns the underlying panel which contains all processing state of the current window. - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns a pointer to window internal `nk_panel` state. DO NOT keep this pointer around it is only valid until `nk_end` */ +/*/// #### nk_window_get_panel +/// Returns the underlying panel which contains all processing state of the current window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// !!! WARNING +/// Do not keep the returned panel pointer around, it is only valid until `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_panel* nk_window_get_panel(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns a pointer to window internal `nk_panel` state. +*/ NK_API struct nk_panel* nk_window_get_panel(struct nk_context*); -/* nk_window_get_content_region - returns the position and size of the currently visible and non-clipped space inside the currently processed window. - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns `nk_rect` struct with screen position and size (no scrollbar offset) of the visible space inside the current window */ +/*/// #### nk_window_get_content_region +/// Returns the position and size of the currently visible and non-clipped space +/// inside the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_rect nk_window_get_content_region(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `nk_rect` struct with screen position and size (no scrollbar offset) +/// of the visible space inside the current window +*/ NK_API struct nk_rect nk_window_get_content_region(struct nk_context*); -/* nk_window_get_content_region_min - returns the upper left position of the currently visible and non-clipped space inside the currently processed window. - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns `nk_vec2` struct with upper left screen position (no scrollbar offset) of the visible space inside the current window */ +/*/// #### nk_window_get_content_region_min +/// Returns the upper left position of the currently visible and non-clipped +/// space inside the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_window_get_content_region_min(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// returns `nk_vec2` struct with upper left screen position (no scrollbar offset) +/// of the visible space inside the current window +*/ NK_API struct nk_vec2 nk_window_get_content_region_min(struct nk_context*); -/* nk_window_get_content_region_max - returns the lower right screen position of the currently visible and non-clipped space inside the currently processed window. - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns `nk_vec2` struct with lower right screen position (no scrollbar offset) of the visible space inside the current window */ +/*/// #### nk_window_get_content_region_max +/// Returns the lower right screen position of the currently visible and +/// non-clipped space inside the currently processed window. +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_window_get_content_region_max(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `nk_vec2` struct with lower right screen position (no scrollbar offset) +/// of the visible space inside the current window +*/ NK_API struct nk_vec2 nk_window_get_content_region_max(struct nk_context*); -/* nk_window_get_content_region_size - returns the size of the currently visible and non-clipped space inside the currently processed window - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns `nk_vec2` struct with size the visible space inside the current window */ +/*/// #### nk_window_get_content_region_size +/// Returns the size of the currently visible and non-clipped space inside the +/// currently processed window +/// +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_window_get_content_region_size(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `nk_vec2` struct with size the visible space inside the current window +*/ NK_API struct nk_vec2 nk_window_get_content_region_size(struct nk_context*); -/* nk_window_get_canvas - returns the draw command buffer. Can be used to draw custom widgets - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns a pointer to window internal `nk_command_buffer` struct used as drawing canvas. Can be used to do custom drawing */ +/*/// #### nk_window_get_canvas +/// Returns the draw command buffer. Can be used to draw custom widgets +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// !!! WARNING +/// Do not keep the returned command buffer pointer around it is only valid until `nk_end` +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_command_buffer* nk_window_get_canvas(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns a pointer to window internal `nk_command_buffer` struct used as +/// drawing canvas. Can be used to do custom drawing. +*/ NK_API struct nk_command_buffer* nk_window_get_canvas(struct nk_context*); -/* nk_window_has_focus - returns if the currently processed window is currently active - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns 0 if current window is not active or 1 if it is */ +/*/// #### nk_window_has_focus +/// Returns if the currently processed window is currently active +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_has_focus(const struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `false(0)` if current window is not active or `true(1)` if it is +*/ NK_API int nk_window_has_focus(const struct nk_context*); -/* nk_window_is_collapsed - returns if the window with given name is currently minimized/collapsed - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of window you want to check is collapsed - * Return values: - * returns 1 if current window is minimized and 0 if window not found or is not minimized */ -NK_API int nk_window_is_collapsed(struct nk_context *ctx, const char *name); -/* nk_window_is_closed - returns if the window with given name was closed by calling `nk_close` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of window you want to check is closed - * Return values: - * returns 1 if current window was closed or 0 window not found or not closed */ -NK_API int nk_window_is_closed(struct nk_context*, const char*); -/* nk_window_is_hidden - returns if the window with given name is hidden - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of window you want to check is hidden - * Return values: - * returns 1 if current window is hidden or 0 window not found or visible */ -NK_API int nk_window_is_hidden(struct nk_context*, const char*); -/* nk_window_is_active - same as nk_window_has_focus for some reason - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of window you want to check is hidden - * Return values: - * returns 1 if current window is active or 0 window not found or not active */ -NK_API int nk_window_is_active(struct nk_context*, const char*); -/* nk_window_is_hovered - return if the current window is being hovered - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns 1 if current window is hovered or 0 otherwise */ +/*/// #### nk_window_is_hovered +/// Return if the current window is being hovered +/// !!! WARNING +/// Only call this function between calls `nk_begin_xxx` and `nk_end` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_is_hovered(struct nk_context *ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `true(1)` if current window is hovered or `false(0)` otherwise +*/ NK_API int nk_window_is_hovered(struct nk_context*); -/* nk_window_is_any_hovered - returns if the any window is being hovered - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns 1 if any window is hovered or 0 otherwise */ +/*/// #### nk_window_is_collapsed +/// Returns if the window with given name is currently minimized/collapsed +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_is_collapsed(struct nk_context *ctx, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of window you want to check if it is collapsed +/// +/// Returns `true(1)` if current window is minimized and `false(0)` if window not +/// found or is not minimized +*/ +NK_API int nk_window_is_collapsed(struct nk_context *ctx, const char *name); +/*/// #### nk_window_is_closed +/// Returns if the window with given name was closed by calling `nk_close` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_is_closed(struct nk_context *ctx, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of window you want to check if it is closed +/// +/// Returns `true(1)` if current window was closed or `false(0)` window not found or not closed +*/ +NK_API int nk_window_is_closed(struct nk_context*, const char*); +/*/// #### nk_window_is_hidden +/// Returns if the window with given name is hidden +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_is_hidden(struct nk_context *ctx, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of window you want to check if it is hidden +/// +/// Returns `true(1)` if current window is hidden or `false(0)` window not found or visible +*/ +NK_API int nk_window_is_hidden(struct nk_context*, const char*); +/*/// #### nk_window_is_active +/// Same as nk_window_has_focus for some reason +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_is_active(struct nk_context *ctx, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of window you want to check if it is active +/// +/// Returns `true(1)` if current window is active or `false(0)` window not found or not active +*/ +NK_API int nk_window_is_active(struct nk_context*, const char*); +/*/// #### nk_window_is_any_hovered +/// Returns if the any window is being hovered +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_window_is_any_hovered(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `true(1)` if any window is hovered or `false(0)` otherwise +*/ NK_API int nk_window_is_any_hovered(struct nk_context*); -/* nk_item_is_any_active - returns if the any window is being hovered or any widget is currently active. - * Can be used to decide if input should be processed by UI or your specific input handling. - * Example could be UI and 3D camera to move inside a 3D space. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * Return values: - * returns 1 if any window is hovered or any item is active or 0 otherwise */ +/*/// #### nk_item_is_any_active +/// Returns if the any window is being hovered or any widget is currently active. +/// Can be used to decide if input should be processed by UI or your specific input handling. +/// Example could be UI and 3D camera to move inside a 3D space. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_item_is_any_active(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// +/// Returns `true(1)` if any window is hovered or any item is active or `false(0)` otherwise +*/ NK_API int nk_item_is_any_active(struct nk_context*); -/* nk_window_set_bounds - updates position and size of the currently processed window - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of the window to modify both position and size - * @bounds points to a `nk_rect` struct with the new position and size of currently active window */ +/*/// #### nk_window_set_bounds +/// Updates position and size of window with passed in name +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_set_bounds(struct nk_context*, const char *name, struct nk_rect bounds); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to modify both position and size +/// __bounds__ | Must point to a `nk_rect` struct with the new position and size +*/ NK_API void nk_window_set_bounds(struct nk_context*, const char *name, struct nk_rect bounds); -/* nk_window_set_position - updates position of the currently processed window - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of the window to modify position of - * @pos points to a `nk_vec2` struct with the new position of currently active window */ +/*/// #### nk_window_set_position +/// Updates position of window with passed name +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_set_position(struct nk_context*, const char *name, struct nk_vec2 pos); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to modify both position +/// __pos__ | Must point to a `nk_vec2` struct with the new position +*/ NK_API void nk_window_set_position(struct nk_context*, const char *name, struct nk_vec2 pos); -/* nk_window_set_size - updates size of the currently processed window - * IMPORTANT: only call this function between calls `nk_begin_xxx` and `nk_end` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of the window to modify size of - * @size points to a `nk_vec2` struct with the new size of currently active window */ +/*/// #### nk_window_set_size +/// Updates size of window with passed in name +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_set_size(struct nk_context*, const char *name, struct nk_vec2); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to modify both window size +/// __size__ | Must point to a `nk_vec2` struct with new window size +*/ NK_API void nk_window_set_size(struct nk_context*, const char *name, struct nk_vec2); -/* nk_window_set_focus - sets the window with given name as active - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of the window to be set active */ +/*/// #### nk_window_set_focus +/// Sets the window with given name as active +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_set_focus(struct nk_context*, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to set focus on +*/ NK_API void nk_window_set_focus(struct nk_context*, const char *name); -/* nk_window_close - closed a window and marks it for being freed at the end of the frame - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of the window to be closed */ +/*/// #### nk_window_close +/// Closes a window and marks it for being freed at the end of the frame +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_close(struct nk_context *ctx, const char *name); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to close +*/ NK_API void nk_window_close(struct nk_context *ctx, const char *name); -/* nk_window_collapse - updates collapse state of a window with given name - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of the window to be either collapse or maximize */ +/*/// #### nk_window_collapse +/// Updates collapse state of a window with given name +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_collapse(struct nk_context*, const char *name, enum nk_collapse_states state); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to close +/// __state__ | value out of nk_collapse_states section +*/ NK_API void nk_window_collapse(struct nk_context*, const char *name, enum nk_collapse_states state); -/* nk_window_collapse - updates collapse state of a window with given name if given condition is met - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of the window to be either collapse or maximize - * @state the window should be put into - * @condition that has to be true to actually commit the collapse state change */ +/*/// #### nk_window_collapse_if +/// Updates collapse state of a window with given name if given condition is met +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_collapse_if(struct nk_context*, const char *name, enum nk_collapse_states, int cond); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to either collapse or maximize +/// __state__ | value out of nk_collapse_states section the window should be put into +/// __cond__ | condition that has to be met to actually commit the collapse state change +*/ NK_API void nk_window_collapse_if(struct nk_context*, const char *name, enum nk_collapse_states, int cond); -/* nk_window_show - updates visibility state of a window with given name - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of the window to be either collapse or maximize - * @state with either visible or hidden to modify the window with */ +/*/// #### nk_window_show +/// updates visibility state of a window with given name +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_show(struct nk_context*, const char *name, enum nk_show_states); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to either collapse or maximize +/// __state__ | state with either visible or hidden to modify the window with +*/ NK_API void nk_window_show(struct nk_context*, const char *name, enum nk_show_states); -/* nk_window_show_if - updates visibility state of a window with given name if a given condition is met - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @name of the window to be either collapse or maximize - * @state with either visible or hidden to modify the window with - * @condition that has to be true to actually commit the visible state change */ +/*/// #### nk_window_show_if +/// Updates visibility state of a window with given name if a given condition is met +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __name__ | Identifier of the window to either hide or show +/// __state__ | state with either visible or hidden to modify the window with +/// __cond__ | condition that has to be met to actually commit the visbility state change +*/ NK_API void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond); /* ============================================================================= * * LAYOUT * - * ============================================================================= */ -/* Layouting in general describes placing widget inside a window with position and size. - * While in this particular implementation there are five different APIs for layouting - * each with different trade offs between control and ease of use. - * - * All layouting methods in this library are based around the concept of a row. - * A row has a height the window content grows by and a number of columns and each - * layouting method specifies how each widget is placed inside the row. - * After a row has been allocated by calling a layouting functions and then - * filled with widgets will advance an internal pointer over the allocated row. - * - * To actually define a layout you just call the appropriate layouting function - * and each subsequent widget call will place the widget as specified. Important - * here is that if you define more widgets then columns defined inside the layout - * functions it will allocate the next row without you having to make another layouting - * call. - * - * Biggest limitation with using all these APIs outside the `nk_layout_space_xxx` API - * is that you have to define the row height for each. However the row height - * often depends on the height of the font. - * - * To fix that internally nuklear uses a minimum row height that is set to the - * height plus padding of currently active font and overwrites the row height - * value if zero. - * - * If you manually want to change the minimum row height then - * use nk_layout_set_min_row_height, and use nk_layout_reset_min_row_height to - * reset it back to be derived from font height. - * - * Also if you change the font in nuklear it will automatically change the minimum - * row height for you and. This means if you change the font but still want - * a minimum row height smaller than the font you have to repush your value. - * - * For actually more advanced UI I would even recommend using the `nk_layout_space_xxx` - * layouting method in combination with a cassowary constraint solver (there are - * some versions on github with permissive license model) to take over all control over widget - * layouting yourself. However for quick and dirty layouting using all the other layouting - * functions should be fine. - * - * Usage - * ------------------- - * 1.) nk_layout_row_dynamic - * The easiest layouting function is `nk_layout_row_dynamic`. It provides each - * widgets with same horizontal space inside the row and dynamically grows - * if the owning window grows in width. So the number of columns dictates - * the size of each widget dynamically by formula: - * - * widget_width = (window_width - padding - spacing) * (1/colum_count) - * - * Just like all other layouting APIs if you define more widget than columns this - * library will allocate a new row and keep all layouting parameters previously - * defined. - * - * if (nk_begin_xxx(...) { - * // first row with height: 30 composed of two widgets - * nk_layout_row_dynamic(&ctx, 30, 2); - * nk_widget(...); - * nk_widget(...); - * - * // second row with same parameter as defined above - * nk_widget(...); - * nk_widget(...); - * - * // third row uses 0 for height which will use auto layouting - * nk_layout_row_dynamic(&ctx, 0, 2); - * nk_widget(...); - * nk_widget(...); - * } - * nk_end(...); - * - * 2.) nk_layout_row_static - * Another easy layouting function is `nk_layout_row_static`. It provides each - * widget with same horizontal pixel width inside the row and does not grow - * if the owning window scales smaller or bigger. - * - * if (nk_begin_xxx(...) { - * // first row with height: 30 composed of two widgets with width: 80 - * nk_layout_row_static(&ctx, 30, 80, 2); - * nk_widget(...); - * nk_widget(...); - * - * // second row with same parameter as defined above - * nk_widget(...); - * nk_widget(...); - * - * // third row uses 0 for height which will use auto layouting - * nk_layout_row_static(&ctx, 0, 80, 2); - * nk_widget(...); - * nk_widget(...); - * } - * nk_end(...); - * - * 3.) nk_layout_row_xxx - * A little bit more advanced layouting API are functions `nk_layout_row_begin`, - * `nk_layout_row_push` and `nk_layout_row_end`. They allow to directly - * specify each column pixel or window ratio in a row. It supports either - * directly setting per column pixel width or widget window ratio but not - * both. Furthermore it is a immediate mode API so each value is directly - * pushed before calling a widget. Therefore the layout is not automatically - * repeating like the last two layouting functions. - * - * if (nk_begin_xxx(...) { - * // first row with height: 25 composed of two widgets with width 60 and 40 - * nk_layout_row_begin(ctx, NK_STATIC, 25, 2); - * nk_layout_row_push(ctx, 60); - * nk_widget(...); - * nk_layout_row_push(ctx, 40); - * nk_widget(...); - * nk_layout_row_end(ctx); - * - * // second row with height: 25 composed of two widgets with window ratio 0.25 and 0.75 - * nk_layout_row_begin(ctx, NK_DYNAMIC, 25, 2); - * nk_layout_row_push(ctx, 0.25f); - * nk_widget(...); - * nk_layout_row_push(ctx, 0.75f); - * nk_widget(...); - * nk_layout_row_end(ctx); - * - * // third row with auto generated height: composed of two widgets with window ratio 0.25 and 0.75 - * nk_layout_row_begin(ctx, NK_DYNAMIC, 0, 2); - * nk_layout_row_push(ctx, 0.25f); - * nk_widget(...); - * nk_layout_row_push(ctx, 0.75f); - * nk_widget(...); - * nk_layout_row_end(ctx); - * } - * nk_end(...); - * - * 4.) nk_layout_row - * The array counterpart to API nk_layout_row_xxx is the single nk_layout_row - * functions. Instead of pushing either pixel or window ratio for every widget - * it allows to define it by array. The trade of for less control is that - * `nk_layout_row` is automatically repeating. Otherwise the behavior is the - * same. - * - * if (nk_begin_xxx(...) { - * // two rows with height: 30 composed of two widgets with width 60 and 40 - * const float size[] = {60,40}; - * nk_layout_row(ctx, NK_STATIC, 30, 2, ratio); - * nk_widget(...); - * nk_widget(...); - * nk_widget(...); - * nk_widget(...); - * - * // two rows with height: 30 composed of two widgets with window ratio 0.25 and 0.75 - * const float ratio[] = {0.25, 0.75}; - * nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratio); - * nk_widget(...); - * nk_widget(...); - * nk_widget(...); - * nk_widget(...); - * - * // two rows with auto generated height composed of two widgets with window ratio 0.25 and 0.75 - * const float ratio[] = {0.25, 0.75}; - * nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratio); - * nk_widget(...); - * nk_widget(...); - * nk_widget(...); - * nk_widget(...); - * } - * nk_end(...); - * - * 5.) nk_layout_row_template_xxx - * The most complex and second most flexible API is a simplified flexbox version without - * line wrapping and weights for dynamic widgets. It is an immediate mode API but - * unlike `nk_layout_row_xxx` it has auto repeat behavior and needs to be called - * before calling the templated widgets. - * The row template layout has three different per widget size specifier. The first - * one is the static widget size specifier with fixed widget pixel width. They do - * not grow if the row grows and will always stay the same. The second size - * specifier is nk_layout_row_template_push_variable which defines a - * minimum widget size but it also can grow if more space is available not taken - * by other widgets. Finally there are dynamic widgets which are completely flexible - * and unlike variable widgets can even shrink to zero if not enough space - * is provided. - * - * if (nk_begin_xxx(...) { - * // two rows with height: 30 composed of three widgets - * nk_layout_row_template_begin(ctx, 30); - * nk_layout_row_template_push_dynamic(ctx); - * nk_layout_row_template_push_variable(ctx, 80); - * nk_layout_row_template_push_static(ctx, 80); - * nk_layout_row_template_end(ctx); - * - * nk_widget(...); // dynamic widget can go to zero if not enough space - * nk_widget(...); // variable widget with min 80 pixel but can grow bigger if enough space - * nk_widget(...); // static widget with fixed 80 pixel width - * - * // second row same layout - * nk_widget(...); - * nk_widget(...); - * nk_widget(...); - * } - * nk_end(...); - * - * 6.) nk_layout_space_xxx - * Finally the most flexible API directly allows you to place widgets inside the - * window. The space layout API is an immediate mode API which does not support - * row auto repeat and directly sets position and size of a widget. Position - * and size hereby can be either specified as ratio of allocated space or - * allocated space local position and pixel size. Since this API is quite - * powerful there are a number of utility functions to get the available space - * and convert between local allocated space and screen space. - * - * if (nk_begin_xxx(...) { - * // static row with height: 500 (you can set column count to INT_MAX if you don't want to be bothered) - * nk_layout_space_begin(ctx, NK_STATIC, 500, INT_MAX); - * nk_layout_space_push(ctx, nk_rect(0,0,150,200)); - * nk_widget(...); - * nk_layout_space_push(ctx, nk_rect(200,200,100,200)); - * nk_widget(...); - * nk_layout_space_end(ctx); - * - * // dynamic row with height: 500 (you can set column count to INT_MAX if you don't want to be bothered) - * nk_layout_space_begin(ctx, NK_DYNAMIC, 500, INT_MAX); - * nk_layout_space_push(ctx, nk_rect(0.5,0.5,0.1,0.1)); - * nk_widget(...); - * nk_layout_space_push(ctx, nk_rect(0.7,0.6,0.1,0.1)); - * nk_widget(...); - * } - * nk_end(...); - * - * Reference - * ------------------- - * nk_layout_set_min_row_height - set the currently used minimum row height to a specified value - * nk_layout_reset_min_row_height - resets the currently used minimum row height to font height - * - * nk_layout_widget_bounds - calculates current width a static layout row can fit inside a window - * nk_layout_ratio_from_pixel - utility functions to calculate window ratio from pixel size - * - * nk_layout_row_dynamic - current layout is divided into n same sized growing columns - * nk_layout_row_static - current layout is divided into n same fixed sized columns - * nk_layout_row_begin - starts a new row with given height and number of columns - * nk_layout_row_push - pushes another column with given size or window ratio - * nk_layout_row_end - finished previously started row - * nk_layout_row - specifies row columns in array as either window ratio or size - * - * nk_layout_row_template_begin - begins the row template declaration - * nk_layout_row_template_push_dynamic - adds a dynamic column that dynamically grows and can go to zero if not enough space - * nk_layout_row_template_push_variable - adds a variable column that dynamically grows but does not shrink below specified pixel width - * nk_layout_row_template_push_static - adds a static column that does not grow and will always have the same size - * nk_layout_row_template_end - marks the end of the row template - * - * nk_layout_space_begin - begins a new layouting space that allows to specify each widgets position and size - * nk_layout_space_push - pushes position and size of the next widget in own coordinate space either as pixel or ratio - * nk_layout_space_end - marks the end of the layouting space - * - * nk_layout_space_bounds - callable after nk_layout_space_begin and returns total space allocated - * nk_layout_space_to_screen - converts vector from nk_layout_space coordinate space into screen space - * nk_layout_space_to_local - converts vector from screen space into nk_layout_space coordinates - * nk_layout_space_rect_to_screen - converts rectangle from nk_layout_space coordinate space into screen space - * nk_layout_space_rect_to_local - converts rectangle from screen space into nk_layout_space coordinates - */ -/* nk_layout_set_min_row_height - sets the currently used minimum row height. - * IMPORTANT: The passed height needs to include both your preferred row height - * as well as padding. No internal padding is added. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` - * @height new minimum row height to be used for auto generating the row height */ + * ============================================================================= +/// ### Layouting +/// Layouting in general describes placing widget inside a window with position and size. +/// While in this particular implementation there are five different APIs for layouting +/// each with different trade offs between control and ease of use.

+/// +/// All layouting methods in this library are based around the concept of a row. +/// A row has a height the window content grows by and a number of columns and each +/// layouting method specifies how each widget is placed inside the row. +/// After a row has been allocated by calling a layouting functions and then +/// filled with widgets will advance an internal pointer over the allocated row.

+/// +/// To actually define a layout you just call the appropriate layouting function +/// and each subsequent widget call will place the widget as specified. Important +/// here is that if you define more widgets then columns defined inside the layout +/// functions it will allocate the next row without you having to make another layouting

+/// call. +/// +/// Biggest limitation with using all these APIs outside the `nk_layout_space_xxx` API +/// is that you have to define the row height for each. However the row height +/// often depends on the height of the font.

+/// +/// To fix that internally nuklear uses a minimum row height that is set to the +/// height plus padding of currently active font and overwrites the row height +/// value if zero.

+/// +/// If you manually want to change the minimum row height then +/// use nk_layout_set_min_row_height, and use nk_layout_reset_min_row_height to +/// reset it back to be derived from font height.

+/// +/// Also if you change the font in nuklear it will automatically change the minimum +/// row height for you and. This means if you change the font but still want +/// a minimum row height smaller than the font you have to repush your value.

+/// +/// For actually more advanced UI I would even recommend using the `nk_layout_space_xxx` +/// layouting method in combination with a cassowary constraint solver (there are +/// some versions on github with permissive license model) to take over all control over widget +/// layouting yourself. However for quick and dirty layouting using all the other layouting +/// functions should be fine. +/// +/// #### Usage +/// 1. __nk_layout_row_dynamic__

+/// The easiest layouting function is `nk_layout_row_dynamic`. It provides each +/// widgets with same horizontal space inside the row and dynamically grows +/// if the owning window grows in width. So the number of columns dictates +/// the size of each widget dynamically by formula: +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// widget_width = (window_width - padding - spacing) * (1/colum_count) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Just like all other layouting APIs if you define more widget than columns this +/// library will allocate a new row and keep all layouting parameters previously +/// defined. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // first row with height: 30 composed of two widgets +/// nk_layout_row_dynamic(&ctx, 30, 2); +/// nk_widget(...); +/// nk_widget(...); +/// // +/// // second row with same parameter as defined above +/// nk_widget(...); +/// nk_widget(...); +/// // +/// // third row uses 0 for height which will use auto layouting +/// nk_layout_row_dynamic(&ctx, 0, 2); +/// nk_widget(...); +/// nk_widget(...); +/// } +/// nk_end(...); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// 2. __nk_layout_row_static__

+/// Another easy layouting function is `nk_layout_row_static`. It provides each +/// widget with same horizontal pixel width inside the row and does not grow +/// if the owning window scales smaller or bigger. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // first row with height: 30 composed of two widgets with width: 80 +/// nk_layout_row_static(&ctx, 30, 80, 2); +/// nk_widget(...); +/// nk_widget(...); +/// // +/// // second row with same parameter as defined above +/// nk_widget(...); +/// nk_widget(...); +/// // +/// // third row uses 0 for height which will use auto layouting +/// nk_layout_row_static(&ctx, 0, 80, 2); +/// nk_widget(...); +/// nk_widget(...); +/// } +/// nk_end(...); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// 3. __nk_layout_row_xxx__

+/// A little bit more advanced layouting API are functions `nk_layout_row_begin`, +/// `nk_layout_row_push` and `nk_layout_row_end`. They allow to directly +/// specify each column pixel or window ratio in a row. It supports either +/// directly setting per column pixel width or widget window ratio but not +/// both. Furthermore it is a immediate mode API so each value is directly +/// pushed before calling a widget. Therefore the layout is not automatically +/// repeating like the last two layouting functions. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // first row with height: 25 composed of two widgets with width 60 and 40 +/// nk_layout_row_begin(ctx, NK_STATIC, 25, 2); +/// nk_layout_row_push(ctx, 60); +/// nk_widget(...); +/// nk_layout_row_push(ctx, 40); +/// nk_widget(...); +/// nk_layout_row_end(ctx); +/// // +/// // second row with height: 25 composed of two widgets with window ratio 0.25 and 0.75 +/// nk_layout_row_begin(ctx, NK_DYNAMIC, 25, 2); +/// nk_layout_row_push(ctx, 0.25f); +/// nk_widget(...); +/// nk_layout_row_push(ctx, 0.75f); +/// nk_widget(...); +/// nk_layout_row_end(ctx); +/// // +/// // third row with auto generated height: composed of two widgets with window ratio 0.25 and 0.75 +/// nk_layout_row_begin(ctx, NK_DYNAMIC, 0, 2); +/// nk_layout_row_push(ctx, 0.25f); +/// nk_widget(...); +/// nk_layout_row_push(ctx, 0.75f); +/// nk_widget(...); +/// nk_layout_row_end(ctx); +/// } +/// nk_end(...); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// 4. __nk_layout_row__

+/// The array counterpart to API nk_layout_row_xxx is the single nk_layout_row +/// functions. Instead of pushing either pixel or window ratio for every widget +/// it allows to define it by array. The trade of for less control is that +/// `nk_layout_row` is automatically repeating. Otherwise the behavior is the +/// same. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // two rows with height: 30 composed of two widgets with width 60 and 40 +/// const float size[] = {60,40}; +/// nk_layout_row(ctx, NK_STATIC, 30, 2, ratio); +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// // +/// // two rows with height: 30 composed of two widgets with window ratio 0.25 and 0.75 +/// const float ratio[] = {0.25, 0.75}; +/// nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratio); +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// // +/// // two rows with auto generated height composed of two widgets with window ratio 0.25 and 0.75 +/// const float ratio[] = {0.25, 0.75}; +/// nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratio); +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// } +/// nk_end(...); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// 5. __nk_layout_row_template_xxx__

+/// The most complex and second most flexible API is a simplified flexbox version without +/// line wrapping and weights for dynamic widgets. It is an immediate mode API but +/// unlike `nk_layout_row_xxx` it has auto repeat behavior and needs to be called +/// before calling the templated widgets. +/// The row template layout has three different per widget size specifier. The first +/// one is the `nk_layout_row_template_push_static` with fixed widget pixel width. +/// They do not grow if the row grows and will always stay the same. +/// The second size specifier is `nk_layout_row_template_push_variable` +/// which defines a minimum widget size but it also can grow if more space is available +/// not taken by other widgets. +/// Finally there are dynamic widgets with `nk_layout_row_template_push_dynamic` +/// which are completely flexible and unlike variable widgets can even shrink +/// to zero if not enough space is provided. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // two rows with height: 30 composed of three widgets +/// nk_layout_row_template_begin(ctx, 30); +/// nk_layout_row_template_push_dynamic(ctx); +/// nk_layout_row_template_push_variable(ctx, 80); +/// nk_layout_row_template_push_static(ctx, 80); +/// nk_layout_row_template_end(ctx); +/// // +/// // first row +/// nk_widget(...); // dynamic widget can go to zero if not enough space +/// nk_widget(...); // variable widget with min 80 pixel but can grow bigger if enough space +/// nk_widget(...); // static widget with fixed 80 pixel width +/// // +/// // second row same layout +/// nk_widget(...); +/// nk_widget(...); +/// nk_widget(...); +/// } +/// nk_end(...); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// 6. __nk_layout_space_xxx__

+/// Finally the most flexible API directly allows you to place widgets inside the +/// window. The space layout API is an immediate mode API which does not support +/// row auto repeat and directly sets position and size of a widget. Position +/// and size hereby can be either specified as ratio of allocated space or +/// allocated space local position and pixel size. Since this API is quite +/// powerful there are a number of utility functions to get the available space +/// and convert between local allocated space and screen space. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_begin_xxx(...) { +/// // static row with height: 500 (you can set column count to INT_MAX if you don't want to be bothered) +/// nk_layout_space_begin(ctx, NK_STATIC, 500, INT_MAX); +/// nk_layout_space_push(ctx, nk_rect(0,0,150,200)); +/// nk_widget(...); +/// nk_layout_space_push(ctx, nk_rect(200,200,100,200)); +/// nk_widget(...); +/// nk_layout_space_end(ctx); +/// // +/// // dynamic row with height: 500 (you can set column count to INT_MAX if you don't want to be bothered) +/// nk_layout_space_begin(ctx, NK_DYNAMIC, 500, INT_MAX); +/// nk_layout_space_push(ctx, nk_rect(0.5,0.5,0.1,0.1)); +/// nk_widget(...); +/// nk_layout_space_push(ctx, nk_rect(0.7,0.6,0.1,0.1)); +/// nk_widget(...); +/// } +/// nk_end(...); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// ----------------------------------------|------------------------------------ +/// nk_layout_set_min_row_height | Set the currently used minimum row height to a specified value +/// nk_layout_reset_min_row_height | Resets the currently used minimum row height to font height +/// nk_layout_widget_bounds | Calculates current width a static layout row can fit inside a window +/// nk_layout_ratio_from_pixel | Utility functions to calculate window ratio from pixel size +// +/// nk_layout_row_dynamic | Current layout is divided into n same sized growing columns +/// nk_layout_row_static | Current layout is divided into n same fixed sized columns +/// nk_layout_row_begin | Starts a new row with given height and number of columns +/// nk_layout_row_push | Pushes another column with given size or window ratio +/// nk_layout_row_end | Finished previously started row +/// nk_layout_row | Specifies row columns in array as either window ratio or size +// +/// nk_layout_row_template_begin | Begins the row template declaration +/// nk_layout_row_template_push_dynamic | Adds a dynamic column that dynamically grows and can go to zero if not enough space +/// nk_layout_row_template_push_variable | Adds a variable column that dynamically grows but does not shrink below specified pixel width +/// nk_layout_row_template_push_static | Adds a static column that does not grow and will always have the same size +/// nk_layout_row_template_end | Marks the end of the row template +// +/// nk_layout_space_begin | Begins a new layouting space that allows to specify each widgets position and size +/// nk_layout_space_push | Pushes position and size of the next widget in own coordinate space either as pixel or ratio +/// nk_layout_space_end | Marks the end of the layouting space +// +/// nk_layout_space_bounds | Callable after nk_layout_space_begin and returns total space allocated +/// nk_layout_space_to_screen | Converts vector from nk_layout_space coordinate space into screen space +/// nk_layout_space_to_local | Converts vector from screen space into nk_layout_space coordinates +/// nk_layout_space_rect_to_screen | Converts rectangle from nk_layout_space coordinate space into screen space +/// nk_layout_space_rect_to_local | Converts rectangle from screen space into nk_layout_space coordinates +*/ +/*/// #### nk_layout_set_min_row_height +/// Sets the currently used minimum row height. +/// !!! WARNING +/// The passed height needs to include both your preferred row height +/// as well as padding. No internal padding is added. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_set_min_row_height(struct nk_context*, float height); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __height__ | New minimum row height to be used for auto generating the row height +*/ NK_API void nk_layout_set_min_row_height(struct nk_context*, float height); -/* nk_layout_reset_min_row_height - Reset the currently used minimum row height - * back to font height + text padding + additional padding (style_window.min_row_height_padding) - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` */ +/*/// #### nk_layout_reset_min_row_height +/// Reset the currently used minimum row height back to `font_height + text_padding + padding` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_reset_min_row_height(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +*/ NK_API void nk_layout_reset_min_row_height(struct nk_context*); -/* nk_layout_widget_bounds - returns the width of the next row allocate by one of the layouting functions - * Parameters: - * @ctx must point to an previously initialized `nk_context` */ +/*/// #### nk_layout_widget_bounds +/// Returns the width of the next row allocate by one of the layouting functions +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_rect nk_layout_widget_bounds(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// +/// Return `nk_rect` with both position and size of the next row +*/ NK_API struct nk_rect nk_layout_widget_bounds(struct nk_context*); -/* nk_layout_ratio_from_pixel - utility functions to calculate window ratio from pixel size - * Parameters: - * @ctx must point to an previously initialized `nk_context` - * @pixel_width to convert to window ratio */ +/*/// #### nk_layout_ratio_from_pixel +/// Utility functions to calculate window ratio from pixel size +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// float nk_layout_ratio_from_pixel(struct nk_context*, float pixel_width); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __pixel__ | Pixel_width to convert to window ratio +/// +/// Returns `nk_rect` with both position and size of the next row +*/ NK_API float nk_layout_ratio_from_pixel(struct nk_context*, float pixel_width); -/* nk_layout_row_dynamic - Sets current row layout to share horizontal space - * between @cols number of widgets evenly. Once called all subsequent widget - * calls greater than @cols will allocate a new row with same layout. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` - * @row_height holds height of each widget in row or zero for auto layouting - * @cols number of widget inside row */ +/*/// #### nk_layout_row_dynamic +/// Sets current row layout to share horizontal space +/// between @cols number of widgets evenly. Once called all subsequent widget +/// calls greater than @cols will allocate a new row with same layout. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __height__ | Holds height of each widget in row or zero for auto layouting +/// __columns__ | Number of widget inside row +*/ NK_API void nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols); -/* nk_layout_row_static - Sets current row layout to fill @cols number of widgets - * in row with same @item_width horizontal size. Once called all subsequent widget - * calls greater than @cols will allocate a new row with same layout. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` - * @height holds row height to allocate from panel for widget height - * @item_width holds width of each widget in row - * @cols number of widget inside row */ +/*/// #### nk_layout_row_static +/// Sets current row layout to fill @cols number of widgets +/// in row with same @item_width horizontal size. Once called all subsequent widget +/// calls greater than @cols will allocate a new row with same layout. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __height__ | Holds height of each widget in row or zero for auto layouting +/// __width__ | Holds pixel width of each widget in the row +/// __columns__ | Number of widget inside row +*/ NK_API void nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols); -/* nk_layout_row_begin - Starts a new dynamic or fixed row with given height and columns. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` - * @fmt either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns - * @row_height holds height of each widget in row or zero for auto layouting - * @cols number of widget inside row */ +/*/// #### nk_layout_row_begin +/// Starts a new dynamic or fixed row with given height and columns. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, float row_height, int cols); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __fmt__ | either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns +/// __height__ | holds height of each widget in row or zero for auto layouting +/// __columns__ | Number of widget inside row +*/ NK_API void nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, float row_height, int cols); -/* nk_layout_row_push - Specifies either window ratio or width of a single column - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_begin` - * @value either a window ratio or fixed width depending on @fmt in previous `nk_layout_row_begin` call */ +/*/// #### nk_layout_row_push +/// Specifies either window ratio or width of a single column +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_push(struct nk_context*, float value); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __value__ | either a window ratio or fixed width depending on @fmt in previous `nk_layout_row_begin` call +*/ NK_API void nk_layout_row_push(struct nk_context*, float value); -/* nk_layout_row_end - finished previously started row - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_begin` */ +/*/// #### nk_layout_row_end +/// Finished previously started row +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_end(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +*/ NK_API void nk_layout_row_end(struct nk_context*); -/* nk_layout_row - specifies row columns in array as either window ratio or size - * Parameters: - * @ctx must point to an previously initialized `nk_context` - * @fmt either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns - * @row_height holds height of each widget in row or zero for auto layouting - * @cols number of widget inside row */ +/*/// #### nk_layout_row +/// Specifies row columns in array as either window ratio or size +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row(struct nk_context*, enum nk_layout_format, float height, int cols, const float *ratio); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __fmt__ | Either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns +/// __height__ | Holds height of each widget in row or zero for auto layouting +/// __columns__ | Number of widget inside row +*/ NK_API void nk_layout_row(struct nk_context*, enum nk_layout_format, float height, int cols, const float *ratio); -/* nk_layout_row_template_begin - Begins the row template declaration - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @row_height holds height of each widget in row or zero for auto layouting */ +/*/// #### nk_layout_row_template_begin +/// Begins the row template declaration +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_template_begin(struct nk_context*, float row_height); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __height__ | Holds height of each widget in row or zero for auto layouting +*/ NK_API void nk_layout_row_template_begin(struct nk_context*, float row_height); -/* nk_layout_row_template_push_dynamic - adds a dynamic column that dynamically grows and can go to zero if not enough space - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_template_begin` */ +/*/// #### nk_layout_row_template_push_dynamic +/// Adds a dynamic column that dynamically grows and can go to zero if not enough space +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_template_push_dynamic(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __height__ | Holds height of each widget in row or zero for auto layouting +*/ NK_API void nk_layout_row_template_push_dynamic(struct nk_context*); -/* nk_layout_row_template_push_variable - adds a variable column that dynamically grows but does not shrink below specified pixel width - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_template_begin` - * @min_width holds the minimum pixel width the next column must be */ +/*/// #### nk_layout_row_template_push_variable +/// Adds a variable column that dynamically grows but does not shrink below specified pixel width +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_template_push_variable(struct nk_context*, float min_width); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __width__ | Holds the minimum pixel width the next column must always be +*/ NK_API void nk_layout_row_template_push_variable(struct nk_context*, float min_width); -/* nk_layout_row_template_push_static - adds a static column that does not grow and will always have the same size - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_template_begin` - * @width holds the absolute pixel width value the next column must be */ +/*/// #### nk_layout_row_template_push_static +/// Adds a static column that does not grow and will always have the same size +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_template_push_static(struct nk_context*, float width); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __width__ | Holds the absolute pixel width value the next column must be +*/ NK_API void nk_layout_row_template_push_static(struct nk_context*, float width); -/* nk_layout_row_template_end - marks the end of the row template - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_row_template_begin` */ +/*/// #### nk_layout_row_template_end +/// Marks the end of the row template +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_row_template_end(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +*/ NK_API void nk_layout_row_template_end(struct nk_context*); -/* nk_layout_space_begin - begins a new layouting space that allows to specify each widgets position and size. - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct - * @fmt either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns - * @row_height holds height of each widget in row or zero for auto layouting - * @widget_count number of widgets inside row */ +/*/// #### nk_layout_space_begin +/// Begins a new layouting space that allows to specify each widgets position and size. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_space_begin(struct nk_context*, enum nk_layout_format, float height, int widget_count); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` +/// __fmt__ | Either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns +/// __height__ | Holds height of each widget in row or zero for auto layouting +/// __columns__ | Number of widgets inside row +*/ NK_API void nk_layout_space_begin(struct nk_context*, enum nk_layout_format, float height, int widget_count); -/* nk_layout_space_push - pushes position and size of the next widget in own coordinate space either as pixel or ratio - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` - * @bounds position and size in laoyut space local coordinates */ -NK_API void nk_layout_space_push(struct nk_context*, struct nk_rect); -/* nk_layout_space_end - marks the end of the layout space - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` */ +/*/// #### nk_layout_space_push +/// Pushes position and size of the next widget in own coordinate space either as pixel or ratio +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_space_push(struct nk_context *ctx, struct nk_rect bounds); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +/// __bounds__ | Position and size in laoyut space local coordinates +*/ +NK_API void nk_layout_space_push(struct nk_context*, struct nk_rect bounds); +/*/// #### nk_layout_space_end +/// Marks the end of the layout space +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_layout_space_end(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +*/ NK_API void nk_layout_space_end(struct nk_context*); -/* nk_layout_space_bounds - returns total space allocated for `nk_layout_space` - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` */ +/*/// #### nk_layout_space_bounds +/// Utility function to calculate total space allocated for `nk_layout_space` +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_rect nk_layout_space_bounds(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +/// +/// Returns `nk_rect` holding the total space allocated +*/ NK_API struct nk_rect nk_layout_space_bounds(struct nk_context*); -/* nk_layout_space_to_screen - converts vector from nk_layout_space coordinate space into screen space - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` - * @vec position to convert from layout space into screen coordinate space */ +/*/// #### nk_layout_space_to_screen +/// Converts vector from nk_layout_space coordinate space into screen space +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_layout_space_to_screen(struct nk_context*, struct nk_vec2); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +/// __vec__ | Position to convert from layout space into screen coordinate space +/// +/// Returns transformed `nk_vec2` in screen space coordinates +*/ NK_API struct nk_vec2 nk_layout_space_to_screen(struct nk_context*, struct nk_vec2); -/* nk_layout_space_to_screen - converts vector from layout space into screen space - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` - * @vec position to convert from screen space into layout coordinate space */ +/*/// #### nk_layout_space_to_local +/// Converts vector from layout space into screen space +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_vec2 nk_layout_space_to_local(struct nk_context*, struct nk_vec2); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +/// __vec__ | Position to convert from screen space into layout coordinate space +/// +/// Returns transformed `nk_vec2` in layout space coordinates +*/ NK_API struct nk_vec2 nk_layout_space_to_local(struct nk_context*, struct nk_vec2); -/* nk_layout_space_rect_to_screen - converts rectangle from screen space into layout space - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` - * @bounds rectangle to convert from layout space into screen space */ +/*/// #### nk_layout_space_rect_to_screen +/// Converts rectangle from screen space into layout space +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_rect nk_layout_space_rect_to_screen(struct nk_context*, struct nk_rect); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +/// __bounds__ | Rectangle to convert from layout space into screen space +/// +/// Returns transformed `nk_rect` in screen space coordinates +*/ NK_API struct nk_rect nk_layout_space_rect_to_screen(struct nk_context*, struct nk_rect); -/* nk_layout_space_rect_to_local - converts rectangle from layout space into screen space - * Parameters: - * @ctx must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` - * @bounds rectangle to convert from screen space into layout space */ +/*/// #### nk_layout_space_rect_to_local +/// Converts rectangle from layout space into screen space +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_rect nk_layout_space_rect_to_local(struct nk_context*, struct nk_rect); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` +/// __bounds__ | Rectangle to convert from layout space into screen space +/// +/// Returns transformed `nk_rect` in layout space coordinates +*/ NK_API struct nk_rect nk_layout_space_rect_to_local(struct nk_context*, struct nk_rect); /* ============================================================================= * * GROUP * - * ============================================================================= */ + * ============================================================================= +/// ### Groups +/// Groups are basically windows inside windows. They allow to subdivide space +/// in a window to layout widgets as a group. Almost all more complex widget +/// layouting requirements can be solved using groups and basic layouting +/// fuctionality. Groups just like windows are identified by an unique name and +/// internally keep track of scrollbar offsets by default. However additional +/// versions are provided to directly manage the scrollbar. +/// +/// #### Usage +/// To create a group you have to call one of the three `nk_group_begin_xxx` +/// functions to start group declarations and `nk_group_end` at the end. Furthermore it +/// is required to check the return value of `nk_group_begin_xxx` and only process +/// widgets inside the window if the value is not 0. +/// Nesting groups is possible and even encouraged since many layouting schemes +/// can only be achieved by nesting. Groups, unlike windows, need `nk_group_end` +/// to be only called if the corosponding `nk_group_begin_xxx` call does not return 0: +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_group_begin_xxx(ctx, ...) { +/// // [... widgets ...] +/// nk_group_end(ctx); +/// } +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// In the grand concept groups can be called after starting a window +/// with `nk_begin_xxx` and before calling `nk_end`: +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// // Input +/// Event evt; +/// nk_input_begin(&ctx); +/// while (GetEvent(&evt)) { +/// if (evt.type == MOUSE_MOVE) +/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); +/// else if (evt.type == [...]) { +/// nk_input_xxx(...); +/// } +/// } +/// nk_input_end(&ctx); +/// // +/// // Window +/// if (nk_begin_xxx(...) { +/// // [...widgets...] +/// nk_layout_row_dynamic(...); +/// if (nk_group_begin_xxx(ctx, ...) { +/// //[... widgets ...] +/// nk_group_end(ctx); +/// } +/// } +/// nk_end(ctx); +/// // +/// // Draw +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// switch (cmd->type) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case ...: +/// // [...] +/// } +// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// #### Reference +/// Function | Description +/// --------------------------------|------------------------------------------- +/// nk_group_begin | Start a new group with internal scrollbar handling +/// nk_group_begin_titled | Start a new group with separeted name and title and internal scrollbar handling +/// nk_group_end | Ends a group. Should only be called if nk_group_begin returned non-zero +/// nk_group_scrolled_offset_begin | Start a new group with manual separated handling of scrollbar x- and y-offset +/// nk_group_scrolled_begin | Start a new group with manual scrollbar handling +/// nk_group_scrolled_end | Ends a group with manual scrollbar handling. Should only be called if nk_group_begin returned non-zero +*/ +/*/// #### nk_group_begin +/// Starts a new widget group. Requires a previous layouting function to specify a pos/size. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_group_begin(struct nk_context*, const char *title, nk_flags); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __title__ | Must be an unique identifier for this group that is also used for the group header +/// __flags__ | Window flags defined in the nk_panel_flags section with a number of different group behaviors +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ NK_API int nk_group_begin(struct nk_context*, const char *title, nk_flags); -NK_API int nk_group_scrolled_offset_begin(struct nk_context*, nk_uint *x_offset, nk_uint *y_offset, const char*, nk_flags); -NK_API int nk_group_scrolled_begin(struct nk_context*, struct nk_scroll*, const char *title, nk_flags); -NK_API void nk_group_scrolled_end(struct nk_context*); +/*/// #### nk_group_begin_titled +/// Starts a new widget group. Requires a previous layouting function to specify a pos/size. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_group_begin_titled(struct nk_context*, const char *name, const char *title, nk_flags); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __id__ | Must be an unique identifier for this group +/// __title__ | Group header title +/// __flags__ | Window flags defined in the nk_panel_flags section with a number of different group behaviors +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API int nk_group_begin_titled(struct nk_context*, const char *name, const char *title, nk_flags); +/*/// #### nk_group_end +/// Ends a widget group +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_group_end(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +*/ NK_API void nk_group_end(struct nk_context*); +/*/// #### nk_group_scrolled_offset_begin +/// starts a new widget group. requires a previous layouting function to specify +/// a size. Does not keep track of scrollbar. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_group_scrolled_offset_begin(struct nk_context*, nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __x_offset__| Scrollbar x-offset to offset all widgets inside the group horizontally. +/// __y_offset__| Scrollbar y-offset to offset all widgets inside the group vertically +/// __title__ | Window unique group title used to both identify and display in the group header +/// __flags__ | Window flags from the nk_panel_flags section +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API int nk_group_scrolled_offset_begin(struct nk_context*, nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags); +/*/// #### nk_group_scrolled_begin +/// Starts a new widget group. requires a previous +/// layouting function to specify a size. Does not keep track of scrollbar. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_group_scrolled_begin(struct nk_context*, struct nk_scroll *off, const char *title, nk_flags); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __off__ | Both x- and y- scroll offset. Allows for manual scrollbar control +/// __title__ | Window unique group title used to both identify and display in the group header +/// __flags__ | Window flags from nk_panel_flags section +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API int nk_group_scrolled_begin(struct nk_context*, struct nk_scroll *off, const char *title, nk_flags); +/*/// #### nk_group_scrolled_end +/// Ends a widget group after calling nk_group_scrolled_offset_begin or nk_group_scrolled_begin. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_group_scrolled_end(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +*/ +NK_API void nk_group_scrolled_end(struct nk_context*); +/* ============================================================================= + * + * TREE + * + * ============================================================================= +/// ### Tree +/// Trees represent two different concept. First the concept of a collapsable +/// UI section that can be either in a hidden or visibile state. They allow the UI +/// user to selectively minimize the current set of visible UI to comprehend. +/// The second concept are tree widgets for visual UI representation of trees.

+/// +/// Trees thereby can be nested for tree representations and multiple nested +/// collapsable UI sections. All trees are started by calling of the +/// `nk_tree_xxx_push_tree` functions and ended by calling one of the +/// `nk_tree_xxx_pop_xxx()` functions. Each starting functions takes a title label +/// and optionally an image to be displayed and the initial collapse state from +/// the nk_collapse_states section.

+/// +/// The runtime state of the tree is either stored outside the library by the caller +/// or inside which requires a unique ID. The unique ID can either be generated +/// automatically from `__FILE__` and `__LINE__` with function `nk_tree_push`, +/// by `__FILE__` and a user provided ID generated for example by loop index with +/// function `nk_tree_push_id` or completely provided from outside by user with +/// function `nk_tree_push_hashed`. +/// +/// #### Usage +/// To create a tree you have to call one of the seven `nk_tree_xxx_push_xxx` +/// functions to start a collapsable UI section and `nk_tree_xxx_pop` to mark the +/// end. +/// Each starting function will either return `false(0)` if the tree is collapsed +/// or hidden and therefore does not need to be filled with content or `true(1)` +/// if visible and required to be filled. +/// +/// !!! Note +/// The tree header does not require and layouting function and instead +/// calculates a auto height based on the currently used font size +/// +/// The tree ending functions only need to be called if the tree content is +/// actually visible. So make sure the tree push function is guarded by `if` +/// and the pop call is only taken if the tree is visible. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// if (nk_tree_push(ctx, NK_TREE_TAB, "Tree", NK_MINIMIZED)) { +/// nk_layout_row_dynamic(...); +/// nk_widget(...); +/// nk_tree_pop(ctx); +/// } +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// ----------------------------|------------------------------------------- +/// nk_tree_push | Start a collapsable UI section with internal state management +/// nk_tree_push_id | Start a collapsable UI section with internal state management callable in a look +/// nk_tree_push_hashed | Start a collapsable UI section with internal state management with full control over internal unique ID use to store state +/// nk_tree_image_push | Start a collapsable UI section with image and label header +/// nk_tree_image_push_id | Start a collapsable UI section with image and label header and internal state management callable in a look +/// nk_tree_image_push_hashed | Start a collapsable UI section with image and label header and internal state management with full control over internal unique ID use to store state +/// nk_tree_pop | Ends a collapsable UI section +// +/// nk_tree_state_push | Start a collapsable UI section with external state management +/// nk_tree_state_image_push | Start a collapsable UI section with image and label header and external state management +/// nk_tree_state_pop | Ends a collapsabale UI section +/// +/// #### nk_tree_type +/// Flag | Description +/// ----------------|---------------------------------------- +/// NK_TREE_NODE | Highlighted tree header to mark a collapsable UI section +/// NK_TREE_TAB | Non-highighted tree header closer to tree representations +*/ +/*/// #### nk_tree_push +/// Starts a collapsable UI section with internal state management +/// !!! WARNING +/// To keep track of the runtime tree collapsable state this function uses +/// defines `__FILE__` and `__LINE__` to generate a unique ID. If you want +/// to call this function in a loop please use `nk_tree_push_id` or +/// `nk_tree_push_hashed` instead. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// #define nk_tree_push(ctx, type, title, state) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __title__ | Label printed in the tree header +/// __state__ | Initial tree state value out of nk_collapse_states +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +#define nk_tree_push(ctx, type, title, state) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) +/*/// #### nk_tree_push_id +/// Starts a collapsable UI section with internal state management callable in a look +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// #define nk_tree_push_id(ctx, type, title, state, id) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __title__ | Label printed in the tree header +/// __state__ | Initial tree state value out of nk_collapse_states +/// __id__ | Loop counter index if this function is called in a loop +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +#define nk_tree_push_id(ctx, type, title, state, id) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) +/*/// #### nk_tree_push_hashed +/// Start a collapsable UI section with internal state management with full +/// control over internal unique ID used to store state +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_tree_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __title__ | Label printed in the tree header +/// __state__ | Initial tree state value out of nk_collapse_states +/// __hash__ | Memory block or string to generate the ID from +/// __len__ | Size of passed memory block or string in __hash__ +/// __seed__ | Seeding value if this function is called in a loop or default to `0` +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API int nk_tree_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); +/*/// #### nk_tree_image_push +/// Start a collapsable UI section with image and label header +/// !!! WARNING +/// To keep track of the runtime tree collapsable state this function uses +/// defines `__FILE__` and `__LINE__` to generate a unique ID. If you want +/// to call this function in a loop please use `nk_tree_image_push_id` or +/// `nk_tree_image_push_hashed` instead. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// #define nk_tree_image_push(ctx, type, img, title, state) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __img__ | Image to display inside the header on the left of the label +/// __title__ | Label printed in the tree header +/// __state__ | Initial tree state value out of nk_collapse_states +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +#define nk_tree_image_push(ctx, type, img, title, state) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) +/*/// #### nk_tree_image_push_id +/// Start a collapsable UI section with image and label header and internal state +/// management callable in a look +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// #define nk_tree_image_push_id(ctx, type, img, title, state, id) +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __img__ | Image to display inside the header on the left of the label +/// __title__ | Label printed in the tree header +/// __state__ | Initial tree state value out of nk_collapse_states +/// __id__ | Loop counter index if this function is called in a loop +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +#define nk_tree_image_push_id(ctx, type, img, title, state, id) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) +/*/// #### nk_tree_image_push_hashed +/// Start a collapsable UI section with internal state management with full +/// control over internal unique ID used to store state +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_tree_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __img__ | Image to display inside the header on the left of the label +/// __title__ | Label printed in the tree header +/// __state__ | Initial tree state value out of nk_collapse_states +/// __hash__ | Memory block or string to generate the ID from +/// __len__ | Size of passed memory block or string in __hash__ +/// __seed__ | Seeding value if this function is called in a loop or default to `0` +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API int nk_tree_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); +/*/// #### nk_tree_pop +/// Ends a collapsabale UI section +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_tree_pop(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx` +*/ +NK_API void nk_tree_pop(struct nk_context*); +/*/// #### nk_tree_state_push +/// Start a collapsable UI section with external state management +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_tree_state_push(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states *state); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx` +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __title__ | Label printed in the tree header +/// __state__ | Persistent state to update +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API int nk_tree_state_push(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states *state); +/*/// #### nk_tree_state_image_push +/// Start a collapsable UI section with image and label header and external state management +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_tree_state_image_push(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states *state); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx` +/// __img__ | Image to display inside the header on the left of the label +/// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node +/// __title__ | Label printed in the tree header +/// __state__ | Persistent state to update +/// +/// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise +*/ +NK_API int nk_tree_state_image_push(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states *state); +/*/// #### nk_tree_state_pop +/// Ends a collapsabale UI section +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_tree_state_pop(struct nk_context*); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// ------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx` +*/ +NK_API void nk_tree_state_pop(struct nk_context*); + +#define nk_tree_element_push(ctx, type, title, state, sel) nk_tree_element_push_hashed(ctx, type, title, state, sel, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) +#define nk_tree_element_push_id(ctx, type, title, state, sel, id) nk_tree_element_push_hashed(ctx, type, title, state, sel, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) +NK_API int nk_tree_element_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, int *selected, const char *hash, int len, int seed); +NK_API int nk_tree_element_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, int *selected, const char *hash, int len,int seed); +NK_API void nk_tree_element_pop(struct nk_context*); + /* ============================================================================= * * LIST VIEW @@ -1810,21 +2961,6 @@ struct nk_list_view { }; NK_API int nk_list_view_begin(struct nk_context*, struct nk_list_view *out, const char *id, nk_flags, int row_height, int row_count); NK_API void nk_list_view_end(struct nk_list_view*); -/* ============================================================================= - * - * TREE - * - * ============================================================================= */ -#define nk_tree_push(ctx, type, title, state) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) -#define nk_tree_push_id(ctx, type, title, state, id) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) -NK_API int nk_tree_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); -#define nk_tree_image_push(ctx, type, img, title, state) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) -#define nk_tree_image_push_id(ctx, type, img, title, state, id) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) -NK_API int nk_tree_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); -NK_API void nk_tree_pop(struct nk_context*); -NK_API int nk_tree_state_push(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states *state); -NK_API int nk_tree_state_image_push(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states *state); -NK_API void nk_tree_state_pop(struct nk_context*); /* ============================================================================= * * WIDGET @@ -1883,11 +3019,16 @@ NK_API void nk_label_colored(struct nk_context*, const char*, nk_flags align, st NK_API void nk_label_wrap(struct nk_context*, const char*); NK_API void nk_label_colored_wrap(struct nk_context*, const char*, struct nk_color); NK_API void nk_image(struct nk_context*, struct nk_image); +NK_API void nk_image_color(struct nk_context*, struct nk_image, struct nk_color); #ifdef NK_INCLUDE_STANDARD_VARARGS -NK_API void nk_labelf(struct nk_context*, nk_flags, const char*, ...); -NK_API void nk_labelf_colored(struct nk_context*, nk_flags align, struct nk_color, const char*,...); -NK_API void nk_labelf_wrap(struct nk_context*, const char*,...); -NK_API void nk_labelf_colored_wrap(struct nk_context*, struct nk_color, const char*,...); +NK_API void nk_labelf(struct nk_context*, nk_flags, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(3); +NK_API void nk_labelf_colored(struct nk_context*, nk_flags, struct nk_color, NK_PRINTF_FORMAT_STRING const char*,...) NK_PRINTF_VARARG_FUNC(4); +NK_API void nk_labelf_wrap(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*,...) NK_PRINTF_VARARG_FUNC(2); +NK_API void nk_labelf_colored_wrap(struct nk_context*, struct nk_color, NK_PRINTF_FORMAT_STRING const char*,...) NK_PRINTF_VARARG_FUNC(3); +NK_API void nk_labelfv(struct nk_context*, nk_flags, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(3); +NK_API void nk_labelfv_colored(struct nk_context*, nk_flags, struct nk_color, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(4); +NK_API void nk_labelfv_wrap(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(2); +NK_API void nk_labelfv_colored_wrap(struct nk_context*, struct nk_color, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(3); NK_API void nk_value_bool(struct nk_context*, const char *prefix, int); NK_API void nk_value_int(struct nk_context*, const char *prefix, int); NK_API void nk_value_uint(struct nk_context*, const char *prefix, unsigned int); @@ -1952,10 +3093,16 @@ NK_API int nk_selectable_label(struct nk_context*, const char*, nk_flags align, NK_API int nk_selectable_text(struct nk_context*, const char*, int, nk_flags align, int *value); NK_API int nk_selectable_image_label(struct nk_context*,struct nk_image, const char*, nk_flags align, int *value); NK_API int nk_selectable_image_text(struct nk_context*,struct nk_image, const char*, int, nk_flags align, int *value); +NK_API int nk_selectable_symbol_label(struct nk_context*,enum nk_symbol_type, const char*, nk_flags align, int *value); +NK_API int nk_selectable_symbol_text(struct nk_context*,enum nk_symbol_type, const char*, int, nk_flags align, int *value); + NK_API int nk_select_label(struct nk_context*, const char*, nk_flags align, int value); NK_API int nk_select_text(struct nk_context*, const char*, int, nk_flags align, int value); NK_API int nk_select_image_label(struct nk_context*, struct nk_image,const char*, nk_flags align, int value); NK_API int nk_select_image_text(struct nk_context*, struct nk_image,const char*, int, nk_flags align, int value); +NK_API int nk_select_symbol_label(struct nk_context*,enum nk_symbol_type, const char*, nk_flags align, int value); +NK_API int nk_select_symbol_text(struct nk_context*,enum nk_symbol_type, const char*, int, nk_flags align, int value); + /* ============================================================================= * * SLIDER @@ -1978,18 +3125,215 @@ NK_API nk_size nk_prog(struct nk_context*, nk_size cur, nk_size max, int modifya * COLOR PICKER * * ============================================================================= */ -NK_API struct nk_color nk_color_picker(struct nk_context*, struct nk_color, enum nk_color_format); -NK_API int nk_color_pick(struct nk_context*, struct nk_color*, enum nk_color_format); +NK_API struct nk_colorf nk_color_picker(struct nk_context*, struct nk_colorf, enum nk_color_format); +NK_API int nk_color_pick(struct nk_context*, struct nk_colorf*, enum nk_color_format); /* ============================================================================= * * PROPERTIES * - * ============================================================================= */ + * ============================================================================= +/// ### Properties +/// Properties are the main value modification widgets in Nuklear. Changing a value +/// can be achieved by dragging, adding/removing incremental steps on button click +/// or by directly typing a number. +/// +/// #### Usage +/// Each property requires a unique name for identifaction that is also used for +/// displaying a label. If you want to use the same name multiple times make sure +/// add a '#' before your name. The '#' will not be shown but will generate a +/// unique ID. Each propery also takes in a minimum and maximum value. If you want +/// to make use of the complete number range of a type just use the provided +/// type limits from `limits.h`. For example `INT_MIN` and `INT_MAX` for +/// `nk_property_int` and `nk_propertyi`. In additional each property takes in +/// a increment value that will be added or subtracted if either the increment +/// decrement button is clicked. Finally there is a value for increment per pixel +/// dragged that is added or subtracted from the value. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int value = 0; +/// struct nk_context ctx; +/// nk_init_xxx(&ctx, ...); +/// while (1) { +/// // Input +/// Event evt; +/// nk_input_begin(&ctx); +/// while (GetEvent(&evt)) { +/// if (evt.type == MOUSE_MOVE) +/// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); +/// else if (evt.type == [...]) { +/// nk_input_xxx(...); +/// } +/// } +/// nk_input_end(&ctx); +/// // +/// // Window +/// if (nk_begin_xxx(...) { +/// // Property +/// nk_layout_row_dynamic(...); +/// nk_property_int(ctx, "ID", INT_MIN, &value, INT_MAX, 1, 1); +/// } +/// nk_end(ctx); +/// // +/// // Draw +/// const struct nk_command *cmd = 0; +/// nk_foreach(cmd, &ctx) { +/// switch (cmd->type) { +/// case NK_COMMAND_LINE: +/// your_draw_line_function(...) +/// break; +/// case NK_COMMAND_RECT +/// your_draw_rect_function(...) +/// break; +/// case ...: +/// // [...] +/// } +// nk_clear(&ctx); +/// } +/// nk_free(&ctx); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// #### Reference +/// Function | Description +/// --------------------|------------------------------------------- +/// nk_property_int | Integer property directly modifing a passed in value +/// nk_property_float | Float property directly modifing a passed in value +/// nk_property_double | Double property directly modifing a passed in value +/// nk_propertyi | Integer property returning the modified int value +/// nk_propertyf | Float property returning the modified float value +/// nk_propertyd | Double property returning the modified double value +/// +*/ +/*/// #### nk_property_int +/// Integer property directly modifing a passed in value +/// !!! WARNING +/// To generate a unique property ID using the same label make sure to insert +/// a `#` at the beginning. It will not be shown but guarantees correct behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_property_int(struct nk_context *ctx, const char *name, int min, int *val, int max, int step, float inc_per_pixel); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// --------------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function +/// __name__ | String used both as a label as well as a unique identifier +/// __min__ | Minimum value not allowed to be underflown +/// __val__ | Integer pointer to be modified +/// __max__ | Maximum value not allowed to be overflown +/// __step__ | Increment added and subtracted on increment and decrement button +/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging +*/ NK_API void nk_property_int(struct nk_context*, const char *name, int min, int *val, int max, int step, float inc_per_pixel); +/*/// #### nk_property_float +/// Float property directly modifing a passed in value +/// !!! WARNING +/// To generate a unique property ID using the same label make sure to insert +/// a `#` at the beginning. It will not be shown but guarantees correct behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_property_float(struct nk_context *ctx, const char *name, float min, float *val, float max, float step, float inc_per_pixel); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// --------------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function +/// __name__ | String used both as a label as well as a unique identifier +/// __min__ | Minimum value not allowed to be underflown +/// __val__ | Float pointer to be modified +/// __max__ | Maximum value not allowed to be overflown +/// __step__ | Increment added and subtracted on increment and decrement button +/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging +*/ NK_API void nk_property_float(struct nk_context*, const char *name, float min, float *val, float max, float step, float inc_per_pixel); +/*/// #### nk_property_double +/// Double property directly modifing a passed in value +/// !!! WARNING +/// To generate a unique property ID using the same label make sure to insert +/// a `#` at the beginning. It will not be shown but guarantees correct behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// void nk_property_double(struct nk_context *ctx, const char *name, double min, double *val, double max, double step, double inc_per_pixel); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// --------------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function +/// __name__ | String used both as a label as well as a unique identifier +/// __min__ | Minimum value not allowed to be underflown +/// __val__ | Double pointer to be modified +/// __max__ | Maximum value not allowed to be overflown +/// __step__ | Increment added and subtracted on increment and decrement button +/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging +*/ NK_API void nk_property_double(struct nk_context*, const char *name, double min, double *val, double max, double step, float inc_per_pixel); +/*/// #### nk_propertyi +/// Integer property modifing a passed in value and returning the new value +/// !!! WARNING +/// To generate a unique property ID using the same label make sure to insert +/// a `#` at the beginning. It will not be shown but guarantees correct behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// int nk_propertyi(struct nk_context *ctx, const char *name, int min, int val, int max, int step, float inc_per_pixel); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// --------------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function +/// __name__ | String used both as a label as well as a unique identifier +/// __min__ | Minimum value not allowed to be underflown +/// __val__ | Current integer value to be modified and returned +/// __max__ | Maximum value not allowed to be overflown +/// __step__ | Increment added and subtracted on increment and decrement button +/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging +/// +/// Returns the new modified integer value +*/ NK_API int nk_propertyi(struct nk_context*, const char *name, int min, int val, int max, int step, float inc_per_pixel); +/*/// #### nk_propertyf +/// Float property modifing a passed in value and returning the new value +/// !!! WARNING +/// To generate a unique property ID using the same label make sure to insert +/// a `#` at the beginning. It will not be shown but guarantees correct behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// float nk_propertyf(struct nk_context *ctx, const char *name, float min, float val, float max, float step, float inc_per_pixel); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// --------------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function +/// __name__ | String used both as a label as well as a unique identifier +/// __min__ | Minimum value not allowed to be underflown +/// __val__ | Current float value to be modified and returned +/// __max__ | Maximum value not allowed to be overflown +/// __step__ | Increment added and subtracted on increment and decrement button +/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging +/// +/// Returns the new modified float value +*/ NK_API float nk_propertyf(struct nk_context*, const char *name, float min, float val, float max, float step, float inc_per_pixel); +/*/// #### nk_propertyd +/// Float property modifing a passed in value and returning the new value +/// !!! WARNING +/// To generate a unique property ID using the same label make sure to insert +/// a `#` at the beginning. It will not be shown but guarantees correct behavior. +/// +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c +/// float nk_propertyd(struct nk_context *ctx, const char *name, double min, double val, double max, double step, double inc_per_pixel); +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/// +/// Parameter | Description +/// --------------------|----------------------------------------------------------- +/// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function +/// __name__ | String used both as a label as well as a unique identifier +/// __min__ | Minimum value not allowed to be underflown +/// __val__ | Current double value to be modified and returned +/// __max__ | Maximum value not allowed to be overflown +/// __step__ | Increment added and subtracted on increment and decrement button +/// __inc_per_pixel__ | Value per pixel added or subtracted on dragging +/// +/// Returns the new modified double value +*/ NK_API double nk_propertyd(struct nk_context*, const char *name, double min, double val, double max, double step, float inc_per_pixel); /* ============================================================================= * @@ -2107,7 +3451,8 @@ NK_API void nk_contextual_end(struct nk_context*); * ============================================================================= */ NK_API void nk_tooltip(struct nk_context*, const char*); #ifdef NK_INCLUDE_STANDARD_VARARGS -NK_API void nk_tooltipf(struct nk_context*, const char*, ...); +NK_API void nk_tooltipf(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(2); +NK_API void nk_tooltipfv(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(2); #endif NK_API int nk_tooltip_begin(struct nk_context*, float width); NK_API void nk_tooltip_end(struct nk_context*); @@ -2213,6 +3558,7 @@ NK_API struct nk_color nk_rgb_iv(const int *rgb); NK_API struct nk_color nk_rgb_bv(const nk_byte* rgb); NK_API struct nk_color nk_rgb_f(float r, float g, float b); NK_API struct nk_color nk_rgb_fv(const float *rgb); +NK_API struct nk_color nk_rgb_cf(struct nk_colorf c); NK_API struct nk_color nk_rgb_hex(const char *rgb); NK_API struct nk_color nk_rgba(int r, int g, int b, int a); @@ -2221,8 +3567,14 @@ NK_API struct nk_color nk_rgba_iv(const int *rgba); NK_API struct nk_color nk_rgba_bv(const nk_byte *rgba); NK_API struct nk_color nk_rgba_f(float r, float g, float b, float a); NK_API struct nk_color nk_rgba_fv(const float *rgba); +NK_API struct nk_color nk_rgba_cf(struct nk_colorf c); NK_API struct nk_color nk_rgba_hex(const char *rgb); +NK_API struct nk_colorf nk_hsva_colorf(float h, float s, float v, float a); +NK_API struct nk_colorf nk_hsva_colorfv(float *c); +NK_API void nk_colorf_hsva_f(float *out_h, float *out_s, float *out_v, float *out_a, struct nk_colorf in); +NK_API void nk_colorf_hsva_fv(float *hsva, struct nk_colorf in); + NK_API struct nk_color nk_hsv(int h, int s, int v); NK_API struct nk_color nk_hsv_iv(const int *hsv); NK_API struct nk_color nk_hsv_bv(const nk_byte *hsv); @@ -2238,6 +3590,7 @@ NK_API struct nk_color nk_hsva_fv(const float *hsva); /* color (conversion nuklear --> user) */ NK_API void nk_color_f(float *r, float *g, float *b, float *a, struct nk_color); NK_API void nk_color_fv(float *rgba_out, struct nk_color); +NK_API struct nk_colorf nk_color_cf(struct nk_color); NK_API void nk_color_d(double *r, double *g, double *b, double *a, struct nk_color); NK_API void nk_color_dv(double *rgba_out, struct nk_color); @@ -3325,14 +4678,12 @@ struct nk_draw_list { /* draw list */ NK_API void nk_draw_list_init(struct nk_draw_list*); NK_API void nk_draw_list_setup(struct nk_draw_list*, const struct nk_convert_config*, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, enum nk_anti_aliasing line_aa,enum nk_anti_aliasing shape_aa); -NK_API void nk_draw_list_clear(struct nk_draw_list*); /* drawing */ #define nk_draw_list_foreach(cmd, can, b) for((cmd)=nk__draw_list_begin(can, b); (cmd)!=0; (cmd)=nk__draw_list_next(cmd, b, can)) NK_API const struct nk_draw_command* nk__draw_list_begin(const struct nk_draw_list*, const struct nk_buffer*); NK_API const struct nk_draw_command* nk__draw_list_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_draw_list*); NK_API const struct nk_draw_command* nk__draw_list_end(const struct nk_draw_list*, const struct nk_buffer*); -NK_API void nk_draw_list_clear(struct nk_draw_list *list); /* path */ NK_API void nk_draw_list_path_clear(struct nk_draw_list*); @@ -3821,6 +5172,7 @@ NK_API struct nk_style_item nk_style_item_hide(void); #endif enum nk_panel_type { + NK_PANEL_NONE = 0, NK_PANEL_WINDOW = NK_FLAG(0), NK_PANEL_GROUP = NK_FLAG(1), NK_PANEL_POPUP = NK_FLAG(2), @@ -4250,15 +5602,13 @@ template struct nk_alignof{struct Big {T x; char c;}; enum { #endif #endif /* NK_NUKLEAR_H_ */ -/* - * ============================================================== - * - * IMPLEMENTATION - * - * =============================================================== - */ + + #ifdef NK_IMPLEMENTATION +#ifndef NK_INTERNAL_H +#define NK_INTERNAL_H + #ifndef NK_POOL_DEFAULT_CAPACITY #define NK_POOL_DEFAULT_CAPACITY 16 #endif @@ -4278,9 +5628,6 @@ template struct nk_alignof{struct Big {T x; char c;}; enum { #ifdef NK_INCLUDE_STANDARD_IO #include /* fopen, fclose,... */ #endif -#ifdef NK_INCLUDE_STANDARD_VARARGS -#include /* valist, va_start, va_end, ... */ -#endif #ifndef NK_ASSERT #include #define NK_ASSERT(expr) assert(expr) @@ -4362,13 +5709,244 @@ NK_GLOBAL const struct nk_color nk_white = {255,255,255,255}; NK_GLOBAL const struct nk_color nk_black = {0,0,0,255}; NK_GLOBAL const struct nk_color nk_yellow = {255,255,0,255}; -/* - * ============================================================== +/* widget */ +#define nk_widget_state_reset(s)\ + if ((*(s)) & NK_WIDGET_STATE_MODIFIED)\ + (*(s)) = NK_WIDGET_STATE_INACTIVE|NK_WIDGET_STATE_MODIFIED;\ + else (*(s)) = NK_WIDGET_STATE_INACTIVE; + +/* math */ +NK_LIB float nk_inv_sqrt(float n); +NK_LIB float nk_sqrt(float x); +NK_LIB float nk_sin(float x); +NK_LIB float nk_cos(float x); +NK_LIB nk_uint nk_round_up_pow2(nk_uint v); +NK_LIB struct nk_rect nk_shrink_rect(struct nk_rect r, float amount); +NK_LIB struct nk_rect nk_pad_rect(struct nk_rect r, struct nk_vec2 pad); +NK_LIB void nk_unify(struct nk_rect *clip, const struct nk_rect *a, float x0, float y0, float x1, float y1); +NK_LIB double nk_pow(double x, int n); +NK_LIB int nk_ifloord(double x); +NK_LIB int nk_ifloorf(float x); +NK_LIB int nk_iceilf(float x); +NK_LIB int nk_log10(double n); + +/* util */ +enum {NK_DO_NOT_STOP_ON_NEW_LINE, NK_STOP_ON_NEW_LINE}; +NK_LIB int nk_is_lower(int c); +NK_LIB int nk_is_upper(int c); +NK_LIB int nk_to_upper(int c); +NK_LIB int nk_to_lower(int c); +NK_LIB void* nk_memcopy(void *dst, const void *src, nk_size n); +NK_LIB void nk_memset(void *ptr, int c0, nk_size size); +NK_LIB void nk_zero(void *ptr, nk_size size); +NK_LIB char *nk_itoa(char *s, long n); +NK_LIB int nk_string_float_limit(char *string, int prec); +NK_LIB char *nk_dtoa(char *s, double n); +NK_LIB int nk_text_clamp(const struct nk_user_font *font, const char *text, int text_len, float space, int *glyphs, float *text_width, nk_rune *sep_list, int sep_count); +NK_LIB struct nk_vec2 nk_text_calculate_text_bounds(const struct nk_user_font *font, const char *begin, int byte_len, float row_height, const char **remaining, struct nk_vec2 *out_offset, int *glyphs, int op); +#ifdef NK_INCLUDE_STANDARD_VARARGS +NK_LIB int nk_strfmt(char *buf, int buf_size, const char *fmt, va_list args); +#endif +#ifdef NK_INCLUDE_STANDARD_IO +NK_LIB char *nk_file_load(const char* path, nk_size* siz, struct nk_allocator *alloc); +#endif + +/* buffer */ +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_LIB void* nk_malloc(nk_handle unused, void *old,nk_size size); +NK_LIB void nk_mfree(nk_handle unused, void *ptr); +#endif +NK_LIB void* nk_buffer_align(void *unaligned, nk_size align, nk_size *alignment, enum nk_buffer_allocation_type type); +NK_LIB void* nk_buffer_alloc(struct nk_buffer *b, enum nk_buffer_allocation_type type, nk_size size, nk_size align); +NK_LIB void* nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size); + +/* draw */ +NK_LIB void nk_command_buffer_init(struct nk_command_buffer *cb, struct nk_buffer *b, enum nk_command_clipping clip); +NK_LIB void nk_command_buffer_reset(struct nk_command_buffer *b); +NK_LIB void* nk_command_buffer_push(struct nk_command_buffer* b, enum nk_command_type t, nk_size size); +NK_LIB void nk_draw_symbol(struct nk_command_buffer *out, enum nk_symbol_type type, struct nk_rect content, struct nk_color background, struct nk_color foreground, float border_width, const struct nk_user_font *font); + +/* buffering */ +NK_LIB void nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *b); +NK_LIB void nk_start(struct nk_context *ctx, struct nk_window *win); +NK_LIB void nk_start_popup(struct nk_context *ctx, struct nk_window *win); +NK_LIB void nk_finish_popup(struct nk_context *ctx, struct nk_window*); +NK_LIB void nk_finish_buffer(struct nk_context *ctx, struct nk_command_buffer *b); +NK_LIB void nk_finish(struct nk_context *ctx, struct nk_window *w); +NK_LIB void nk_build(struct nk_context *ctx); + +/* text editor */ +NK_LIB void nk_textedit_clear_state(struct nk_text_edit *state, enum nk_text_edit_type type, nk_plugin_filter filter); +NK_LIB void nk_textedit_click(struct nk_text_edit *state, float x, float y, const struct nk_user_font *font, float row_height); +NK_LIB void nk_textedit_drag(struct nk_text_edit *state, float x, float y, const struct nk_user_font *font, float row_height); +NK_LIB void nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod, const struct nk_user_font *font, float row_height); + +/* window */ +enum nk_window_insert_location { + NK_INSERT_BACK, /* inserts window into the back of list (front of screen) */ + NK_INSERT_FRONT /* inserts window into the front of list (back of screen) */ +}; +NK_LIB void *nk_create_window(struct nk_context *ctx); +NK_LIB void nk_remove_window(struct nk_context*, struct nk_window*); +NK_LIB void nk_free_window(struct nk_context *ctx, struct nk_window *win); +NK_LIB struct nk_window *nk_find_window(struct nk_context *ctx, nk_hash hash, const char *name); +NK_LIB void nk_insert_window(struct nk_context *ctx, struct nk_window *win, enum nk_window_insert_location loc); + +/* pool */ +NK_LIB void nk_pool_init(struct nk_pool *pool, struct nk_allocator *alloc, unsigned int capacity); +NK_LIB void nk_pool_free(struct nk_pool *pool); +NK_LIB void nk_pool_init_fixed(struct nk_pool *pool, void *memory, nk_size size); +NK_LIB struct nk_page_element *nk_pool_alloc(struct nk_pool *pool); + +/* page-element */ +NK_LIB struct nk_page_element* nk_create_page_element(struct nk_context *ctx); +NK_LIB void nk_link_page_element_into_freelist(struct nk_context *ctx, struct nk_page_element *elem); +NK_LIB void nk_free_page_element(struct nk_context *ctx, struct nk_page_element *elem); + +/* table */ +NK_LIB struct nk_table* nk_create_table(struct nk_context *ctx); +NK_LIB void nk_remove_table(struct nk_window *win, struct nk_table *tbl); +NK_LIB void nk_free_table(struct nk_context *ctx, struct nk_table *tbl); +NK_LIB void nk_push_table(struct nk_window *win, struct nk_table *tbl); +NK_LIB nk_uint *nk_add_value(struct nk_context *ctx, struct nk_window *win, nk_hash name, nk_uint value); +NK_LIB nk_uint *nk_find_value(struct nk_window *win, nk_hash name); + +/* panel */ +NK_LIB void *nk_create_panel(struct nk_context *ctx); +NK_LIB void nk_free_panel(struct nk_context*, struct nk_panel *pan); +NK_LIB int nk_panel_has_header(nk_flags flags, const char *title); +NK_LIB struct nk_vec2 nk_panel_get_padding(const struct nk_style *style, enum nk_panel_type type); +NK_LIB float nk_panel_get_border(const struct nk_style *style, nk_flags flags, enum nk_panel_type type); +NK_LIB struct nk_color nk_panel_get_border_color(const struct nk_style *style, enum nk_panel_type type); +NK_LIB int nk_panel_is_sub(enum nk_panel_type type); +NK_LIB int nk_panel_is_nonblock(enum nk_panel_type type); +NK_LIB int nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type panel_type); +NK_LIB void nk_panel_end(struct nk_context *ctx); + +/* layout */ +NK_LIB float nk_layout_row_calculate_usable_space(const struct nk_style *style, enum nk_panel_type type, float total_space, int columns); +NK_LIB void nk_panel_layout(const struct nk_context *ctx, struct nk_window *win, float height, int cols); +NK_LIB void nk_row_layout(struct nk_context *ctx, enum nk_layout_format fmt, float height, int cols, int width); +NK_LIB void nk_panel_alloc_row(const struct nk_context *ctx, struct nk_window *win); +NK_LIB void nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, struct nk_window *win, int modify); +NK_LIB void nk_panel_alloc_space(struct nk_rect *bounds, const struct nk_context *ctx); +NK_LIB void nk_layout_peek(struct nk_rect *bounds, struct nk_context *ctx); + +/* popup */ +NK_LIB int nk_nonblock_begin(struct nk_context *ctx, nk_flags flags, struct nk_rect body, struct nk_rect header, enum nk_panel_type panel_type); + +/* text */ +struct nk_text { + struct nk_vec2 padding; + struct nk_color background; + struct nk_color text; +}; +NK_LIB void nk_widget_text(struct nk_command_buffer *o, struct nk_rect b, const char *string, int len, const struct nk_text *t, nk_flags a, const struct nk_user_font *f); +NK_LIB void nk_widget_text_wrap(struct nk_command_buffer *o, struct nk_rect b, const char *string, int len, const struct nk_text *t, const struct nk_user_font *f); + +/* button */ +NK_LIB int nk_button_behavior(nk_flags *state, struct nk_rect r, const struct nk_input *i, enum nk_button_behavior behavior); +NK_LIB const struct nk_style_item* nk_draw_button(struct nk_command_buffer *out, const struct nk_rect *bounds, nk_flags state, const struct nk_style_button *style); +NK_LIB int nk_do_button(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, const struct nk_style_button *style, const struct nk_input *in, enum nk_button_behavior behavior, struct nk_rect *content); +NK_LIB void nk_draw_button_text(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, const struct nk_style_button *style, const char *txt, int len, nk_flags text_alignment, const struct nk_user_font *font); +NK_LIB int nk_do_button_text(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *string, int len, nk_flags align, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_input *in, const struct nk_user_font *font); +NK_LIB void nk_draw_button_symbol(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, const struct nk_style_button *style, enum nk_symbol_type type, const struct nk_user_font *font); +NK_LIB int nk_do_button_symbol(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, enum nk_symbol_type symbol, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_input *in, const struct nk_user_font *font); +NK_LIB void nk_draw_button_image(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, const struct nk_style_button *style, const struct nk_image *img); +NK_LIB int nk_do_button_image(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, struct nk_image img, enum nk_button_behavior b, const struct nk_style_button *style, const struct nk_input *in); +NK_LIB void nk_draw_button_text_symbol(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *label, const struct nk_rect *symbol, nk_flags state, const struct nk_style_button *style, const char *str, int len, enum nk_symbol_type type, const struct nk_user_font *font); +NK_LIB int nk_do_button_text_symbol(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, enum nk_symbol_type symbol, const char *str, int len, nk_flags align, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_user_font *font, const struct nk_input *in); +NK_LIB void nk_draw_button_text_image(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *label, const struct nk_rect *image, nk_flags state, const struct nk_style_button *style, const char *str, int len, const struct nk_user_font *font, const struct nk_image *img); +NK_LIB int nk_do_button_text_image(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, struct nk_image img, const char* str, int len, nk_flags align, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_user_font *font, const struct nk_input *in); + +/* toggle */ +enum nk_toggle_type { + NK_TOGGLE_CHECK, + NK_TOGGLE_OPTION +}; +NK_LIB int nk_toggle_behavior(const struct nk_input *in, struct nk_rect select, nk_flags *state, int active); +NK_LIB void nk_draw_checkbox(struct nk_command_buffer *out, nk_flags state, const struct nk_style_toggle *style, int active, const struct nk_rect *label, const struct nk_rect *selector, const struct nk_rect *cursors, const char *string, int len, const struct nk_user_font *font); +NK_LIB void nk_draw_option(struct nk_command_buffer *out, nk_flags state, const struct nk_style_toggle *style, int active, const struct nk_rect *label, const struct nk_rect *selector, const struct nk_rect *cursors, const char *string, int len, const struct nk_user_font *font); +NK_LIB int nk_do_toggle(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, int *active, const char *str, int len, enum nk_toggle_type type, const struct nk_style_toggle *style, const struct nk_input *in, const struct nk_user_font *font); + +/* progress */ +NK_LIB nk_size nk_progress_behavior(nk_flags *state, struct nk_input *in, struct nk_rect r, struct nk_rect cursor, nk_size max, nk_size value, int modifiable); +NK_LIB void nk_draw_progress(struct nk_command_buffer *out, nk_flags state, const struct nk_style_progress *style, const struct nk_rect *bounds, const struct nk_rect *scursor, nk_size value, nk_size max); +NK_LIB nk_size nk_do_progress(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, nk_size value, nk_size max, int modifiable, const struct nk_style_progress *style, struct nk_input *in); + +/* slider */ +NK_LIB float nk_slider_behavior(nk_flags *state, struct nk_rect *logical_cursor, struct nk_rect *visual_cursor, struct nk_input *in, struct nk_rect bounds, float slider_min, float slider_max, float slider_value, float slider_step, float slider_steps); +NK_LIB void nk_draw_slider(struct nk_command_buffer *out, nk_flags state, const struct nk_style_slider *style, const struct nk_rect *bounds, const struct nk_rect *visual_cursor, float min, float value, float max); +NK_LIB float nk_do_slider(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, float min, float val, float max, float step, const struct nk_style_slider *style, struct nk_input *in, const struct nk_user_font *font); + +/* scrollbar */ +NK_LIB float nk_scrollbar_behavior(nk_flags *state, struct nk_input *in, int has_scrolling, const struct nk_rect *scroll, const struct nk_rect *cursor, const struct nk_rect *empty0, const struct nk_rect *empty1, float scroll_offset, float target, float scroll_step, enum nk_orientation o); +NK_LIB void nk_draw_scrollbar(struct nk_command_buffer *out, nk_flags state, const struct nk_style_scrollbar *style, const struct nk_rect *bounds, const struct nk_rect *scroll); +NK_LIB float nk_do_scrollbarv(nk_flags *state, struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, float offset, float target, float step, float button_pixel_inc, const struct nk_style_scrollbar *style, struct nk_input *in, const struct nk_user_font *font); +NK_LIB float nk_do_scrollbarh(nk_flags *state, struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, float offset, float target, float step, float button_pixel_inc, const struct nk_style_scrollbar *style, struct nk_input *in, const struct nk_user_font *font); + +/* selectable */ +NK_LIB void nk_draw_selectable(struct nk_command_buffer *out, nk_flags state, const struct nk_style_selectable *style, int active, const struct nk_rect *bounds, const struct nk_rect *icon, const struct nk_image *img, enum nk_symbol_type sym, const char *string, int len, nk_flags align, const struct nk_user_font *font); +NK_LIB int nk_do_selectable(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *str, int len, nk_flags align, int *value, const struct nk_style_selectable *style, const struct nk_input *in, const struct nk_user_font *font); +NK_LIB int nk_do_selectable_image(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *str, int len, nk_flags align, int *value, const struct nk_image *img, const struct nk_style_selectable *style, const struct nk_input *in, const struct nk_user_font *font); + +/* edit */ +NK_LIB void nk_edit_draw_text(struct nk_command_buffer *out, const struct nk_style_edit *style, float pos_x, float pos_y, float x_offset, const char *text, int byte_len, float row_height, const struct nk_user_font *font, struct nk_color background, struct nk_color foreground, int is_selected); +NK_LIB nk_flags nk_do_edit(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, nk_flags flags, nk_plugin_filter filter, struct nk_text_edit *edit, const struct nk_style_edit *style, struct nk_input *in, const struct nk_user_font *font); + +/* color-picker */ +NK_LIB int nk_color_picker_behavior(nk_flags *state, const struct nk_rect *bounds, const struct nk_rect *matrix, const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, struct nk_colorf *color, const struct nk_input *in); +NK_LIB void nk_draw_color_picker(struct nk_command_buffer *o, const struct nk_rect *matrix, const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, struct nk_colorf col); +NK_LIB int nk_do_color_picker(nk_flags *state, struct nk_command_buffer *out, struct nk_colorf *col, enum nk_color_format fmt, struct nk_rect bounds, struct nk_vec2 padding, const struct nk_input *in, const struct nk_user_font *font); + +/* property */ +enum nk_property_status { + NK_PROPERTY_DEFAULT, + NK_PROPERTY_EDIT, + NK_PROPERTY_DRAG +}; +enum nk_property_filter { + NK_FILTER_INT, + NK_FILTER_FLOAT +}; +enum nk_property_kind { + NK_PROPERTY_INT, + NK_PROPERTY_FLOAT, + NK_PROPERTY_DOUBLE +}; +union nk_property { + int i; + float f; + double d; +}; +struct nk_property_variant { + enum nk_property_kind kind; + union nk_property value; + union nk_property min_value; + union nk_property max_value; + union nk_property step; +}; +NK_LIB struct nk_property_variant nk_property_variant_int(int value, int min_value, int max_value, int step); +NK_LIB struct nk_property_variant nk_property_variant_float(float value, float min_value, float max_value, float step); +NK_LIB struct nk_property_variant nk_property_variant_double(double value, double min_value, double max_value, double step); + +NK_LIB void nk_drag_behavior(nk_flags *state, const struct nk_input *in, struct nk_rect drag, struct nk_property_variant *variant, float inc_per_pixel); +NK_LIB void nk_property_behavior(nk_flags *ws, const struct nk_input *in, struct nk_rect property, struct nk_rect label, struct nk_rect edit, struct nk_rect empty, int *state, struct nk_property_variant *variant, float inc_per_pixel); +NK_LIB void nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *style, const struct nk_rect *bounds, const struct nk_rect *label, nk_flags state, const char *name, int len, const struct nk_user_font *font); +NK_LIB void nk_do_property(nk_flags *ws, struct nk_command_buffer *out, struct nk_rect property, const char *name, struct nk_property_variant *variant, float inc_per_pixel, char *buffer, int *len, int *state, int *cursor, int *select_begin, int *select_end, const struct nk_style_property *style, enum nk_property_filter filter, struct nk_input *in, const struct nk_user_font *font, struct nk_text_edit *text_edit, enum nk_button_behavior behavior); +NK_LIB void nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant *variant, float inc_per_pixel, const enum nk_property_filter filter); + +#endif + + + + + +/* =============================================================== * - * MATH + * MATH * - * =============================================================== - */ + * ===============================================================*/ /* Since nuklear is supposed to work on all systems providing floating point math without any dependencies I also had to implement my own math functions for sqrt, sin and cos. Since the actual highly accurate implementations for @@ -4396,26 +5974,24 @@ NK_GLOBAL const struct nk_color nk_yellow = {255,255,0,255}; (it can actually approximate a lot more functions) can be found here: www.lolengine.net/wiki/oss/lolremez */ -NK_INTERN float -nk_inv_sqrt(float number) +NK_LIB float +nk_inv_sqrt(float n) { float x2; const float threehalfs = 1.5f; union {nk_uint i; float f;} conv = {0}; - conv.f = number; - x2 = number * 0.5f; + conv.f = n; + x2 = n * 0.5f; conv.i = 0x5f375A84 - (conv.i >> 1); conv.f = conv.f * (threehalfs - (x2 * conv.f * conv.f)); return conv.f; } - -NK_INTERN float +NK_LIB float nk_sqrt(float x) { return x * nk_inv_sqrt(x); } - -NK_INTERN float +NK_LIB float nk_sin(float x) { NK_STORAGE const float a0 = +1.91059300966915117e-31f; @@ -4428,8 +6004,7 @@ nk_sin(float x) NK_STORAGE const float a7 = +1.38235642404333740e-4f; return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7)))))); } - -NK_INTERN float +NK_LIB float nk_cos(float x) { NK_STORAGE const float a0 = +1.00238601909309722f; @@ -4442,8 +6017,7 @@ nk_cos(float x) NK_STORAGE const float a7 = -5.23022132118824778e-14f; return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7)))))); } - -NK_INTERN nk_uint +NK_LIB nk_uint nk_round_up_pow2(nk_uint v) { v--; @@ -4455,13 +6029,66 @@ nk_round_up_pow2(nk_uint v) v++; return v; } +NK_LIB double +nk_pow(double x, int n) +{ + /* check the sign of n */ + double r = 1; + int plus = n >= 0; + n = (plus) ? n : -n; + while (n > 0) { + if ((n & 1) == 1) + r *= x; + n /= 2; + x *= x; + } + return plus ? r : 1.0 / r; +} +NK_LIB int +nk_ifloord(double x) +{ + x = (double)((int)x - ((x < 0.0) ? 1 : 0)); + return (int)x; +} +NK_LIB int +nk_ifloorf(float x) +{ + x = (float)((int)x - ((x < 0.0f) ? 1 : 0)); + return (int)x; +} +NK_LIB int +nk_iceilf(float x) +{ + if (x >= 0) { + int i = (int)x; + return (x > i) ? i+1: i; + } else { + int t = (int)x; + float r = x - (float)t; + return (r > 0.0f) ? t+1: t; + } +} +NK_LIB int +nk_log10(double n) +{ + int neg; + int ret; + int exp = 0; + neg = (n < 0) ? 1 : 0; + ret = (neg) ? (int)-n : (int)n; + while ((ret / 10) > 0) { + ret /= 10; + exp++; + } + if (neg) exp = -exp; + return exp; +} NK_API struct nk_rect nk_get_null_rect(void) { return nk_null_rect; } - NK_API struct nk_rect nk_rect(float x, float y, float w, float h) { @@ -4470,7 +6097,6 @@ nk_rect(float x, float y, float w, float h) r.w = w; r.h = h; return r; } - NK_API struct nk_rect nk_recti(int x, int y, int w, int h) { @@ -4481,25 +6107,21 @@ nk_recti(int x, int y, int w, int h) r.h = (float)h; return r; } - NK_API struct nk_rect nk_recta(struct nk_vec2 pos, struct nk_vec2 size) { return nk_rect(pos.x, pos.y, size.x, size.y); } - NK_API struct nk_rect nk_rectv(const float *r) { return nk_rect(r[0], r[1], r[2], r[3]); } - NK_API struct nk_rect nk_rectiv(const int *r) { return nk_recti(r[0], r[1], r[2], r[3]); } - NK_API struct nk_vec2 nk_rect_pos(struct nk_rect r) { @@ -4507,7 +6129,6 @@ nk_rect_pos(struct nk_rect r) ret.x = r.x; ret.y = r.y; return ret; } - NK_API struct nk_vec2 nk_rect_size(struct nk_rect r) { @@ -4515,8 +6136,7 @@ nk_rect_size(struct nk_rect r) ret.x = r.w; ret.y = r.h; return ret; } - -NK_INTERN struct nk_rect +NK_LIB struct nk_rect nk_shrink_rect(struct nk_rect r, float amount) { struct nk_rect res; @@ -4528,8 +6148,7 @@ nk_shrink_rect(struct nk_rect r, float amount) res.h = r.h - 2 * amount; return res; } - -NK_INTERN struct nk_rect +NK_LIB struct nk_rect nk_pad_rect(struct nk_rect r, struct nk_vec2 pad) { r.w = NK_MAX(r.w, 2 * pad.x); @@ -4539,7 +6158,6 @@ nk_pad_rect(struct nk_rect r, struct nk_vec2 pad) r.h -= 2 * pad.y; return r; } - NK_API struct nk_vec2 nk_vec2(float x, float y) { @@ -4547,7 +6165,6 @@ nk_vec2(float x, float y) ret.x = x; ret.y = y; return ret; } - NK_API struct nk_vec2 nk_vec2i(int x, int y) { @@ -4556,34 +6173,84 @@ nk_vec2i(int x, int y) ret.y = (float)y; return ret; } - NK_API struct nk_vec2 nk_vec2v(const float *v) { return nk_vec2(v[0], v[1]); } - NK_API struct nk_vec2 nk_vec2iv(const int *v) { return nk_vec2i(v[0], v[1]); } +NK_LIB void +nk_unify(struct nk_rect *clip, const struct nk_rect *a, float x0, float y0, + float x1, float y1) +{ + NK_ASSERT(a); + NK_ASSERT(clip); + clip->x = NK_MAX(a->x, x0); + clip->y = NK_MAX(a->y, y0); + clip->w = NK_MIN(a->x + a->w, x1) - clip->x; + clip->h = NK_MIN(a->y + a->h, y1) - clip->y; + clip->w = NK_MAX(0, clip->w); + clip->h = NK_MAX(0, clip->h); +} -/* - * ============================================================== +NK_API void +nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, + float pad_x, float pad_y, enum nk_heading direction) +{ + float w_half, h_half; + NK_ASSERT(result); + + r.w = NK_MAX(2 * pad_x, r.w); + r.h = NK_MAX(2 * pad_y, r.h); + r.w = r.w - 2 * pad_x; + r.h = r.h - 2 * pad_y; + + r.x = r.x + pad_x; + r.y = r.y + pad_y; + + w_half = r.w / 2.0f; + h_half = r.h / 2.0f; + + if (direction == NK_UP) { + result[0] = nk_vec2(r.x + w_half, r.y); + result[1] = nk_vec2(r.x + r.w, r.y + r.h); + result[2] = nk_vec2(r.x, r.y + r.h); + } else if (direction == NK_RIGHT) { + result[0] = nk_vec2(r.x, r.y); + result[1] = nk_vec2(r.x + r.w, r.y + h_half); + result[2] = nk_vec2(r.x, r.y + r.h); + } else if (direction == NK_DOWN) { + result[0] = nk_vec2(r.x, r.y); + result[1] = nk_vec2(r.x + r.w, r.y); + result[2] = nk_vec2(r.x + w_half, r.y + r.h); + } else { + result[0] = nk_vec2(r.x, r.y + h_half); + result[1] = nk_vec2(r.x + r.w, r.y); + result[2] = nk_vec2(r.x + r.w, r.y + r.h); + } +} + + + + + +/* =============================================================== * - * UTIL + * UTIL * - * =============================================================== - */ + * ===============================================================*/ NK_INTERN int nk_str_match_here(const char *regexp, const char *text); NK_INTERN int nk_str_match_star(int c, const char *regexp, const char *text); -NK_INTERN int nk_is_lower(int c) {return (c >= 'a' && c <= 'z') || (c >= 0xE0 && c <= 0xFF);} -NK_INTERN int nk_is_upper(int c){return (c >= 'A' && c <= 'Z') || (c >= 0xC0 && c <= 0xDF);} -NK_INTERN int nk_to_upper(int c) {return (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;} -NK_INTERN int nk_to_lower(int c) {return (c >= 'A' && c <= 'Z') ? (c - ('a' + 'A')) : c;} +NK_LIB int nk_is_lower(int c) {return (c >= 'a' && c <= 'z') || (c >= 0xE0 && c <= 0xFF);} +NK_LIB int nk_is_upper(int c){return (c >= 'A' && c <= 'Z') || (c >= 0xC0 && c <= 0xDF);} +NK_LIB int nk_to_upper(int c) {return (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;} +NK_LIB int nk_to_lower(int c) {return (c >= 'A' && c <= 'Z') ? (c - ('a' + 'A')) : c;} -NK_INTERN void* +NK_LIB void* nk_memcopy(void *dst0, const void *src0, nk_size length) { nk_ptr t; @@ -4639,8 +6306,7 @@ nk_memcopy(void *dst0, const void *src0, nk_size length) done: return (dst0); } - -NK_INTERN void +NK_LIB void nk_memset(void *ptr, int c0, nk_size size) { #define nk_word unsigned @@ -4691,14 +6357,12 @@ nk_memset(void *ptr, int c0, nk_size size) #undef nk_wsize #undef nk_wmask } - -NK_INTERN void +NK_LIB void nk_zero(void *ptr, nk_size size) { NK_ASSERT(ptr); NK_MEMSET(ptr, 0, size); } - NK_API int nk_strlen(const char *str) { @@ -4707,7 +6371,6 @@ nk_strlen(const char *str) while (str && *str++ != '\0') siz++; return siz; } - NK_API int nk_strtoi(const char *str, const char **endptr) { @@ -4732,7 +6395,6 @@ nk_strtoi(const char *str, const char **endptr) *endptr = p; return neg*value; } - NK_API double nk_strtod(const char *str, const char **endptr) { @@ -4790,7 +6452,6 @@ nk_strtod(const char *str, const char **endptr) *endptr = p; return number; } - NK_API float nk_strtof(const char *str, const char **endptr) { @@ -4800,7 +6461,6 @@ nk_strtof(const char *str, const char **endptr) float_value = (float)double_value; return float_value; } - NK_API int nk_stricmp(const char *s1, const char *s2) { @@ -4823,7 +6483,6 @@ nk_stricmp(const char *s1, const char *s2) } while (c1); return 0; } - NK_API int nk_stricmpn(const char *s1, const char *s2, int n) { @@ -4849,7 +6508,6 @@ nk_stricmpn(const char *s1, const char *s2, int n) } while (c1); return 0; } - NK_INTERN int nk_str_match_here(const char *regexp, const char *text) { @@ -4863,7 +6521,6 @@ nk_str_match_here(const char *regexp, const char *text) return nk_str_match_here(regexp+1, text+1); return 0; } - NK_INTERN int nk_str_match_star(int c, const char *regexp, const char *text) { @@ -4873,7 +6530,6 @@ nk_str_match_star(int c, const char *regexp, const char *text) } while (*text != '\0' && (*text++ == c || c == '.')); return 0; } - NK_API int nk_strfilter(const char *text, const char *regexp) { @@ -4891,7 +6547,6 @@ nk_strfilter(const char *text, const char *regexp) } while (*text++ != '\0'); return 0; } - NK_API int nk_strmatch_fuzzy_text(const char *str, int str_len, const char *pattern, int *out_score) @@ -4900,7 +6555,6 @@ nk_strmatch_fuzzy_text(const char *str, int str_len, * if found then out_score is also set. Score value has no intrinsic meaning. * Range varies with pattern. Can only compare scores with same search pattern. */ - /* ------- scores --------- */ /* bonus for adjacent matches */ #define NK_ADJACENCY_BONUS 5 /* bonus if match occurs after a separator */ @@ -5014,12 +6668,12 @@ nk_strmatch_fuzzy_text(const char *str, int str_len, *out_score = score; return nk_true; } - NK_API int nk_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score) -{return nk_strmatch_fuzzy_text(str, nk_strlen(str), pattern, out_score);} - -NK_INTERN int +{ + return nk_strmatch_fuzzy_text(str, nk_strlen(str), pattern, out_score); +} +NK_LIB int nk_string_float_limit(char *string, int prec) { int dot = 0; @@ -5039,67 +6693,6 @@ nk_string_float_limit(char *string, int prec) } return (int)(c - string); } - -NK_INTERN double -nk_pow(double x, int n) -{ - /* check the sign of n */ - double r = 1; - int plus = n >= 0; - n = (plus) ? n : -n; - while (n > 0) { - if ((n & 1) == 1) - r *= x; - n /= 2; - x *= x; - } - return plus ? r : 1.0 / r; -} - -NK_INTERN int -nk_ifloord(double x) -{ - x = (double)((int)x - ((x < 0.0) ? 1 : 0)); - return (int)x; -} - -NK_INTERN int -nk_ifloorf(float x) -{ - x = (float)((int)x - ((x < 0.0f) ? 1 : 0)); - return (int)x; -} - -NK_INTERN int -nk_iceilf(float x) -{ - if (x >= 0) { - int i = (int)x; - return (x > i) ? i+1: i; - } else { - int t = (int)x; - float r = x - (float)t; - return (r > 0.0f) ? t+1: t; - } -} - -NK_INTERN int -nk_log10(double n) -{ - int neg; - int ret; - int exp = 0; - - neg = (n < 0) ? 1 : 0; - ret = (neg) ? (int)-n : (int)n; - while ((ret / 10) > 0) { - ret /= 10; - exp++; - } - if (neg) exp = -exp; - return exp; -} - NK_INTERN void nk_strrev_ascii(char *s) { @@ -5113,8 +6706,7 @@ nk_strrev_ascii(char *s) s[len -1 - i] = t; } } - -NK_INTERN char* +NK_LIB char* nk_itoa(char *s, long n) { long i = 0; @@ -5138,8 +6730,7 @@ nk_itoa(char *s, long n) nk_strrev_ascii(s); return s; } - -NK_INTERN char* +NK_LIB char* nk_dtoa(char *s, double n) { int useExp = 0; @@ -5217,10 +6808,9 @@ nk_dtoa(char *s, double n) *(c) = '\0'; return s; } - #ifdef NK_INCLUDE_STANDARD_VARARGS #ifndef NK_INCLUDE_STANDARD_IO -static int +NK_INTERN int nk_vsnprintf(char *buf, int buf_size, const char *fmt, va_list args) { enum nk_arg_type { @@ -5539,8 +7129,7 @@ nk_vsnprintf(char *buf, int buf_size, const char *fmt, va_list args) return result; } #endif - -NK_INTERN int +NK_LIB int nk_strfmt(char *buf, int buf_size, const char *fmt, va_list args) { int result = -1; @@ -5557,7 +7146,6 @@ nk_strfmt(char *buf, int buf_size, const char *fmt, va_list args) return result; } #endif - NK_API nk_hash nk_murmur_hash(const void * key, int len, nk_hash seed) { @@ -5593,13 +7181,14 @@ nk_murmur_hash(const void * key, int len, nk_hash seed) tail = (const nk_byte*)(data + nblocks*4); k1 = 0; switch (len & 3) { - case 3: k1 ^= (nk_uint)(tail[2] << 16); - case 2: k1 ^= (nk_uint)(tail[1] << 8u); + case 3: k1 ^= (nk_uint)(tail[2] << 16); /* fallthrough */ + case 2: k1 ^= (nk_uint)(tail[1] << 8u); /* fallthrough */ case 1: k1 ^= tail[0]; k1 *= c1; k1 = NK_ROTL(k1,15); k1 *= c2; h1 ^= k1; + break; default: break; } @@ -5615,9 +7204,8 @@ nk_murmur_hash(const void * key, int len, nk_hash seed) #undef NK_ROTL return h1; } - #ifdef NK_INCLUDE_STANDARD_IO -NK_INTERN char* +NK_LIB char* nk_file_load(const char* path, nk_size* siz, struct nk_allocator *alloc) { char *buf; @@ -5651,593 +7239,7 @@ nk_file_load(const char* path, nk_size* siz, struct nk_allocator *alloc) return buf; } #endif - -/* - * ============================================================== - * - * COLOR - * - * =============================================================== - */ -NK_INTERN int -nk_parse_hex(const char *p, int length) -{ - int i = 0; - int len = 0; - while (len < length) { - i <<= 4; - if (p[len] >= 'a' && p[len] <= 'f') - i += ((p[len] - 'a') + 10); - else if (p[len] >= 'A' && p[len] <= 'F') - i += ((p[len] - 'A') + 10); - else i += (p[len] - '0'); - len++; - } - return i; -} - -NK_API struct nk_color -nk_rgba(int r, int g, int b, int a) -{ - struct nk_color ret; - ret.r = (nk_byte)NK_CLAMP(0, r, 255); - ret.g = (nk_byte)NK_CLAMP(0, g, 255); - ret.b = (nk_byte)NK_CLAMP(0, b, 255); - ret.a = (nk_byte)NK_CLAMP(0, a, 255); - return ret; -} - -NK_API struct nk_color -nk_rgb_hex(const char *rgb) -{ - struct nk_color col; - const char *c = rgb; - if (*c == '#') c++; - col.r = (nk_byte)nk_parse_hex(c, 2); - col.g = (nk_byte)nk_parse_hex(c+2, 2); - col.b = (nk_byte)nk_parse_hex(c+4, 2); - col.a = 255; - return col; -} - -NK_API struct nk_color -nk_rgba_hex(const char *rgb) -{ - struct nk_color col; - const char *c = rgb; - if (*c == '#') c++; - col.r = (nk_byte)nk_parse_hex(c, 2); - col.g = (nk_byte)nk_parse_hex(c+2, 2); - col.b = (nk_byte)nk_parse_hex(c+4, 2); - col.a = (nk_byte)nk_parse_hex(c+6, 2); - return col; -} - -NK_API void -nk_color_hex_rgba(char *output, struct nk_color col) -{ - #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) - output[0] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); - output[1] = (char)NK_TO_HEX((col.r & 0x0F)); - output[2] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); - output[3] = (char)NK_TO_HEX((col.g & 0x0F)); - output[4] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); - output[5] = (char)NK_TO_HEX((col.b & 0x0F)); - output[6] = (char)NK_TO_HEX((col.a & 0xF0) >> 4); - output[7] = (char)NK_TO_HEX((col.a & 0x0F)); - output[8] = '\0'; - #undef NK_TO_HEX -} - -NK_API void -nk_color_hex_rgb(char *output, struct nk_color col) -{ - #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) - output[0] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); - output[1] = (char)NK_TO_HEX((col.r & 0x0F)); - output[2] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); - output[3] = (char)NK_TO_HEX((col.g & 0x0F)); - output[4] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); - output[5] = (char)NK_TO_HEX((col.b & 0x0F)); - output[6] = '\0'; - #undef NK_TO_HEX -} - -NK_API struct nk_color -nk_rgba_iv(const int *c) -{ - return nk_rgba(c[0], c[1], c[2], c[3]); -} - -NK_API struct nk_color -nk_rgba_bv(const nk_byte *c) -{ - return nk_rgba(c[0], c[1], c[2], c[3]); -} - -NK_API struct nk_color -nk_rgb(int r, int g, int b) -{ - struct nk_color ret; - ret.r = (nk_byte)NK_CLAMP(0, r, 255); - ret.g = (nk_byte)NK_CLAMP(0, g, 255); - ret.b = (nk_byte)NK_CLAMP(0, b, 255); - ret.a = (nk_byte)255; - return ret; -} - -NK_API struct nk_color -nk_rgb_iv(const int *c) -{ - return nk_rgb(c[0], c[1], c[2]); -} - -NK_API struct nk_color -nk_rgb_bv(const nk_byte* c) -{ - return nk_rgb(c[0], c[1], c[2]); -} - -NK_API struct nk_color -nk_rgba_u32(nk_uint in) -{ - struct nk_color ret; - ret.r = (in & 0xFF); - ret.g = ((in >> 8) & 0xFF); - ret.b = ((in >> 16) & 0xFF); - ret.a = (nk_byte)((in >> 24) & 0xFF); - return ret; -} - -NK_API struct nk_color -nk_rgba_f(float r, float g, float b, float a) -{ - struct nk_color ret; - ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); - ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); - ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); - ret.a = (nk_byte)(NK_SATURATE(a) * 255.0f); - return ret; -} - -NK_API struct nk_color -nk_rgba_fv(const float *c) -{ - return nk_rgba_f(c[0], c[1], c[2], c[3]); -} - -NK_API struct nk_color -nk_rgb_f(float r, float g, float b) -{ - struct nk_color ret; - ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); - ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); - ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); - ret.a = 255; - return ret; -} - -NK_API struct nk_color -nk_rgb_fv(const float *c) -{ - return nk_rgb_f(c[0], c[1], c[2]); -} - -NK_API struct nk_color -nk_hsv(int h, int s, int v) -{ - return nk_hsva(h, s, v, 255); -} - -NK_API struct nk_color -nk_hsv_iv(const int *c) -{ - return nk_hsv(c[0], c[1], c[2]); -} - -NK_API struct nk_color -nk_hsv_bv(const nk_byte *c) -{ - return nk_hsv(c[0], c[1], c[2]); -} - -NK_API struct nk_color -nk_hsv_f(float h, float s, float v) -{ - return nk_hsva_f(h, s, v, 1.0f); -} - -NK_API struct nk_color -nk_hsv_fv(const float *c) -{ - return nk_hsv_f(c[0], c[1], c[2]); -} - -NK_API struct nk_color -nk_hsva(int h, int s, int v, int a) -{ - float hf = ((float)NK_CLAMP(0, h, 255)) / 255.0f; - float sf = ((float)NK_CLAMP(0, s, 255)) / 255.0f; - float vf = ((float)NK_CLAMP(0, v, 255)) / 255.0f; - float af = ((float)NK_CLAMP(0, a, 255)) / 255.0f; - return nk_hsva_f(hf, sf, vf, af); -} - -NK_API struct nk_color -nk_hsva_iv(const int *c) -{ - return nk_hsva(c[0], c[1], c[2], c[3]); -} - -NK_API struct nk_color -nk_hsva_bv(const nk_byte *c) -{ - return nk_hsva(c[0], c[1], c[2], c[3]); -} - -NK_API struct nk_color -nk_hsva_f(float h, float s, float v, float a) -{ - struct nk_colorf out = {0,0,0,0}; - float p, q, t, f; - int i; - - if (s <= 0.0f) { - out.r = v; out.g = v; out.b = v; - return nk_rgb_f(out.r, out.g, out.b); - } - - h = h / (60.0f/360.0f); - i = (int)h; - f = h - (float)i; - p = v * (1.0f - s); - q = v * (1.0f - (s * f)); - t = v * (1.0f - s * (1.0f - f)); - - switch (i) { - case 0: default: out.r = v; out.g = t; out.b = p; break; - case 1: out.r = q; out.g = v; out.b = p; break; - case 2: out.r = p; out.g = v; out.b = t; break; - case 3: out.r = p; out.g = q; out.b = v; break; - case 4: out.r = t; out.g = p; out.b = v; break; - case 5: out.r = v; out.g = p; out.b = q; break; - } - return nk_rgba_f(out.r, out.g, out.b, a); -} - -NK_API struct nk_color -nk_hsva_fv(const float *c) -{ - return nk_hsva_f(c[0], c[1], c[2], c[3]); -} - -NK_API nk_uint -nk_color_u32(struct nk_color in) -{ - nk_uint out = (nk_uint)in.r; - out |= ((nk_uint)in.g << 8); - out |= ((nk_uint)in.b << 16); - out |= ((nk_uint)in.a << 24); - return out; -} - -NK_API void -nk_color_f(float *r, float *g, float *b, float *a, struct nk_color in) -{ - NK_STORAGE const float s = 1.0f/255.0f; - *r = (float)in.r * s; - *g = (float)in.g * s; - *b = (float)in.b * s; - *a = (float)in.a * s; -} - -NK_API void -nk_color_fv(float *c, struct nk_color in) -{ - nk_color_f(&c[0], &c[1], &c[2], &c[3], in); -} - -NK_API void -nk_color_d(double *r, double *g, double *b, double *a, struct nk_color in) -{ - NK_STORAGE const double s = 1.0/255.0; - *r = (double)in.r * s; - *g = (double)in.g * s; - *b = (double)in.b * s; - *a = (double)in.a * s; -} - -NK_API void -nk_color_dv(double *c, struct nk_color in) -{ - nk_color_d(&c[0], &c[1], &c[2], &c[3], in); -} - -NK_API void -nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color in) -{ - float a; - nk_color_hsva_f(out_h, out_s, out_v, &a, in); -} - -NK_API void -nk_color_hsv_fv(float *out, struct nk_color in) -{ - float a; - nk_color_hsva_f(&out[0], &out[1], &out[2], &a, in); -} - -NK_API void -nk_color_hsva_f(float *out_h, float *out_s, - float *out_v, float *out_a, struct nk_color in) -{ - float chroma; - float K = 0.0f; - float r,g,b,a; - - nk_color_f(&r,&g,&b,&a, in); - if (g < b) { - const float t = g; g = b; b = t; - K = -1.f; - } - if (r < g) { - const float t = r; r = g; g = t; - K = -2.f/6.0f - K; - } - chroma = r - ((g < b) ? g: b); - *out_h = NK_ABS(K + (g - b)/(6.0f * chroma + 1e-20f)); - *out_s = chroma / (r + 1e-20f); - *out_v = r; - *out_a = (float)in.a / 255.0f; -} - -NK_API void -nk_color_hsva_fv(float *out, struct nk_color in) -{ - nk_color_hsva_f(&out[0], &out[1], &out[2], &out[3], in); -} - -NK_API void -nk_color_hsva_i(int *out_h, int *out_s, int *out_v, - int *out_a, struct nk_color in) -{ - float h,s,v,a; - nk_color_hsva_f(&h, &s, &v, &a, in); - *out_h = (nk_byte)(h * 255.0f); - *out_s = (nk_byte)(s * 255.0f); - *out_v = (nk_byte)(v * 255.0f); - *out_a = (nk_byte)(a * 255.0f); -} - -NK_API void -nk_color_hsva_iv(int *out, struct nk_color in) -{ - nk_color_hsva_i(&out[0], &out[1], &out[2], &out[3], in); -} - -NK_API void -nk_color_hsva_bv(nk_byte *out, struct nk_color in) -{ - int tmp[4]; - nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); - out[0] = (nk_byte)tmp[0]; - out[1] = (nk_byte)tmp[1]; - out[2] = (nk_byte)tmp[2]; - out[3] = (nk_byte)tmp[3]; -} - -NK_API void -nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color in) -{ - int tmp[4]; - nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); - *h = (nk_byte)tmp[0]; - *s = (nk_byte)tmp[1]; - *v = (nk_byte)tmp[2]; - *a = (nk_byte)tmp[3]; -} - -NK_API void -nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color in) -{ - int a; - nk_color_hsva_i(out_h, out_s, out_v, &a, in); -} - -NK_API void -nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color in) -{ - int tmp[4]; - nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); - *out_h = (nk_byte)tmp[0]; - *out_s = (nk_byte)tmp[1]; - *out_v = (nk_byte)tmp[2]; -} - -NK_API void -nk_color_hsv_iv(int *out, struct nk_color in) -{ - nk_color_hsv_i(&out[0], &out[1], &out[2], in); -} - -NK_API void -nk_color_hsv_bv(nk_byte *out, struct nk_color in) -{ - int tmp[4]; - nk_color_hsv_i(&tmp[0], &tmp[1], &tmp[2], in); - out[0] = (nk_byte)tmp[0]; - out[1] = (nk_byte)tmp[1]; - out[2] = (nk_byte)tmp[2]; -} -/* - * ============================================================== - * - * IMAGE - * - * =============================================================== - */ -NK_API nk_handle -nk_handle_ptr(void *ptr) -{ - nk_handle handle = {0}; - handle.ptr = ptr; - return handle; -} - -NK_API nk_handle -nk_handle_id(int id) -{ - nk_handle handle; - nk_zero_struct(handle); - handle.id = id; - return handle; -} - -NK_API struct nk_image -nk_subimage_ptr(void *ptr, unsigned short w, unsigned short h, struct nk_rect r) -{ - struct nk_image s; - nk_zero(&s, sizeof(s)); - s.handle.ptr = ptr; - s.w = w; s.h = h; - s.region[0] = (unsigned short)r.x; - s.region[1] = (unsigned short)r.y; - s.region[2] = (unsigned short)r.w; - s.region[3] = (unsigned short)r.h; - return s; -} - -NK_API struct nk_image -nk_subimage_id(int id, unsigned short w, unsigned short h, struct nk_rect r) -{ - struct nk_image s; - nk_zero(&s, sizeof(s)); - s.handle.id = id; - s.w = w; s.h = h; - s.region[0] = (unsigned short)r.x; - s.region[1] = (unsigned short)r.y; - s.region[2] = (unsigned short)r.w; - s.region[3] = (unsigned short)r.h; - return s; -} - -NK_API struct nk_image -nk_subimage_handle(nk_handle handle, unsigned short w, unsigned short h, - struct nk_rect r) -{ - struct nk_image s; - nk_zero(&s, sizeof(s)); - s.handle = handle; - s.w = w; s.h = h; - s.region[0] = (unsigned short)r.x; - s.region[1] = (unsigned short)r.y; - s.region[2] = (unsigned short)r.w; - s.region[3] = (unsigned short)r.h; - return s; -} - -NK_API struct nk_image -nk_image_handle(nk_handle handle) -{ - struct nk_image s; - nk_zero(&s, sizeof(s)); - s.handle = handle; - s.w = 0; s.h = 0; - s.region[0] = 0; - s.region[1] = 0; - s.region[2] = 0; - s.region[3] = 0; - return s; -} - -NK_API struct nk_image -nk_image_ptr(void *ptr) -{ - struct nk_image s; - nk_zero(&s, sizeof(s)); - NK_ASSERT(ptr); - s.handle.ptr = ptr; - s.w = 0; s.h = 0; - s.region[0] = 0; - s.region[1] = 0; - s.region[2] = 0; - s.region[3] = 0; - return s; -} - -NK_API struct nk_image -nk_image_id(int id) -{ - struct nk_image s; - nk_zero(&s, sizeof(s)); - s.handle.id = id; - s.w = 0; s.h = 0; - s.region[0] = 0; - s.region[1] = 0; - s.region[2] = 0; - s.region[3] = 0; - return s; -} - -NK_API int -nk_image_is_subimage(const struct nk_image* img) -{ - NK_ASSERT(img); - return !(img->w == 0 && img->h == 0); -} - -NK_INTERN void -nk_unify(struct nk_rect *clip, const struct nk_rect *a, float x0, float y0, - float x1, float y1) -{ - NK_ASSERT(a); - NK_ASSERT(clip); - clip->x = NK_MAX(a->x, x0); - clip->y = NK_MAX(a->y, y0); - clip->w = NK_MIN(a->x + a->w, x1) - clip->x; - clip->h = NK_MIN(a->y + a->h, y1) - clip->y; - clip->w = NK_MAX(0, clip->w); - clip->h = NK_MAX(0, clip->h); -} - -NK_API void -nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, - float pad_x, float pad_y, enum nk_heading direction) -{ - float w_half, h_half; - NK_ASSERT(result); - - r.w = NK_MAX(2 * pad_x, r.w); - r.h = NK_MAX(2 * pad_y, r.h); - r.w = r.w - 2 * pad_x; - r.h = r.h - 2 * pad_y; - - r.x = r.x + pad_x; - r.y = r.y + pad_y; - - w_half = r.w / 2.0f; - h_half = r.h / 2.0f; - - if (direction == NK_UP) { - result[0] = nk_vec2(r.x + w_half, r.y); - result[1] = nk_vec2(r.x + r.w, r.y + r.h); - result[2] = nk_vec2(r.x, r.y + r.h); - } else if (direction == NK_RIGHT) { - result[0] = nk_vec2(r.x, r.y); - result[1] = nk_vec2(r.x + r.w, r.y + h_half); - result[2] = nk_vec2(r.x, r.y + r.h); - } else if (direction == NK_DOWN) { - result[0] = nk_vec2(r.x, r.y); - result[1] = nk_vec2(r.x + r.w, r.y); - result[2] = nk_vec2(r.x + w_half, r.y + r.h); - } else { - result[0] = nk_vec2(r.x, r.y + h_half); - result[1] = nk_vec2(r.x + r.w, r.y); - result[2] = nk_vec2(r.x + r.w, r.y + r.h); - } -} - -NK_INTERN int +NK_LIB int nk_text_clamp(const struct nk_user_font *font, const char *text, int text_len, float space, int *glyphs, float *text_width, nk_rune *sep_list, int sep_count) @@ -6285,9 +7287,7 @@ nk_text_clamp(const struct nk_user_font *font, const char *text, return (!sep_len) ? len: sep_len; } } - -enum {NK_DO_NOT_STOP_ON_NEW_LINE, NK_STOP_ON_NEW_LINE}; -NK_INTERN struct nk_vec2 +NK_LIB struct nk_vec2 nk_text_calculate_text_bounds(const struct nk_user_font *font, const char *begin, int byte_len, float row_height, const char **remaining, struct nk_vec2 *out_offset, int *glyphs, int op) @@ -6348,9 +7348,428 @@ nk_text_calculate_text_bounds(const struct nk_user_font *font, return text_size; } + + + + /* ============================================================== * - * UTF-8 + * COLOR + * + * ===============================================================*/ +NK_INTERN int +nk_parse_hex(const char *p, int length) +{ + int i = 0; + int len = 0; + while (len < length) { + i <<= 4; + if (p[len] >= 'a' && p[len] <= 'f') + i += ((p[len] - 'a') + 10); + else if (p[len] >= 'A' && p[len] <= 'F') + i += ((p[len] - 'A') + 10); + else i += (p[len] - '0'); + len++; + } + return i; +} +NK_API struct nk_color +nk_rgba(int r, int g, int b, int a) +{ + struct nk_color ret; + ret.r = (nk_byte)NK_CLAMP(0, r, 255); + ret.g = (nk_byte)NK_CLAMP(0, g, 255); + ret.b = (nk_byte)NK_CLAMP(0, b, 255); + ret.a = (nk_byte)NK_CLAMP(0, a, 255); + return ret; +} +NK_API struct nk_color +nk_rgb_hex(const char *rgb) +{ + struct nk_color col; + const char *c = rgb; + if (*c == '#') c++; + col.r = (nk_byte)nk_parse_hex(c, 2); + col.g = (nk_byte)nk_parse_hex(c+2, 2); + col.b = (nk_byte)nk_parse_hex(c+4, 2); + col.a = 255; + return col; +} +NK_API struct nk_color +nk_rgba_hex(const char *rgb) +{ + struct nk_color col; + const char *c = rgb; + if (*c == '#') c++; + col.r = (nk_byte)nk_parse_hex(c, 2); + col.g = (nk_byte)nk_parse_hex(c+2, 2); + col.b = (nk_byte)nk_parse_hex(c+4, 2); + col.a = (nk_byte)nk_parse_hex(c+6, 2); + return col; +} +NK_API void +nk_color_hex_rgba(char *output, struct nk_color col) +{ + #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) + output[0] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); + output[1] = (char)NK_TO_HEX((col.r & 0x0F)); + output[2] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); + output[3] = (char)NK_TO_HEX((col.g & 0x0F)); + output[4] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); + output[5] = (char)NK_TO_HEX((col.b & 0x0F)); + output[6] = (char)NK_TO_HEX((col.a & 0xF0) >> 4); + output[7] = (char)NK_TO_HEX((col.a & 0x0F)); + output[8] = '\0'; + #undef NK_TO_HEX +} +NK_API void +nk_color_hex_rgb(char *output, struct nk_color col) +{ + #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) + output[0] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); + output[1] = (char)NK_TO_HEX((col.r & 0x0F)); + output[2] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); + output[3] = (char)NK_TO_HEX((col.g & 0x0F)); + output[4] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); + output[5] = (char)NK_TO_HEX((col.b & 0x0F)); + output[6] = '\0'; + #undef NK_TO_HEX +} +NK_API struct nk_color +nk_rgba_iv(const int *c) +{ + return nk_rgba(c[0], c[1], c[2], c[3]); +} +NK_API struct nk_color +nk_rgba_bv(const nk_byte *c) +{ + return nk_rgba(c[0], c[1], c[2], c[3]); +} +NK_API struct nk_color +nk_rgb(int r, int g, int b) +{ + struct nk_color ret; + ret.r = (nk_byte)NK_CLAMP(0, r, 255); + ret.g = (nk_byte)NK_CLAMP(0, g, 255); + ret.b = (nk_byte)NK_CLAMP(0, b, 255); + ret.a = (nk_byte)255; + return ret; +} +NK_API struct nk_color +nk_rgb_iv(const int *c) +{ + return nk_rgb(c[0], c[1], c[2]); +} +NK_API struct nk_color +nk_rgb_bv(const nk_byte* c) +{ + return nk_rgb(c[0], c[1], c[2]); +} +NK_API struct nk_color +nk_rgba_u32(nk_uint in) +{ + struct nk_color ret; + ret.r = (in & 0xFF); + ret.g = ((in >> 8) & 0xFF); + ret.b = ((in >> 16) & 0xFF); + ret.a = (nk_byte)((in >> 24) & 0xFF); + return ret; +} +NK_API struct nk_color +nk_rgba_f(float r, float g, float b, float a) +{ + struct nk_color ret; + ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); + ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); + ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); + ret.a = (nk_byte)(NK_SATURATE(a) * 255.0f); + return ret; +} +NK_API struct nk_color +nk_rgba_fv(const float *c) +{ + return nk_rgba_f(c[0], c[1], c[2], c[3]); +} +NK_API struct nk_color +nk_rgba_cf(struct nk_colorf c) +{ + return nk_rgba_f(c.r, c.g, c.b, c.a); +} +NK_API struct nk_color +nk_rgb_f(float r, float g, float b) +{ + struct nk_color ret; + ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); + ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); + ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); + ret.a = 255; + return ret; +} +NK_API struct nk_color +nk_rgb_fv(const float *c) +{ + return nk_rgb_f(c[0], c[1], c[2]); +} +NK_API struct nk_color +nk_rgb_cf(struct nk_colorf c) +{ + return nk_rgb_f(c.r, c.g, c.b); +} +NK_API struct nk_color +nk_hsv(int h, int s, int v) +{ + return nk_hsva(h, s, v, 255); +} +NK_API struct nk_color +nk_hsv_iv(const int *c) +{ + return nk_hsv(c[0], c[1], c[2]); +} +NK_API struct nk_color +nk_hsv_bv(const nk_byte *c) +{ + return nk_hsv(c[0], c[1], c[2]); +} +NK_API struct nk_color +nk_hsv_f(float h, float s, float v) +{ + return nk_hsva_f(h, s, v, 1.0f); +} +NK_API struct nk_color +nk_hsv_fv(const float *c) +{ + return nk_hsv_f(c[0], c[1], c[2]); +} +NK_API struct nk_color +nk_hsva(int h, int s, int v, int a) +{ + float hf = ((float)NK_CLAMP(0, h, 255)) / 255.0f; + float sf = ((float)NK_CLAMP(0, s, 255)) / 255.0f; + float vf = ((float)NK_CLAMP(0, v, 255)) / 255.0f; + float af = ((float)NK_CLAMP(0, a, 255)) / 255.0f; + return nk_hsva_f(hf, sf, vf, af); +} +NK_API struct nk_color +nk_hsva_iv(const int *c) +{ + return nk_hsva(c[0], c[1], c[2], c[3]); +} +NK_API struct nk_color +nk_hsva_bv(const nk_byte *c) +{ + return nk_hsva(c[0], c[1], c[2], c[3]); +} +NK_API struct nk_colorf +nk_hsva_colorf(float h, float s, float v, float a) +{ + int i; + float p, q, t, f; + struct nk_colorf out = {0,0,0,0}; + if (s <= 0.0f) { + out.r = v; out.g = v; out.b = v; out.a = a; + return out; + } + h = h / (60.0f/360.0f); + i = (int)h; + f = h - (float)i; + p = v * (1.0f - s); + q = v * (1.0f - (s * f)); + t = v * (1.0f - s * (1.0f - f)); + + switch (i) { + case 0: default: out.r = v; out.g = t; out.b = p; break; + case 1: out.r = q; out.g = v; out.b = p; break; + case 2: out.r = p; out.g = v; out.b = t; break; + case 3: out.r = p; out.g = q; out.b = v; break; + case 4: out.r = t; out.g = p; out.b = v; break; + case 5: out.r = v; out.g = p; out.b = q; break;} + out.a = a; + return out; +} +NK_API struct nk_colorf +nk_hsva_colorfv(float *c) +{ + return nk_hsva_colorf(c[0], c[1], c[2], c[3]); +} +NK_API struct nk_color +nk_hsva_f(float h, float s, float v, float a) +{ + struct nk_colorf c = nk_hsva_colorf(h, s, v, a); + return nk_rgba_f(c.r, c.g, c.b, c.a); +} +NK_API struct nk_color +nk_hsva_fv(const float *c) +{ + return nk_hsva_f(c[0], c[1], c[2], c[3]); +} +NK_API nk_uint +nk_color_u32(struct nk_color in) +{ + nk_uint out = (nk_uint)in.r; + out |= ((nk_uint)in.g << 8); + out |= ((nk_uint)in.b << 16); + out |= ((nk_uint)in.a << 24); + return out; +} +NK_API void +nk_color_f(float *r, float *g, float *b, float *a, struct nk_color in) +{ + NK_STORAGE const float s = 1.0f/255.0f; + *r = (float)in.r * s; + *g = (float)in.g * s; + *b = (float)in.b * s; + *a = (float)in.a * s; +} +NK_API void +nk_color_fv(float *c, struct nk_color in) +{ + nk_color_f(&c[0], &c[1], &c[2], &c[3], in); +} +NK_API struct nk_colorf +nk_color_cf(struct nk_color in) +{ + struct nk_colorf o; + nk_color_f(&o.r, &o.g, &o.b, &o.a, in); + return o; +} +NK_API void +nk_color_d(double *r, double *g, double *b, double *a, struct nk_color in) +{ + NK_STORAGE const double s = 1.0/255.0; + *r = (double)in.r * s; + *g = (double)in.g * s; + *b = (double)in.b * s; + *a = (double)in.a * s; +} +NK_API void +nk_color_dv(double *c, struct nk_color in) +{ + nk_color_d(&c[0], &c[1], &c[2], &c[3], in); +} +NK_API void +nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color in) +{ + float a; + nk_color_hsva_f(out_h, out_s, out_v, &a, in); +} +NK_API void +nk_color_hsv_fv(float *out, struct nk_color in) +{ + float a; + nk_color_hsva_f(&out[0], &out[1], &out[2], &a, in); +} +NK_API void +nk_colorf_hsva_f(float *out_h, float *out_s, + float *out_v, float *out_a, struct nk_colorf in) +{ + float chroma; + float K = 0.0f; + if (in.g < in.b) { + const float t = in.g; in.g = in.b; in.b = t; + K = -1.f; + } + if (in.r < in.g) { + const float t = in.r; in.r = in.g; in.g = t; + K = -2.f/6.0f - K; + } + chroma = in.r - ((in.g < in.b) ? in.g: in.b); + *out_h = NK_ABS(K + (in.g - in.b)/(6.0f * chroma + 1e-20f)); + *out_s = chroma / (in.r + 1e-20f); + *out_v = in.r; + *out_a = in.a; + +} +NK_API void +nk_colorf_hsva_fv(float *hsva, struct nk_colorf in) +{ + nk_colorf_hsva_f(&hsva[0], &hsva[1], &hsva[2], &hsva[3], in); +} +NK_API void +nk_color_hsva_f(float *out_h, float *out_s, + float *out_v, float *out_a, struct nk_color in) +{ + struct nk_colorf col; + nk_color_f(&col.r,&col.g,&col.b,&col.a, in); + nk_colorf_hsva_f(out_h, out_s, out_v, out_a, col); +} +NK_API void +nk_color_hsva_fv(float *out, struct nk_color in) +{ + nk_color_hsva_f(&out[0], &out[1], &out[2], &out[3], in); +} +NK_API void +nk_color_hsva_i(int *out_h, int *out_s, int *out_v, + int *out_a, struct nk_color in) +{ + float h,s,v,a; + nk_color_hsva_f(&h, &s, &v, &a, in); + *out_h = (nk_byte)(h * 255.0f); + *out_s = (nk_byte)(s * 255.0f); + *out_v = (nk_byte)(v * 255.0f); + *out_a = (nk_byte)(a * 255.0f); +} +NK_API void +nk_color_hsva_iv(int *out, struct nk_color in) +{ + nk_color_hsva_i(&out[0], &out[1], &out[2], &out[3], in); +} +NK_API void +nk_color_hsva_bv(nk_byte *out, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + out[0] = (nk_byte)tmp[0]; + out[1] = (nk_byte)tmp[1]; + out[2] = (nk_byte)tmp[2]; + out[3] = (nk_byte)tmp[3]; +} +NK_API void +nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + *h = (nk_byte)tmp[0]; + *s = (nk_byte)tmp[1]; + *v = (nk_byte)tmp[2]; + *a = (nk_byte)tmp[3]; +} +NK_API void +nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color in) +{ + int a; + nk_color_hsva_i(out_h, out_s, out_v, &a, in); +} +NK_API void +nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color in) +{ + int tmp[4]; + nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); + *out_h = (nk_byte)tmp[0]; + *out_s = (nk_byte)tmp[1]; + *out_v = (nk_byte)tmp[2]; +} +NK_API void +nk_color_hsv_iv(int *out, struct nk_color in) +{ + nk_color_hsv_i(&out[0], &out[1], &out[2], in); +} +NK_API void +nk_color_hsv_bv(nk_byte *out, struct nk_color in) +{ + int tmp[4]; + nk_color_hsv_i(&tmp[0], &tmp[1], &tmp[2], in); + out[0] = (nk_byte)tmp[0]; + out[1] = (nk_byte)tmp[1]; + out[2] = (nk_byte)tmp[2]; +} + + + + + +/* =============================================================== + * + * UTF-8 * * ===============================================================*/ NK_GLOBAL const nk_byte nk_utfbyte[NK_UTF_SIZE+1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; @@ -6369,7 +7788,6 @@ nk_utf_validate(nk_rune *u, int i) for (i = 1; *u > nk_utfmax[i]; ++i); return i; } - NK_INTERN nk_rune nk_utf_decode_byte(char c, int *i) { @@ -6381,7 +7799,6 @@ nk_utf_decode_byte(char c, int *i) } return 0; } - NK_API int nk_utf_decode(const char *c, nk_rune *u, int clen) { @@ -6410,13 +7827,11 @@ nk_utf_decode(const char *c, nk_rune *u, int clen) nk_utf_validate(u, len); return len; } - NK_INTERN char nk_utf_encode_byte(nk_rune u, int i) { return (char)((nk_utfbyte[i]) | ((nk_byte)u & ~nk_utfmask[i])); } - NK_API int nk_utf_encode(nk_rune u, char *c, int clen) { @@ -6432,7 +7847,6 @@ nk_utf_encode(nk_rune u, char *c, int clen) c[0] = nk_utf_encode_byte(u, len); return len; } - NK_API int nk_utf_len(const char *str, int len) { @@ -6456,7 +7870,6 @@ nk_utf_len(const char *str, int len) } return glyphs; } - NK_API const char* nk_utf_at(const char *buffer, int length, int index, nk_rune *unicode, int *len) @@ -6495,17 +7908,29 @@ nk_utf_at(const char *buffer, int length, int index, return buffer + src_len; } + + + + /* ============================================================== * * BUFFER * * ===============================================================*/ #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR -NK_INTERN void* nk_malloc(nk_handle unused, void *old,nk_size size) -{NK_UNUSED(unused); NK_UNUSED(old); return malloc(size);} -NK_INTERN void nk_mfree(nk_handle unused, void *ptr) -{NK_UNUSED(unused); free(ptr);} - +NK_LIB void* +nk_malloc(nk_handle unused, void *old,nk_size size) +{ + NK_UNUSED(unused); + NK_UNUSED(old); + return malloc(size); +} +NK_LIB void +nk_mfree(nk_handle unused, void *ptr) +{ + NK_UNUSED(unused); + free(ptr); +} NK_API void nk_buffer_init_default(struct nk_buffer *buffer) { @@ -6534,7 +7959,6 @@ nk_buffer_init(struct nk_buffer *b, const struct nk_allocator *a, b->grow_factor = 2.0f; b->pool = *a; } - NK_API void nk_buffer_init_fixed(struct nk_buffer *b, void *m, nk_size size) { @@ -6549,9 +7973,9 @@ nk_buffer_init_fixed(struct nk_buffer *b, void *m, nk_size size) b->memory.size = size; b->size = size; } - -NK_INTERN void* -nk_buffer_align(void *unaligned, nk_size align, nk_size *alignment, +NK_LIB void* +nk_buffer_align(void *unaligned, + nk_size align, nk_size *alignment, enum nk_buffer_allocation_type type) { void *memory = 0; @@ -6579,8 +8003,7 @@ nk_buffer_align(void *unaligned, nk_size align, nk_size *alignment, } return memory; } - -NK_INTERN void* +NK_LIB void* nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size) { void *temp; @@ -6618,8 +8041,7 @@ nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size) } return temp; } - -NK_INTERN void* +NK_LIB void* nk_buffer_alloc(struct nk_buffer *b, enum nk_buffer_allocation_type type, nk_size size, nk_size align) { @@ -6671,7 +8093,6 @@ nk_buffer_alloc(struct nk_buffer *b, enum nk_buffer_allocation_type type, b->calls++; return memory; } - NK_API void nk_buffer_push(struct nk_buffer *b, enum nk_buffer_allocation_type type, const void *memory, nk_size size, nk_size align) @@ -6680,7 +8101,6 @@ nk_buffer_push(struct nk_buffer *b, enum nk_buffer_allocation_type type, if (!mem) return; NK_MEMCPY(mem, memory, size); } - NK_API void nk_buffer_mark(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) { @@ -6691,7 +8111,6 @@ nk_buffer_mark(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) buffer->marker[type].offset = buffer->size; else buffer->marker[type].offset = buffer->allocated; } - NK_API void nk_buffer_reset(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) { @@ -6713,7 +8132,6 @@ nk_buffer_reset(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) buffer->marker[type].active = nk_false; } } - NK_API void nk_buffer_clear(struct nk_buffer *b) { @@ -6724,7 +8142,6 @@ nk_buffer_clear(struct nk_buffer *b) b->calls = 0; b->needed = 0; } - NK_API void nk_buffer_free(struct nk_buffer *b) { @@ -6735,7 +8152,6 @@ nk_buffer_free(struct nk_buffer *b) NK_ASSERT(b->pool.free); b->pool.free(b->pool.userdata, b->memory.ptr); } - NK_API void nk_buffer_info(struct nk_memory_status *s, struct nk_buffer *b) { @@ -6748,7 +8164,6 @@ nk_buffer_info(struct nk_memory_status *s, struct nk_buffer *b) s->memory = b->memory.ptr; s->calls = b->calls; } - NK_API void* nk_buffer_memory(struct nk_buffer *buffer) { @@ -6756,7 +8171,6 @@ nk_buffer_memory(struct nk_buffer *buffer) if (!buffer) return 0; return buffer->memory.ptr; } - NK_API const void* nk_buffer_memory_const(const struct nk_buffer *buffer) { @@ -6764,7 +8178,6 @@ nk_buffer_memory_const(const struct nk_buffer *buffer) if (!buffer) return 0; return buffer->memory.ptr; } - NK_API nk_size nk_buffer_total(struct nk_buffer *buffer) { @@ -6773,13 +8186,15 @@ nk_buffer_total(struct nk_buffer *buffer) return buffer->memory.size; } -/* - * ============================================================== + + + + +/* =============================================================== * - * STRING + * STRING * - * =============================================================== - */ + * ===============================================================*/ #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_API void nk_str_init_default(struct nk_str *str) @@ -6799,14 +8214,12 @@ nk_str_init(struct nk_str *str, const struct nk_allocator *alloc, nk_size size) nk_buffer_init(&str->buffer, alloc, size); str->len = 0; } - NK_API void nk_str_init_fixed(struct nk_str *str, void *memory, nk_size size) { nk_buffer_init_fixed(&str->buffer, memory, size); str->len = 0; } - NK_API int nk_str_append_text_char(struct nk_str *s, const char *str, int len) { @@ -6820,13 +8233,11 @@ nk_str_append_text_char(struct nk_str *s, const char *str, int len) s->len += nk_utf_len(str, len); return len; } - NK_API int nk_str_append_str_char(struct nk_str *s, const char *str) { return nk_str_append_text_char(s, str, nk_strlen(str)); } - NK_API int nk_str_append_text_utf8(struct nk_str *str, const char *text, int len) { @@ -6839,7 +8250,6 @@ nk_str_append_text_utf8(struct nk_str *str, const char *text, int len) nk_str_append_text_char(str, text, byte_len); return len; } - NK_API int nk_str_append_str_utf8(struct nk_str *str, const char *text) { @@ -6859,7 +8269,6 @@ nk_str_append_str_utf8(struct nk_str *str, const char *text) nk_str_append_text_char(str, text, byte_len); return runes; } - NK_API int nk_str_append_text_runes(struct nk_str *str, const nk_rune *text, int len) { @@ -6876,7 +8285,6 @@ nk_str_append_text_runes(struct nk_str *str, const nk_rune *text, int len) } return len; } - NK_API int nk_str_append_str_runes(struct nk_str *str, const nk_rune *runes) { @@ -6892,7 +8300,6 @@ nk_str_append_str_runes(struct nk_str *str, const nk_rune *runes) } return i; } - NK_API int nk_str_insert_at_char(struct nk_str *s, int pos, const char *str, int len) { @@ -6928,7 +8335,6 @@ nk_str_insert_at_char(struct nk_str *s, int pos, const char *str, int len) s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); return 1; } - NK_API int nk_str_insert_at_rune(struct nk_str *str, int pos, const char *cstr, int len) { @@ -6948,19 +8354,16 @@ nk_str_insert_at_rune(struct nk_str *str, int pos, const char *cstr, int len) if (!begin) return 0; return nk_str_insert_at_char(str, (int)(begin - buffer), cstr, len); } - NK_API int nk_str_insert_text_char(struct nk_str *str, int pos, const char *text, int len) { return nk_str_insert_text_utf8(str, pos, text, len); } - NK_API int nk_str_insert_str_char(struct nk_str *str, int pos, const char *text) { return nk_str_insert_text_utf8(str, pos, text, nk_strlen(text)); } - NK_API int nk_str_insert_text_utf8(struct nk_str *str, int pos, const char *text, int len) { @@ -6976,7 +8379,6 @@ nk_str_insert_text_utf8(struct nk_str *str, int pos, const char *text, int len) nk_str_insert_at_rune(str, pos, text, byte_len); return len; } - NK_API int nk_str_insert_str_utf8(struct nk_str *str, int pos, const char *text) { @@ -6996,7 +8398,6 @@ nk_str_insert_str_utf8(struct nk_str *str, int pos, const char *text) nk_str_insert_at_rune(str, pos, text, byte_len); return runes; } - NK_API int nk_str_insert_text_runes(struct nk_str *str, int pos, const nk_rune *runes, int len) { @@ -7013,7 +8414,6 @@ nk_str_insert_text_runes(struct nk_str *str, int pos, const nk_rune *runes, int } return len; } - NK_API int nk_str_insert_str_runes(struct nk_str *str, int pos, const nk_rune *runes) { @@ -7029,7 +8429,6 @@ nk_str_insert_str_runes(struct nk_str *str, int pos, const nk_rune *runes) } return i; } - NK_API void nk_str_remove_chars(struct nk_str *s, int len) { @@ -7040,7 +8439,6 @@ nk_str_remove_chars(struct nk_str *s, int len) s->buffer.allocated -= (nk_size)len; s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); } - NK_API void nk_str_remove_runes(struct nk_str *str, int len) { @@ -7062,7 +8460,6 @@ nk_str_remove_runes(struct nk_str *str, int len) end = (const char*)str->buffer.memory.ptr + str->buffer.allocated; nk_str_remove_chars(str, (int)(end-begin)+1); } - NK_API void nk_str_delete_chars(struct nk_str *s, int pos, int len) { @@ -7080,7 +8477,6 @@ nk_str_delete_chars(struct nk_str *s, int pos, int len) } else nk_str_remove_chars(s, len); s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); } - NK_API void nk_str_delete_runes(struct nk_str *s, int pos, int len) { @@ -7105,7 +8501,6 @@ nk_str_delete_runes(struct nk_str *s, int pos, int len) if (!end) return; nk_str_delete_chars(s, (int)(begin - temp), (int)(end - begin)); } - NK_API char* nk_str_at_char(struct nk_str *s, int pos) { @@ -7113,7 +8508,6 @@ nk_str_at_char(struct nk_str *s, int pos) if (!s || pos > (int)s->buffer.allocated) return 0; return nk_ptr_add(char, s->buffer.memory.ptr, pos); } - NK_API char* nk_str_at_rune(struct nk_str *str, int pos, nk_rune *unicode, int *len) { @@ -7150,7 +8544,6 @@ nk_str_at_rune(struct nk_str *str, int pos, nk_rune *unicode, int *len) if (i != pos) return 0; return text + src_len; } - NK_API const char* nk_str_at_char_const(const struct nk_str *s, int pos) { @@ -7158,7 +8551,6 @@ nk_str_at_char_const(const struct nk_str *s, int pos) if (!s || pos > (int)s->buffer.allocated) return 0; return nk_ptr_add(char, s->buffer.memory.ptr, pos); } - NK_API const char* nk_str_at_const(const struct nk_str *str, int pos, nk_rune *unicode, int *len) { @@ -7195,7 +8587,6 @@ nk_str_at_const(const struct nk_str *str, int pos, nk_rune *unicode, int *len) if (i != pos) return 0; return text + src_len; } - NK_API nk_rune nk_str_rune_at(const struct nk_str *str, int pos) { @@ -7204,7 +8595,6 @@ nk_str_rune_at(const struct nk_str *str, int pos) nk_str_at_const(str, pos, &unicode, &len); return unicode; } - NK_API char* nk_str_get(struct nk_str *s) { @@ -7212,7 +8602,6 @@ nk_str_get(struct nk_str *s) if (!s || !s->len || !s->buffer.allocated) return 0; return (char*)s->buffer.memory.ptr; } - NK_API const char* nk_str_get_const(const struct nk_str *s) { @@ -7220,7 +8609,6 @@ nk_str_get_const(const struct nk_str *s) if (!s || !s->len || !s->buffer.allocated) return 0; return (const char*)s->buffer.memory.ptr; } - NK_API int nk_str_len(struct nk_str *s) { @@ -7228,7 +8616,6 @@ nk_str_len(struct nk_str *s) if (!s || !s->len || !s->buffer.allocated) return 0; return s->len; } - NK_API int nk_str_len_char(struct nk_str *s) { @@ -7236,7 +8623,6 @@ nk_str_len_char(struct nk_str *s) if (!s || !s->len || !s->buffer.allocated) return 0; return (int)s->buffer.allocated; } - NK_API void nk_str_clear(struct nk_str *str) { @@ -7244,7 +8630,6 @@ nk_str_clear(struct nk_str *str) nk_buffer_clear(&str->buffer); str->len = 0; } - NK_API void nk_str_free(struct nk_str *str) { @@ -7253,42 +8638,42 @@ nk_str_free(struct nk_str *str) str->len = 0; } -/* - * ============================================================== - * - * Command buffer - * - * =============================================================== -*/ -NK_INTERN void -nk_command_buffer_init(struct nk_command_buffer *cmdbuf, - struct nk_buffer *buffer, enum nk_command_clipping clip) -{ - NK_ASSERT(cmdbuf); - NK_ASSERT(buffer); - if (!cmdbuf || !buffer) return; - cmdbuf->base = buffer; - cmdbuf->use_clipping = clip; - cmdbuf->begin = buffer->allocated; - cmdbuf->end = buffer->allocated; - cmdbuf->last = buffer->allocated; -} -NK_INTERN void -nk_command_buffer_reset(struct nk_command_buffer *buffer) + + + +/* ============================================================== + * + * DRAW + * + * ===============================================================*/ +NK_LIB void +nk_command_buffer_init(struct nk_command_buffer *cb, + struct nk_buffer *b, enum nk_command_clipping clip) { - NK_ASSERT(buffer); - if (!buffer) return; - buffer->begin = 0; - buffer->end = 0; - buffer->last = 0; - buffer->clip = nk_null_rect; + NK_ASSERT(cb); + NK_ASSERT(b); + if (!cb || !b) return; + cb->base = b; + cb->use_clipping = (int)clip; + cb->begin = b->allocated; + cb->end = b->allocated; + cb->last = b->allocated; +} +NK_LIB void +nk_command_buffer_reset(struct nk_command_buffer *b) +{ + NK_ASSERT(b); + if (!b) return; + b->begin = 0; + b->end = 0; + b->last = 0; + b->clip = nk_null_rect; #ifdef NK_INCLUDE_COMMAND_USERDATA - buffer->userdata.ptr = 0; + b->userdata.ptr = 0; #endif } - -NK_INTERN void* +NK_LIB void* nk_command_buffer_push(struct nk_command_buffer* b, enum nk_command_type t, nk_size size) { @@ -7321,7 +8706,6 @@ nk_command_buffer_push(struct nk_command_buffer* b, b->end = cmd->next; return cmd; } - NK_API void nk_push_scissor(struct nk_command_buffer *b, struct nk_rect r) { @@ -7342,7 +8726,6 @@ nk_push_scissor(struct nk_command_buffer *b, struct nk_rect r) cmd->w = (unsigned short)NK_MAX(0, r.w); cmd->h = (unsigned short)NK_MAX(0, r.h); } - NK_API void nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, float x1, float y1, float line_thickness, struct nk_color c) @@ -7360,7 +8743,6 @@ nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, cmd->end.y = (short)y1; cmd->color = c; } - NK_API void nk_stroke_curve(struct nk_command_buffer *b, float ax, float ay, float ctrl0x, float ctrl0y, float ctrl1x, float ctrl1y, @@ -7384,7 +8766,6 @@ nk_stroke_curve(struct nk_command_buffer *b, float ax, float ay, cmd->end.y = (short)by; cmd->color = col; } - NK_API void nk_stroke_rect(struct nk_command_buffer *b, struct nk_rect rect, float rounding, float line_thickness, struct nk_color c) @@ -7408,7 +8789,6 @@ nk_stroke_rect(struct nk_command_buffer *b, struct nk_rect rect, cmd->h = (unsigned short)NK_MAX(0, rect.h); cmd->color = c; } - NK_API void nk_fill_rect(struct nk_command_buffer *b, struct nk_rect rect, float rounding, struct nk_color c) @@ -7432,7 +8812,6 @@ nk_fill_rect(struct nk_command_buffer *b, struct nk_rect rect, cmd->h = (unsigned short)NK_MAX(0, rect.h); cmd->color = c; } - NK_API void nk_fill_rect_multi_color(struct nk_command_buffer *b, struct nk_rect rect, struct nk_color left, struct nk_color top, struct nk_color right, @@ -7459,7 +8838,6 @@ nk_fill_rect_multi_color(struct nk_command_buffer *b, struct nk_rect rect, cmd->right = right; cmd->bottom = bottom; } - NK_API void nk_stroke_circle(struct nk_command_buffer *b, struct nk_rect r, float line_thickness, struct nk_color c) @@ -7482,7 +8860,6 @@ nk_stroke_circle(struct nk_command_buffer *b, struct nk_rect r, cmd->h = (unsigned short)NK_MAX(r.h, 0); cmd->color = c; } - NK_API void nk_fill_circle(struct nk_command_buffer *b, struct nk_rect r, struct nk_color c) { @@ -7504,7 +8881,6 @@ nk_fill_circle(struct nk_command_buffer *b, struct nk_rect r, struct nk_color c) cmd->h = (unsigned short)NK_MAX(r.h, 0); cmd->color = c; } - NK_API void nk_stroke_arc(struct nk_command_buffer *b, float cx, float cy, float radius, float a_min, float a_max, float line_thickness, struct nk_color c) @@ -7522,7 +8898,6 @@ nk_stroke_arc(struct nk_command_buffer *b, float cx, float cy, float radius, cmd->a[1] = a_max; cmd->color = c; } - NK_API void nk_fill_arc(struct nk_command_buffer *b, float cx, float cy, float radius, float a_min, float a_max, struct nk_color c) @@ -7540,7 +8915,6 @@ nk_fill_arc(struct nk_command_buffer *b, float cx, float cy, float radius, cmd->a[1] = a_max; cmd->color = c; } - NK_API void nk_stroke_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, float y1, float x2, float y2, float line_thickness, struct nk_color c) @@ -7568,7 +8942,6 @@ nk_stroke_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, cmd->c.y = (short)y2; cmd->color = c; } - NK_API void nk_fill_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, float y1, float x2, float y2, struct nk_color c) @@ -7596,7 +8969,6 @@ nk_fill_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, cmd->c.y = (short)y2; cmd->color = c; } - NK_API void nk_stroke_polygon(struct nk_command_buffer *b, float *points, int point_count, float line_thickness, struct nk_color col) @@ -7618,7 +8990,6 @@ nk_stroke_polygon(struct nk_command_buffer *b, float *points, int point_count, cmd->points[i].y = (short)points[i*2+1]; } } - NK_API void nk_fill_polygon(struct nk_command_buffer *b, float *points, int point_count, struct nk_color col) @@ -7640,7 +9011,6 @@ nk_fill_polygon(struct nk_command_buffer *b, float *points, int point_count, cmd->points[i].y = (short)points[i*2+1]; } } - NK_API void nk_stroke_polyline(struct nk_command_buffer *b, float *points, int point_count, float line_thickness, struct nk_color col) @@ -7662,7 +9032,6 @@ nk_stroke_polyline(struct nk_command_buffer *b, float *points, int point_count, cmd->points[i].y = (short)points[i*2+1]; } } - NK_API void nk_draw_image(struct nk_command_buffer *b, struct nk_rect r, const struct nk_image *img, struct nk_color col) @@ -7686,7 +9055,6 @@ nk_draw_image(struct nk_command_buffer *b, struct nk_rect r, cmd->img = *img; cmd->col = col; } - NK_API void nk_push_custom(struct nk_command_buffer *b, struct nk_rect r, nk_command_custom_callback cb, nk_handle usr) @@ -7710,7 +9078,6 @@ nk_push_custom(struct nk_command_buffer *b, struct nk_rect r, cmd->callback_data = usr; cmd->callback = cb; } - NK_API void nk_draw_text(struct nk_command_buffer *b, struct nk_rect r, const char *string, int length, const struct nk_user_font *font, @@ -7753,9 +9120,13 @@ nk_draw_text(struct nk_command_buffer *b, struct nk_rect r, cmd->string[length] = '\0'; } -/* ============================================================== + + + + +/* =============================================================== * - * DRAW LIST + * VERTEX * * ===============================================================*/ #ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT @@ -7772,7 +9143,6 @@ nk_draw_list_init(struct nk_draw_list *list) list->circle_vtx[i].y = (float)NK_SIN(a); } } - NK_API void nk_draw_list_setup(struct nk_draw_list *canvas, const struct nk_convert_config *config, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, @@ -7793,8 +9163,14 @@ nk_draw_list_setup(struct nk_draw_list *canvas, const struct nk_convert_config * canvas->line_AA = line_aa; canvas->shape_AA = shape_aa; canvas->clip_rect = nk_null_rect; -} + canvas->cmd_offset = 0; + canvas->element_count = 0; + canvas->vertex_count = 0; + canvas->cmd_offset = 0; + canvas->cmd_count = 0; + canvas->path_count = 0; +} NK_API const struct nk_draw_command* nk__draw_list_begin(const struct nk_draw_list *canvas, const struct nk_buffer *buffer) { @@ -7811,7 +9187,6 @@ nk__draw_list_begin(const struct nk_draw_list *canvas, const struct nk_buffer *b cmd = nk_ptr_add(const struct nk_draw_command, memory, offset); return cmd; } - NK_API const struct nk_draw_command* nk__draw_list_end(const struct nk_draw_list *canvas, const struct nk_buffer *buffer) { @@ -7832,7 +9207,6 @@ nk__draw_list_end(const struct nk_draw_list *canvas, const struct nk_buffer *buf end -= (canvas->cmd_count-1); return end; } - NK_API const struct nk_draw_command* nk__draw_list_next(const struct nk_draw_command *cmd, const struct nk_buffer *buffer, const struct nk_draw_list *canvas) @@ -7847,29 +9221,6 @@ nk__draw_list_next(const struct nk_draw_command *cmd, if (cmd <= end) return 0; return (cmd-1); } - -NK_API void -nk_draw_list_clear(struct nk_draw_list *list) -{ - NK_ASSERT(list); - if (!list) return; - if (list->buffer) - nk_buffer_clear(list->buffer); - if (list->vertices) - nk_buffer_clear(list->vertices); - if (list->elements) - nk_buffer_clear(list->elements); - - list->element_count = 0; - list->vertex_count = 0; - list->cmd_offset = 0; - list->cmd_count = 0; - list->path_count = 0; - list->vertices = 0; - list->elements = 0; - list->clip_rect = nk_null_rect; -} - NK_INTERN struct nk_vec2* nk_draw_list_alloc_path(struct nk_draw_list *list, int count) { @@ -7888,7 +9239,6 @@ nk_draw_list_alloc_path(struct nk_draw_list *list, int count) list->path_count += (unsigned int)count; return points; } - NK_INTERN struct nk_vec2 nk_draw_list_path_last(struct nk_draw_list *list) { @@ -7900,7 +9250,6 @@ nk_draw_list_path_last(struct nk_draw_list *list) point += (list->path_count-1); return *point; } - NK_INTERN struct nk_draw_command* nk_draw_list_push_command(struct nk_draw_list *list, struct nk_rect clip, nk_handle texture) @@ -7932,7 +9281,6 @@ nk_draw_list_push_command(struct nk_draw_list *list, struct nk_rect clip, list->clip_rect = clip; return cmd; } - NK_INTERN struct nk_draw_command* nk_draw_list_command_last(struct nk_draw_list *list) { @@ -7946,7 +9294,6 @@ nk_draw_list_command_last(struct nk_draw_list *list) cmd = nk_ptr_add(struct nk_draw_command, memory, size - list->cmd_offset); return (cmd - (list->cmd_count-1)); } - NK_INTERN void nk_draw_list_add_clip(struct nk_draw_list *list, struct nk_rect rect) { @@ -7961,7 +9308,6 @@ nk_draw_list_add_clip(struct nk_draw_list *list, struct nk_rect rect) nk_draw_list_push_command(list, rect, prev->texture); } } - NK_INTERN void nk_draw_list_push_image(struct nk_draw_list *list, nk_handle texture) { @@ -7983,7 +9329,6 @@ nk_draw_list_push_image(struct nk_draw_list *list, nk_handle texture) ) nk_draw_list_push_command(list, prev->clip_rect, texture); } } - #ifdef NK_INCLUDE_COMMAND_USERDATA NK_API void nk_draw_list_push_userdata(struct nk_draw_list *list, nk_handle userdata) @@ -7991,7 +9336,6 @@ nk_draw_list_push_userdata(struct nk_draw_list *list, nk_handle userdata) list->userdata = userdata; } #endif - NK_INTERN void* nk_draw_list_alloc_vertices(struct nk_draw_list *list, nk_size count) { @@ -8002,9 +9346,20 @@ nk_draw_list_alloc_vertices(struct nk_draw_list *list, nk_size count) list->config.vertex_size*count, list->config.vertex_alignment); if (!vtx) return 0; list->vertex_count += (unsigned int)count; + + /* This assert triggers because your are drawing a lot of stuff and nuklear + * defined `nk_draw_index` as `nk_ushort` to safe space be default. + * + * So you reached the maximum number of indicies or rather vertexes. + * To solve this issue please change typdef `nk_draw_index` to `nk_uint` + * and don't forget to specify the new element size in your drawing + * backend (OpenGL, DirectX, ...). For example in OpenGL for `glDrawElements` + * instead of specifing `GL_UNSIGNED_SHORT` you have to define `GL_UNSIGNED_INT`. + * Sorry for the inconvenience. */ + NK_ASSERT((sizeof(nk_draw_index) == 2 && list->vertex_count < NK_USHORT_MAX && + "To many verticies for 16-bit vertex indicies. Please read comment above on how to solve this problem")); return vtx; } - NK_INTERN nk_draw_index* nk_draw_list_alloc_elements(struct nk_draw_list *list, nk_size count) { @@ -8023,7 +9378,6 @@ nk_draw_list_alloc_elements(struct nk_draw_list *list, nk_size count) cmd->elem_count += (unsigned int)count; return ids; } - NK_INTERN int nk_draw_vertex_layout_element_is_end_of_layout( const struct nk_draw_vertex_layout_element *element) @@ -8031,78 +9385,81 @@ nk_draw_vertex_layout_element_is_end_of_layout( return (element->attribute == NK_VERTEX_ATTRIBUTE_COUNT || element->format == NK_FORMAT_COUNT); } - NK_INTERN void -nk_draw_vertex_color(void *attribute, const float *values, +nk_draw_vertex_color(void *attr, const float *vals, enum nk_draw_vertex_layout_format format) { /* if this triggers you tried to provide a value format for a color */ + float val[4]; NK_ASSERT(format >= NK_FORMAT_COLOR_BEGIN); NK_ASSERT(format <= NK_FORMAT_COLOR_END); if (format < NK_FORMAT_COLOR_BEGIN || format > NK_FORMAT_COLOR_END) return; + val[0] = NK_SATURATE(vals[0]); + val[1] = NK_SATURATE(vals[1]); + val[2] = NK_SATURATE(vals[2]); + val[3] = NK_SATURATE(vals[3]); + switch (format) { default: NK_ASSERT(0 && "Invalid vertex layout color format"); break; case NK_FORMAT_R8G8B8A8: case NK_FORMAT_R8G8B8: { - struct nk_color col = nk_rgba_fv(values); - NK_MEMCPY(attribute, &col.r, sizeof(col)); + struct nk_color col = nk_rgba_fv(val); + NK_MEMCPY(attr, &col.r, sizeof(col)); } break; case NK_FORMAT_B8G8R8A8: { - struct nk_color col = nk_rgba_fv(values); + struct nk_color col = nk_rgba_fv(val); struct nk_color bgra = nk_rgba(col.b, col.g, col.r, col.a); - NK_MEMCPY(attribute, &bgra, sizeof(bgra)); + NK_MEMCPY(attr, &bgra, sizeof(bgra)); } break; case NK_FORMAT_R16G15B16: { nk_ushort col[3]; - col[0] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[0] * NK_USHORT_MAX, NK_USHORT_MAX); - col[1] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[1] * NK_USHORT_MAX, NK_USHORT_MAX); - col[2] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[2] * NK_USHORT_MAX, NK_USHORT_MAX); - NK_MEMCPY(attribute, col, sizeof(col)); + col[0] = (nk_ushort)(val[0]*(float)NK_USHORT_MAX); + col[1] = (nk_ushort)(val[1]*(float)NK_USHORT_MAX); + col[2] = (nk_ushort)(val[2]*(float)NK_USHORT_MAX); + NK_MEMCPY(attr, col, sizeof(col)); } break; case NK_FORMAT_R16G15B16A16: { nk_ushort col[4]; - col[0] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[0] * NK_USHORT_MAX, NK_USHORT_MAX); - col[1] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[1] * NK_USHORT_MAX, NK_USHORT_MAX); - col[2] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[2] * NK_USHORT_MAX, NK_USHORT_MAX); - col[3] = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[3] * NK_USHORT_MAX, NK_USHORT_MAX); - NK_MEMCPY(attribute, col, sizeof(col)); + col[0] = (nk_ushort)(val[0]*(float)NK_USHORT_MAX); + col[1] = (nk_ushort)(val[1]*(float)NK_USHORT_MAX); + col[2] = (nk_ushort)(val[2]*(float)NK_USHORT_MAX); + col[3] = (nk_ushort)(val[3]*(float)NK_USHORT_MAX); + NK_MEMCPY(attr, col, sizeof(col)); } break; case NK_FORMAT_R32G32B32: { nk_uint col[3]; - col[0] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[0] * NK_UINT_MAX, NK_UINT_MAX); - col[1] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[1] * NK_UINT_MAX, NK_UINT_MAX); - col[2] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[2] * NK_UINT_MAX, NK_UINT_MAX); - NK_MEMCPY(attribute, col, sizeof(col)); + col[0] = (nk_uint)(val[0]*(float)NK_UINT_MAX); + col[1] = (nk_uint)(val[1]*(float)NK_UINT_MAX); + col[2] = (nk_uint)(val[2]*(float)NK_UINT_MAX); + NK_MEMCPY(attr, col, sizeof(col)); } break; case NK_FORMAT_R32G32B32A32: { nk_uint col[4]; - col[0] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[0] * NK_UINT_MAX, NK_UINT_MAX); - col[1] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[1] * NK_UINT_MAX, NK_UINT_MAX); - col[2] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[2] * NK_UINT_MAX, NK_UINT_MAX); - col[3] = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[3] * NK_UINT_MAX, NK_UINT_MAX); - NK_MEMCPY(attribute, col, sizeof(col)); + col[0] = (nk_uint)(val[0]*(float)NK_UINT_MAX); + col[1] = (nk_uint)(val[1]*(float)NK_UINT_MAX); + col[2] = (nk_uint)(val[2]*(float)NK_UINT_MAX); + col[3] = (nk_uint)(val[3]*(float)NK_UINT_MAX); + NK_MEMCPY(attr, col, sizeof(col)); } break; case NK_FORMAT_R32G32B32A32_FLOAT: - NK_MEMCPY(attribute, values, sizeof(float)*4); + NK_MEMCPY(attr, val, sizeof(float)*4); break; case NK_FORMAT_R32G32B32A32_DOUBLE: { double col[4]; - col[0] = (double)NK_SATURATE(values[0]); - col[1] = (double)NK_SATURATE(values[1]); - col[2] = (double)NK_SATURATE(values[2]); - col[3] = (double)NK_SATURATE(values[3]); - NK_MEMCPY(attribute, col, sizeof(col)); + col[0] = (double)val[0]; + col[1] = (double)val[1]; + col[2] = (double)val[2]; + col[3] = (double)val[3]; + NK_MEMCPY(attr, col, sizeof(col)); } break; case NK_FORMAT_RGB32: case NK_FORMAT_RGBA32: { - struct nk_color col = nk_rgba_fv(values); + struct nk_color col = nk_rgba_fv(val); nk_uint color = nk_color_u32(col); - NK_MEMCPY(attribute, &color, sizeof(color)); - } break; - } + NK_MEMCPY(attr, &color, sizeof(color)); + } break; } } - NK_INTERN void nk_draw_vertex_element(void *dst, const float *values, int value_count, enum nk_draw_vertex_layout_format format) @@ -8116,32 +9473,32 @@ nk_draw_vertex_element(void *dst, const float *values, int value_count, switch (format) { default: NK_ASSERT(0 && "invalid vertex layout format"); break; case NK_FORMAT_SCHAR: { - char value = (char)NK_CLAMP(NK_SCHAR_MIN, values[value_index], NK_SCHAR_MAX); + char value = (char)NK_CLAMP((float)NK_SCHAR_MIN, values[value_index], (float)NK_SCHAR_MAX); NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(char)); } break; case NK_FORMAT_SSHORT: { - nk_short value = (nk_short)NK_CLAMP(NK_SSHORT_MIN, values[value_index], NK_SSHORT_MAX); + nk_short value = (nk_short)NK_CLAMP((float)NK_SSHORT_MIN, values[value_index], (float)NK_SSHORT_MAX); NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(value)); } break; case NK_FORMAT_SINT: { - nk_int value = (nk_int)NK_CLAMP(NK_SINT_MIN, values[value_index], NK_SINT_MAX); + nk_int value = (nk_int)NK_CLAMP((float)NK_SINT_MIN, values[value_index], (float)NK_SINT_MAX); NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(nk_int)); } break; case NK_FORMAT_UCHAR: { - unsigned char value = (unsigned char)NK_CLAMP(NK_UCHAR_MIN, values[value_index], NK_UCHAR_MAX); + unsigned char value = (unsigned char)NK_CLAMP((float)NK_UCHAR_MIN, values[value_index], (float)NK_UCHAR_MAX); NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(unsigned char)); } break; case NK_FORMAT_USHORT: { - nk_ushort value = (nk_ushort)NK_CLAMP(NK_USHORT_MIN, values[value_index], NK_USHORT_MAX); + nk_ushort value = (nk_ushort)NK_CLAMP((float)NK_USHORT_MIN, values[value_index], (float)NK_USHORT_MAX); NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(value)); } break; case NK_FORMAT_UINT: { - nk_uint value = (nk_uint)NK_CLAMP(NK_UINT_MIN, values[value_index], NK_UINT_MAX); + nk_uint value = (nk_uint)NK_CLAMP((float)NK_UINT_MIN, values[value_index], (float)NK_UINT_MAX); NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(nk_uint)); } break; @@ -8157,7 +9514,6 @@ nk_draw_vertex_element(void *dst, const float *values, int value_count, } } } - NK_INTERN void* nk_draw_vertex(void *dst, const struct nk_convert_config *config, struct nk_vec2 pos, struct nk_vec2 uv, struct nk_colorf color) @@ -8168,7 +9524,7 @@ nk_draw_vertex(void *dst, const struct nk_convert_config *config, void *address = (void*)((char*)dst + elem_iter->offset); switch (elem_iter->attribute) { case NK_VERTEX_ATTRIBUTE_COUNT: - default: NK_ASSERT(0 && "wrong element attribute"); + default: NK_ASSERT(0 && "wrong element attribute"); break; case NK_VERTEX_POSITION: nk_draw_vertex_element(address, &pos.x, 2, elem_iter->format); break; case NK_VERTEX_TEXCOORD: nk_draw_vertex_element(address, &uv.x, 2, elem_iter->format); break; case NK_VERTEX_COLOR: nk_draw_vertex_color(address, &color.r, elem_iter->format); break; @@ -8177,7 +9533,6 @@ nk_draw_vertex(void *dst, const struct nk_convert_config *config, } return result; } - NK_API void nk_draw_list_stroke_poly_line(struct nk_draw_list *list, const struct nk_vec2 *points, const unsigned int points_count, struct nk_color color, enum nk_draw_list_stroke closed, @@ -8230,7 +9585,6 @@ nk_draw_list_stroke_poly_line(struct nk_draw_list *list, const struct nk_vec2 *p nk_buffer_mark(list->vertices, NK_BUFFER_FRONT); size = pnt_size * ((thick_line) ? 5 : 3) * points_count; normals = (struct nk_vec2*) nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align); - NK_ASSERT(normals); if (!normals) return; temp = normals + points_count; @@ -8419,7 +9773,6 @@ nk_draw_list_stroke_poly_line(struct nk_draw_list *list, const struct nk_vec2 *p } } } - NK_API void nk_draw_list_fill_poly_convex(struct nk_draw_list *list, const struct nk_vec2 *points, const unsigned int points_count, @@ -8468,7 +9821,6 @@ nk_draw_list_fill_poly_convex(struct nk_draw_list *list, nk_buffer_mark(list->vertices, NK_BUFFER_FRONT); size = pnt_size * points_count; normals = (struct nk_vec2*) nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align); - NK_ASSERT(normals); if (!normals) return; vtx = (void*)((nk_byte*)list->vertices->memory.ptr + vertex_offset); @@ -8545,7 +9897,6 @@ nk_draw_list_fill_poly_convex(struct nk_draw_list *list, } } } - NK_API void nk_draw_list_path_clear(struct nk_draw_list *list) { @@ -8555,7 +9906,6 @@ nk_draw_list_path_clear(struct nk_draw_list *list) list->path_count = 0; list->path_offset = 0; } - NK_API void nk_draw_list_path_line_to(struct nk_draw_list *list, struct nk_vec2 pos) { @@ -8574,7 +9924,6 @@ nk_draw_list_path_line_to(struct nk_draw_list *list, struct nk_vec2 pos) if (!points) return; points[0] = pos; } - NK_API void nk_draw_list_path_arc_to_fast(struct nk_draw_list *list, struct nk_vec2 center, float radius, int a_min, int a_max) @@ -8591,7 +9940,6 @@ nk_draw_list_path_arc_to_fast(struct nk_draw_list *list, struct nk_vec2 center, } } } - NK_API void nk_draw_list_path_arc_to(struct nk_draw_list *list, struct nk_vec2 center, float radius, float a_min, float a_max, unsigned int segments) @@ -8637,7 +9985,6 @@ nk_draw_list_path_arc_to(struct nk_draw_list *list, struct nk_vec2 center, cy = new_cy; }} } - NK_API void nk_draw_list_path_rect_to(struct nk_draw_list *list, struct nk_vec2 a, struct nk_vec2 b, float rounding) @@ -8661,7 +10008,6 @@ nk_draw_list_path_rect_to(struct nk_draw_list *list, struct nk_vec2 a, nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, b.y - r), r, 3, 6); } } - NK_API void nk_draw_list_path_curve_to(struct nk_draw_list *list, struct nk_vec2 p2, struct nk_vec2 p3, struct nk_vec2 p4, unsigned int num_segments) @@ -8689,7 +10035,6 @@ nk_draw_list_path_curve_to(struct nk_draw_list *list, struct nk_vec2 p2, nk_draw_list_path_line_to(list, nk_vec2(x,y)); } } - NK_API void nk_draw_list_path_fill(struct nk_draw_list *list, struct nk_color color) { @@ -8700,7 +10045,6 @@ nk_draw_list_path_fill(struct nk_draw_list *list, struct nk_color color) nk_draw_list_fill_poly_convex(list, points, list->path_count, color, list->config.shape_AA); nk_draw_list_path_clear(list); } - NK_API void nk_draw_list_path_stroke(struct nk_draw_list *list, struct nk_color color, enum nk_draw_list_stroke closed, float thickness) @@ -8713,7 +10057,6 @@ nk_draw_list_path_stroke(struct nk_draw_list *list, struct nk_color color, closed, thickness, list->config.line_AA); nk_draw_list_path_clear(list); } - NK_API void nk_draw_list_stroke_line(struct nk_draw_list *list, struct nk_vec2 a, struct nk_vec2 b, struct nk_color col, float thickness) @@ -8729,7 +10072,6 @@ nk_draw_list_stroke_line(struct nk_draw_list *list, struct nk_vec2 a, } nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness); } - NK_API void nk_draw_list_fill_rect(struct nk_draw_list *list, struct nk_rect rect, struct nk_color col, float rounding) @@ -8745,7 +10087,6 @@ nk_draw_list_fill_rect(struct nk_draw_list *list, struct nk_rect rect, nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); } nk_draw_list_path_fill(list, col); } - NK_API void nk_draw_list_stroke_rect(struct nk_draw_list *list, struct nk_rect rect, struct nk_color col, float rounding, float thickness) @@ -8760,7 +10101,6 @@ nk_draw_list_stroke_rect(struct nk_draw_list *list, struct nk_rect rect, nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); } nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); } - NK_API void nk_draw_list_fill_rect_multi_color(struct nk_draw_list *list, struct nk_rect rect, struct nk_color left, struct nk_color top, struct nk_color right, @@ -8795,7 +10135,6 @@ nk_draw_list_fill_rect_multi_color(struct nk_draw_list *list, struct nk_rect rec vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y + rect.h), list->config.null.uv, col_right); vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y + rect.h), list->config.null.uv, col_bottom); } - NK_API void nk_draw_list_fill_triangle(struct nk_draw_list *list, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color col) @@ -8807,7 +10146,6 @@ nk_draw_list_fill_triangle(struct nk_draw_list *list, struct nk_vec2 a, nk_draw_list_path_line_to(list, c); nk_draw_list_path_fill(list, col); } - NK_API void nk_draw_list_stroke_triangle(struct nk_draw_list *list, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color col, float thickness) @@ -8819,7 +10157,6 @@ nk_draw_list_stroke_triangle(struct nk_draw_list *list, struct nk_vec2 a, nk_draw_list_path_line_to(list, c); nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); } - NK_API void nk_draw_list_fill_circle(struct nk_draw_list *list, struct nk_vec2 center, float radius, struct nk_color col, unsigned int segs) @@ -8831,7 +10168,6 @@ nk_draw_list_fill_circle(struct nk_draw_list *list, struct nk_vec2 center, nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); nk_draw_list_path_fill(list, col); } - NK_API void nk_draw_list_stroke_circle(struct nk_draw_list *list, struct nk_vec2 center, float radius, struct nk_color col, unsigned int segs, float thickness) @@ -8843,7 +10179,6 @@ nk_draw_list_stroke_circle(struct nk_draw_list *list, struct nk_vec2 center, nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); } - NK_API void nk_draw_list_stroke_curve(struct nk_draw_list *list, struct nk_vec2 p0, struct nk_vec2 cp0, struct nk_vec2 cp1, struct nk_vec2 p1, @@ -8855,7 +10190,6 @@ nk_draw_list_stroke_curve(struct nk_draw_list *list, struct nk_vec2 p0, nk_draw_list_path_curve_to(list, cp0, cp1, p1, segments); nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness); } - NK_INTERN void nk_draw_list_push_rect_uv(struct nk_draw_list *list, struct nk_vec2 a, struct nk_vec2 c, struct nk_vec2 uva, struct nk_vec2 uvc, @@ -8893,7 +10227,6 @@ nk_draw_list_push_rect_uv(struct nk_draw_list *list, struct nk_vec2 a, vtx = nk_draw_vertex(vtx, &list->config, c, uvc, col); vtx = nk_draw_vertex(vtx, &list->config, d, uvd, col); } - NK_API void nk_draw_list_add_image(struct nk_draw_list *list, struct nk_image texture, struct nk_rect rect, struct nk_color color) @@ -8915,7 +10248,6 @@ nk_draw_list_add_image(struct nk_draw_list *list, struct nk_image texture, nk_vec2(rect.x + rect.w, rect.y + rect.h), nk_vec2(0.0f, 0.0f), nk_vec2(1.0f, 1.0f),color); } - NK_API void nk_draw_list_add_text(struct nk_draw_list *list, const struct nk_user_font *font, struct nk_rect rect, const char *text, int len, float font_height, @@ -8966,7 +10298,6 @@ nk_draw_list_add_text(struct nk_draw_list *list, const struct nk_user_font *font unicode = next; } } - NK_API nk_flags nk_convert(struct nk_context *ctx, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, @@ -9112,26 +10443,26 @@ nk_convert(struct nk_context *ctx, struct nk_buffer *cmds, NK_API const struct nk_draw_command* nk__draw_begin(const struct nk_context *ctx, const struct nk_buffer *buffer) -{return nk__draw_list_begin(&ctx->draw_list, buffer);} - +{ + return nk__draw_list_begin(&ctx->draw_list, buffer); +} NK_API const struct nk_draw_command* nk__draw_end(const struct nk_context *ctx, const struct nk_buffer *buffer) -{return nk__draw_list_end(&ctx->draw_list, buffer);} - +{ + return nk__draw_list_end(&ctx->draw_list, buffer); +} NK_API const struct nk_draw_command* nk__draw_next(const struct nk_draw_command *cmd, const struct nk_buffer *buffer, const struct nk_context *ctx) -{return nk__draw_list_next(cmd, buffer, &ctx->draw_list);} - +{ + return nk__draw_list_next(cmd, buffer, &ctx->draw_list); +} #endif -/* - * ============================================================== - * - * FONT HANDLING - * - * =============================================================== - */ + + + + #ifdef NK_INCLUDE_FONT_BAKING /* ------------------------------------------------------------- * @@ -9203,7 +10534,6 @@ nk_rp_setup_allow_out_of_mem(struct nk_rp_context *context, int allow_out_of_mem context->align = (context->width + context->num_nodes-1) / context->num_nodes; } } - NK_INTERN void nk_rp_init_target(struct nk_rp_context *context, int width, int height, struct nk_rp_node *nodes, int num_nodes) @@ -9233,7 +10563,6 @@ nk_rp_init_target(struct nk_rp_context *context, int width, int height, context->extra[1].y = 65535; context->extra[1].next = 0; } - /* find minimum y position if it starts at x1 */ NK_INTERN int nk_rp__skyline_find_min_y(struct nk_rp_context *c, struct nk_rp_node *first, @@ -9278,7 +10607,6 @@ nk_rp__skyline_find_min_y(struct nk_rp_context *c, struct nk_rp_node *first, *pwaste = waste_area; return min_y; } - NK_INTERN struct nk_rp__findresult nk_rp__skyline_find_best_pos(struct nk_rp_context *c, int width, int height) { @@ -9374,7 +10702,6 @@ nk_rp__skyline_find_best_pos(struct nk_rp_context *c, int width, int height) fr.y = best_y; return fr; } - NK_INTERN struct nk_rp__findresult nk_rp__skyline_pack_rectangle(struct nk_rp_context *context, int width, int height) { @@ -9427,7 +10754,6 @@ nk_rp__skyline_pack_rectangle(struct nk_rp_context *context, int width, int heig cur->x = (nk_rp_coord) (res.x + width); return res; } - NK_INTERN int nk_rect_height_compare(const void *a, const void *b) { @@ -9439,7 +10765,6 @@ nk_rect_height_compare(const void *a, const void *b) return 1; return (p->w > q->w) ? -1 : (p->w < q->w); } - NK_INTERN int nk_rect_original_order(const void *a, const void *b) { @@ -9447,7 +10772,6 @@ nk_rect_original_order(const void *a, const void *b) const struct nk_rp_rect *q = (const struct nk_rp_rect *) b; return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); } - NK_INTERN void nk_rp_qsort(struct nk_rp_rect *array, unsigned int len, int(*cmp)(const void*,const void*)) { @@ -9477,7 +10801,6 @@ nk_rp_qsort(struct nk_rp_rect *array, unsigned int len, int(*cmp)(const void*,co } #undef NK_MAX_SORT_STACK } - NK_INTERN void nk_rp_pack_rects(struct nk_rp_context *context, struct nk_rp_rect *rects, int num_rects) { @@ -9700,7 +11023,6 @@ nk_tt__find_table(const nk_byte *data, nk_uint fontstart, const char *tag) } return 0; } - NK_INTERN int nk_tt_InitFont(struct nk_tt_fontinfo *info, const unsigned char *data2, int fontstart) { @@ -9757,7 +11079,6 @@ nk_tt_InitFont(struct nk_tt_fontinfo *info, const unsigned char *data2, int font info->indexToLocFormat = nk_ttUSHORT(data+info->head + 50); return 1; } - NK_INTERN int nk_tt_FindGlyphIndex(const struct nk_tt_fontinfo *info, int unicode_codepoint) { @@ -9851,7 +11172,6 @@ nk_tt_FindGlyphIndex(const struct nk_tt_fontinfo *info, int unicode_codepoint) NK_ASSERT(0); return 0; } - NK_INTERN void nk_tt_setvertex(struct nk_tt_vertex *v, nk_byte type, nk_int x, nk_int y, nk_int cx, nk_int cy) { @@ -9861,7 +11181,6 @@ nk_tt_setvertex(struct nk_tt_vertex *v, nk_byte type, nk_int x, nk_int y, nk_int v->cx = (nk_short) cx; v->cy = (nk_short) cy; } - NK_INTERN int nk_tt__GetGlyfOffset(const struct nk_tt_fontinfo *info, int glyph_index) { @@ -9878,7 +11197,6 @@ nk_tt__GetGlyfOffset(const struct nk_tt_fontinfo *info, int glyph_index) } return g1==g2 ? -1 : g1; /* if length is 0, return -1 */ } - NK_INTERN int nk_tt_GetGlyphBox(const struct nk_tt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) @@ -9892,9 +11210,8 @@ nk_tt_GetGlyphBox(const struct nk_tt_fontinfo *info, int glyph_index, if (y1) *y1 = nk_ttSHORT(info->data + g + 8); return 1; } - NK_INTERN int -stbtt__close_shape(struct nk_tt_vertex *vertices, int num_vertices, int was_off, +nk_tt__close_shape(struct nk_tt_vertex *vertices, int num_vertices, int was_off, int start_off, nk_int sx, nk_int sy, nk_int scx, nk_int scy, nk_int cx, nk_int cy) { if (start_off) { @@ -9909,7 +11226,6 @@ stbtt__close_shape(struct nk_tt_vertex *vertices, int num_vertices, int was_off, } return num_vertices; } - NK_INTERN int nk_tt_GetGlyphShape(const struct nk_tt_fontinfo *info, struct nk_allocator *alloc, int glyph_index, struct nk_tt_vertex **pvertices) @@ -10000,7 +11316,7 @@ nk_tt_GetGlyphShape(const struct nk_tt_fontinfo *info, struct nk_allocator *allo if (next_move == i) { if (i != 0) - num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + num_vertices = nk_tt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); /* now start the new one */ start_off = !(flags & 1); @@ -10043,7 +11359,7 @@ nk_tt_GetGlyphShape(const struct nk_tt_fontinfo *info, struct nk_allocator *allo } } } - num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + num_vertices = nk_tt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); } else if (numberOfContours == -1) { /* Compound shapes. */ int more = 1; @@ -10133,7 +11449,6 @@ nk_tt_GetGlyphShape(const struct nk_tt_fontinfo *info, struct nk_allocator *allo *pvertices = vertices; return num_vertices; } - NK_INTERN void nk_tt_GetGlyphHMetrics(const struct nk_tt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) @@ -10151,7 +11466,6 @@ nk_tt_GetGlyphHMetrics(const struct nk_tt_fontinfo *info, int glyph_index, *leftSideBearing = nk_ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); } } - NK_INTERN void nk_tt_GetFontVMetrics(const struct nk_tt_fontinfo *info, int *ascent, int *descent, int *lineGap) @@ -10160,14 +11474,12 @@ nk_tt_GetFontVMetrics(const struct nk_tt_fontinfo *info, if (descent) *descent = nk_ttSHORT(info->data+info->hhea + 6); if (lineGap) *lineGap = nk_ttSHORT(info->data+info->hhea + 8); } - NK_INTERN float nk_tt_ScaleForPixelHeight(const struct nk_tt_fontinfo *info, float height) { int fheight = nk_ttSHORT(info->data + info->hhea + 4) - nk_ttSHORT(info->data + info->hhea + 6); return (float) height / (float)fheight; } - NK_INTERN float nk_tt_ScaleForMappingEmToPixels(const struct nk_tt_fontinfo *info, float pixels) { @@ -10198,7 +11510,6 @@ nk_tt_GetGlyphBitmapBoxSubpixel(const struct nk_tt_fontinfo *font, if (iy1) *iy1 = nk_iceilf ((float)-y0 * scale_y + shift_y); } } - NK_INTERN void nk_tt_GetGlyphBitmapBox(const struct nk_tt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) @@ -10231,14 +11542,12 @@ nk_tt__hheap_alloc(struct nk_tt__hheap *hh, nk_size size) return (char *) (hh->head) + size * (nk_size)hh->num_remaining_in_head_chunk; } } - NK_INTERN void nk_tt__hheap_free(struct nk_tt__hheap *hh, void *p) { *(void **) p = hh->first_free; hh->first_free = p; } - NK_INTERN void nk_tt__hheap_cleanup(struct nk_tt__hheap *hh) { @@ -10249,7 +11558,6 @@ nk_tt__hheap_cleanup(struct nk_tt__hheap *hh) c = n; } } - NK_INTERN struct nk_tt__active_edge* nk_tt__new_active(struct nk_tt__hheap *hh, struct nk_tt__edge *e, int off_x, float start_point) @@ -10269,7 +11577,6 @@ nk_tt__new_active(struct nk_tt__hheap *hh, struct nk_tt__edge *e, z->next = 0; return z; } - NK_INTERN void nk_tt__handle_clipped_edge(float *scanline, int x, struct nk_tt__active_edge *e, float x0, float y0, float x1, float y1) @@ -10303,7 +11610,6 @@ nk_tt__handle_clipped_edge(float *scanline, int x, struct nk_tt__active_edge *e, scanline[x] += (float)e->direction * (float)(y1-y0) * (1.0f-((x0-(float)x)+(x1-(float)x))/2.0f); } } - NK_INTERN void nk_tt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, struct nk_tt__active_edge *e, float y_top) @@ -10464,12 +11770,11 @@ nk_tt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, e = e->next; } } - -/* directly AA rasterize edges w/o supersampling */ NK_INTERN void nk_tt__rasterize_sorted_edges(struct nk_tt__bitmap *result, struct nk_tt__edge *e, int n, int vsubsample, int off_x, int off_y, struct nk_allocator *alloc) { + /* directly AA rasterize edges w/o supersampling */ struct nk_tt__hheap hh; struct nk_tt__active_edge *active = 0; int y,j=0, i; @@ -10556,12 +11861,11 @@ nk_tt__rasterize_sorted_edges(struct nk_tt__bitmap *result, struct nk_tt__edge * if (scanline != scanline_data) alloc->free(alloc->userdata, scanline); } - -#define NK_TT__COMPARE(a,b) ((a)->y0 < (b)->y0) NK_INTERN void nk_tt__sort_edges_ins_sort(struct nk_tt__edge *p, int n) { int i,j; + #define NK_TT__COMPARE(a,b) ((a)->y0 < (b)->y0) for (i=1; i < n; ++i) { struct nk_tt__edge t = p[i], *a = &t; j = i; @@ -10576,7 +11880,6 @@ nk_tt__sort_edges_ins_sort(struct nk_tt__edge *p, int n) p[j] = t; } } - NK_INTERN void nk_tt__sort_edges_quicksort(struct nk_tt__edge *p, int n) { @@ -10644,14 +11947,12 @@ nk_tt__sort_edges_quicksort(struct nk_tt__edge *p, int n) } } } - NK_INTERN void nk_tt__sort_edges(struct nk_tt__edge *p, int n) { nk_tt__sort_edges_quicksort(p, n); nk_tt__sort_edges_ins_sort(p, n); } - NK_INTERN void nk_tt__rasterize(struct nk_tt__bitmap *result, struct nk_tt__point *pts, int *wcount, int windings, float scale_x, float scale_y, @@ -10701,13 +12002,12 @@ nk_tt__rasterize(struct nk_tt__bitmap *result, struct nk_tt__point *pts, } /* now sort the edges by their highest point (should snap to integer, and then by x) */ - /*STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); */ + /*STBTT_sort(e, n, sizeof(e[0]), nk_tt__edge_compare); */ nk_tt__sort_edges(e, n); /* now, traverse the scanlines and find the intersections on each scanline, use xor winding rule */ nk_tt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, alloc); alloc->free(alloc->userdata, e); } - NK_INTERN void nk_tt__add_point(struct nk_tt__point *points, int n, float x, float y) { @@ -10715,7 +12015,6 @@ nk_tt__add_point(struct nk_tt__point *points, int n, float x, float y) points[n].x = x; points[n].y = y; } - NK_INTERN int nk_tt__tesselate_curve(struct nk_tt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, @@ -10744,13 +12043,12 @@ nk_tt__tesselate_curve(struct nk_tt__point *points, int *num_points, } return 1; } - -/* returns number of contours */ NK_INTERN struct nk_tt__point* nk_tt_FlattenCurves(struct nk_tt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, struct nk_allocator *alloc) { + /* returns number of contours */ struct nk_tt__point *points=0; int num_points=0; float objspace_flatness_squared = objspace_flatness * objspace_flatness; @@ -10823,7 +12121,6 @@ error: *num_contours = 0; return 0; } - NK_INTERN void nk_tt_Rasterize(struct nk_tt__bitmap *result, float flatness_in_pixels, struct nk_tt_vertex *vertices, int num_verts, @@ -10843,7 +12140,6 @@ nk_tt_Rasterize(struct nk_tt__bitmap *result, float flatness_in_pixels, alloc->free(alloc->userdata, windings); } } - NK_INTERN void nk_tt_MakeGlyphBitmapSubpixel(const struct nk_tt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, @@ -10901,14 +12197,12 @@ nk_tt_PackBegin(struct nk_tt_pack_context *spc, unsigned char *pixels, NK_MEMSET(pixels, 0, (nk_size)(pw*ph)); /* background of 0 around pixels */ return 1; } - NK_INTERN void nk_tt_PackEnd(struct nk_tt_pack_context *spc, struct nk_allocator *alloc) { alloc->free(alloc->userdata, spc->nodes); alloc->free(alloc->userdata, spc->pack_info); } - NK_INTERN void nk_tt_PackSetOversampling(struct nk_tt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) @@ -10920,7 +12214,6 @@ nk_tt_PackSetOversampling(struct nk_tt_pack_context *spc, if (v_oversample <= NK_TT_MAX_OVERSAMPLE) spc->v_oversample = v_oversample; } - NK_INTERN void nk_tt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, int kernel_width) @@ -10984,7 +12277,6 @@ nk_tt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, pixels += stride_in_bytes; } } - NK_INTERN void nk_tt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, int kernel_width) @@ -11048,7 +12340,6 @@ nk_tt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, pixels += 1; } } - NK_INTERN float nk_tt__oversample_shift(int oversample) { @@ -11061,13 +12352,12 @@ nk_tt__oversample_shift(int oversample) /* direction to counter this. */ return (float)-(oversample - 1) / (2.0f * (float)oversample); } - -/* rects array must be big enough to accommodate all characters in the given ranges */ NK_INTERN int nk_tt_PackFontRangesGatherRects(struct nk_tt_pack_context *spc, struct nk_tt_fontinfo *info, struct nk_tt_pack_range *ranges, int num_ranges, struct nk_rp_rect *rects) { + /* rects array must be big enough to accommodate all characters in the given ranges */ int i,j,k; k = 0; @@ -11093,7 +12383,6 @@ nk_tt_PackFontRangesGatherRects(struct nk_tt_pack_context *spc, } return k; } - NK_INTERN int nk_tt_PackFontRangesRenderIntoRects(struct nk_tt_pack_context *spc, struct nk_tt_fontinfo *info, struct nk_tt_pack_range *ranges, @@ -11177,7 +12466,6 @@ nk_tt_PackFontRangesRenderIntoRects(struct nk_tt_pack_context *spc, spc->v_oversample = (unsigned int)old_v_over; return return_value; } - NK_INTERN void nk_tt_GetPackedQuad(struct nk_tt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, struct nk_tt_aligned_quad *q, @@ -11245,7 +12533,6 @@ nk_range_count(const nk_rune *range) while (*(iter++) != 0); return (iter == range) ? 0 : (int)((iter - range)/2); } - NK_INTERN int nk_range_glyph_count(const nk_rune *range, int count) { @@ -11261,14 +12548,12 @@ nk_range_glyph_count(const nk_rune *range, int count) } return total_glyphs; } - NK_API const nk_rune* nk_font_default_glyph_ranges(void) { NK_STORAGE const nk_rune ranges[] = {0x0020, 0x00FF, 0}; return ranges; } - NK_API const nk_rune* nk_font_chinese_glyph_ranges(void) { @@ -11282,7 +12567,6 @@ nk_font_chinese_glyph_ranges(void) }; return ranges; } - NK_API const nk_rune* nk_font_cyrillic_glyph_ranges(void) { @@ -11295,7 +12579,6 @@ nk_font_cyrillic_glyph_ranges(void) }; return ranges; } - NK_API const nk_rune* nk_font_korean_glyph_ranges(void) { @@ -11307,7 +12590,6 @@ nk_font_korean_glyph_ranges(void) }; return ranges; } - NK_INTERN void nk_font_baker_memory(nk_size *temp, int *glyph_count, struct nk_font_config *config_list, int count) @@ -11340,7 +12622,6 @@ nk_font_baker_memory(nk_size *temp, int *glyph_count, *temp += nk_rect_align + nk_range_align + nk_char_align; *temp += nk_build_align + nk_baker_align; } - NK_INTERN struct nk_font_baker* nk_font_baker(void *memory, int glyph_count, int count, struct nk_allocator *alloc) { @@ -11355,7 +12636,6 @@ nk_font_baker(void *memory, int glyph_count, int count, struct nk_allocator *all baker->alloc = *alloc; return baker; } - NK_INTERN int nk_font_bake_pack(struct nk_font_baker *baker, nk_size *image_memory, int *width, int *height, struct nk_recti *custom, @@ -11404,8 +12684,8 @@ nk_font_bake_pack(struct nk_font_baker *baker, /* pack custom user data first so it will be in the upper left corner*/ struct nk_rp_rect custom_space; nk_zero(&custom_space, sizeof(custom_space)); - custom_space.w = (nk_rp_coord)((custom->w * 2) + 1); - custom_space.h = (nk_rp_coord)(custom->h + 1); + custom_space.w = (nk_rp_coord)(custom->w); + custom_space.h = (nk_rp_coord)(custom->h); nk_tt_PackSetOversampling(&baker->spc, 1, 1); nk_rp_pack_rects((struct nk_rp_context*)baker->spc.pack_info, &custom_space, 1); @@ -11470,7 +12750,6 @@ nk_font_bake_pack(struct nk_font_baker *baker, *image_memory = (nk_size)(*width) * (nk_size)(*height); return nk_true; } - NK_INTERN void nk_font_bake(struct nk_font_baker *baker, void *image_memory, int width, int height, struct nk_font_glyph *glyphs, int glyphs_count, @@ -11581,7 +12860,6 @@ nk_font_bake(struct nk_font_baker *baker, void *image_memory, int width, int hei } while ((it = it->n) != config_iter); } } - NK_INTERN void nk_font_bake_custom_data(void *img_memory, int img_width, int img_height, struct nk_recti img_dst, const char *texture_data_mask, int tex_width, @@ -11610,7 +12888,6 @@ nk_font_bake_custom_data(void *img_memory, int img_width, int img_height, } } } - NK_INTERN void nk_font_bake_convert(void *out_memory, int img_width, int img_height, const void *in_memory) @@ -11668,7 +12945,6 @@ nk_font_text_width(nk_handle handle, float height, const char *text, int len) } return text_width; } - #ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT NK_INTERN void nk_font_query_font_glyph(nk_handle handle, float height, @@ -11697,7 +12973,6 @@ nk_font_query_font_glyph(nk_handle handle, float height, glyph->uv[1] = nk_vec2(g->u1, g->v1); } #endif - NK_API const struct nk_font_glyph* nk_font_find_glyph(struct nk_font *font, nk_rune unicode) { @@ -11726,7 +13001,6 @@ nk_font_find_glyph(struct nk_font *font, nk_rune unicode) } while ((iter = iter->n) != font->config); return glyph; } - NK_INTERN void nk_font_init(struct nk_font *font, float pixel_height, nk_rune fallback_codepoint, struct nk_font_glyph *glyphs, @@ -11766,17 +13040,16 @@ nk_font_init(struct nk_font *font, float pixel_height, * MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) * Download and more information at http://upperbounds.net *-----------------------------------------------------------------------------*/ -#ifdef NK_INCLUDE_DEFAULT_FONT - - #ifdef __clang__ +#ifdef __clang__ #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Woverlength-strings" #elif defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Woverlength-strings" #endif +#ifdef NK_INCLUDE_DEFAULT_FONT + NK_GLOBAL const char nk_proggy_clean_ttf_compressed_data_base85[11980+1] = "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/" "2*>]b(MC;$jPfY.;h^`IWM9= 0x20) { /* use fewer if's for cases that expand small */ if (*i >= 0x80) nk__match(nk__dout-i[1]-1, (unsigned int)i[0] - 0x80 + 1), i += 2; else if (*i >= 0x40) nk__match(nk__dout-(nk__in2(0) - 0x4000 + 1), (unsigned int)i[2]+1), i += 3; @@ -11958,7 +13229,6 @@ nk_decompress_token(unsigned char *i) } return i; } - NK_INTERN unsigned int nk_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) { @@ -11989,7 +13259,6 @@ nk_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) } return (unsigned int)(s2 << 16) + (unsigned int)s1; } - NK_INTERN unsigned int nk_decompress(unsigned char *output, unsigned char *i, unsigned int length) { @@ -12024,11 +13293,11 @@ nk_decompress(unsigned char *output, unsigned char *i, unsigned int length) return 0; } } - NK_INTERN unsigned int nk_decode_85_byte(char c) -{ return (unsigned int)((c >= '\\') ? c-36 : c-35); } - +{ + return (unsigned int)((c >= '\\') ? c-36 : c-35); +} NK_INTERN void nk_decode_85(unsigned char* dst, const unsigned char* src) { @@ -12078,7 +13347,6 @@ nk_font_config(float pixel_height) cfg.n = 0; return cfg; } - #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_API void nk_font_atlas_init_default(struct nk_font_atlas *atlas) @@ -12094,7 +13362,6 @@ nk_font_atlas_init_default(struct nk_font_atlas *atlas) atlas->permanent.free = nk_mfree; } #endif - NK_API void nk_font_atlas_init(struct nk_font_atlas *atlas, struct nk_allocator *alloc) { @@ -12105,7 +13372,6 @@ nk_font_atlas_init(struct nk_font_atlas *atlas, struct nk_allocator *alloc) atlas->permanent = *alloc; atlas->temporary = *alloc; } - NK_API void nk_font_atlas_init_custom(struct nk_font_atlas *atlas, struct nk_allocator *permanent, struct nk_allocator *temporary) @@ -12118,7 +13384,6 @@ nk_font_atlas_init_custom(struct nk_font_atlas *atlas, atlas->permanent = *permanent; atlas->temporary = *temporary; } - NK_API void nk_font_atlas_begin(struct nk_font_atlas *atlas) { @@ -12136,7 +13401,6 @@ nk_font_atlas_begin(struct nk_font_atlas *atlas) atlas->pixel = 0; } } - NK_API struct nk_font* nk_font_atlas_add(struct nk_font_atlas *atlas, const struct nk_font_config *config) { @@ -12224,7 +13488,6 @@ nk_font_atlas_add(struct nk_font_atlas *atlas, const struct nk_font_config *conf atlas->font_num++; return font; } - NK_API struct nk_font* nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, void *memory, nk_size size, float height, const struct nk_font_config *config) @@ -12249,7 +13512,6 @@ nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, void *memory, cfg.ttf_data_owned_by_atlas = 0; return nk_font_atlas_add(atlas, &cfg); } - #ifdef NK_INCLUDE_STANDARD_IO NK_API struct nk_font* nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, const char *file_path, @@ -12277,7 +13539,6 @@ nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, const char *file_path, return nk_font_atlas_add(atlas, &cfg); } #endif - NK_API struct nk_font* nk_font_atlas_add_compressed(struct nk_font_atlas *atlas, void *compressed_data, nk_size compressed_size, float height, @@ -12313,7 +13574,6 @@ nk_font_atlas_add_compressed(struct nk_font_atlas *atlas, cfg.ttf_data_owned_by_atlas = 1; return nk_font_atlas_add(atlas, &cfg); } - NK_API struct nk_font* nk_font_atlas_add_compressed_base85(struct nk_font_atlas *atlas, const char *data_base85, float height, const struct nk_font_config *config) @@ -12358,7 +13618,6 @@ nk_font_atlas_add_default(struct nk_font_atlas *atlas, nk_proggy_clean_ttf_compressed_data_base85, pixel_height, config); } #endif - NK_API const void* nk_font_atlas_bake(struct nk_font_atlas *atlas, int *width, int *height, enum nk_font_atlas_format fmt) @@ -12446,7 +13705,7 @@ nk_font_atlas_bake(struct nk_font_atlas *atlas, int *width, int *height, /* initialize each cursor */ {NK_STORAGE const struct nk_vec2 nk_cursor_data[NK_CURSOR_COUNT][3] = { - /* Pos ----- Size ------- Offset --*/ + /* Pos Size Offset */ {{ 0, 3}, {12,19}, { 0, 0}}, {{13, 0}, { 7,16}, { 4, 8}}, {{31, 0}, {23,23}, {11,11}}, @@ -12483,7 +13742,6 @@ failed: } return 0; } - NK_API void nk_font_atlas_end(struct nk_font_atlas *atlas, nk_handle texture, struct nk_draw_null_texture *null) @@ -12519,7 +13777,6 @@ nk_font_atlas_end(struct nk_font_atlas *atlas, nk_handle texture, atlas->custom.w = 0; atlas->custom.h = 0; } - NK_API void nk_font_atlas_cleanup(struct nk_font_atlas *atlas) { @@ -12542,7 +13799,6 @@ nk_font_atlas_cleanup(struct nk_font_atlas *atlas) } } } - NK_API void nk_font_atlas_clear(struct nk_font_atlas *atlas) { @@ -12583,7 +13839,12 @@ nk_font_atlas_clear(struct nk_font_atlas *atlas) nk_zero_struct(*atlas); } #endif -/* ============================================================== + + + + + +/* =============================================================== * * INPUT * @@ -12608,7 +13869,6 @@ nk_input_begin(struct nk_context *ctx) for (i = 0; i < NK_KEY_MAX; i++) in->keyboard.keys[i].clicked = 0; } - NK_API void nk_input_end(struct nk_context *ctx) { @@ -12624,7 +13884,6 @@ nk_input_end(struct nk_context *ctx) in->mouse.grab = 0; } } - NK_API void nk_input_motion(struct nk_context *ctx, int x, int y) { @@ -12637,7 +13896,6 @@ nk_input_motion(struct nk_context *ctx, int x, int y) in->mouse.delta.x = in->mouse.pos.x - in->mouse.prev.x; in->mouse.delta.y = in->mouse.pos.y - in->mouse.prev.y; } - NK_API void nk_input_key(struct nk_context *ctx, enum nk_keys key, int down) { @@ -12649,7 +13907,6 @@ nk_input_key(struct nk_context *ctx, enum nk_keys key, int down) in->keyboard.keys[key].clicked++; in->keyboard.keys[key].down = down; } - NK_API void nk_input_button(struct nk_context *ctx, enum nk_buttons id, int x, int y, int down) { @@ -12666,7 +13923,6 @@ nk_input_button(struct nk_context *ctx, enum nk_buttons id, int x, int y, int do btn->down = down; btn->clicked++; } - NK_API void nk_input_scroll(struct nk_context *ctx, struct nk_vec2 val) { @@ -12675,7 +13931,6 @@ nk_input_scroll(struct nk_context *ctx, struct nk_vec2 val) ctx->input.mouse.scroll_delta.x += val.x; ctx->input.mouse.scroll_delta.y += val.y; } - NK_API void nk_input_glyph(struct nk_context *ctx, const nk_glyph glyph) { @@ -12694,7 +13949,6 @@ nk_input_glyph(struct nk_context *ctx, const nk_glyph glyph) in->keyboard.text_len += len; } } - NK_API void nk_input_char(struct nk_context *ctx, char c) { @@ -12704,7 +13958,6 @@ nk_input_char(struct nk_context *ctx, char c) glyph[0] = c; nk_input_glyph(ctx, glyph); } - NK_API void nk_input_unicode(struct nk_context *ctx, nk_rune unicode) { @@ -12714,7 +13967,6 @@ nk_input_unicode(struct nk_context *ctx, nk_rune unicode) nk_utf_encode(unicode, rune, NK_UTF_SIZE); nk_input_glyph(ctx, rune); } - NK_API int nk_input_has_mouse_click(const struct nk_input *i, enum nk_buttons id) { @@ -12723,7 +13975,6 @@ nk_input_has_mouse_click(const struct nk_input *i, enum nk_buttons id) btn = &i->mouse.buttons[id]; return (btn->clicked && btn->down == nk_false) ? nk_true : nk_false; } - NK_API int nk_input_has_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b) @@ -12735,7 +13986,6 @@ nk_input_has_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, return nk_false; return nk_true; } - NK_API int nk_input_has_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b, int down) @@ -12745,7 +13995,6 @@ nk_input_has_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons btn = &i->mouse.buttons[id]; return nk_input_has_mouse_click_in_rect(i, id, b) && (btn->down == down); } - NK_API int nk_input_is_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b) @@ -12756,7 +14005,6 @@ nk_input_is_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, return (nk_input_has_mouse_click_down_in_rect(i, id, b, nk_false) && btn->clicked) ? nk_true : nk_false; } - NK_API int nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b, int down) @@ -12767,7 +14015,6 @@ nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons i return (nk_input_has_mouse_click_down_in_rect(i, id, b, down) && btn->clicked) ? nk_true : nk_false; } - NK_API int nk_input_any_mouse_click_in_rect(const struct nk_input *in, struct nk_rect b) { @@ -12776,21 +14023,18 @@ nk_input_any_mouse_click_in_rect(const struct nk_input *in, struct nk_rect b) down = down || nk_input_is_mouse_click_in_rect(in, (enum nk_buttons)i, b); return down; } - NK_API int nk_input_is_mouse_hovering_rect(const struct nk_input *i, struct nk_rect rect) { if (!i) return nk_false; return NK_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h); } - NK_API int nk_input_is_mouse_prev_hovering_rect(const struct nk_input *i, struct nk_rect rect) { if (!i) return nk_false; return NK_INBOX(i->mouse.prev.x, i->mouse.prev.y, rect.x, rect.y, rect.w, rect.h); } - NK_API int nk_input_mouse_clicked(const struct nk_input *i, enum nk_buttons id, struct nk_rect rect) { @@ -12798,14 +14042,12 @@ nk_input_mouse_clicked(const struct nk_input *i, enum nk_buttons id, struct nk_r if (!nk_input_is_mouse_hovering_rect(i, rect)) return nk_false; return nk_input_is_mouse_click_in_rect(i, id, rect); } - NK_API int nk_input_is_mouse_down(const struct nk_input *i, enum nk_buttons id) { if (!i) return nk_false; return i->mouse.buttons[id].down; } - NK_API int nk_input_is_mouse_pressed(const struct nk_input *i, enum nk_buttons id) { @@ -12816,14 +14058,12 @@ nk_input_is_mouse_pressed(const struct nk_input *i, enum nk_buttons id) return nk_true; return nk_false; } - NK_API int nk_input_is_mouse_released(const struct nk_input *i, enum nk_buttons id) { if (!i) return nk_false; return (!i->mouse.buttons[id].down && i->mouse.buttons[id].clicked); } - NK_API int nk_input_is_key_pressed(const struct nk_input *i, enum nk_keys key) { @@ -12834,7 +14074,6 @@ nk_input_is_key_pressed(const struct nk_input *i, enum nk_keys key) return nk_true; return nk_false; } - NK_API int nk_input_is_key_released(const struct nk_input *i, enum nk_keys key) { @@ -12845,7 +14084,6 @@ nk_input_is_key_released(const struct nk_input *i, enum nk_keys key) return nk_true; return nk_false; } - NK_API int nk_input_is_key_down(const struct nk_input *i, enum nk_keys key) { @@ -12856,3637 +14094,45 @@ nk_input_is_key_down(const struct nk_input *i, enum nk_keys key) return nk_false; } -/* - * ============================================================== - * - * TEXT EDITOR - * - * =============================================================== - */ -/* stb_textedit.h - v1.8 - public domain - Sean Barrett */ -struct nk_text_find { - float x,y; /* position of n'th character */ - float height; /* height of line */ - int first_char, length; /* first char of row, and length */ - int prev_first; /*_ first char of previous row */ -}; -struct nk_text_edit_row { - float x0,x1; - /* starting x location, end x location (allows for align=right, etc) */ - float baseline_y_delta; - /* position of baseline relative to previous row's baseline*/ - float ymin,ymax; - /* height of row above and below baseline */ - int num_chars; -}; -/* forward declarations */ -NK_INTERN void nk_textedit_makeundo_delete(struct nk_text_edit*, int, int); -NK_INTERN void nk_textedit_makeundo_insert(struct nk_text_edit*, int, int); -NK_INTERN void nk_textedit_makeundo_replace(struct nk_text_edit*, int, int, int); -#define NK_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) - -NK_INTERN float -nk_textedit_get_width(const struct nk_text_edit *edit, int line_start, int char_id, - const struct nk_user_font *font) -{ - int len = 0; - nk_rune unicode = 0; - const char *str = nk_str_at_const(&edit->string, line_start + char_id, &unicode, &len); - return font->width(font->userdata, font->height, str, len); -} - -NK_INTERN void -nk_textedit_layout_row(struct nk_text_edit_row *r, struct nk_text_edit *edit, - int line_start_id, float row_height, const struct nk_user_font *font) -{ - int l; - int glyphs = 0; - nk_rune unicode; - const char *remaining; - int len = nk_str_len_char(&edit->string); - const char *end = nk_str_get_const(&edit->string) + len; - const char *text = nk_str_at_const(&edit->string, line_start_id, &unicode, &l); - const struct nk_vec2 size = nk_text_calculate_text_bounds(font, - text, (int)(end - text), row_height, &remaining, 0, &glyphs, NK_STOP_ON_NEW_LINE); - - r->x0 = 0.0f; - r->x1 = size.x; - r->baseline_y_delta = size.y; - r->ymin = 0.0f; - r->ymax = size.y; - r->num_chars = glyphs; -} - -NK_INTERN int -nk_textedit_locate_coord(struct nk_text_edit *edit, float x, float y, - const struct nk_user_font *font, float row_height) -{ - struct nk_text_edit_row r; - int n = edit->string.len; - float base_y = 0, prev_x; - int i=0, k; - - r.x0 = r.x1 = 0; - r.ymin = r.ymax = 0; - r.num_chars = 0; - - /* search rows to find one that straddles 'y' */ - while (i < n) { - nk_textedit_layout_row(&r, edit, i, row_height, font); - if (r.num_chars <= 0) - return n; - - if (i==0 && y < base_y + r.ymin) - return 0; - - if (y < base_y + r.ymax) - break; - - i += r.num_chars; - base_y += r.baseline_y_delta; - } - - /* below all text, return 'after' last character */ - if (i >= n) - return n; - - /* check if it's before the beginning of the line */ - if (x < r.x0) - return i; - - /* check if it's before the end of the line */ - if (x < r.x1) { - /* search characters in row for one that straddles 'x' */ - k = i; - prev_x = r.x0; - for (i=0; i < r.num_chars; ++i) { - float w = nk_textedit_get_width(edit, k, i, font); - if (x < prev_x+w) { - if (x < prev_x+w/2) - return k+i; - else return k+i+1; - } - prev_x += w; - } - /* shouldn't happen, but if it does, fall through to end-of-line case */ - } - - /* if the last character is a newline, return that. - * otherwise return 'after' the last character */ - if (nk_str_rune_at(&edit->string, i+r.num_chars-1) == '\n') - return i+r.num_chars-1; - else return i+r.num_chars; -} - -NK_INTERN void -nk_textedit_click(struct nk_text_edit *state, float x, float y, - const struct nk_user_font *font, float row_height) -{ - /* API click: on mouse down, move the cursor to the clicked location, - * and reset the selection */ - state->cursor = nk_textedit_locate_coord(state, x, y, font, row_height); - state->select_start = state->cursor; - state->select_end = state->cursor; - state->has_preferred_x = 0; -} - -NK_INTERN void -nk_textedit_drag(struct nk_text_edit *state, float x, float y, - const struct nk_user_font *font, float row_height) -{ - /* API drag: on mouse drag, move the cursor and selection endpoint - * to the clicked location */ - int p = nk_textedit_locate_coord(state, x, y, font, row_height); - if (state->select_start == state->select_end) - state->select_start = state->cursor; - state->cursor = state->select_end = p; -} - -NK_INTERN void -nk_textedit_find_charpos(struct nk_text_find *find, struct nk_text_edit *state, - int n, int single_line, const struct nk_user_font *font, float row_height) -{ - /* find the x/y location of a character, and remember info about the previous - * row in case we get a move-up event (for page up, we'll have to rescan) */ - struct nk_text_edit_row r; - int prev_start = 0; - int z = state->string.len; - int i=0, first; - - nk_zero_struct(r); - if (n == z) { - /* if it's at the end, then find the last line -- simpler than trying to - explicitly handle this case in the regular code */ - nk_textedit_layout_row(&r, state, 0, row_height, font); - if (single_line) { - find->first_char = 0; - find->length = z; - } else { - while (i < z) { - prev_start = i; - i += r.num_chars; - nk_textedit_layout_row(&r, state, i, row_height, font); - } - - find->first_char = i; - find->length = r.num_chars; - } - find->x = r.x1; - find->y = r.ymin; - find->height = r.ymax - r.ymin; - find->prev_first = prev_start; - return; - } - - /* search rows to find the one that straddles character n */ - find->y = 0; - - for(;;) { - nk_textedit_layout_row(&r, state, i, row_height, font); - if (n < i + r.num_chars) break; - prev_start = i; - i += r.num_chars; - find->y += r.baseline_y_delta; - } - - find->first_char = first = i; - find->length = r.num_chars; - find->height = r.ymax - r.ymin; - find->prev_first = prev_start; - - /* now scan to find xpos */ - find->x = r.x0; - for (i=0; first+i < n; ++i) - find->x += nk_textedit_get_width(state, first, i, font); -} - -NK_INTERN void -nk_textedit_clamp(struct nk_text_edit *state) -{ - /* make the selection/cursor state valid if client altered the string */ - int n = state->string.len; - if (NK_TEXT_HAS_SELECTION(state)) { - if (state->select_start > n) state->select_start = n; - if (state->select_end > n) state->select_end = n; - /* if clamping forced them to be equal, move the cursor to match */ - if (state->select_start == state->select_end) - state->cursor = state->select_start; - } - if (state->cursor > n) state->cursor = n; -} - -NK_API void -nk_textedit_delete(struct nk_text_edit *state, int where, int len) -{ - /* delete characters while updating undo */ - nk_textedit_makeundo_delete(state, where, len); - nk_str_delete_runes(&state->string, where, len); - state->has_preferred_x = 0; -} - -NK_API void -nk_textedit_delete_selection(struct nk_text_edit *state) -{ - /* delete the section */ - nk_textedit_clamp(state); - if (NK_TEXT_HAS_SELECTION(state)) { - if (state->select_start < state->select_end) { - nk_textedit_delete(state, state->select_start, - state->select_end - state->select_start); - state->select_end = state->cursor = state->select_start; - } else { - nk_textedit_delete(state, state->select_end, - state->select_start - state->select_end); - state->select_start = state->cursor = state->select_end; - } - state->has_preferred_x = 0; - } -} - -NK_INTERN void -nk_textedit_sortselection(struct nk_text_edit *state) -{ - /* canonicalize the selection so start <= end */ - if (state->select_end < state->select_start) { - int temp = state->select_end; - state->select_end = state->select_start; - state->select_start = temp; - } -} - -NK_INTERN void -nk_textedit_move_to_first(struct nk_text_edit *state) -{ - /* move cursor to first character of selection */ - if (NK_TEXT_HAS_SELECTION(state)) { - nk_textedit_sortselection(state); - state->cursor = state->select_start; - state->select_end = state->select_start; - state->has_preferred_x = 0; - } -} - -NK_INTERN void -nk_textedit_move_to_last(struct nk_text_edit *state) -{ - /* move cursor to last character of selection */ - if (NK_TEXT_HAS_SELECTION(state)) { - nk_textedit_sortselection(state); - nk_textedit_clamp(state); - state->cursor = state->select_end; - state->select_start = state->select_end; - state->has_preferred_x = 0; - } -} - -NK_INTERN int -nk_is_word_boundary( struct nk_text_edit *state, int idx) -{ - int len; - nk_rune c; - if (idx <= 0) return 1; - if (!nk_str_at_rune(&state->string, idx, &c, &len)) return 1; - return (c == ' ' || c == '\t' ||c == 0x3000 || c == ',' || c == ';' || - c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || - c == '|'); -} - -NK_INTERN int -nk_textedit_move_to_word_previous(struct nk_text_edit *state) -{ - int c = state->cursor - 1; - while( c >= 0 && !nk_is_word_boundary(state, c)) - --c; - - if( c < 0 ) - c = 0; - - return c; -} - -NK_INTERN int -nk_textedit_move_to_word_next(struct nk_text_edit *state) -{ - const int len = state->string.len; - int c = state->cursor+1; - while( c < len && !nk_is_word_boundary(state, c)) - ++c; - - if( c > len ) - c = len; - - return c; -} - -NK_INTERN void -nk_textedit_prep_selection_at_cursor(struct nk_text_edit *state) -{ - /* update selection and cursor to match each other */ - if (!NK_TEXT_HAS_SELECTION(state)) - state->select_start = state->select_end = state->cursor; - else state->cursor = state->select_end; -} - -NK_API int -nk_textedit_cut(struct nk_text_edit *state) -{ - /* API cut: delete selection */ - if (state->mode == NK_TEXT_EDIT_MODE_VIEW) - return 0; - if (NK_TEXT_HAS_SELECTION(state)) { - nk_textedit_delete_selection(state); /* implicitly clamps */ - state->has_preferred_x = 0; - return 1; - } - return 0; -} - -NK_API int -nk_textedit_paste(struct nk_text_edit *state, char const *ctext, int len) -{ - /* API paste: replace existing selection with passed-in text */ - int glyphs; - const char *text = (const char *) ctext; - if (state->mode == NK_TEXT_EDIT_MODE_VIEW) return 0; - - /* if there's a selection, the paste should delete it */ - nk_textedit_clamp(state); - nk_textedit_delete_selection(state); - - /* try to insert the characters */ - glyphs = nk_utf_len(ctext, len); - if (nk_str_insert_text_char(&state->string, state->cursor, text, len)) { - nk_textedit_makeundo_insert(state, state->cursor, glyphs); - state->cursor += len; - state->has_preferred_x = 0; - return 1; - } - /* remove the undo since we didn't actually insert the characters */ - if (state->undo.undo_point) - --state->undo.undo_point; - return 0; -} - -NK_API void -nk_textedit_text(struct nk_text_edit *state, const char *text, int total_len) -{ - nk_rune unicode; - int glyph_len; - int text_len = 0; - - NK_ASSERT(state); - NK_ASSERT(text); - if (!text || !total_len || state->mode == NK_TEXT_EDIT_MODE_VIEW) return; - - glyph_len = nk_utf_decode(text, &unicode, total_len); - while ((text_len < total_len) && glyph_len) - { - /* don't insert a backward delete, just process the event */ - if (unicode == 127) goto next; - /* can't add newline in single-line mode */ - if (unicode == '\n' && state->single_line) goto next; - /* filter incoming text */ - if (state->filter && !state->filter(state, unicode)) goto next; - - if (!NK_TEXT_HAS_SELECTION(state) && - state->cursor < state->string.len) - { - if (state->mode == NK_TEXT_EDIT_MODE_REPLACE) { - nk_textedit_makeundo_replace(state, state->cursor, 1, 1); - nk_str_delete_runes(&state->string, state->cursor, 1); - } - if (nk_str_insert_text_utf8(&state->string, state->cursor, - text+text_len, 1)) - { - ++state->cursor; - state->has_preferred_x = 0; - } - } else { - nk_textedit_delete_selection(state); /* implicitly clamps */ - if (nk_str_insert_text_utf8(&state->string, state->cursor, - text+text_len, 1)) - { - nk_textedit_makeundo_insert(state, state->cursor, 1); - ++state->cursor; - state->has_preferred_x = 0; - } - } - next: - text_len += glyph_len; - glyph_len = nk_utf_decode(text + text_len, &unicode, total_len-text_len); - } -} - -NK_INTERN void -nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod, - const struct nk_user_font *font, float row_height) -{ -retry: - switch (key) - { - case NK_KEY_NONE: - case NK_KEY_CTRL: - case NK_KEY_ENTER: - case NK_KEY_SHIFT: - case NK_KEY_TAB: - case NK_KEY_COPY: - case NK_KEY_CUT: - case NK_KEY_PASTE: - case NK_KEY_MAX: - default: break; - case NK_KEY_TEXT_UNDO: - nk_textedit_undo(state); - state->has_preferred_x = 0; - break; - - case NK_KEY_TEXT_REDO: - nk_textedit_redo(state); - state->has_preferred_x = 0; - break; - - case NK_KEY_TEXT_SELECT_ALL: - nk_textedit_select_all(state); - state->has_preferred_x = 0; - break; - - case NK_KEY_TEXT_INSERT_MODE: - if (state->mode == NK_TEXT_EDIT_MODE_VIEW) - state->mode = NK_TEXT_EDIT_MODE_INSERT; - break; - case NK_KEY_TEXT_REPLACE_MODE: - if (state->mode == NK_TEXT_EDIT_MODE_VIEW) - state->mode = NK_TEXT_EDIT_MODE_REPLACE; - break; - case NK_KEY_TEXT_RESET_MODE: - if (state->mode == NK_TEXT_EDIT_MODE_INSERT || - state->mode == NK_TEXT_EDIT_MODE_REPLACE) - state->mode = NK_TEXT_EDIT_MODE_VIEW; - break; - - case NK_KEY_LEFT: - if (shift_mod) { - nk_textedit_clamp(state); - nk_textedit_prep_selection_at_cursor(state); - /* move selection left */ - if (state->select_end > 0) - --state->select_end; - state->cursor = state->select_end; - state->has_preferred_x = 0; - } else { - /* if currently there's a selection, - * move cursor to start of selection */ - if (NK_TEXT_HAS_SELECTION(state)) - nk_textedit_move_to_first(state); - else if (state->cursor > 0) - --state->cursor; - state->has_preferred_x = 0; - } break; - - case NK_KEY_RIGHT: - if (shift_mod) { - nk_textedit_prep_selection_at_cursor(state); - /* move selection right */ - ++state->select_end; - nk_textedit_clamp(state); - state->cursor = state->select_end; - state->has_preferred_x = 0; - } else { - /* if currently there's a selection, - * move cursor to end of selection */ - if (NK_TEXT_HAS_SELECTION(state)) - nk_textedit_move_to_last(state); - else ++state->cursor; - nk_textedit_clamp(state); - state->has_preferred_x = 0; - } break; - - case NK_KEY_TEXT_WORD_LEFT: - if (shift_mod) { - if( !NK_TEXT_HAS_SELECTION( state ) ) - nk_textedit_prep_selection_at_cursor(state); - state->cursor = nk_textedit_move_to_word_previous(state); - state->select_end = state->cursor; - nk_textedit_clamp(state ); - } else { - if (NK_TEXT_HAS_SELECTION(state)) - nk_textedit_move_to_first(state); - else { - state->cursor = nk_textedit_move_to_word_previous(state); - nk_textedit_clamp(state ); - } - } break; - - case NK_KEY_TEXT_WORD_RIGHT: - if (shift_mod) { - if( !NK_TEXT_HAS_SELECTION( state ) ) - nk_textedit_prep_selection_at_cursor(state); - state->cursor = nk_textedit_move_to_word_next(state); - state->select_end = state->cursor; - nk_textedit_clamp(state); - } else { - if (NK_TEXT_HAS_SELECTION(state)) - nk_textedit_move_to_last(state); - else { - state->cursor = nk_textedit_move_to_word_next(state); - nk_textedit_clamp(state ); - } - } break; - - case NK_KEY_DOWN: { - struct nk_text_find find; - struct nk_text_edit_row row; - int i, sel = shift_mod; - - if (state->single_line) { - /* on windows, up&down in single-line behave like left&right */ - key = NK_KEY_RIGHT; - goto retry; - } - - if (sel) - nk_textedit_prep_selection_at_cursor(state); - else if (NK_TEXT_HAS_SELECTION(state)) - nk_textedit_move_to_last(state); - - /* compute current position of cursor point */ - nk_textedit_clamp(state); - nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, - font, row_height); - - /* now find character position down a row */ - if (find.length) - { - float x; - float goal_x = state->has_preferred_x ? state->preferred_x : find.x; - int start = find.first_char + find.length; - - state->cursor = start; - nk_textedit_layout_row(&row, state, state->cursor, row_height, font); - x = row.x0; - - for (i=0; i < row.num_chars && x < row.x1; ++i) { - float dx = nk_textedit_get_width(state, start, i, font); - x += dx; - if (x > goal_x) - break; - ++state->cursor; - } - nk_textedit_clamp(state); - - state->has_preferred_x = 1; - state->preferred_x = goal_x; - if (sel) - state->select_end = state->cursor; - } - } break; - - case NK_KEY_UP: { - struct nk_text_find find; - struct nk_text_edit_row row; - int i, sel = shift_mod; - - if (state->single_line) { - /* on windows, up&down become left&right */ - key = NK_KEY_LEFT; - goto retry; - } - - if (sel) - nk_textedit_prep_selection_at_cursor(state); - else if (NK_TEXT_HAS_SELECTION(state)) - nk_textedit_move_to_first(state); - - /* compute current position of cursor point */ - nk_textedit_clamp(state); - nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, - font, row_height); - - /* can only go up if there's a previous row */ - if (find.prev_first != find.first_char) { - /* now find character position up a row */ - float x; - float goal_x = state->has_preferred_x ? state->preferred_x : find.x; - - state->cursor = find.prev_first; - nk_textedit_layout_row(&row, state, state->cursor, row_height, font); - x = row.x0; - - for (i=0; i < row.num_chars && x < row.x1; ++i) { - float dx = nk_textedit_get_width(state, find.prev_first, i, font); - x += dx; - if (x > goal_x) - break; - ++state->cursor; - } - nk_textedit_clamp(state); - - state->has_preferred_x = 1; - state->preferred_x = goal_x; - if (sel) state->select_end = state->cursor; - } - } break; - - case NK_KEY_DEL: - if (state->mode == NK_TEXT_EDIT_MODE_VIEW) - break; - if (NK_TEXT_HAS_SELECTION(state)) - nk_textedit_delete_selection(state); - else { - int n = state->string.len; - if (state->cursor < n) - nk_textedit_delete(state, state->cursor, 1); - } - state->has_preferred_x = 0; - break; - - case NK_KEY_BACKSPACE: - if (state->mode == NK_TEXT_EDIT_MODE_VIEW) - break; - if (NK_TEXT_HAS_SELECTION(state)) - nk_textedit_delete_selection(state); - else { - nk_textedit_clamp(state); - if (state->cursor > 0) { - nk_textedit_delete(state, state->cursor-1, 1); - --state->cursor; - } - } - state->has_preferred_x = 0; - break; - - case NK_KEY_TEXT_START: - if (shift_mod) { - nk_textedit_prep_selection_at_cursor(state); - state->cursor = state->select_end = 0; - state->has_preferred_x = 0; - } else { - state->cursor = state->select_start = state->select_end = 0; - state->has_preferred_x = 0; - } - break; - - case NK_KEY_TEXT_END: - if (shift_mod) { - nk_textedit_prep_selection_at_cursor(state); - state->cursor = state->select_end = state->string.len; - state->has_preferred_x = 0; - } else { - state->cursor = state->string.len; - state->select_start = state->select_end = 0; - state->has_preferred_x = 0; - } - break; - - case NK_KEY_TEXT_LINE_START: { - if (shift_mod) { - struct nk_text_find find; - nk_textedit_clamp(state); - nk_textedit_prep_selection_at_cursor(state); - if (state->string.len && state->cursor == state->string.len) - --state->cursor; - nk_textedit_find_charpos(&find, state,state->cursor, state->single_line, - font, row_height); - state->cursor = state->select_end = find.first_char; - state->has_preferred_x = 0; - } else { - struct nk_text_find find; - if (state->string.len && state->cursor == state->string.len) - --state->cursor; - nk_textedit_clamp(state); - nk_textedit_move_to_first(state); - nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, - font, row_height); - state->cursor = find.first_char; - state->has_preferred_x = 0; - } - } break; - - case NK_KEY_TEXT_LINE_END: { - if (shift_mod) { - struct nk_text_find find; - nk_textedit_clamp(state); - nk_textedit_prep_selection_at_cursor(state); - nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, - font, row_height); - state->has_preferred_x = 0; - state->cursor = find.first_char + find.length; - if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') - --state->cursor; - state->select_end = state->cursor; - } else { - struct nk_text_find find; - nk_textedit_clamp(state); - nk_textedit_move_to_first(state); - nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, - font, row_height); - - state->has_preferred_x = 0; - state->cursor = find.first_char + find.length; - if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') - --state->cursor; - }} break; - } -} - -NK_INTERN void -nk_textedit_flush_redo(struct nk_text_undo_state *state) -{ - state->redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; - state->redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; -} - -NK_INTERN void -nk_textedit_discard_undo(struct nk_text_undo_state *state) -{ - /* discard the oldest entry in the undo list */ - if (state->undo_point > 0) { - /* if the 0th undo state has characters, clean those up */ - if (state->undo_rec[0].char_storage >= 0) { - int n = state->undo_rec[0].insert_length, i; - /* delete n characters from all other records */ - state->undo_char_point = (short)(state->undo_char_point - n); - NK_MEMCPY(state->undo_char, state->undo_char + n, - (nk_size)state->undo_char_point*sizeof(nk_rune)); - for (i=0; i < state->undo_point; ++i) { - if (state->undo_rec[i].char_storage >= 0) - state->undo_rec[i].char_storage = (short) - (state->undo_rec[i].char_storage - n); - } - } - --state->undo_point; - NK_MEMCPY(state->undo_rec, state->undo_rec+1, - (nk_size)((nk_size)state->undo_point * sizeof(state->undo_rec[0]))); - } -} - -NK_INTERN void -nk_textedit_discard_redo(struct nk_text_undo_state *state) -{ -/* discard the oldest entry in the redo list--it's bad if this - ever happens, but because undo & redo have to store the actual - characters in different cases, the redo character buffer can - fill up even though the undo buffer didn't */ - nk_size num; - int k = NK_TEXTEDIT_UNDOSTATECOUNT-1; - if (state->redo_point <= k) { - /* if the k'th undo state has characters, clean those up */ - if (state->undo_rec[k].char_storage >= 0) { - int n = state->undo_rec[k].insert_length, i; - /* delete n characters from all other records */ - state->redo_char_point = (short)(state->redo_char_point + n); - num = (nk_size)(NK_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point); - NK_MEMCPY(state->undo_char + state->redo_char_point, - state->undo_char + state->redo_char_point-n, num * sizeof(char)); - for (i = state->redo_point; i < k; ++i) { - if (state->undo_rec[i].char_storage >= 0) { - state->undo_rec[i].char_storage = (short) - (state->undo_rec[i].char_storage + n); - } - } - } - ++state->redo_point; - num = (nk_size)(NK_TEXTEDIT_UNDOSTATECOUNT - state->redo_point); - if (num) NK_MEMCPY(state->undo_rec + state->redo_point-1, - state->undo_rec + state->redo_point, num * sizeof(state->undo_rec[0])); - } -} - -NK_INTERN struct nk_text_undo_record* -nk_textedit_create_undo_record(struct nk_text_undo_state *state, int numchars) -{ - /* any time we create a new undo record, we discard redo*/ - nk_textedit_flush_redo(state); - - /* if we have no free records, we have to make room, - * by sliding the existing records down */ - if (state->undo_point == NK_TEXTEDIT_UNDOSTATECOUNT) - nk_textedit_discard_undo(state); - - /* if the characters to store won't possibly fit in the buffer, - * we can't undo */ - if (numchars > NK_TEXTEDIT_UNDOCHARCOUNT) { - state->undo_point = 0; - state->undo_char_point = 0; - return 0; - } - - /* if we don't have enough free characters in the buffer, - * we have to make room */ - while (state->undo_char_point + numchars > NK_TEXTEDIT_UNDOCHARCOUNT) - nk_textedit_discard_undo(state); - return &state->undo_rec[state->undo_point++]; -} - -NK_INTERN nk_rune* -nk_textedit_createundo(struct nk_text_undo_state *state, int pos, - int insert_len, int delete_len) -{ - struct nk_text_undo_record *r = nk_textedit_create_undo_record(state, insert_len); - if (r == 0) - return 0; - - r->where = pos; - r->insert_length = (short) insert_len; - r->delete_length = (short) delete_len; - - if (insert_len == 0) { - r->char_storage = -1; - return 0; - } else { - r->char_storage = state->undo_char_point; - state->undo_char_point = (short)(state->undo_char_point + insert_len); - return &state->undo_char[r->char_storage]; - } -} - -NK_API void -nk_textedit_undo(struct nk_text_edit *state) -{ - struct nk_text_undo_state *s = &state->undo; - struct nk_text_undo_record u, *r; - if (s->undo_point == 0) - return; - - /* we need to do two things: apply the undo record, and create a redo record */ - u = s->undo_rec[s->undo_point-1]; - r = &s->undo_rec[s->redo_point-1]; - r->char_storage = -1; - - r->insert_length = u.delete_length; - r->delete_length = u.insert_length; - r->where = u.where; - - if (u.delete_length) - { - /* if the undo record says to delete characters, then the redo record will - need to re-insert the characters that get deleted, so we need to store - them. - there are three cases: - - there's enough room to store the characters - - characters stored for *redoing* don't leave room for redo - - characters stored for *undoing* don't leave room for redo - if the last is true, we have to bail */ - if (s->undo_char_point + u.delete_length >= NK_TEXTEDIT_UNDOCHARCOUNT) { - /* the undo records take up too much character space; there's no space - * to store the redo characters */ - r->insert_length = 0; - } else { - int i; - /* there's definitely room to store the characters eventually */ - while (s->undo_char_point + u.delete_length > s->redo_char_point) { - /* there's currently not enough room, so discard a redo record */ - nk_textedit_discard_redo(s); - /* should never happen: */ - if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) - return; - } - - r = &s->undo_rec[s->redo_point-1]; - r->char_storage = (short)(s->redo_char_point - u.delete_length); - s->redo_char_point = (short)(s->redo_char_point - u.delete_length); - - /* now save the characters */ - for (i=0; i < u.delete_length; ++i) - s->undo_char[r->char_storage + i] = - nk_str_rune_at(&state->string, u.where + i); - } - /* now we can carry out the deletion */ - nk_str_delete_runes(&state->string, u.where, u.delete_length); - } - - /* check type of recorded action: */ - if (u.insert_length) { - /* easy case: was a deletion, so we need to insert n characters */ - nk_str_insert_text_runes(&state->string, u.where, - &s->undo_char[u.char_storage], u.insert_length); - s->undo_char_point = (short)(s->undo_char_point - u.insert_length); - } - state->cursor = (short)(u.where + u.insert_length); - - s->undo_point--; - s->redo_point--; -} - -NK_API void -nk_textedit_redo(struct nk_text_edit *state) -{ - struct nk_text_undo_state *s = &state->undo; - struct nk_text_undo_record *u, r; - if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) - return; - - /* we need to do two things: apply the redo record, and create an undo record */ - u = &s->undo_rec[s->undo_point]; - r = s->undo_rec[s->redo_point]; - - /* we KNOW there must be room for the undo record, because the redo record - was derived from an undo record */ - u->delete_length = r.insert_length; - u->insert_length = r.delete_length; - u->where = r.where; - u->char_storage = -1; - - if (r.delete_length) { - /* the redo record requires us to delete characters, so the undo record - needs to store the characters */ - if (s->undo_char_point + u->insert_length > s->redo_char_point) { - u->insert_length = 0; - u->delete_length = 0; - } else { - int i; - u->char_storage = s->undo_char_point; - s->undo_char_point = (short)(s->undo_char_point + u->insert_length); - - /* now save the characters */ - for (i=0; i < u->insert_length; ++i) { - s->undo_char[u->char_storage + i] = - nk_str_rune_at(&state->string, u->where + i); - } - } - nk_str_delete_runes(&state->string, r.where, r.delete_length); - } - - if (r.insert_length) { - /* easy case: need to insert n characters */ - nk_str_insert_text_runes(&state->string, r.where, - &s->undo_char[r.char_storage], r.insert_length); - } - state->cursor = r.where + r.insert_length; - - s->undo_point++; - s->redo_point++; -} - -NK_INTERN void -nk_textedit_makeundo_insert(struct nk_text_edit *state, int where, int length) -{ - nk_textedit_createundo(&state->undo, where, 0, length); -} - -NK_INTERN void -nk_textedit_makeundo_delete(struct nk_text_edit *state, int where, int length) -{ - int i; - nk_rune *p = nk_textedit_createundo(&state->undo, where, length, 0); - if (p) { - for (i=0; i < length; ++i) - p[i] = nk_str_rune_at(&state->string, where+i); - } -} - -NK_INTERN void -nk_textedit_makeundo_replace(struct nk_text_edit *state, int where, - int old_length, int new_length) -{ - int i; - nk_rune *p = nk_textedit_createundo(&state->undo, where, old_length, new_length); - if (p) { - for (i=0; i < old_length; ++i) - p[i] = nk_str_rune_at(&state->string, where+i); - } -} - -NK_INTERN void -nk_textedit_clear_state(struct nk_text_edit *state, enum nk_text_edit_type type, - nk_plugin_filter filter) -{ - /* reset the state to default */ - state->undo.undo_point = 0; - state->undo.undo_char_point = 0; - state->undo.redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; - state->undo.redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; - state->select_end = state->select_start = 0; - state->cursor = 0; - state->has_preferred_x = 0; - state->preferred_x = 0; - state->cursor_at_end_of_line = 0; - state->initialized = 1; - state->single_line = (unsigned char)(type == NK_TEXT_EDIT_SINGLE_LINE); - state->mode = NK_TEXT_EDIT_MODE_VIEW; - state->filter = filter; - state->scrollbar = nk_vec2(0,0); -} - -NK_API void -nk_textedit_init_fixed(struct nk_text_edit *state, void *memory, nk_size size) -{ - NK_ASSERT(state); - NK_ASSERT(memory); - if (!state || !memory || !size) return; - NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); - nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); - nk_str_init_fixed(&state->string, memory, size); -} - -NK_API void -nk_textedit_init(struct nk_text_edit *state, struct nk_allocator *alloc, nk_size size) -{ - NK_ASSERT(state); - NK_ASSERT(alloc); - if (!state || !alloc) return; - NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); - nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); - nk_str_init(&state->string, alloc, size); -} - -#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR -NK_API void -nk_textedit_init_default(struct nk_text_edit *state) -{ - NK_ASSERT(state); - if (!state) return; - NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); - nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); - nk_str_init_default(&state->string); -} -#endif - -NK_API void -nk_textedit_select_all(struct nk_text_edit *state) -{ - NK_ASSERT(state); - state->select_start = 0; - state->select_end = state->string.len; -} - -NK_API void -nk_textedit_free(struct nk_text_edit *state) -{ - NK_ASSERT(state); - if (!state) return; - nk_str_free(&state->string); -} - -/* =============================================================== - * - * TEXT WIDGET - * - * ===============================================================*/ -#define nk_widget_state_reset(s)\ - if ((*(s)) & NK_WIDGET_STATE_MODIFIED)\ - (*(s)) = NK_WIDGET_STATE_INACTIVE|NK_WIDGET_STATE_MODIFIED;\ - else (*(s)) = NK_WIDGET_STATE_INACTIVE; - -struct nk_text { - struct nk_vec2 padding; - struct nk_color background; - struct nk_color text; -}; - -NK_INTERN void -nk_widget_text(struct nk_command_buffer *o, struct nk_rect b, - const char *string, int len, const struct nk_text *t, - nk_flags a, const struct nk_user_font *f) -{ - struct nk_rect label; - float text_width; - - NK_ASSERT(o); - NK_ASSERT(t); - if (!o || !t) return; - - b.h = NK_MAX(b.h, 2 * t->padding.y); - label.x = 0; label.w = 0; - label.y = b.y + t->padding.y; - label.h = NK_MIN(f->height, b.h - 2 * t->padding.y); - - text_width = f->width(f->userdata, f->height, (const char*)string, len); - text_width += (2.0f * t->padding.x); - - /* align in x-axis */ - if (a & NK_TEXT_ALIGN_LEFT) { - label.x = b.x + t->padding.x; - label.w = NK_MAX(0, b.w - 2 * t->padding.x); - } else if (a & NK_TEXT_ALIGN_CENTERED) { - label.w = NK_MAX(1, 2 * t->padding.x + (float)text_width); - label.x = (b.x + t->padding.x + ((b.w - 2 * t->padding.x) - label.w) / 2); - label.x = NK_MAX(b.x + t->padding.x, label.x); - label.w = NK_MIN(b.x + b.w, label.x + label.w); - if (label.w >= label.x) label.w -= label.x; - } else if (a & NK_TEXT_ALIGN_RIGHT) { - label.x = NK_MAX(b.x + t->padding.x, (b.x + b.w) - (2 * t->padding.x + (float)text_width)); - label.w = (float)text_width + 2 * t->padding.x; - } else return; - - /* align in y-axis */ - if (a & NK_TEXT_ALIGN_MIDDLE) { - label.y = b.y + b.h/2.0f - (float)f->height/2.0f; - label.h = NK_MAX(b.h/2.0f, b.h - (b.h/2.0f + f->height/2.0f)); - } else if (a & NK_TEXT_ALIGN_BOTTOM) { - label.y = b.y + b.h - f->height; - label.h = f->height; - } - nk_draw_text(o, label, (const char*)string, - len, f, t->background, t->text); -} - -NK_INTERN void -nk_widget_text_wrap(struct nk_command_buffer *o, struct nk_rect b, - const char *string, int len, const struct nk_text *t, - const struct nk_user_font *f) -{ - float width; - int glyphs = 0; - int fitting = 0; - int done = 0; - struct nk_rect line; - struct nk_text text; - NK_INTERN nk_rune seperator[] = {' '}; - - NK_ASSERT(o); - NK_ASSERT(t); - if (!o || !t) return; - - text.padding = nk_vec2(0,0); - text.background = t->background; - text.text = t->text; - - b.w = NK_MAX(b.w, 2 * t->padding.x); - b.h = NK_MAX(b.h, 2 * t->padding.y); - b.h = b.h - 2 * t->padding.y; - - line.x = b.x + t->padding.x; - line.y = b.y + t->padding.y; - line.w = b.w - 2 * t->padding.x; - line.h = 2 * t->padding.y + f->height; - - fitting = nk_text_clamp(f, string, len, line.w, &glyphs, &width, seperator,NK_LEN(seperator)); - while (done < len) { - if (!fitting || line.y + line.h >= (b.y + b.h)) break; - nk_widget_text(o, line, &string[done], fitting, &text, NK_TEXT_LEFT, f); - done += fitting; - line.y += f->height + 2 * t->padding.y; - fitting = nk_text_clamp(f, &string[done], len - done, line.w, &glyphs, &width, seperator,NK_LEN(seperator)); - } -} - -/* =============================================================== - * - * BUTTON - * - * ===============================================================*/ -NK_INTERN void -nk_draw_symbol(struct nk_command_buffer *out, enum nk_symbol_type type, - struct nk_rect content, struct nk_color background, struct nk_color foreground, - float border_width, const struct nk_user_font *font) -{ - switch (type) { - case NK_SYMBOL_X: - case NK_SYMBOL_UNDERSCORE: - case NK_SYMBOL_PLUS: - case NK_SYMBOL_MINUS: { - /* single character text symbol */ - const char *X = (type == NK_SYMBOL_X) ? "x": - (type == NK_SYMBOL_UNDERSCORE) ? "_": - (type == NK_SYMBOL_PLUS) ? "+": "-"; - struct nk_text text; - text.padding = nk_vec2(0,0); - text.background = background; - text.text = foreground; - nk_widget_text(out, content, X, 1, &text, NK_TEXT_CENTERED, font); - } break; - case NK_SYMBOL_CIRCLE_SOLID: - case NK_SYMBOL_CIRCLE_OUTLINE: - case NK_SYMBOL_RECT_SOLID: - case NK_SYMBOL_RECT_OUTLINE: { - /* simple empty/filled shapes */ - if (type == NK_SYMBOL_RECT_SOLID || type == NK_SYMBOL_RECT_OUTLINE) { - nk_fill_rect(out, content, 0, foreground); - if (type == NK_SYMBOL_RECT_OUTLINE) - nk_fill_rect(out, nk_shrink_rect(content, border_width), 0, background); - } else { - nk_fill_circle(out, content, foreground); - if (type == NK_SYMBOL_CIRCLE_OUTLINE) - nk_fill_circle(out, nk_shrink_rect(content, 1), background); - } - } break; - case NK_SYMBOL_TRIANGLE_UP: - case NK_SYMBOL_TRIANGLE_DOWN: - case NK_SYMBOL_TRIANGLE_LEFT: - case NK_SYMBOL_TRIANGLE_RIGHT: { - enum nk_heading heading; - struct nk_vec2 points[3]; - heading = (type == NK_SYMBOL_TRIANGLE_RIGHT) ? NK_RIGHT : - (type == NK_SYMBOL_TRIANGLE_LEFT) ? NK_LEFT: - (type == NK_SYMBOL_TRIANGLE_UP) ? NK_UP: NK_DOWN; - nk_triangle_from_direction(points, content, 0, 0, heading); - nk_fill_triangle(out, points[0].x, points[0].y, points[1].x, points[1].y, - points[2].x, points[2].y, foreground); - } break; - default: - case NK_SYMBOL_NONE: - case NK_SYMBOL_MAX: break; - } -} - -NK_INTERN int -nk_button_behavior(nk_flags *state, struct nk_rect r, - const struct nk_input *i, enum nk_button_behavior behavior) -{ - int ret = 0; - nk_widget_state_reset(state); - if (!i) return 0; - if (nk_input_is_mouse_hovering_rect(i, r)) { - *state = NK_WIDGET_STATE_HOVERED; - if (nk_input_is_mouse_down(i, NK_BUTTON_LEFT)) - *state = NK_WIDGET_STATE_ACTIVE; - if (nk_input_has_mouse_click_in_rect(i, NK_BUTTON_LEFT, r)) { - ret = (behavior != NK_BUTTON_DEFAULT) ? - nk_input_is_mouse_down(i, NK_BUTTON_LEFT): -#ifdef NK_BUTTON_TRIGGER_ON_RELEASE - nk_input_is_mouse_released(i, NK_BUTTON_LEFT); -#else - nk_input_is_mouse_pressed(i, NK_BUTTON_LEFT); -#endif - } - } - if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(i, r)) - *state |= NK_WIDGET_STATE_ENTERED; - else if (nk_input_is_mouse_prev_hovering_rect(i, r)) - *state |= NK_WIDGET_STATE_LEFT; - return ret; -} - -NK_INTERN const struct nk_style_item* -nk_draw_button(struct nk_command_buffer *out, - const struct nk_rect *bounds, nk_flags state, - const struct nk_style_button *style) -{ - const struct nk_style_item *background; - if (state & NK_WIDGET_STATE_HOVER) - background = &style->hover; - else if (state & NK_WIDGET_STATE_ACTIVED) - background = &style->active; - else background = &style->normal; - - if (background->type == NK_STYLE_ITEM_IMAGE) { - nk_draw_image(out, *bounds, &background->data.image, nk_white); - } else { - nk_fill_rect(out, *bounds, style->rounding, background->data.color); - nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); - } - return background; -} - -NK_INTERN int -nk_do_button(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, - const struct nk_style_button *style, const struct nk_input *in, - enum nk_button_behavior behavior, struct nk_rect *content) -{ - struct nk_rect bounds; - NK_ASSERT(style); - NK_ASSERT(state); - NK_ASSERT(out); - if (!out || !style) - return nk_false; - - /* calculate button content space */ - content->x = r.x + style->padding.x + style->border + style->rounding; - content->y = r.y + style->padding.y + style->border + style->rounding; - content->w = r.w - (2 * style->padding.x + style->border + style->rounding*2); - content->h = r.h - (2 * style->padding.y + style->border + style->rounding*2); - - /* execute button behavior */ - bounds.x = r.x - style->touch_padding.x; - bounds.y = r.y - style->touch_padding.y; - bounds.w = r.w + 2 * style->touch_padding.x; - bounds.h = r.h + 2 * style->touch_padding.y; - return nk_button_behavior(state, bounds, in, behavior); -} - -NK_INTERN void -nk_draw_button_text(struct nk_command_buffer *out, - const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, - const struct nk_style_button *style, const char *txt, int len, - nk_flags text_alignment, const struct nk_user_font *font) -{ - struct nk_text text; - const struct nk_style_item *background; - background = nk_draw_button(out, bounds, state, style); - - /* select correct colors/images */ - if (background->type == NK_STYLE_ITEM_COLOR) - text.background = background->data.color; - else text.background = style->text_background; - if (state & NK_WIDGET_STATE_HOVER) - text.text = style->text_hover; - else if (state & NK_WIDGET_STATE_ACTIVED) - text.text = style->text_active; - else text.text = style->text_normal; - - text.padding = nk_vec2(0,0); - nk_widget_text(out, *content, txt, len, &text, text_alignment, font); -} - -NK_INTERN int -nk_do_button_text(nk_flags *state, - struct nk_command_buffer *out, struct nk_rect bounds, - const char *string, int len, nk_flags align, enum nk_button_behavior behavior, - const struct nk_style_button *style, const struct nk_input *in, - const struct nk_user_font *font) -{ - struct nk_rect content; - int ret = nk_false; - - NK_ASSERT(state); - NK_ASSERT(style); - NK_ASSERT(out); - NK_ASSERT(string); - NK_ASSERT(font); - if (!out || !style || !font || !string) - return nk_false; - - ret = nk_do_button(state, out, bounds, style, in, behavior, &content); - if (style->draw_begin) style->draw_begin(out, style->userdata); - nk_draw_button_text(out, &bounds, &content, *state, style, string, len, align, font); - if (style->draw_end) style->draw_end(out, style->userdata); - return ret; -} - -NK_INTERN void -nk_draw_button_symbol(struct nk_command_buffer *out, - const struct nk_rect *bounds, const struct nk_rect *content, - nk_flags state, const struct nk_style_button *style, - enum nk_symbol_type type, const struct nk_user_font *font) -{ - struct nk_color sym, bg; - const struct nk_style_item *background; - - /* select correct colors/images */ - background = nk_draw_button(out, bounds, state, style); - if (background->type == NK_STYLE_ITEM_COLOR) - bg = background->data.color; - else bg = style->text_background; - - if (state & NK_WIDGET_STATE_HOVER) - sym = style->text_hover; - else if (state & NK_WIDGET_STATE_ACTIVED) - sym = style->text_active; - else sym = style->text_normal; - nk_draw_symbol(out, type, *content, bg, sym, 1, font); -} - -NK_INTERN int -nk_do_button_symbol(nk_flags *state, - struct nk_command_buffer *out, struct nk_rect bounds, - enum nk_symbol_type symbol, enum nk_button_behavior behavior, - const struct nk_style_button *style, const struct nk_input *in, - const struct nk_user_font *font) -{ - int ret; - struct nk_rect content; - - NK_ASSERT(state); - NK_ASSERT(style); - NK_ASSERT(font); - NK_ASSERT(out); - if (!out || !style || !font || !state) - return nk_false; - - ret = nk_do_button(state, out, bounds, style, in, behavior, &content); - if (style->draw_begin) style->draw_begin(out, style->userdata); - nk_draw_button_symbol(out, &bounds, &content, *state, style, symbol, font); - if (style->draw_end) style->draw_end(out, style->userdata); - return ret; -} - -NK_INTERN void -nk_draw_button_image(struct nk_command_buffer *out, - const struct nk_rect *bounds, const struct nk_rect *content, - nk_flags state, const struct nk_style_button *style, const struct nk_image *img) -{ - nk_draw_button(out, bounds, state, style); - nk_draw_image(out, *content, img, nk_white); -} - -NK_INTERN int -nk_do_button_image(nk_flags *state, - struct nk_command_buffer *out, struct nk_rect bounds, - struct nk_image img, enum nk_button_behavior b, - const struct nk_style_button *style, const struct nk_input *in) -{ - int ret; - struct nk_rect content; - - NK_ASSERT(state); - NK_ASSERT(style); - NK_ASSERT(out); - if (!out || !style || !state) - return nk_false; - - ret = nk_do_button(state, out, bounds, style, in, b, &content); - content.x += style->image_padding.x; - content.y += style->image_padding.y; - content.w -= 2 * style->image_padding.x; - content.h -= 2 * style->image_padding.y; - - if (style->draw_begin) style->draw_begin(out, style->userdata); - nk_draw_button_image(out, &bounds, &content, *state, style, &img); - if (style->draw_end) style->draw_end(out, style->userdata); - return ret; -} - -NK_INTERN void -nk_draw_button_text_symbol(struct nk_command_buffer *out, - const struct nk_rect *bounds, const struct nk_rect *label, - const struct nk_rect *symbol, nk_flags state, const struct nk_style_button *style, - const char *str, int len, enum nk_symbol_type type, - const struct nk_user_font *font) -{ - struct nk_color sym; - struct nk_text text; - const struct nk_style_item *background; - - /* select correct background colors/images */ - background = nk_draw_button(out, bounds, state, style); - if (background->type == NK_STYLE_ITEM_COLOR) - text.background = background->data.color; - else text.background = style->text_background; - - /* select correct text colors */ - if (state & NK_WIDGET_STATE_HOVER) { - sym = style->text_hover; - text.text = style->text_hover; - } else if (state & NK_WIDGET_STATE_ACTIVED) { - sym = style->text_active; - text.text = style->text_active; - } else { - sym = style->text_normal; - text.text = style->text_normal; - } - - text.padding = nk_vec2(0,0); - nk_draw_symbol(out, type, *symbol, style->text_background, sym, 0, font); - nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); -} - -NK_INTERN int -nk_do_button_text_symbol(nk_flags *state, - struct nk_command_buffer *out, struct nk_rect bounds, - enum nk_symbol_type symbol, const char *str, int len, nk_flags align, - enum nk_button_behavior behavior, const struct nk_style_button *style, - const struct nk_user_font *font, const struct nk_input *in) -{ - int ret; - struct nk_rect tri = {0,0,0,0}; - struct nk_rect content; - - NK_ASSERT(style); - NK_ASSERT(out); - NK_ASSERT(font); - if (!out || !style || !font) - return nk_false; - - ret = nk_do_button(state, out, bounds, style, in, behavior, &content); - tri.y = content.y + (content.h/2) - font->height/2; - tri.w = font->height; tri.h = font->height; - if (align & NK_TEXT_ALIGN_LEFT) { - tri.x = (content.x + content.w) - (2 * style->padding.x + tri.w); - tri.x = NK_MAX(tri.x, 0); - } else tri.x = content.x + 2 * style->padding.x; - - /* draw button */ - if (style->draw_begin) style->draw_begin(out, style->userdata); - nk_draw_button_text_symbol(out, &bounds, &content, &tri, - *state, style, str, len, symbol, font); - if (style->draw_end) style->draw_end(out, style->userdata); - return ret; -} - -NK_INTERN void -nk_draw_button_text_image(struct nk_command_buffer *out, - const struct nk_rect *bounds, const struct nk_rect *label, - const struct nk_rect *image, nk_flags state, const struct nk_style_button *style, - const char *str, int len, const struct nk_user_font *font, - const struct nk_image *img) -{ - struct nk_text text; - const struct nk_style_item *background; - background = nk_draw_button(out, bounds, state, style); - - /* select correct colors */ - if (background->type == NK_STYLE_ITEM_COLOR) - text.background = background->data.color; - else text.background = style->text_background; - if (state & NK_WIDGET_STATE_HOVER) - text.text = style->text_hover; - else if (state & NK_WIDGET_STATE_ACTIVED) - text.text = style->text_active; - else text.text = style->text_normal; - - text.padding = nk_vec2(0,0); - nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); - nk_draw_image(out, *image, img, nk_white); -} - -NK_INTERN int -nk_do_button_text_image(nk_flags *state, - struct nk_command_buffer *out, struct nk_rect bounds, - struct nk_image img, const char* str, int len, nk_flags align, - enum nk_button_behavior behavior, const struct nk_style_button *style, - const struct nk_user_font *font, const struct nk_input *in) -{ - int ret; - struct nk_rect icon; - struct nk_rect content; - - NK_ASSERT(style); - NK_ASSERT(state); - NK_ASSERT(font); - NK_ASSERT(out); - if (!out || !font || !style || !str) - return nk_false; - - ret = nk_do_button(state, out, bounds, style, in, behavior, &content); - icon.y = bounds.y + style->padding.y; - icon.w = icon.h = bounds.h - 2 * style->padding.y; - if (align & NK_TEXT_ALIGN_LEFT) { - icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); - icon.x = NK_MAX(icon.x, 0); - } else icon.x = bounds.x + 2 * style->padding.x; - - icon.x += style->image_padding.x; - icon.y += style->image_padding.y; - icon.w -= 2 * style->image_padding.x; - icon.h -= 2 * style->image_padding.y; - - if (style->draw_begin) style->draw_begin(out, style->userdata); - nk_draw_button_text_image(out, &bounds, &content, &icon, *state, style, str, len, font, &img); - if (style->draw_end) style->draw_end(out, style->userdata); - return ret; -} - -/* =============================================================== - * - * TOGGLE - * - * ===============================================================*/ -enum nk_toggle_type { - NK_TOGGLE_CHECK, - NK_TOGGLE_OPTION -}; - -NK_INTERN int -nk_toggle_behavior(const struct nk_input *in, struct nk_rect select, - nk_flags *state, int active) -{ - nk_widget_state_reset(state); - if (nk_button_behavior(state, select, in, NK_BUTTON_DEFAULT)) { - *state = NK_WIDGET_STATE_ACTIVE; - active = !active; - } - if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, select)) - *state |= NK_WIDGET_STATE_ENTERED; - else if (nk_input_is_mouse_prev_hovering_rect(in, select)) - *state |= NK_WIDGET_STATE_LEFT; - return active; -} - -NK_INTERN void -nk_draw_checkbox(struct nk_command_buffer *out, - nk_flags state, const struct nk_style_toggle *style, int active, - const struct nk_rect *label, const struct nk_rect *selector, - const struct nk_rect *cursors, const char *string, int len, - const struct nk_user_font *font) -{ - const struct nk_style_item *background; - const struct nk_style_item *cursor; - struct nk_text text; - - /* select correct colors/images */ - if (state & NK_WIDGET_STATE_HOVER) { - background = &style->hover; - cursor = &style->cursor_hover; - text.text = style->text_hover; - } else if (state & NK_WIDGET_STATE_ACTIVED) { - background = &style->hover; - cursor = &style->cursor_hover; - text.text = style->text_active; - } else { - background = &style->normal; - cursor = &style->cursor_normal; - text.text = style->text_normal; - } - - /* draw background and cursor */ - if (background->type == NK_STYLE_ITEM_COLOR) { - nk_fill_rect(out, *selector, 0, style->border_color); - nk_fill_rect(out, nk_shrink_rect(*selector, style->border), 0, background->data.color); - } else nk_draw_image(out, *selector, &background->data.image, nk_white); - if (active) { - if (cursor->type == NK_STYLE_ITEM_IMAGE) - nk_draw_image(out, *cursors, &cursor->data.image, nk_white); - else nk_fill_rect(out, *cursors, 0, cursor->data.color); - } - - text.padding.x = 0; - text.padding.y = 0; - text.background = style->text_background; - nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); -} - -NK_INTERN void -nk_draw_option(struct nk_command_buffer *out, - nk_flags state, const struct nk_style_toggle *style, int active, - const struct nk_rect *label, const struct nk_rect *selector, - const struct nk_rect *cursors, const char *string, int len, - const struct nk_user_font *font) -{ - const struct nk_style_item *background; - const struct nk_style_item *cursor; - struct nk_text text; - - /* select correct colors/images */ - if (state & NK_WIDGET_STATE_HOVER) { - background = &style->hover; - cursor = &style->cursor_hover; - text.text = style->text_hover; - } else if (state & NK_WIDGET_STATE_ACTIVED) { - background = &style->hover; - cursor = &style->cursor_hover; - text.text = style->text_active; - } else { - background = &style->normal; - cursor = &style->cursor_normal; - text.text = style->text_normal; - } - - /* draw background and cursor */ - if (background->type == NK_STYLE_ITEM_COLOR) { - nk_fill_circle(out, *selector, style->border_color); - nk_fill_circle(out, nk_shrink_rect(*selector, style->border), background->data.color); - } else nk_draw_image(out, *selector, &background->data.image, nk_white); - if (active) { - if (cursor->type == NK_STYLE_ITEM_IMAGE) - nk_draw_image(out, *cursors, &cursor->data.image, nk_white); - else nk_fill_circle(out, *cursors, cursor->data.color); - } - - text.padding.x = 0; - text.padding.y = 0; - text.background = style->text_background; - nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); -} - -NK_INTERN int -nk_do_toggle(nk_flags *state, - struct nk_command_buffer *out, struct nk_rect r, - int *active, const char *str, int len, enum nk_toggle_type type, - const struct nk_style_toggle *style, const struct nk_input *in, - const struct nk_user_font *font) -{ - int was_active; - struct nk_rect bounds; - struct nk_rect select; - struct nk_rect cursor; - struct nk_rect label; - - NK_ASSERT(style); - NK_ASSERT(out); - NK_ASSERT(font); - if (!out || !style || !font || !active) - return 0; - - r.w = NK_MAX(r.w, font->height + 2 * style->padding.x); - r.h = NK_MAX(r.h, font->height + 2 * style->padding.y); - - /* add additional touch padding for touch screen devices */ - bounds.x = r.x - style->touch_padding.x; - bounds.y = r.y - style->touch_padding.y; - bounds.w = r.w + 2 * style->touch_padding.x; - bounds.h = r.h + 2 * style->touch_padding.y; - - /* calculate the selector space */ - select.w = font->height; - select.h = select.w; - select.y = r.y + r.h/2.0f - select.h/2.0f; - select.x = r.x; - - /* calculate the bounds of the cursor inside the selector */ - cursor.x = select.x + style->padding.x + style->border; - cursor.y = select.y + style->padding.y + style->border; - cursor.w = select.w - (2 * style->padding.x + 2 * style->border); - cursor.h = select.h - (2 * style->padding.y + 2 * style->border); - - /* label behind the selector */ - label.x = select.x + select.w + style->spacing; - label.y = select.y; - label.w = NK_MAX(r.x + r.w, label.x) - label.x; - label.h = select.w; - - /* update selector */ - was_active = *active; - *active = nk_toggle_behavior(in, bounds, state, *active); - - /* draw selector */ - if (style->draw_begin) - style->draw_begin(out, style->userdata); - if (type == NK_TOGGLE_CHECK) { - nk_draw_checkbox(out, *state, style, *active, &label, &select, &cursor, str, len, font); - } else { - nk_draw_option(out, *state, style, *active, &label, &select, &cursor, str, len, font); - } - if (style->draw_end) - style->draw_end(out, style->userdata); - return (was_active != *active); -} - -/* =============================================================== - * - * SELECTABLE - * - * ===============================================================*/ -NK_INTERN void -nk_draw_selectable(struct nk_command_buffer *out, - nk_flags state, const struct nk_style_selectable *style, int active, - const struct nk_rect *bounds, const struct nk_rect *icon, const struct nk_image *img, - const char *string, int len, nk_flags align, const struct nk_user_font *font) -{ - const struct nk_style_item *background; - struct nk_text text; - text.padding = style->padding; - - /* select correct colors/images */ - if (!active) { - if (state & NK_WIDGET_STATE_ACTIVED) { - background = &style->pressed; - text.text = style->text_pressed; - } else if (state & NK_WIDGET_STATE_HOVER) { - background = &style->hover; - text.text = style->text_hover; - } else { - background = &style->normal; - text.text = style->text_normal; - } - } else { - if (state & NK_WIDGET_STATE_ACTIVED) { - background = &style->pressed_active; - text.text = style->text_pressed_active; - } else if (state & NK_WIDGET_STATE_HOVER) { - background = &style->hover_active; - text.text = style->text_hover_active; - } else { - background = &style->normal_active; - text.text = style->text_normal_active; - } - } - - - /* draw selectable background and text */ - if (background->type == NK_STYLE_ITEM_IMAGE) { - nk_draw_image(out, *bounds, &background->data.image, nk_white); - text.background = nk_rgba(0,0,0,0); - } else { - nk_fill_rect(out, *bounds, style->rounding, background->data.color); - text.background = background->data.color; - } - if (img && icon) nk_draw_image(out, *icon, img, nk_white); - nk_widget_text(out, *bounds, string, len, &text, align, font); -} - -NK_INTERN int -nk_do_selectable(nk_flags *state, struct nk_command_buffer *out, - struct nk_rect bounds, const char *str, int len, nk_flags align, int *value, - const struct nk_style_selectable *style, const struct nk_input *in, - const struct nk_user_font *font) -{ - int old_value; - struct nk_rect touch; - - NK_ASSERT(state); - NK_ASSERT(out); - NK_ASSERT(str); - NK_ASSERT(len); - NK_ASSERT(value); - NK_ASSERT(style); - NK_ASSERT(font); - - if (!state || !out || !str || !len || !value || !style || !font) return 0; - old_value = *value; - - /* remove padding */ - touch.x = bounds.x - style->touch_padding.x; - touch.y = bounds.y - style->touch_padding.y; - touch.w = bounds.w + style->touch_padding.x * 2; - touch.h = bounds.h + style->touch_padding.y * 2; - - /* update button */ - if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) - *value = !(*value); - - /* draw selectable */ - if (style->draw_begin) style->draw_begin(out, style->userdata); - nk_draw_selectable(out, *state, style, *value, &bounds, 0,0, str, len, align, font); - if (style->draw_end) style->draw_end(out, style->userdata); - return old_value != *value; -} - -NK_INTERN int -nk_do_selectable_image(nk_flags *state, struct nk_command_buffer *out, - struct nk_rect bounds, const char *str, int len, nk_flags align, int *value, - const struct nk_image *img, const struct nk_style_selectable *style, - const struct nk_input *in, const struct nk_user_font *font) -{ - int old_value; - struct nk_rect touch; - struct nk_rect icon; - - NK_ASSERT(state); - NK_ASSERT(out); - NK_ASSERT(str); - NK_ASSERT(len); - NK_ASSERT(value); - NK_ASSERT(style); - NK_ASSERT(font); - - if (!state || !out || !str || !len || !value || !style || !font) return 0; - old_value = *value; - - /* toggle behavior */ - touch.x = bounds.x - style->touch_padding.x; - touch.y = bounds.y - style->touch_padding.y; - touch.w = bounds.w + style->touch_padding.x * 2; - touch.h = bounds.h + style->touch_padding.y * 2; - if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) - *value = !(*value); - - icon.y = bounds.y + style->padding.y; - icon.w = icon.h = bounds.h - 2 * style->padding.y; - if (align & NK_TEXT_ALIGN_LEFT) { - icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); - icon.x = NK_MAX(icon.x, 0); - } else icon.x = bounds.x + 2 * style->padding.x; - - icon.x += style->image_padding.x; - icon.y += style->image_padding.y; - icon.w -= 2 * style->image_padding.x; - icon.h -= 2 * style->image_padding.y; - - /* draw selectable */ - if (style->draw_begin) style->draw_begin(out, style->userdata); - nk_draw_selectable(out, *state, style, *value, &bounds, &icon, img, str, len, align, font); - if (style->draw_end) style->draw_end(out, style->userdata); - return old_value != *value; -} /* =============================================================== * - * SLIDER - * - * ===============================================================*/ -NK_INTERN float -nk_slider_behavior(nk_flags *state, struct nk_rect *logical_cursor, - struct nk_rect *visual_cursor, struct nk_input *in, - struct nk_rect bounds, float slider_min, float slider_max, float slider_value, - float slider_step, float slider_steps) -{ - int left_mouse_down; - int left_mouse_click_in_cursor; - - /* check if visual cursor is being dragged */ - nk_widget_state_reset(state); - left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; - left_mouse_click_in_cursor = in && nk_input_has_mouse_click_down_in_rect(in, - NK_BUTTON_LEFT, *visual_cursor, nk_true); - - if (left_mouse_down && left_mouse_click_in_cursor) - { - float ratio = 0; - const float d = in->mouse.pos.x - (visual_cursor->x+visual_cursor->w*0.5f); - const float pxstep = bounds.w / slider_steps; - - /* only update value if the next slider step is reached */ - *state = NK_WIDGET_STATE_ACTIVE; - if (NK_ABS(d) >= pxstep) { - const float steps = (float)((int)(NK_ABS(d) / pxstep)); - slider_value += (d > 0) ? (slider_step*steps) : -(slider_step*steps); - slider_value = NK_CLAMP(slider_min, slider_value, slider_max); - ratio = (slider_value - slider_min)/slider_step; - logical_cursor->x = bounds.x + (logical_cursor->w * ratio); - in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = logical_cursor->x; - } - } - - /* slider widget state */ - if (nk_input_is_mouse_hovering_rect(in, bounds)) - *state = NK_WIDGET_STATE_HOVERED; - if (*state & NK_WIDGET_STATE_HOVER && - !nk_input_is_mouse_prev_hovering_rect(in, bounds)) - *state |= NK_WIDGET_STATE_ENTERED; - else if (nk_input_is_mouse_prev_hovering_rect(in, bounds)) - *state |= NK_WIDGET_STATE_LEFT; - return slider_value; -} - -NK_INTERN void -nk_draw_slider(struct nk_command_buffer *out, nk_flags state, - const struct nk_style_slider *style, const struct nk_rect *bounds, - const struct nk_rect *visual_cursor, float min, float value, float max) -{ - struct nk_rect fill; - struct nk_rect bar; - const struct nk_style_item *background; - - /* select correct slider images/colors */ - struct nk_color bar_color; - const struct nk_style_item *cursor; - - NK_UNUSED(min); - NK_UNUSED(max); - NK_UNUSED(value); - - if (state & NK_WIDGET_STATE_ACTIVED) { - background = &style->active; - bar_color = style->bar_active; - cursor = &style->cursor_active; - } else if (state & NK_WIDGET_STATE_HOVER) { - background = &style->hover; - bar_color = style->bar_hover; - cursor = &style->cursor_hover; - } else { - background = &style->normal; - bar_color = style->bar_normal; - cursor = &style->cursor_normal; - } - - /* calculate slider background bar */ - bar.x = bounds->x; - bar.y = (visual_cursor->y + visual_cursor->h/2) - bounds->h/12; - bar.w = bounds->w; - bar.h = bounds->h/6; - - /* filled background bar style */ - fill.w = (visual_cursor->x + (visual_cursor->w/2.0f)) - bar.x; - fill.x = bar.x; - fill.y = bar.y; - fill.h = bar.h; - - /* draw background */ - if (background->type == NK_STYLE_ITEM_IMAGE) { - nk_draw_image(out, *bounds, &background->data.image, nk_white); - } else { - nk_fill_rect(out, *bounds, style->rounding, background->data.color); - nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); - } - - /* draw slider bar */ - nk_fill_rect(out, bar, style->rounding, bar_color); - nk_fill_rect(out, fill, style->rounding, style->bar_filled); - - /* draw cursor */ - if (cursor->type == NK_STYLE_ITEM_IMAGE) - nk_draw_image(out, *visual_cursor, &cursor->data.image, nk_white); - else nk_fill_circle(out, *visual_cursor, cursor->data.color); -} - -NK_INTERN float -nk_do_slider(nk_flags *state, - struct nk_command_buffer *out, struct nk_rect bounds, - float min, float val, float max, float step, - const struct nk_style_slider *style, struct nk_input *in, - const struct nk_user_font *font) -{ - float slider_range; - float slider_min; - float slider_max; - float slider_value; - float slider_steps; - float cursor_offset; - - struct nk_rect visual_cursor; - struct nk_rect logical_cursor; - - NK_ASSERT(style); - NK_ASSERT(out); - if (!out || !style) - return 0; - - /* remove padding from slider bounds */ - bounds.x = bounds.x + style->padding.x; - bounds.y = bounds.y + style->padding.y; - bounds.h = NK_MAX(bounds.h, 2*style->padding.y); - bounds.w = NK_MAX(bounds.w, 2*style->padding.x + style->cursor_size.x); - bounds.w -= 2 * style->padding.x; - bounds.h -= 2 * style->padding.y; - - /* optional buttons */ - if (style->show_buttons) { - nk_flags ws; - struct nk_rect button; - button.y = bounds.y; - button.w = bounds.h; - button.h = bounds.h; - - /* decrement button */ - button.x = bounds.x; - if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, NK_BUTTON_DEFAULT, - &style->dec_button, in, font)) - val -= step; - - /* increment button */ - button.x = (bounds.x + bounds.w) - button.w; - if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, NK_BUTTON_DEFAULT, - &style->inc_button, in, font)) - val += step; - - bounds.x = bounds.x + button.w + style->spacing.x; - bounds.w = bounds.w - (2*button.w + 2*style->spacing.x); - } - - /* remove one cursor size to support visual cursor */ - bounds.x += style->cursor_size.x*0.5f; - bounds.w -= style->cursor_size.x; - - /* make sure the provided values are correct */ - slider_max = NK_MAX(min, max); - slider_min = NK_MIN(min, max); - slider_value = NK_CLAMP(slider_min, val, slider_max); - slider_range = slider_max - slider_min; - slider_steps = slider_range / step; - cursor_offset = (slider_value - slider_min) / step; - - /* calculate cursor - Basically you have two cursors. One for visual representation and interaction - and one for updating the actual cursor value. */ - logical_cursor.h = bounds.h; - logical_cursor.w = bounds.w / slider_steps; - logical_cursor.x = bounds.x + (logical_cursor.w * cursor_offset); - logical_cursor.y = bounds.y; - - visual_cursor.h = style->cursor_size.y; - visual_cursor.w = style->cursor_size.x; - visual_cursor.y = (bounds.y + bounds.h*0.5f) - visual_cursor.h*0.5f; - visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f; - - slider_value = nk_slider_behavior(state, &logical_cursor, &visual_cursor, - in, bounds, slider_min, slider_max, slider_value, step, slider_steps); - visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f; - - /* draw slider */ - if (style->draw_begin) style->draw_begin(out, style->userdata); - nk_draw_slider(out, *state, style, &bounds, &visual_cursor, slider_min, slider_value, slider_max); - if (style->draw_end) style->draw_end(out, style->userdata); - return slider_value; -} - -/* =============================================================== - * - * PROGRESSBAR - * - * ===============================================================*/ -NK_INTERN nk_size -nk_progress_behavior(nk_flags *state, const struct nk_input *in, - struct nk_rect r, nk_size max, nk_size value, int modifiable) -{ - nk_widget_state_reset(state); - if (in && modifiable && nk_input_is_mouse_hovering_rect(in, r)) { - int left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; - int left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, - NK_BUTTON_LEFT, r, nk_true); - - if (left_mouse_down && left_mouse_click_in_cursor) { - float ratio = NK_MAX(0, (float)(in->mouse.pos.x - r.x)) / (float)r.w; - value = (nk_size)NK_MAX(0,((float)max * ratio)); - *state = NK_WIDGET_STATE_ACTIVE; - } else *state = NK_WIDGET_STATE_HOVERED; - } - - /* set progressbar widget state */ - if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, r)) - *state |= NK_WIDGET_STATE_ENTERED; - else if (nk_input_is_mouse_prev_hovering_rect(in, r)) - *state |= NK_WIDGET_STATE_LEFT; - - if (!max) return value; - value = NK_MIN(value, max); - return value; -} - -NK_INTERN void -nk_draw_progress(struct nk_command_buffer *out, nk_flags state, - const struct nk_style_progress *style, const struct nk_rect *bounds, - const struct nk_rect *scursor, nk_size value, nk_size max) -{ - const struct nk_style_item *background; - const struct nk_style_item *cursor; - - NK_UNUSED(max); - NK_UNUSED(value); - - /* select correct colors/images to draw */ - if (state & NK_WIDGET_STATE_ACTIVED) { - background = &style->active; - cursor = &style->cursor_active; - } else if (state & NK_WIDGET_STATE_HOVER){ - background = &style->hover; - cursor = &style->cursor_hover; - } else { - background = &style->normal; - cursor = &style->cursor_normal; - } - - /* draw background */ - if (background->type == NK_STYLE_ITEM_COLOR) { - nk_fill_rect(out, *bounds, style->rounding, background->data.color); - nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); - } else nk_draw_image(out, *bounds, &background->data.image, nk_white); - - /* draw cursor */ - if (cursor->type == NK_STYLE_ITEM_COLOR) { - nk_fill_rect(out, *scursor, style->rounding, cursor->data.color); - nk_stroke_rect(out, *scursor, style->rounding, style->border, style->border_color); - } else nk_draw_image(out, *scursor, &cursor->data.image, nk_white); -} - -NK_INTERN nk_size -nk_do_progress(nk_flags *state, - struct nk_command_buffer *out, struct nk_rect bounds, - nk_size value, nk_size max, int modifiable, - const struct nk_style_progress *style, const struct nk_input *in) -{ - float prog_scale; - nk_size prog_value; - struct nk_rect cursor; - - NK_ASSERT(style); - NK_ASSERT(out); - if (!out || !style) return 0; - - /* calculate progressbar cursor */ - cursor.w = NK_MAX(bounds.w, 2 * style->padding.x + 2 * style->border); - cursor.h = NK_MAX(bounds.h, 2 * style->padding.y + 2 * style->border); - cursor = nk_pad_rect(bounds, nk_vec2(style->padding.x + style->border, style->padding.y + style->border)); - prog_scale = (float)value / (float)max; - cursor.w = (bounds.w - 2) * prog_scale; - - /* update progressbar */ - prog_value = NK_MIN(value, max); - prog_value = nk_progress_behavior(state, in, bounds, max, prog_value, modifiable); - - /* draw progressbar */ - if (style->draw_begin) style->draw_begin(out, style->userdata); - nk_draw_progress(out, *state, style, &bounds, &cursor, value, max); - if (style->draw_end) style->draw_end(out, style->userdata); - return prog_value; -} - -/* =============================================================== - * - * SCROLLBAR - * - * ===============================================================*/ -NK_INTERN float -nk_scrollbar_behavior(nk_flags *state, struct nk_input *in, - int has_scrolling, const struct nk_rect *scroll, - const struct nk_rect *cursor, const struct nk_rect *empty0, - const struct nk_rect *empty1, float scroll_offset, - float target, float scroll_step, enum nk_orientation o) -{ - nk_flags ws = 0; - int left_mouse_down; - int left_mouse_click_in_cursor; - float scroll_delta; - - nk_widget_state_reset(state); - if (!in) return scroll_offset; - - left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; - left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, - NK_BUTTON_LEFT, *cursor, nk_true); - if (nk_input_is_mouse_hovering_rect(in, *scroll)) - *state = NK_WIDGET_STATE_HOVERED; - - scroll_delta = (o == NK_VERTICAL) ? in->mouse.scroll_delta.y: in->mouse.scroll_delta.x; - if (left_mouse_down && left_mouse_click_in_cursor) { - /* update cursor by mouse dragging */ - float pixel, delta; - *state = NK_WIDGET_STATE_ACTIVE; - if (o == NK_VERTICAL) { - float cursor_y; - pixel = in->mouse.delta.y; - delta = (pixel / scroll->h) * target; - scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->h); - cursor_y = scroll->y + ((scroll_offset/target) * scroll->h); - in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = cursor_y + cursor->h/2.0f; - } else { - float cursor_x; - pixel = in->mouse.delta.x; - delta = (pixel / scroll->w) * target; - scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->w); - cursor_x = scroll->x + ((scroll_offset/target) * scroll->w); - in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = cursor_x + cursor->w/2.0f; - } - } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_UP) && o == NK_VERTICAL && has_scrolling)|| - nk_button_behavior(&ws, *empty0, in, NK_BUTTON_DEFAULT)) { - /* scroll page up by click on empty space or shortcut */ - if (o == NK_VERTICAL) - scroll_offset = NK_MAX(0, scroll_offset - scroll->h); - else scroll_offset = NK_MAX(0, scroll_offset - scroll->w); - } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_DOWN) && o == NK_VERTICAL && has_scrolling) || - nk_button_behavior(&ws, *empty1, in, NK_BUTTON_DEFAULT)) { - /* scroll page down by click on empty space or shortcut */ - if (o == NK_VERTICAL) - scroll_offset = NK_MIN(scroll_offset + scroll->h, target - scroll->h); - else scroll_offset = NK_MIN(scroll_offset + scroll->w, target - scroll->w); - } else if (has_scrolling) { - if ((scroll_delta < 0 || (scroll_delta > 0))) { - /* update cursor by mouse scrolling */ - scroll_offset = scroll_offset + scroll_step * (-scroll_delta); - if (o == NK_VERTICAL) - scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->h); - else scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->w); - } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_START)) { - /* update cursor to the beginning */ - if (o == NK_VERTICAL) scroll_offset = 0; - } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_END)) { - /* update cursor to the end */ - if (o == NK_VERTICAL) scroll_offset = target - scroll->h; - } - } - if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *scroll)) - *state |= NK_WIDGET_STATE_ENTERED; - else if (nk_input_is_mouse_prev_hovering_rect(in, *scroll)) - *state |= NK_WIDGET_STATE_LEFT; - return scroll_offset; -} - -NK_INTERN void -nk_draw_scrollbar(struct nk_command_buffer *out, nk_flags state, - const struct nk_style_scrollbar *style, const struct nk_rect *bounds, - const struct nk_rect *scroll) -{ - const struct nk_style_item *background; - const struct nk_style_item *cursor; - - /* select correct colors/images to draw */ - if (state & NK_WIDGET_STATE_ACTIVED) { - background = &style->active; - cursor = &style->cursor_active; - } else if (state & NK_WIDGET_STATE_HOVER) { - background = &style->hover; - cursor = &style->cursor_hover; - } else { - background = &style->normal; - cursor = &style->cursor_normal; - } - - /* draw background */ - if (background->type == NK_STYLE_ITEM_COLOR) { - nk_fill_rect(out, *bounds, style->rounding, background->data.color); - nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); - } else { - nk_draw_image(out, *bounds, &background->data.image, nk_white); - } - - /* draw cursor */ - if (background->type == NK_STYLE_ITEM_COLOR) { - nk_fill_rect(out, *scroll, style->rounding_cursor, cursor->data.color); - nk_stroke_rect(out, *scroll, style->rounding_cursor, style->border_cursor, style->cursor_border_color); - } else nk_draw_image(out, *scroll, &cursor->data.image, nk_white); -} - -NK_INTERN float -nk_do_scrollbarv(nk_flags *state, - struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, - float offset, float target, float step, float button_pixel_inc, - const struct nk_style_scrollbar *style, struct nk_input *in, - const struct nk_user_font *font) -{ - struct nk_rect empty_north; - struct nk_rect empty_south; - struct nk_rect cursor; - - float scroll_step; - float scroll_offset; - float scroll_off; - float scroll_ratio; - - NK_ASSERT(out); - NK_ASSERT(style); - NK_ASSERT(state); - if (!out || !style) return 0; - - scroll.w = NK_MAX(scroll.w, 1); - scroll.h = NK_MAX(scroll.h, 0); - if (target <= scroll.h) return 0; - - /* optional scrollbar buttons */ - if (style->show_buttons) { - nk_flags ws; - float scroll_h; - struct nk_rect button; - - button.x = scroll.x; - button.w = scroll.w; - button.h = scroll.w; - - scroll_h = NK_MAX(scroll.h - 2 * button.h,0); - scroll_step = NK_MIN(step, button_pixel_inc); - - /* decrement button */ - button.y = scroll.y; - if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, - NK_BUTTON_REPEATER, &style->dec_button, in, font)) - offset = offset - scroll_step; - - /* increment button */ - button.y = scroll.y + scroll.h - button.h; - if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, - NK_BUTTON_REPEATER, &style->inc_button, in, font)) - offset = offset + scroll_step; - - scroll.y = scroll.y + button.h; - scroll.h = scroll_h; - } - - /* calculate scrollbar constants */ - scroll_step = NK_MIN(step, scroll.h); - scroll_offset = NK_CLAMP(0, offset, target - scroll.h); - scroll_ratio = scroll.h / target; - scroll_off = scroll_offset / target; - - /* calculate scrollbar cursor bounds */ - cursor.h = NK_MAX((scroll_ratio * scroll.h) - (2*style->border + 2*style->padding.y), 0); - cursor.y = scroll.y + (scroll_off * scroll.h) + style->border + style->padding.y; - cursor.w = scroll.w - (2 * style->border + 2 * style->padding.x); - cursor.x = scroll.x + style->border + style->padding.x; - - /* calculate empty space around cursor */ - empty_north.x = scroll.x; - empty_north.y = scroll.y; - empty_north.w = scroll.w; - empty_north.h = NK_MAX(cursor.y - scroll.y, 0); - - empty_south.x = scroll.x; - empty_south.y = cursor.y + cursor.h; - empty_south.w = scroll.w; - empty_south.h = NK_MAX((scroll.y + scroll.h) - (cursor.y + cursor.h), 0); - - /* update scrollbar */ - scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor, - &empty_north, &empty_south, scroll_offset, target, scroll_step, NK_VERTICAL); - scroll_off = scroll_offset / target; - cursor.y = scroll.y + (scroll_off * scroll.h) + style->border_cursor + style->padding.y; - - /* draw scrollbar */ - if (style->draw_begin) style->draw_begin(out, style->userdata); - nk_draw_scrollbar(out, *state, style, &scroll, &cursor); - if (style->draw_end) style->draw_end(out, style->userdata); - return scroll_offset; -} - -NK_INTERN float -nk_do_scrollbarh(nk_flags *state, - struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, - float offset, float target, float step, float button_pixel_inc, - const struct nk_style_scrollbar *style, struct nk_input *in, - const struct nk_user_font *font) -{ - struct nk_rect cursor; - struct nk_rect empty_west; - struct nk_rect empty_east; - - float scroll_step; - float scroll_offset; - float scroll_off; - float scroll_ratio; - - NK_ASSERT(out); - NK_ASSERT(style); - if (!out || !style) return 0; - - /* scrollbar background */ - scroll.h = NK_MAX(scroll.h, 1); - scroll.w = NK_MAX(scroll.w, 2 * scroll.h); - if (target <= scroll.w) return 0; - - /* optional scrollbar buttons */ - if (style->show_buttons) { - nk_flags ws; - float scroll_w; - struct nk_rect button; - button.y = scroll.y; - button.w = scroll.h; - button.h = scroll.h; - - scroll_w = scroll.w - 2 * button.w; - scroll_step = NK_MIN(step, button_pixel_inc); - - /* decrement button */ - button.x = scroll.x; - if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, - NK_BUTTON_REPEATER, &style->dec_button, in, font)) - offset = offset - scroll_step; - - /* increment button */ - button.x = scroll.x + scroll.w - button.w; - if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, - NK_BUTTON_REPEATER, &style->inc_button, in, font)) - offset = offset + scroll_step; - - scroll.x = scroll.x + button.w; - scroll.w = scroll_w; - } - - /* calculate scrollbar constants */ - scroll_step = NK_MIN(step, scroll.w); - scroll_offset = NK_CLAMP(0, offset, target - scroll.w); - scroll_ratio = scroll.w / target; - scroll_off = scroll_offset / target; - - /* calculate cursor bounds */ - cursor.w = (scroll_ratio * scroll.w) - (2*style->border + 2*style->padding.x); - cursor.x = scroll.x + (scroll_off * scroll.w) + style->border + style->padding.x; - cursor.h = scroll.h - (2 * style->border + 2 * style->padding.y); - cursor.y = scroll.y + style->border + style->padding.y; - - /* calculate empty space around cursor */ - empty_west.x = scroll.x; - empty_west.y = scroll.y; - empty_west.w = cursor.x - scroll.x; - empty_west.h = scroll.h; - - empty_east.x = cursor.x + cursor.w; - empty_east.y = scroll.y; - empty_east.w = (scroll.x + scroll.w) - (cursor.x + cursor.w); - empty_east.h = scroll.h; - - /* update scrollbar */ - scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor, - &empty_west, &empty_east, scroll_offset, target, scroll_step, NK_HORIZONTAL); - scroll_off = scroll_offset / target; - cursor.x = scroll.x + (scroll_off * scroll.w); - - /* draw scrollbar */ - if (style->draw_begin) style->draw_begin(out, style->userdata); - nk_draw_scrollbar(out, *state, style, &scroll, &cursor); - if (style->draw_end) style->draw_end(out, style->userdata); - return scroll_offset; -} - -/* =============================================================== - * - * FILTER - * - * ===============================================================*/ -NK_API int nk_filter_default(const struct nk_text_edit *box, nk_rune unicode) -{(void)unicode;NK_UNUSED(box);return nk_true;} - -NK_API int -nk_filter_ascii(const struct nk_text_edit *box, nk_rune unicode) -{ - NK_UNUSED(box); - if (unicode > 128) return nk_false; - else return nk_true; -} - -NK_API int -nk_filter_float(const struct nk_text_edit *box, nk_rune unicode) -{ - NK_UNUSED(box); - if ((unicode < '0' || unicode > '9') && unicode != '.' && unicode != '-') - return nk_false; - else return nk_true; -} - -NK_API int -nk_filter_decimal(const struct nk_text_edit *box, nk_rune unicode) -{ - NK_UNUSED(box); - if ((unicode < '0' || unicode > '9') && unicode != '-') - return nk_false; - else return nk_true; -} - -NK_API int -nk_filter_hex(const struct nk_text_edit *box, nk_rune unicode) -{ - NK_UNUSED(box); - if ((unicode < '0' || unicode > '9') && - (unicode < 'a' || unicode > 'f') && - (unicode < 'A' || unicode > 'F')) - return nk_false; - else return nk_true; -} - -NK_API int -nk_filter_oct(const struct nk_text_edit *box, nk_rune unicode) -{ - NK_UNUSED(box); - if (unicode < '0' || unicode > '7') - return nk_false; - else return nk_true; -} - -NK_API int -nk_filter_binary(const struct nk_text_edit *box, nk_rune unicode) -{ - NK_UNUSED(box); - if (unicode != '0' && unicode != '1') - return nk_false; - else return nk_true; -} - -/* =============================================================== - * - * EDIT - * - * ===============================================================*/ -NK_INTERN void -nk_edit_draw_text(struct nk_command_buffer *out, - const struct nk_style_edit *style, float pos_x, float pos_y, - float x_offset, const char *text, int byte_len, float row_height, - const struct nk_user_font *font, struct nk_color background, - struct nk_color foreground, int is_selected) -{ - NK_ASSERT(out); - NK_ASSERT(font); - NK_ASSERT(style); - if (!text || !byte_len || !out || !style) return; - - {int glyph_len = 0; - nk_rune unicode = 0; - int text_len = 0; - float line_width = 0; - float glyph_width; - const char *line = text; - float line_offset = 0; - int line_count = 0; - - struct nk_text txt; - txt.padding = nk_vec2(0,0); - txt.background = background; - txt.text = foreground; - - glyph_len = nk_utf_decode(text+text_len, &unicode, byte_len-text_len); - if (!glyph_len) return; - while ((text_len < byte_len) && glyph_len) - { - if (unicode == '\n') { - /* new line separator so draw previous line */ - struct nk_rect label; - label.y = pos_y + line_offset; - label.h = row_height; - label.w = line_width; - label.x = pos_x; - if (!line_count) - label.x += x_offset; - - if (is_selected) /* selection needs to draw different background color */ - nk_fill_rect(out, label, 0, background); - nk_widget_text(out, label, line, (int)((text + text_len) - line), - &txt, NK_TEXT_CENTERED, font); - - text_len++; - line_count++; - line_width = 0; - line = text + text_len; - line_offset += row_height; - glyph_len = nk_utf_decode(text + text_len, &unicode, (int)(byte_len-text_len)); - continue; - } - if (unicode == '\r') { - text_len++; - glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); - continue; - } - glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); - line_width += (float)glyph_width; - text_len += glyph_len; - glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); - continue; - } - if (line_width > 0) { - /* draw last line */ - struct nk_rect label; - label.y = pos_y + line_offset; - label.h = row_height; - label.w = line_width; - label.x = pos_x; - if (!line_count) - label.x += x_offset; - - if (is_selected) - nk_fill_rect(out, label, 0, background); - nk_widget_text(out, label, line, (int)((text + text_len) - line), - &txt, NK_TEXT_LEFT, font); - }} -} - -NK_INTERN nk_flags -nk_do_edit(nk_flags *state, struct nk_command_buffer *out, - struct nk_rect bounds, nk_flags flags, nk_plugin_filter filter, - struct nk_text_edit *edit, const struct nk_style_edit *style, - struct nk_input *in, const struct nk_user_font *font) -{ - struct nk_rect area; - nk_flags ret = 0; - float row_height; - char prev_state = 0; - char is_hovered = 0; - char select_all = 0; - char cursor_follow = 0; - struct nk_rect old_clip; - struct nk_rect clip; - - NK_ASSERT(state); - NK_ASSERT(out); - NK_ASSERT(style); - if (!state || !out || !style) - return ret; - - /* visible text area calculation */ - area.x = bounds.x + style->padding.x + style->border; - area.y = bounds.y + style->padding.y + style->border; - area.w = bounds.w - (2.0f * style->padding.x + 2 * style->border); - area.h = bounds.h - (2.0f * style->padding.y + 2 * style->border); - if (flags & NK_EDIT_MULTILINE) - area.w = NK_MAX(0, area.w - style->scrollbar_size.x); - row_height = (flags & NK_EDIT_MULTILINE)? font->height + style->row_padding: area.h; - - /* calculate clipping rectangle */ - old_clip = out->clip; - nk_unify(&clip, &old_clip, area.x, area.y, area.x + area.w, area.y + area.h); - - /* update edit state */ - prev_state = (char)edit->active; - is_hovered = (char)nk_input_is_mouse_hovering_rect(in, bounds); - if (in && in->mouse.buttons[NK_BUTTON_LEFT].clicked && in->mouse.buttons[NK_BUTTON_LEFT].down) { - edit->active = NK_INBOX(in->mouse.pos.x, in->mouse.pos.y, - bounds.x, bounds.y, bounds.w, bounds.h); - } - - /* (de)activate text editor */ - if (!prev_state && edit->active) { - const enum nk_text_edit_type type = (flags & NK_EDIT_MULTILINE) ? - NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE; - nk_textedit_clear_state(edit, type, filter); - if (flags & NK_EDIT_ALWAYS_INSERT_MODE) - edit->mode = NK_TEXT_EDIT_MODE_INSERT; - if (flags & NK_EDIT_AUTO_SELECT) - select_all = nk_true; - if (flags & NK_EDIT_GOTO_END_ON_ACTIVATE) { - edit->cursor = edit->string.len; - in = 0; - } - } else if (!edit->active) edit->mode = NK_TEXT_EDIT_MODE_VIEW; - if (flags & NK_EDIT_READ_ONLY) - edit->mode = NK_TEXT_EDIT_MODE_VIEW; - - ret = (edit->active) ? NK_EDIT_ACTIVE: NK_EDIT_INACTIVE; - if (prev_state != edit->active) - ret |= (edit->active) ? NK_EDIT_ACTIVATED: NK_EDIT_DEACTIVATED; - - /* handle user input */ - if (edit->active && in) - { - int shift_mod = in->keyboard.keys[NK_KEY_SHIFT].down; - const float mouse_x = (in->mouse.pos.x - area.x) + edit->scrollbar.x; - const float mouse_y = (in->mouse.pos.y - area.y) + edit->scrollbar.y; - - /* mouse click handler */ - is_hovered = (char)nk_input_is_mouse_hovering_rect(in, area); - if (select_all) { - nk_textedit_select_all(edit); - } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && - in->mouse.buttons[NK_BUTTON_LEFT].clicked) { - nk_textedit_click(edit, mouse_x, mouse_y, font, row_height); - } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && - (in->mouse.delta.x != 0.0f || in->mouse.delta.y != 0.0f)) { - nk_textedit_drag(edit, mouse_x, mouse_y, font, row_height); - cursor_follow = nk_true; - } else if (is_hovered && in->mouse.buttons[NK_BUTTON_RIGHT].clicked && - in->mouse.buttons[NK_BUTTON_RIGHT].down) { - nk_textedit_key(edit, NK_KEY_TEXT_WORD_LEFT, nk_false, font, row_height); - nk_textedit_key(edit, NK_KEY_TEXT_WORD_RIGHT, nk_true, font, row_height); - cursor_follow = nk_true; - } - - {int i; /* keyboard input */ - int old_mode = edit->mode; - for (i = 0; i < NK_KEY_MAX; ++i) { - if (i == NK_KEY_ENTER || i == NK_KEY_TAB) continue; /* special case */ - if (nk_input_is_key_pressed(in, (enum nk_keys)i)) { - nk_textedit_key(edit, (enum nk_keys)i, shift_mod, font, row_height); - cursor_follow = nk_true; - } - } - if (old_mode != edit->mode) { - in->keyboard.text_len = 0; - }} - - /* text input */ - edit->filter = filter; - if (in->keyboard.text_len) { - nk_textedit_text(edit, in->keyboard.text, in->keyboard.text_len); - cursor_follow = nk_true; - in->keyboard.text_len = 0; - } - - /* enter key handler */ - if (nk_input_is_key_pressed(in, NK_KEY_ENTER)) { - cursor_follow = nk_true; - if (flags & NK_EDIT_CTRL_ENTER_NEWLINE && shift_mod) - nk_textedit_text(edit, "\n", 1); - else if (flags & NK_EDIT_SIG_ENTER) - ret |= NK_EDIT_COMMITED; - else nk_textedit_text(edit, "\n", 1); - } - - /* cut & copy handler */ - {int copy= nk_input_is_key_pressed(in, NK_KEY_COPY); - int cut = nk_input_is_key_pressed(in, NK_KEY_CUT); - if ((copy || cut) && (flags & NK_EDIT_CLIPBOARD)) - { - int glyph_len; - nk_rune unicode; - const char *text; - int b = edit->select_start; - int e = edit->select_end; - - int begin = NK_MIN(b, e); - int end = NK_MAX(b, e); - text = nk_str_at_const(&edit->string, begin, &unicode, &glyph_len); - if (edit->clip.copy) - edit->clip.copy(edit->clip.userdata, text, end - begin); - if (cut && !(flags & NK_EDIT_READ_ONLY)){ - nk_textedit_cut(edit); - cursor_follow = nk_true; - } - }} - - /* paste handler */ - {int paste = nk_input_is_key_pressed(in, NK_KEY_PASTE); - if (paste && (flags & NK_EDIT_CLIPBOARD) && edit->clip.paste) { - edit->clip.paste(edit->clip.userdata, edit); - cursor_follow = nk_true; - }} - - /* tab handler */ - {int tab = nk_input_is_key_pressed(in, NK_KEY_TAB); - if (tab && (flags & NK_EDIT_ALLOW_TAB)) { - nk_textedit_text(edit, " ", 4); - cursor_follow = nk_true; - }} - } - - /* set widget state */ - if (edit->active) - *state = NK_WIDGET_STATE_ACTIVE; - else nk_widget_state_reset(state); - - if (is_hovered) - *state |= NK_WIDGET_STATE_HOVERED; - - /* DRAW EDIT */ - {const char *text = nk_str_get_const(&edit->string); - int len = nk_str_len_char(&edit->string); - - {/* select background colors/images */ - const struct nk_style_item *background; - if (*state & NK_WIDGET_STATE_ACTIVED) - background = &style->active; - else if (*state & NK_WIDGET_STATE_HOVER) - background = &style->hover; - else background = &style->normal; - - /* draw background frame */ - if (background->type == NK_STYLE_ITEM_COLOR) { - nk_stroke_rect(out, bounds, style->rounding, style->border, style->border_color); - nk_fill_rect(out, bounds, style->rounding, background->data.color); - } else nk_draw_image(out, bounds, &background->data.image, nk_white);} - - area.w = NK_MAX(0, area.w - style->cursor_size); - if (edit->active) - { - int total_lines = 1; - struct nk_vec2 text_size = nk_vec2(0,0); - - /* text pointer positions */ - const char *cursor_ptr = 0; - const char *select_begin_ptr = 0; - const char *select_end_ptr = 0; - - /* 2D pixel positions */ - struct nk_vec2 cursor_pos = nk_vec2(0,0); - struct nk_vec2 selection_offset_start = nk_vec2(0,0); - struct nk_vec2 selection_offset_end = nk_vec2(0,0); - - int selection_begin = NK_MIN(edit->select_start, edit->select_end); - int selection_end = NK_MAX(edit->select_start, edit->select_end); - - /* calculate total line count + total space + cursor/selection position */ - float line_width = 0.0f; - if (text && len) - { - /* utf8 encoding */ - float glyph_width; - int glyph_len = 0; - nk_rune unicode = 0; - int text_len = 0; - int glyphs = 0; - int row_begin = 0; - - glyph_len = nk_utf_decode(text, &unicode, len); - glyph_width = font->width(font->userdata, font->height, text, glyph_len); - line_width = 0; - - /* iterate all lines */ - while ((text_len < len) && glyph_len) - { - /* set cursor 2D position and line */ - if (!cursor_ptr && glyphs == edit->cursor) - { - int glyph_offset; - struct nk_vec2 out_offset; - struct nk_vec2 row_size; - const char *remaining; - - /* calculate 2d position */ - cursor_pos.y = (float)(total_lines-1) * row_height; - row_size = nk_text_calculate_text_bounds(font, text+row_begin, - text_len-row_begin, row_height, &remaining, - &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); - cursor_pos.x = row_size.x; - cursor_ptr = text + text_len; - } - - /* set start selection 2D position and line */ - if (!select_begin_ptr && edit->select_start != edit->select_end && - glyphs == selection_begin) - { - int glyph_offset; - struct nk_vec2 out_offset; - struct nk_vec2 row_size; - const char *remaining; - - /* calculate 2d position */ - selection_offset_start.y = (float)(NK_MAX(total_lines-1,0)) * row_height; - row_size = nk_text_calculate_text_bounds(font, text+row_begin, - text_len-row_begin, row_height, &remaining, - &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); - selection_offset_start.x = row_size.x; - select_begin_ptr = text + text_len; - } - - /* set end selection 2D position and line */ - if (!select_end_ptr && edit->select_start != edit->select_end && - glyphs == selection_end) - { - int glyph_offset; - struct nk_vec2 out_offset; - struct nk_vec2 row_size; - const char *remaining; - - /* calculate 2d position */ - selection_offset_end.y = (float)(total_lines-1) * row_height; - row_size = nk_text_calculate_text_bounds(font, text+row_begin, - text_len-row_begin, row_height, &remaining, - &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); - selection_offset_end.x = row_size.x; - select_end_ptr = text + text_len; - } - if (unicode == '\n') { - text_size.x = NK_MAX(text_size.x, line_width); - total_lines++; - line_width = 0; - text_len++; - glyphs++; - row_begin = text_len; - glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); - glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); - continue; - } - - glyphs++; - text_len += glyph_len; - line_width += (float)glyph_width; - - glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); - glyph_width = font->width(font->userdata, font->height, - text+text_len, glyph_len); - continue; - } - text_size.y = (float)total_lines * row_height; - - /* handle case when cursor is at end of text buffer */ - if (!cursor_ptr && edit->cursor == edit->string.len) { - cursor_pos.x = line_width; - cursor_pos.y = text_size.y - row_height; - } - } - { - /* scrollbar */ - if (cursor_follow) - { - /* update scrollbar to follow cursor */ - if (!(flags & NK_EDIT_NO_HORIZONTAL_SCROLL)) { - /* horizontal scroll */ - const float scroll_increment = area.w * 0.25f; - if (cursor_pos.x < edit->scrollbar.x) - edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x - scroll_increment); - if (cursor_pos.x >= edit->scrollbar.x + area.w) - edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x); - } else edit->scrollbar.x = 0; - - if (flags & NK_EDIT_MULTILINE) { - /* vertical scroll */ - if (cursor_pos.y < edit->scrollbar.y) - edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y - row_height); - if (cursor_pos.y >= edit->scrollbar.y + area.h) - edit->scrollbar.y = edit->scrollbar.y + row_height; - } else edit->scrollbar.y = 0; - } - - /* scrollbar widget */ - if (flags & NK_EDIT_MULTILINE) - { - nk_flags ws; - struct nk_rect scroll; - float scroll_target; - float scroll_offset; - float scroll_step; - float scroll_inc; - - scroll = area; - scroll.x = (bounds.x + bounds.w - style->border) - style->scrollbar_size.x; - scroll.w = style->scrollbar_size.x; - - scroll_offset = edit->scrollbar.y; - scroll_step = scroll.h * 0.10f; - scroll_inc = scroll.h * 0.01f; - scroll_target = text_size.y; - edit->scrollbar.y = nk_do_scrollbarv(&ws, out, scroll, 0, - scroll_offset, scroll_target, scroll_step, scroll_inc, - &style->scrollbar, in, font); - } - } - - /* draw text */ - {struct nk_color background_color; - struct nk_color text_color; - struct nk_color sel_background_color; - struct nk_color sel_text_color; - struct nk_color cursor_color; - struct nk_color cursor_text_color; - const struct nk_style_item *background; - nk_push_scissor(out, clip); - - /* select correct colors to draw */ - if (*state & NK_WIDGET_STATE_ACTIVED) { - background = &style->active; - text_color = style->text_active; - sel_text_color = style->selected_text_hover; - sel_background_color = style->selected_hover; - cursor_color = style->cursor_hover; - cursor_text_color = style->cursor_text_hover; - } else if (*state & NK_WIDGET_STATE_HOVER) { - background = &style->hover; - text_color = style->text_hover; - sel_text_color = style->selected_text_hover; - sel_background_color = style->selected_hover; - cursor_text_color = style->cursor_text_hover; - cursor_color = style->cursor_hover; - } else { - background = &style->normal; - text_color = style->text_normal; - sel_text_color = style->selected_text_normal; - sel_background_color = style->selected_normal; - cursor_color = style->cursor_normal; - cursor_text_color = style->cursor_text_normal; - } - if (background->type == NK_STYLE_ITEM_IMAGE) - background_color = nk_rgba(0,0,0,0); - else background_color = background->data.color; - - - if (edit->select_start == edit->select_end) { - /* no selection so just draw the complete text */ - const char *begin = nk_str_get_const(&edit->string); - int l = nk_str_len_char(&edit->string); - nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, - area.y - edit->scrollbar.y, 0, begin, l, row_height, font, - background_color, text_color, nk_false); - } else { - /* edit has selection so draw 1-3 text chunks */ - if (edit->select_start != edit->select_end && selection_begin > 0){ - /* draw unselected text before selection */ - const char *begin = nk_str_get_const(&edit->string); - NK_ASSERT(select_begin_ptr); - nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, - area.y - edit->scrollbar.y, 0, begin, (int)(select_begin_ptr - begin), - row_height, font, background_color, text_color, nk_false); - } - if (edit->select_start != edit->select_end) { - /* draw selected text */ - NK_ASSERT(select_begin_ptr); - if (!select_end_ptr) { - const char *begin = nk_str_get_const(&edit->string); - select_end_ptr = begin + nk_str_len_char(&edit->string); - } - nk_edit_draw_text(out, style, - area.x - edit->scrollbar.x, - area.y + selection_offset_start.y - edit->scrollbar.y, - selection_offset_start.x, - select_begin_ptr, (int)(select_end_ptr - select_begin_ptr), - row_height, font, sel_background_color, sel_text_color, nk_true); - } - if ((edit->select_start != edit->select_end && - selection_end < edit->string.len)) - { - /* draw unselected text after selected text */ - const char *begin = select_end_ptr; - const char *end = nk_str_get_const(&edit->string) + - nk_str_len_char(&edit->string); - NK_ASSERT(select_end_ptr); - nk_edit_draw_text(out, style, - area.x - edit->scrollbar.x, - area.y + selection_offset_end.y - edit->scrollbar.y, - selection_offset_end.x, - begin, (int)(end - begin), row_height, font, - background_color, text_color, nk_true); - } - } - - /* cursor */ - if (edit->select_start == edit->select_end) - { - if (edit->cursor >= nk_str_len(&edit->string) || - (cursor_ptr && *cursor_ptr == '\n')) { - /* draw cursor at end of line */ - struct nk_rect cursor; - cursor.w = style->cursor_size; - cursor.h = font->height; - cursor.x = area.x + cursor_pos.x - edit->scrollbar.x; - cursor.y = area.y + cursor_pos.y + row_height/2.0f - cursor.h/2.0f; - cursor.y -= edit->scrollbar.y; - nk_fill_rect(out, cursor, 0, cursor_color); - } else { - /* draw cursor inside text */ - int glyph_len; - struct nk_rect label; - struct nk_text txt; - - nk_rune unicode; - NK_ASSERT(cursor_ptr); - glyph_len = nk_utf_decode(cursor_ptr, &unicode, 4); - - label.x = area.x + cursor_pos.x - edit->scrollbar.x; - label.y = area.y + cursor_pos.y - edit->scrollbar.y; - label.w = font->width(font->userdata, font->height, cursor_ptr, glyph_len); - label.h = row_height; - - txt.padding = nk_vec2(0,0); - txt.background = cursor_color;; - txt.text = cursor_text_color; - nk_fill_rect(out, label, 0, cursor_color); - nk_widget_text(out, label, cursor_ptr, glyph_len, &txt, NK_TEXT_LEFT, font); - } - }} - } else { - /* not active so just draw text */ - int l = nk_str_len_char(&edit->string); - const char *begin = nk_str_get_const(&edit->string); - - const struct nk_style_item *background; - struct nk_color background_color; - struct nk_color text_color; - nk_push_scissor(out, clip); - if (*state & NK_WIDGET_STATE_ACTIVED) { - background = &style->active; - text_color = style->text_active; - } else if (*state & NK_WIDGET_STATE_HOVER) { - background = &style->hover; - text_color = style->text_hover; - } else { - background = &style->normal; - text_color = style->text_normal; - } - if (background->type == NK_STYLE_ITEM_IMAGE) - background_color = nk_rgba(0,0,0,0); - else background_color = background->data.color; - nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, - area.y - edit->scrollbar.y, 0, begin, l, row_height, font, - background_color, text_color, nk_false); - } - nk_push_scissor(out, old_clip);} - return ret; -} - -/* =============================================================== - * - * PROPERTY - * - * ===============================================================*/ -enum nk_property_status { - NK_PROPERTY_DEFAULT, - NK_PROPERTY_EDIT, - NK_PROPERTY_DRAG -}; -enum nk_property_filter { - NK_FILTER_INT, - NK_FILTER_FLOAT -}; -enum nk_property_kind { - NK_PROPERTY_INT, - NK_PROPERTY_FLOAT, - NK_PROPERTY_DOUBLE -}; -union nk_property { - int i; - float f; - double d; -}; -struct nk_property_variant { - enum nk_property_kind kind; - union nk_property value; - union nk_property min_value; - union nk_property max_value; - union nk_property step; -}; - -NK_INTERN void -nk_drag_behavior(nk_flags *state, const struct nk_input *in, - struct nk_rect drag, struct nk_property_variant *variant, - float inc_per_pixel) -{ - int left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; - int left_mouse_click_in_cursor = in && - nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, drag, nk_true); - - nk_widget_state_reset(state); - if (nk_input_is_mouse_hovering_rect(in, drag)) - *state = NK_WIDGET_STATE_HOVERED; - - if (left_mouse_down && left_mouse_click_in_cursor) { - float delta, pixels; - pixels = in->mouse.delta.x; - delta = pixels * inc_per_pixel; - switch (variant->kind) { - default: break; - case NK_PROPERTY_INT: - variant->value.i = variant->value.i + (int)delta; - variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i); - break; - case NK_PROPERTY_FLOAT: - variant->value.f = variant->value.f + (float)delta; - variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f); - break; - case NK_PROPERTY_DOUBLE: - variant->value.d = variant->value.d + (double)delta; - variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d); - break; - } - *state = NK_WIDGET_STATE_ACTIVE; - } - if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, drag)) - *state |= NK_WIDGET_STATE_ENTERED; - else if (nk_input_is_mouse_prev_hovering_rect(in, drag)) - *state |= NK_WIDGET_STATE_LEFT; -} - -NK_INTERN void -nk_property_behavior(nk_flags *ws, const struct nk_input *in, - struct nk_rect property, struct nk_rect label, struct nk_rect edit, - struct nk_rect empty, int *state, struct nk_property_variant *variant, - float inc_per_pixel) -{ - if (in && *state == NK_PROPERTY_DEFAULT) { - if (nk_button_behavior(ws, edit, in, NK_BUTTON_DEFAULT)) - *state = NK_PROPERTY_EDIT; - else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, label, nk_true)) - *state = NK_PROPERTY_DRAG; - else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, empty, nk_true)) - *state = NK_PROPERTY_DRAG; - } - if (*state == NK_PROPERTY_DRAG) { - nk_drag_behavior(ws, in, property, variant, inc_per_pixel); - if (!(*ws & NK_WIDGET_STATE_ACTIVED)) *state = NK_PROPERTY_DEFAULT; - } -} - -NK_INTERN void -nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *style, - const struct nk_rect *bounds, const struct nk_rect *label, nk_flags state, - const char *name, int len, const struct nk_user_font *font) -{ - struct nk_text text; - const struct nk_style_item *background; - - /* select correct background and text color */ - if (state & NK_WIDGET_STATE_ACTIVED) { - background = &style->active; - text.text = style->label_active; - } else if (state & NK_WIDGET_STATE_HOVER) { - background = &style->hover; - text.text = style->label_hover; - } else { - background = &style->normal; - text.text = style->label_normal; - } - - /* draw background */ - if (background->type == NK_STYLE_ITEM_IMAGE) { - nk_draw_image(out, *bounds, &background->data.image, nk_white); - text.background = nk_rgba(0,0,0,0); - } else { - text.background = background->data.color; - nk_fill_rect(out, *bounds, style->rounding, background->data.color); - nk_stroke_rect(out, *bounds, style->rounding, style->border, background->data.color); - } - - /* draw label */ - text.padding = nk_vec2(0,0); - nk_widget_text(out, *label, name, len, &text, NK_TEXT_CENTERED, font); -} - -NK_INTERN void -nk_do_property(nk_flags *ws, - struct nk_command_buffer *out, struct nk_rect property, - const char *name, struct nk_property_variant *variant, - float inc_per_pixel, char *buffer, int *len, - int *state, int *cursor, int *select_begin, int *select_end, - const struct nk_style_property *style, - enum nk_property_filter filter, struct nk_input *in, - const struct nk_user_font *font, struct nk_text_edit *text_edit, - enum nk_button_behavior behavior) -{ - const nk_plugin_filter filters[] = { - nk_filter_decimal, - nk_filter_float - }; - int active, old; - int num_len, name_len; - char string[NK_MAX_NUMBER_BUFFER]; - float size; - - char *dst = 0; - int *length; - - struct nk_rect left; - struct nk_rect right; - struct nk_rect label; - struct nk_rect edit; - struct nk_rect empty; - - /* left decrement button */ - left.h = font->height/2; - left.w = left.h; - left.x = property.x + style->border + style->padding.x; - left.y = property.y + style->border + property.h/2.0f - left.h/2; - - /* text label */ - name_len = nk_strlen(name); - size = font->width(font->userdata, font->height, name, name_len); - label.x = left.x + left.w + style->padding.x; - label.w = (float)size + 2 * style->padding.x; - label.y = property.y + style->border + style->padding.y; - label.h = property.h - (2 * style->border + 2 * style->padding.y); - - /* right increment button */ - right.y = left.y; - right.w = left.w; - right.h = left.h; - right.x = property.x + property.w - (right.w + style->padding.x); - - /* edit */ - if (*state == NK_PROPERTY_EDIT) { - size = font->width(font->userdata, font->height, buffer, *len); - size += style->edit.cursor_size; - length = len; - dst = buffer; - } else { - switch (variant->kind) { - default: break; - case NK_PROPERTY_INT: - nk_itoa(string, variant->value.i); - num_len = nk_strlen(string); - break; - case NK_PROPERTY_FLOAT: - NK_DTOA(string, (double)variant->value.f); - num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); - break; - case NK_PROPERTY_DOUBLE: - NK_DTOA(string, variant->value.d); - num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); - break; - } - size = font->width(font->userdata, font->height, string, num_len); - dst = string; - length = &num_len; - } - - edit.w = (float)size + 2 * style->padding.x; - edit.w = NK_MIN(edit.w, right.x - (label.x + label.w)); - edit.x = right.x - (edit.w + style->padding.x); - edit.y = property.y + style->border; - edit.h = property.h - (2 * style->border); - - /* empty left space activator */ - empty.w = edit.x - (label.x + label.w); - empty.x = label.x + label.w; - empty.y = property.y; - empty.h = property.h; - - /* update property */ - old = (*state == NK_PROPERTY_EDIT); - nk_property_behavior(ws, in, property, label, edit, empty, state, variant, inc_per_pixel); - - /* draw property */ - if (style->draw_begin) style->draw_begin(out, style->userdata); - nk_draw_property(out, style, &property, &label, *ws, name, name_len, font); - if (style->draw_end) style->draw_end(out, style->userdata); - - /* execute right button */ - if (nk_do_button_symbol(ws, out, left, style->sym_left, behavior, &style->dec_button, in, font)) { - switch (variant->kind) { - default: break; - case NK_PROPERTY_INT: - variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i - variant->step.i, variant->max_value.i); break; - case NK_PROPERTY_FLOAT: - variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f - variant->step.f, variant->max_value.f); break; - case NK_PROPERTY_DOUBLE: - variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d - variant->step.d, variant->max_value.d); break; - } - } - /* execute left button */ - if (nk_do_button_symbol(ws, out, right, style->sym_right, behavior, &style->inc_button, in, font)) { - switch (variant->kind) { - default: break; - case NK_PROPERTY_INT: - variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i + variant->step.i, variant->max_value.i); break; - case NK_PROPERTY_FLOAT: - variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f + variant->step.f, variant->max_value.f); break; - case NK_PROPERTY_DOUBLE: - variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d + variant->step.d, variant->max_value.d); break; - } - } - if (old != NK_PROPERTY_EDIT && (*state == NK_PROPERTY_EDIT)) { - /* property has been activated so setup buffer */ - NK_MEMCPY(buffer, dst, (nk_size)*length); - *cursor = nk_utf_len(buffer, *length); - *len = *length; - length = len; - dst = buffer; - active = 0; - } else active = (*state == NK_PROPERTY_EDIT); - - /* execute and run text edit field */ - nk_textedit_clear_state(text_edit, NK_TEXT_EDIT_SINGLE_LINE, filters[filter]); - text_edit->active = (unsigned char)active; - text_edit->string.len = *length; - text_edit->cursor = NK_CLAMP(0, *cursor, *length); - text_edit->select_start = NK_CLAMP(0,*select_begin, *length); - text_edit->select_end = NK_CLAMP(0,*select_end, *length); - text_edit->string.buffer.allocated = (nk_size)*length; - text_edit->string.buffer.memory.size = NK_MAX_NUMBER_BUFFER; - text_edit->string.buffer.memory.ptr = dst; - text_edit->string.buffer.size = NK_MAX_NUMBER_BUFFER; - text_edit->mode = NK_TEXT_EDIT_MODE_INSERT; - nk_do_edit(ws, out, edit, NK_EDIT_FIELD|NK_EDIT_AUTO_SELECT, - filters[filter], text_edit, &style->edit, (*state == NK_PROPERTY_EDIT) ? in: 0, font); - - *length = text_edit->string.len; - *cursor = text_edit->cursor; - *select_begin = text_edit->select_start; - *select_end = text_edit->select_end; - if (text_edit->active && nk_input_is_key_pressed(in, NK_KEY_ENTER)) - text_edit->active = nk_false; - - if (active && !text_edit->active) { - /* property is now not active so convert edit text to value*/ - *state = NK_PROPERTY_DEFAULT; - buffer[*len] = '\0'; - switch (variant->kind) { - default: break; - case NK_PROPERTY_INT: - variant->value.i = nk_strtoi(buffer, 0); - variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i); - break; - case NK_PROPERTY_FLOAT: - nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); - variant->value.f = nk_strtof(buffer, 0); - variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f); - break; - case NK_PROPERTY_DOUBLE: - nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); - variant->value.d = nk_strtod(buffer, 0); - variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d); - break; - } - } -} -/* =============================================================== - * - * COLOR PICKER - * - * ===============================================================*/ -NK_INTERN int -nk_color_picker_behavior(nk_flags *state, - const struct nk_rect *bounds, const struct nk_rect *matrix, - const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, - struct nk_color *color, const struct nk_input *in) -{ - float hsva[4]; - int value_changed = 0; - int hsv_changed = 0; - - NK_ASSERT(state); - NK_ASSERT(matrix); - NK_ASSERT(hue_bar); - NK_ASSERT(color); - - /* color matrix */ - nk_color_hsva_fv(hsva, *color); - if (nk_button_behavior(state, *matrix, in, NK_BUTTON_REPEATER)) { - hsva[1] = NK_SATURATE((in->mouse.pos.x - matrix->x) / (matrix->w-1)); - hsva[2] = 1.0f - NK_SATURATE((in->mouse.pos.y - matrix->y) / (matrix->h-1)); - value_changed = hsv_changed = 1; - } - - /* hue bar */ - if (nk_button_behavior(state, *hue_bar, in, NK_BUTTON_REPEATER)) { - hsva[0] = NK_SATURATE((in->mouse.pos.y - hue_bar->y) / (hue_bar->h-1)); - value_changed = hsv_changed = 1; - } - - /* alpha bar */ - if (alpha_bar) { - if (nk_button_behavior(state, *alpha_bar, in, NK_BUTTON_REPEATER)) { - hsva[3] = 1.0f - NK_SATURATE((in->mouse.pos.y - alpha_bar->y) / (alpha_bar->h-1)); - value_changed = 1; - } - } - nk_widget_state_reset(state); - if (hsv_changed) { - *color = nk_hsva_fv(hsva); - *state = NK_WIDGET_STATE_ACTIVE; - } - if (value_changed) { - color->a = (nk_byte)(hsva[3] * 255.0f); - *state = NK_WIDGET_STATE_ACTIVE; - } - - /* set color picker widget state */ - if (nk_input_is_mouse_hovering_rect(in, *bounds)) - *state = NK_WIDGET_STATE_HOVERED; - if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *bounds)) - *state |= NK_WIDGET_STATE_ENTERED; - else if (nk_input_is_mouse_prev_hovering_rect(in, *bounds)) - *state |= NK_WIDGET_STATE_LEFT; - return value_changed; -} - -NK_INTERN void -nk_draw_color_picker(struct nk_command_buffer *o, const struct nk_rect *matrix, - const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, - struct nk_color color) -{ - NK_STORAGE const struct nk_color black = {0,0,0,255}; - NK_STORAGE const struct nk_color white = {255, 255, 255, 255}; - NK_STORAGE const struct nk_color black_trans = {0,0,0,0}; - - const float crosshair_size = 7.0f; - struct nk_color temp; - float hsva[4]; - float line_y; - int i; - - NK_ASSERT(o); - NK_ASSERT(matrix); - NK_ASSERT(hue_bar); - - /* draw hue bar */ - nk_color_hsv_fv(hsva, color); - for (i = 0; i < 6; ++i) { - NK_GLOBAL const struct nk_color hue_colors[] = { - {255, 0, 0, 255}, - {255,255,0,255}, - {0,255,0,255}, - {0, 255,255,255}, - {0,0,255,255}, - {255, 0, 255, 255}, - {255, 0, 0, 255} - }; - nk_fill_rect_multi_color(o, - nk_rect(hue_bar->x, hue_bar->y + (float)i * (hue_bar->h/6.0f) + 0.5f, - hue_bar->w, (hue_bar->h/6.0f) + 0.5f), hue_colors[i], hue_colors[i], - hue_colors[i+1], hue_colors[i+1]); - } - line_y = (float)(int)(hue_bar->y + hsva[0] * matrix->h + 0.5f); - nk_stroke_line(o, hue_bar->x-1, line_y, hue_bar->x + hue_bar->w + 2, - line_y, 1, nk_rgb(255,255,255)); - - /* draw alpha bar */ - if (alpha_bar) { - float alpha = NK_SATURATE((float)color.a/255.0f); - line_y = (float)(int)(alpha_bar->y + (1.0f - alpha) * matrix->h + 0.5f); - - nk_fill_rect_multi_color(o, *alpha_bar, white, white, black, black); - nk_stroke_line(o, alpha_bar->x-1, line_y, alpha_bar->x + alpha_bar->w + 2, - line_y, 1, nk_rgb(255,255,255)); - } - - /* draw color matrix */ - temp = nk_hsv_f(hsva[0], 1.0f, 1.0f); - nk_fill_rect_multi_color(o, *matrix, white, temp, temp, white); - nk_fill_rect_multi_color(o, *matrix, black_trans, black_trans, black, black); - - /* draw cross-hair */ - {struct nk_vec2 p; float S = hsva[1]; float V = hsva[2]; - p.x = (float)(int)(matrix->x + S * matrix->w); - p.y = (float)(int)(matrix->y + (1.0f - V) * matrix->h); - nk_stroke_line(o, p.x - crosshair_size, p.y, p.x-2, p.y, 1.0f, white); - nk_stroke_line(o, p.x + crosshair_size + 1, p.y, p.x+3, p.y, 1.0f, white); - nk_stroke_line(o, p.x, p.y + crosshair_size + 1, p.x, p.y+3, 1.0f, white); - nk_stroke_line(o, p.x, p.y - crosshair_size, p.x, p.y-2, 1.0f, white);} -} - -NK_INTERN int -nk_do_color_picker(nk_flags *state, - struct nk_command_buffer *out, struct nk_color *color, - enum nk_color_format fmt, struct nk_rect bounds, - struct nk_vec2 padding, const struct nk_input *in, - const struct nk_user_font *font) -{ - int ret = 0; - struct nk_rect matrix; - struct nk_rect hue_bar; - struct nk_rect alpha_bar; - float bar_w; - - NK_ASSERT(out); - NK_ASSERT(color); - NK_ASSERT(state); - NK_ASSERT(font); - if (!out || !color || !state || !font) - return ret; - - bar_w = font->height; - bounds.x += padding.x; - bounds.y += padding.x; - bounds.w -= 2 * padding.x; - bounds.h -= 2 * padding.y; - - matrix.x = bounds.x; - matrix.y = bounds.y; - matrix.h = bounds.h; - matrix.w = bounds.w - (3 * padding.x + 2 * bar_w); - - hue_bar.w = bar_w; - hue_bar.y = bounds.y; - hue_bar.h = matrix.h; - hue_bar.x = matrix.x + matrix.w + padding.x; - - alpha_bar.x = hue_bar.x + hue_bar.w + padding.x; - alpha_bar.y = bounds.y; - alpha_bar.w = bar_w; - alpha_bar.h = matrix.h; - - ret = nk_color_picker_behavior(state, &bounds, &matrix, &hue_bar, - (fmt == NK_RGBA) ? &alpha_bar:0, color, in); - nk_draw_color_picker(out, &matrix, &hue_bar, (fmt == NK_RGBA) ? &alpha_bar:0, *color); - return ret; -} - -/* ============================================================== - * - * STYLE + * STYLE * * ===============================================================*/ NK_API void nk_style_default(struct nk_context *ctx){nk_style_from_table(ctx, 0);} #define NK_COLOR_MAP(NK_COLOR)\ - NK_COLOR(NK_COLOR_TEXT, 175,175,175,255) \ - NK_COLOR(NK_COLOR_WINDOW, 45, 45, 45, 255) \ - NK_COLOR(NK_COLOR_HEADER, 40, 40, 40, 255) \ - NK_COLOR(NK_COLOR_BORDER, 65, 65, 65, 255) \ - NK_COLOR(NK_COLOR_BUTTON, 50, 50, 50, 255) \ - NK_COLOR(NK_COLOR_BUTTON_HOVER, 40, 40, 40, 255) \ - NK_COLOR(NK_COLOR_BUTTON_ACTIVE, 35, 35, 35, 255) \ - NK_COLOR(NK_COLOR_TOGGLE, 100,100,100,255) \ - NK_COLOR(NK_COLOR_TOGGLE_HOVER, 120,120,120,255) \ - NK_COLOR(NK_COLOR_TOGGLE_CURSOR, 45, 45, 45, 255) \ - NK_COLOR(NK_COLOR_SELECT, 45, 45, 45, 255) \ - NK_COLOR(NK_COLOR_SELECT_ACTIVE, 35, 35, 35,255) \ - NK_COLOR(NK_COLOR_SLIDER, 38, 38, 38, 255) \ - NK_COLOR(NK_COLOR_SLIDER_CURSOR, 100,100,100,255) \ - NK_COLOR(NK_COLOR_SLIDER_CURSOR_HOVER, 120,120,120,255) \ - NK_COLOR(NK_COLOR_SLIDER_CURSOR_ACTIVE, 150,150,150,255) \ - NK_COLOR(NK_COLOR_PROPERTY, 38, 38, 38, 255) \ - NK_COLOR(NK_COLOR_EDIT, 38, 38, 38, 255) \ - NK_COLOR(NK_COLOR_EDIT_CURSOR, 175,175,175,255) \ - NK_COLOR(NK_COLOR_COMBO, 45, 45, 45, 255) \ - NK_COLOR(NK_COLOR_CHART, 120,120,120,255) \ - NK_COLOR(NK_COLOR_CHART_COLOR, 45, 45, 45, 255) \ - NK_COLOR(NK_COLOR_CHART_COLOR_HIGHLIGHT,255, 0, 0, 255) \ - NK_COLOR(NK_COLOR_SCROLLBAR, 40, 40, 40, 255) \ - NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR, 100,100,100,255) \ - NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_HOVER,120,120,120,255) \ - NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_ACTIVE,150,150,150,255) \ - NK_COLOR(NK_COLOR_TAB_HEADER, 40, 40, 40,255) + NK_COLOR(NK_COLOR_TEXT, 175,175,175,255) \ + NK_COLOR(NK_COLOR_WINDOW, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_HEADER, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_BORDER, 65, 65, 65, 255) \ + NK_COLOR(NK_COLOR_BUTTON, 50, 50, 50, 255) \ + NK_COLOR(NK_COLOR_BUTTON_HOVER, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_BUTTON_ACTIVE, 35, 35, 35, 255) \ + NK_COLOR(NK_COLOR_TOGGLE, 100,100,100,255) \ + NK_COLOR(NK_COLOR_TOGGLE_HOVER, 120,120,120,255) \ + NK_COLOR(NK_COLOR_TOGGLE_CURSOR, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_SELECT, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_SELECT_ACTIVE, 35, 35, 35,255) \ + NK_COLOR(NK_COLOR_SLIDER, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR, 100,100,100,255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR_HOVER, 120,120,120,255) \ + NK_COLOR(NK_COLOR_SLIDER_CURSOR_ACTIVE, 150,150,150,255) \ + NK_COLOR(NK_COLOR_PROPERTY, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_EDIT, 38, 38, 38, 255) \ + NK_COLOR(NK_COLOR_EDIT_CURSOR, 175,175,175,255) \ + NK_COLOR(NK_COLOR_COMBO, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_CHART, 120,120,120,255) \ + NK_COLOR(NK_COLOR_CHART_COLOR, 45, 45, 45, 255) \ + NK_COLOR(NK_COLOR_CHART_COLOR_HIGHLIGHT, 255, 0, 0, 255) \ + NK_COLOR(NK_COLOR_SCROLLBAR, 40, 40, 40, 255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR, 100,100,100,255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_HOVER, 120,120,120,255) \ + NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_ACTIVE, 150,150,150,255) \ + NK_COLOR(NK_COLOR_TAB_HEADER, 40, 40, 40,255) NK_GLOBAL const struct nk_color nk_default_color_style[NK_COLOR_COUNT] = { @@ -16494,25 +14140,41 @@ nk_default_color_style[NK_COLOR_COUNT] = { NK_COLOR_MAP(NK_COLOR) #undef NK_COLOR }; - NK_GLOBAL const char *nk_color_names[NK_COLOR_COUNT] = { #define NK_COLOR(a,b,c,d,e) #a, NK_COLOR_MAP(NK_COLOR) #undef NK_COLOR }; -NK_API const char *nk_style_get_color_by_name(enum nk_style_colors c) -{return nk_color_names[c];} - -NK_API struct nk_style_item nk_style_item_image(struct nk_image img) -{struct nk_style_item i; i.type = NK_STYLE_ITEM_IMAGE; i.data.image = img; return i;} - -NK_API struct nk_style_item nk_style_item_color(struct nk_color col) -{struct nk_style_item i; i.type = NK_STYLE_ITEM_COLOR; i.data.color = col; return i;} - -NK_API struct nk_style_item nk_style_item_hide(void) -{struct nk_style_item i; i.type = NK_STYLE_ITEM_COLOR; i.data.color = nk_rgba(0,0,0,0); return i;} - +NK_API const char* +nk_style_get_color_by_name(enum nk_style_colors c) +{ + return nk_color_names[c]; +} +NK_API struct nk_style_item +nk_style_item_image(struct nk_image img) +{ + struct nk_style_item i; + i.type = NK_STYLE_ITEM_IMAGE; + i.data.image = img; + return i; +} +NK_API struct nk_style_item +nk_style_item_color(struct nk_color col) +{ + struct nk_style_item i; + i.type = NK_STYLE_ITEM_COLOR; + i.data.color = col; + return i; +} +NK_API struct nk_style_item +nk_style_item_hide(void) +{ + struct nk_style_item i; + i.type = NK_STYLE_ITEM_COLOR; + i.data.color = nk_rgba(0,0,0,0); + return i; +} NK_API void nk_style_from_table(struct nk_context *ctx, const struct nk_color *table) { @@ -16656,6 +14318,7 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table) select->text_hover_active = table[NK_COLOR_TEXT]; select->text_pressed_active = table[NK_COLOR_TEXT]; select->padding = nk_vec2(2.0f,2.0f); + select->image_padding = nk_vec2(2.0f,2.0f); select->touch_padding = nk_vec2(0,0); select->userdata = nk_handle_ptr(0); select->rounding = 0.0f; @@ -17056,7 +14719,6 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table) win->menu_padding = nk_vec2(4,4); win->tooltip_padding = nk_vec2(4,4); } - NK_API void nk_style_set_font(struct nk_context *ctx, const struct nk_user_font *font) { @@ -17070,7 +14732,6 @@ nk_style_set_font(struct nk_context *ctx, const struct nk_user_font *font) if (ctx->current) nk_layout_reset_min_row_height(ctx); } - NK_API int nk_style_push_font(struct nk_context *ctx, const struct nk_user_font *font) { @@ -17091,7 +14752,6 @@ nk_style_push_font(struct nk_context *ctx, const struct nk_user_font *font) ctx->style.font = font; return 1; } - NK_API int nk_style_pop_font(struct nk_context *ctx) { @@ -17110,7 +14770,6 @@ nk_style_pop_font(struct nk_context *ctx) *element->address = element->old_value; return 1; } - #define NK_STYLE_PUSH_IMPLEMENATION(prefix, type, stack) \ nk_style_push_##type(struct nk_context *ctx, prefix##_##type *address, prefix##_##type value)\ {\ @@ -17128,7 +14787,6 @@ nk_style_push_##type(struct nk_context *ctx, prefix##_##type *address, prefix##_ *address = value;\ return 1;\ } - #define NK_STYLE_POP_IMPLEMENATION(type, stack) \ nk_style_pop_##type(struct nk_context *ctx)\ {\ @@ -17144,7 +14802,6 @@ nk_style_pop_##type(struct nk_context *ctx)\ *element->address = element->old_value;\ return 1;\ } - NK_API int NK_STYLE_PUSH_IMPLEMENATION(struct nk, style_item, style_items) NK_API int NK_STYLE_PUSH_IMPLEMENATION(nk,float, floats) NK_API int NK_STYLE_PUSH_IMPLEMENATION(struct nk, vec2, vectors) @@ -17170,19 +14827,16 @@ nk_style_set_cursor(struct nk_context *ctx, enum nk_style_cursor c) } return 0; } - NK_API void nk_style_show_cursor(struct nk_context *ctx) { ctx->style.cursor_visible = nk_true; } - NK_API void nk_style_hide_cursor(struct nk_context *ctx) { ctx->style.cursor_visible = nk_false; } - NK_API void nk_style_load_cursor(struct nk_context *ctx, enum nk_style_cursor cursor, const struct nk_cursor *c) @@ -17193,7 +14847,6 @@ nk_style_load_cursor(struct nk_context *ctx, enum nk_style_cursor cursor, style = &ctx->style; style->cursors[cursor] = c; } - NK_API void nk_style_load_all_cursors(struct nk_context *ctx, struct nk_cursor *cursors) { @@ -17207,85 +14860,15 @@ nk_style_load_all_cursors(struct nk_context *ctx, struct nk_cursor *cursors) style->cursor_visible = nk_true; } -/* =============================================================== - * - * POOL - * - * ===============================================================*/ -NK_INTERN void -nk_pool_init(struct nk_pool *pool, struct nk_allocator *alloc, - unsigned int capacity) -{ - nk_zero(pool, sizeof(*pool)); - pool->alloc = *alloc; - pool->capacity = capacity; - pool->type = NK_BUFFER_DYNAMIC; - pool->pages = 0; -} -NK_INTERN void -nk_pool_free(struct nk_pool *pool) -{ - struct nk_page *iter = pool->pages; - if (!pool) return; - if (pool->type == NK_BUFFER_FIXED) return; - while (iter) { - struct nk_page *next = iter->next; - pool->alloc.free(pool->alloc.userdata, iter); - iter = next; - } -} -NK_INTERN void -nk_pool_init_fixed(struct nk_pool *pool, void *memory, nk_size size) -{ - nk_zero(pool, sizeof(*pool)); - NK_ASSERT(size >= sizeof(struct nk_page)); - if (size < sizeof(struct nk_page)) return; - pool->capacity = (unsigned)(size - sizeof(struct nk_page)) / sizeof(struct nk_page_element); - pool->pages = (struct nk_page*)memory; - pool->type = NK_BUFFER_FIXED; - pool->size = size; -} -NK_INTERN struct nk_page_element* -nk_pool_alloc(struct nk_pool *pool) -{ - if (!pool->pages || pool->pages->size >= pool->capacity) { - /* allocate new page */ - struct nk_page *page; - if (pool->type == NK_BUFFER_FIXED) { - if (!pool->pages) { - NK_ASSERT(pool->pages); - return 0; - } - NK_ASSERT(pool->pages->size < pool->capacity); - return 0; - } else { - nk_size size = sizeof(struct nk_page); - size += NK_POOL_DEFAULT_CAPACITY * sizeof(union nk_page_data); - page = (struct nk_page*)pool->alloc.alloc(pool->alloc.userdata,0, size); - page->next = pool->pages; - pool->pages = page; - page->size = 0; - } - } - return &pool->pages->win[pool->pages->size++]; -} -/* =============================================================== +/* ============================================================== * * CONTEXT * * ===============================================================*/ -NK_INTERN void* nk_create_window(struct nk_context *ctx); -NK_INTERN void nk_remove_window(struct nk_context*, struct nk_window*); -NK_INTERN void nk_free_window(struct nk_context *ctx, struct nk_window *win); -NK_INTERN void nk_free_table(struct nk_context *ctx, struct nk_table *tbl); -NK_INTERN void nk_remove_table(struct nk_window *win, struct nk_table *tbl); -NK_INTERN void* nk_create_panel(struct nk_context *ctx); -NK_INTERN void nk_free_panel(struct nk_context*, struct nk_panel *pan); - NK_INTERN void nk_setup(struct nk_context *ctx, const struct nk_user_font *font) { @@ -17299,7 +14882,6 @@ nk_setup(struct nk_context *ctx, const struct nk_user_font *font) nk_draw_list_init(&ctx->draw_list); #endif } - #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_API int nk_init_default(struct nk_context *ctx, const struct nk_user_font *font) @@ -17311,7 +14893,6 @@ nk_init_default(struct nk_context *ctx, const struct nk_user_font *font) return nk_init(ctx, &alloc, font); } #endif - NK_API int nk_init_fixed(struct nk_context *ctx, void *memory, nk_size size, const struct nk_user_font *font) @@ -17323,7 +14904,6 @@ nk_init_fixed(struct nk_context *ctx, void *memory, nk_size size, ctx->use_pool = nk_false; return 1; } - NK_API int nk_init_custom(struct nk_context *ctx, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font *font) @@ -17345,7 +14925,6 @@ nk_init_custom(struct nk_context *ctx, struct nk_buffer *cmds, ctx->use_pool = nk_true; return 1; } - NK_API int nk_init(struct nk_context *ctx, struct nk_allocator *alloc, const struct nk_user_font *font) @@ -17358,7 +14937,6 @@ nk_init(struct nk_context *ctx, struct nk_allocator *alloc, ctx->use_pool = nk_true; return 1; } - #ifdef NK_INCLUDE_COMMAND_USERDATA NK_API void nk_set_user_data(struct nk_context *ctx, nk_handle handle) @@ -17369,7 +14947,6 @@ nk_set_user_data(struct nk_context *ctx, nk_handle handle) ctx->current->buffer.userdata = handle; } #endif - NK_API void nk_free(struct nk_context *ctx) { @@ -17392,7 +14969,6 @@ nk_free(struct nk_context *ctx) ctx->freelist = 0; ctx->count = 0; } - NK_API void nk_clear(struct nk_context *ctx) { @@ -17410,9 +14986,6 @@ nk_clear(struct nk_context *ctx) ctx->last_widget_state = 0; ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_ARROW]; NK_MEMSET(&ctx->overlay, 0, sizeof(ctx->overlay)); -#ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT - nk_draw_list_clear(&ctx->draw_list); -#endif /* garbage collector */ iter = ctx->begin; @@ -17430,6 +15003,8 @@ nk_clear(struct nk_context *ctx) iter == ctx->active) { ctx->active = iter->prev; ctx->end = iter->prev; + if (!ctx->end) + ctx->begin = 0; if (ctx->active) ctx->active->flags &= ~(unsigned)NK_WINDOW_ROM; } @@ -17460,13 +15035,7 @@ nk_clear(struct nk_context *ctx) } ctx->seq++; } - -/* ---------------------------------------------------------------- - * - * BUFFERING - * - * ---------------------------------------------------------------*/ -NK_INTERN void +NK_LIB void nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) { NK_ASSERT(ctx); @@ -17477,16 +15046,14 @@ nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) buffer->last = buffer->begin; buffer->clip = nk_null_rect; } - -NK_INTERN void +NK_LIB void nk_start(struct nk_context *ctx, struct nk_window *win) { NK_ASSERT(ctx); NK_ASSERT(win); nk_start_buffer(ctx, &win->buffer); } - -NK_INTERN void +NK_LIB void nk_start_popup(struct nk_context *ctx, struct nk_window *win) { struct nk_popup_buffer *buf; @@ -17502,8 +15069,7 @@ nk_start_popup(struct nk_context *ctx, struct nk_window *win) buf->last = buf->begin; buf->active = nk_true; } - -NK_INTERN void +NK_LIB void nk_finish_popup(struct nk_context *ctx, struct nk_window *win) { struct nk_popup_buffer *buf; @@ -17515,8 +15081,7 @@ nk_finish_popup(struct nk_context *ctx, struct nk_window *win) buf->last = win->buffer.last; buf->end = win->buffer.end; } - -NK_INTERN void +NK_LIB void nk_finish_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) { NK_ASSERT(ctx); @@ -17524,8 +15089,7 @@ nk_finish_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) if (!ctx || !buffer) return; buffer->end = ctx->memory.allocated; } - -NK_INTERN void +NK_LIB void nk_finish(struct nk_context *ctx, struct nk_window *win) { struct nk_popup_buffer *buf; @@ -17543,8 +15107,7 @@ nk_finish(struct nk_context *ctx, struct nk_window *win) parent_last = nk_ptr_add(struct nk_command, memory, buf->parent); parent_last->next = buf->end; } - -NK_INTERN void +NK_LIB void nk_build(struct nk_context *ctx) { struct nk_window *it = 0; @@ -17579,7 +15142,7 @@ nk_build(struct nk_context *ctx) cmd = nk_ptr_add(struct nk_command, buffer, it->buffer.last); while (next && ((next->buffer.last == next->buffer.begin) || - (next->flags & NK_WINDOW_HIDDEN))) + (next->flags & NK_WINDOW_HIDDEN) || next->seq != ctx->seq)) next = next->next; /* skip empty command buffers */ if (next) cmd->next = next->buffer.begin; @@ -17606,7 +15169,6 @@ nk_build(struct nk_context *ctx) else cmd->next = ctx->memory.allocated; } } - NK_API const struct nk_command* nk__begin(struct nk_context *ctx) { @@ -17642,12 +15204,251 @@ nk__next(struct nk_context *ctx, const struct nk_command *cmd) return next; } -/* ---------------------------------------------------------------- + + + + + +/* =============================================================== * - * PANEL + * POOL * - * ---------------------------------------------------------------*/ -static int + * ===============================================================*/ +NK_LIB void +nk_pool_init(struct nk_pool *pool, struct nk_allocator *alloc, + unsigned int capacity) +{ + nk_zero(pool, sizeof(*pool)); + pool->alloc = *alloc; + pool->capacity = capacity; + pool->type = NK_BUFFER_DYNAMIC; + pool->pages = 0; +} +NK_LIB void +nk_pool_free(struct nk_pool *pool) +{ + struct nk_page *iter = pool->pages; + if (!pool) return; + if (pool->type == NK_BUFFER_FIXED) return; + while (iter) { + struct nk_page *next = iter->next; + pool->alloc.free(pool->alloc.userdata, iter); + iter = next; + } +} +NK_LIB void +nk_pool_init_fixed(struct nk_pool *pool, void *memory, nk_size size) +{ + nk_zero(pool, sizeof(*pool)); + NK_ASSERT(size >= sizeof(struct nk_page)); + if (size < sizeof(struct nk_page)) return; + pool->capacity = (unsigned)(size - sizeof(struct nk_page)) / sizeof(struct nk_page_element); + pool->pages = (struct nk_page*)memory; + pool->type = NK_BUFFER_FIXED; + pool->size = size; +} +NK_LIB struct nk_page_element* +nk_pool_alloc(struct nk_pool *pool) +{ + if (!pool->pages || pool->pages->size >= pool->capacity) { + /* allocate new page */ + struct nk_page *page; + if (pool->type == NK_BUFFER_FIXED) { + NK_ASSERT(pool->pages); + if (!pool->pages) return 0; + NK_ASSERT(pool->pages->size < pool->capacity); + return 0; + } else { + nk_size size = sizeof(struct nk_page); + size += NK_POOL_DEFAULT_CAPACITY * sizeof(union nk_page_data); + page = (struct nk_page*)pool->alloc.alloc(pool->alloc.userdata,0, size); + page->next = pool->pages; + pool->pages = page; + page->size = 0; + } + } return &pool->pages->win[pool->pages->size++]; +} + + + + + +/* =============================================================== + * + * PAGE ELEMENT + * + * ===============================================================*/ +NK_LIB struct nk_page_element* +nk_create_page_element(struct nk_context *ctx) +{ + struct nk_page_element *elem; + if (ctx->freelist) { + /* unlink page element from free list */ + elem = ctx->freelist; + ctx->freelist = elem->next; + } else if (ctx->use_pool) { + /* allocate page element from memory pool */ + elem = nk_pool_alloc(&ctx->pool); + NK_ASSERT(elem); + if (!elem) return 0; + } else { + /* allocate new page element from back of fixed size memory buffer */ + NK_STORAGE const nk_size size = sizeof(struct nk_page_element); + NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_page_element); + elem = (struct nk_page_element*)nk_buffer_alloc(&ctx->memory, NK_BUFFER_BACK, size, align); + NK_ASSERT(elem); + if (!elem) return 0; + } + nk_zero_struct(*elem); + elem->next = 0; + elem->prev = 0; + return elem; +} +NK_LIB void +nk_link_page_element_into_freelist(struct nk_context *ctx, + struct nk_page_element *elem) +{ + /* link table into freelist */ + if (!ctx->freelist) { + ctx->freelist = elem; + } else { + elem->next = ctx->freelist; + ctx->freelist = elem; + } +} +NK_LIB void +nk_free_page_element(struct nk_context *ctx, struct nk_page_element *elem) +{ + /* we have a pool so just add to free list */ + if (ctx->use_pool) { + nk_link_page_element_into_freelist(ctx, elem); + return; + } + /* if possible remove last element from back of fixed memory buffer */ + {void *elem_end = (void*)(elem + 1); + void *buffer_end = (nk_byte*)ctx->memory.memory.ptr + ctx->memory.size; + if (elem_end == buffer_end) + ctx->memory.size -= sizeof(struct nk_page_element); + else nk_link_page_element_into_freelist(ctx, elem);} +} + + + + + +/* =============================================================== + * + * TABLE + * + * ===============================================================*/ +NK_LIB struct nk_table* +nk_create_table(struct nk_context *ctx) +{ + struct nk_page_element *elem; + elem = nk_create_page_element(ctx); + if (!elem) return 0; + nk_zero_struct(*elem); + return &elem->data.tbl; +} +NK_LIB void +nk_free_table(struct nk_context *ctx, struct nk_table *tbl) +{ + union nk_page_data *pd = NK_CONTAINER_OF(tbl, union nk_page_data, tbl); + struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); + nk_free_page_element(ctx, pe); +} +NK_LIB void +nk_push_table(struct nk_window *win, struct nk_table *tbl) +{ + if (!win->tables) { + win->tables = tbl; + tbl->next = 0; + tbl->prev = 0; + tbl->size = 0; + win->table_count = 1; + return; + } + win->tables->prev = tbl; + tbl->next = win->tables; + tbl->prev = 0; + tbl->size = 0; + win->tables = tbl; + win->table_count++; +} +NK_LIB void +nk_remove_table(struct nk_window *win, struct nk_table *tbl) +{ + if (win->tables == tbl) + win->tables = tbl->next; + if (tbl->next) + tbl->next->prev = tbl->prev; + if (tbl->prev) + tbl->prev->next = tbl->next; + tbl->next = 0; + tbl->prev = 0; +} +NK_LIB nk_uint* +nk_add_value(struct nk_context *ctx, struct nk_window *win, + nk_hash name, nk_uint value) +{ + NK_ASSERT(ctx); + NK_ASSERT(win); + if (!win || !ctx) return 0; + if (!win->tables || win->tables->size >= NK_VALUE_PAGE_CAPACITY) { + struct nk_table *tbl = nk_create_table(ctx); + NK_ASSERT(tbl); + if (!tbl) return 0; + nk_push_table(win, tbl); + } + win->tables->seq = win->seq; + win->tables->keys[win->tables->size] = name; + win->tables->values[win->tables->size] = value; + return &win->tables->values[win->tables->size++]; +} +NK_LIB nk_uint* +nk_find_value(struct nk_window *win, nk_hash name) +{ + struct nk_table *iter = win->tables; + while (iter) { + unsigned int i = 0; + unsigned int size = iter->size; + for (i = 0; i < size; ++i) { + if (iter->keys[i] == name) { + iter->seq = win->seq; + return &iter->values[i]; + } + } size = NK_VALUE_PAGE_CAPACITY; + iter = iter->next; + } + return 0; +} + + + + + +/* =============================================================== + * + * PANEL + * + * ===============================================================*/ +NK_LIB void* +nk_create_panel(struct nk_context *ctx) +{ + struct nk_page_element *elem; + elem = nk_create_page_element(ctx); + if (!elem) return 0; + nk_zero_struct(*elem); + return &elem->data.pan; +} +NK_LIB void +nk_free_panel(struct nk_context *ctx, struct nk_panel *pan) +{ + union nk_page_data *pd = NK_CONTAINER_OF(pan, union nk_page_data, pan); + struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); + nk_free_page_element(ctx, pe); +} +NK_LIB int nk_panel_has_header(nk_flags flags, const char *title) { int active = 0; @@ -17656,8 +15457,7 @@ nk_panel_has_header(nk_flags flags, const char *title) active = active && !(flags & NK_WINDOW_HIDDEN) && title; return active; } - -NK_INTERN struct nk_vec2 +NK_LIB struct nk_vec2 nk_panel_get_padding(const struct nk_style *style, enum nk_panel_type type) { switch (type) { @@ -17668,11 +15468,9 @@ nk_panel_get_padding(const struct nk_style *style, enum nk_panel_type type) case NK_PANEL_CONTEXTUAL: return style->window.contextual_padding; case NK_PANEL_COMBO: return style->window.combo_padding; case NK_PANEL_MENU: return style->window.menu_padding; - case NK_PANEL_TOOLTIP: return style->window.menu_padding; - } + case NK_PANEL_TOOLTIP: return style->window.menu_padding;} } - -NK_INTERN float +NK_LIB float nk_panel_get_border(const struct nk_style *style, nk_flags flags, enum nk_panel_type type) { @@ -17688,8 +15486,7 @@ nk_panel_get_border(const struct nk_style *style, nk_flags flags, case NK_PANEL_TOOLTIP: return style->window.menu_border; }} else return 0; } - -NK_INTERN struct nk_color +NK_LIB struct nk_color nk_panel_get_border_color(const struct nk_style *style, enum nk_panel_type type) { switch (type) { @@ -17700,23 +15497,19 @@ nk_panel_get_border_color(const struct nk_style *style, enum nk_panel_type type) case NK_PANEL_CONTEXTUAL: return style->window.contextual_border_color; case NK_PANEL_COMBO: return style->window.combo_border_color; case NK_PANEL_MENU: return style->window.menu_border_color; - case NK_PANEL_TOOLTIP: return style->window.menu_border_color; - } + case NK_PANEL_TOOLTIP: return style->window.menu_border_color;} } - -NK_INTERN int +NK_LIB int nk_panel_is_sub(enum nk_panel_type type) { return (type & NK_PANEL_SET_SUB)?1:0; } - -NK_INTERN int +NK_LIB int nk_panel_is_nonblock(enum nk_panel_type type) { return (type & NK_PANEL_SET_NONBLOCK)?1:0; } - -NK_INTERN int +NK_LIB int nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type panel_type) { struct nk_input *in; @@ -17756,6 +15549,7 @@ nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type pan /* window movement */ if ((win->flags & NK_WINDOW_MOVABLE) && !(win->flags & NK_WINDOW_ROM)) { int left_mouse_down; + int left_mouse_clicked; int left_mouse_click_in_cursor; /* calculate draggable window space */ @@ -17770,9 +15564,10 @@ nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type pan /* window movement by dragging */ left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_clicked = (int)in->mouse.buttons[NK_BUTTON_LEFT].clicked; left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, header, nk_true); - if (left_mouse_down && left_mouse_click_in_cursor) { + if (left_mouse_down && left_mouse_click_in_cursor && !left_mouse_clicked) { win->bounds.x = win->bounds.x + in->mouse.delta.x; win->bounds.y = win->bounds.y + in->mouse.delta.y; in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x += in->mouse.delta.x; @@ -17937,8 +15732,7 @@ nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type pan layout->clip = clip;} return !(layout->flags & NK_WINDOW_HIDDEN) && !(layout->flags & NK_WINDOW_MINIMIZED); } - -NK_INTERN void +NK_LIB void nk_panel_end(struct nk_context *ctx) { struct nk_input *in; @@ -18113,30 +15907,14 @@ nk_panel_end(struct nk_context *ctx) if (layout->flags & NK_WINDOW_BORDER) { struct nk_color border_color = nk_panel_get_border_color(style, layout->type); - const float padding_y = (layout->flags & NK_WINDOW_MINIMIZED) ? - style->window.border + window->bounds.y + layout->header_height: - (layout->flags & NK_WINDOW_DYNAMIC)? - layout->bounds.y + layout->bounds.h + layout->footer_height: - window->bounds.y + window->bounds.h; - - /* draw border top */ - nk_stroke_line(out,window->bounds.x,window->bounds.y, - window->bounds.x + window->bounds.w, window->bounds.y, - layout->border, border_color); - - /* draw bottom border */ - nk_stroke_line(out, window->bounds.x, padding_y, - window->bounds.x + window->bounds.w, padding_y, layout->border, border_color); - - /* draw left border */ - nk_stroke_line(out, window->bounds.x + layout->border*0.5f, - window->bounds.y, window->bounds.x + layout->border*0.5f, - padding_y, layout->border, border_color); - - /* draw right border */ - nk_stroke_line(out, window->bounds.x + window->bounds.w - layout->border*0.5f, - window->bounds.y, window->bounds.x + window->bounds.w - layout->border*0.5f, - padding_y, layout->border, border_color); + const float padding_y = (layout->flags & NK_WINDOW_MINIMIZED) + ? (style->window.border + window->bounds.y + layout->header_height) + : ((layout->flags & NK_WINDOW_DYNAMIC) + ? (layout->bounds.y + layout->bounds.h + layout->footer_height) + : (window->bounds.y + window->bounds.h)); + struct nk_rect b = window->bounds; + b.h = padding_y - window->bounds.y; + nk_stroke_rect(out, b, 0, layout->border, border_color); } /* scaler */ @@ -18250,188 +16028,16 @@ nk_panel_end(struct nk_context *ctx) NK_ASSERT(!layout->row.tree_depth); } -/* ---------------------------------------------------------------- + + + + +/* =============================================================== * - * PAGE ELEMENT + * WINDOW * - * ---------------------------------------------------------------*/ -NK_INTERN struct nk_page_element* -nk_create_page_element(struct nk_context *ctx) -{ - struct nk_page_element *elem; - if (ctx->freelist) { - /* unlink page element from free list */ - elem = ctx->freelist; - ctx->freelist = elem->next; - } else if (ctx->use_pool) { - /* allocate page element from memory pool */ - elem = nk_pool_alloc(&ctx->pool); - NK_ASSERT(elem); - if (!elem) return 0; - } else { - /* allocate new page element from back of fixed size memory buffer */ - NK_STORAGE const nk_size size = sizeof(struct nk_page_element); - NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_page_element); - elem = (struct nk_page_element*)nk_buffer_alloc(&ctx->memory, NK_BUFFER_BACK, size, align); - NK_ASSERT(elem); - if (!elem) return 0; - } - nk_zero_struct(*elem); - elem->next = 0; - elem->prev = 0; - return elem; -} - -NK_INTERN void -nk_link_page_element_into_freelist(struct nk_context *ctx, - struct nk_page_element *elem) -{ - /* link table into freelist */ - if (!ctx->freelist) { - ctx->freelist = elem; - } else { - elem->next = ctx->freelist; - ctx->freelist = elem; - } -} - -NK_INTERN void -nk_free_page_element(struct nk_context *ctx, struct nk_page_element *elem) -{ - /* we have a pool so just add to free list */ - if (ctx->use_pool) { - nk_link_page_element_into_freelist(ctx, elem); - return; - } - /* if possible remove last element from back of fixed memory buffer */ - {void *elem_end = (void*)(elem + 1); - void *buffer_end = (nk_byte*)ctx->memory.memory.ptr + ctx->memory.size; - if (elem_end == buffer_end) - ctx->memory.size -= sizeof(struct nk_page_element); - else nk_link_page_element_into_freelist(ctx, elem);} -} - -/* ---------------------------------------------------------------- - * - * PANEL - * - * ---------------------------------------------------------------*/ -NK_INTERN void* -nk_create_panel(struct nk_context *ctx) -{ - struct nk_page_element *elem; - elem = nk_create_page_element(ctx); - if (!elem) return 0; - nk_zero_struct(*elem); - return &elem->data.pan; -} - -NK_INTERN void -nk_free_panel(struct nk_context *ctx, struct nk_panel *pan) -{ - union nk_page_data *pd = NK_CONTAINER_OF(pan, union nk_page_data, pan); - struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); - nk_free_page_element(ctx, pe); -} - -/* ---------------------------------------------------------------- - * - * TABLES - * - * ---------------------------------------------------------------*/ -NK_INTERN struct nk_table* -nk_create_table(struct nk_context *ctx) -{ - struct nk_page_element *elem; - elem = nk_create_page_element(ctx); - if (!elem) return 0; - nk_zero_struct(*elem); - return &elem->data.tbl; -} - -NK_INTERN void -nk_free_table(struct nk_context *ctx, struct nk_table *tbl) -{ - union nk_page_data *pd = NK_CONTAINER_OF(tbl, union nk_page_data, tbl); - struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); - nk_free_page_element(ctx, pe); -} - -NK_INTERN void -nk_push_table(struct nk_window *win, struct nk_table *tbl) -{ - if (!win->tables) { - win->tables = tbl; - tbl->next = 0; - tbl->prev = 0; - tbl->size = 0; - win->table_count = 1; - return; - } - win->tables->prev = tbl; - tbl->next = win->tables; - tbl->prev = 0; - tbl->size = 0; - win->tables = tbl; - win->table_count++; -} - -NK_INTERN void -nk_remove_table(struct nk_window *win, struct nk_table *tbl) -{ - if (win->tables == tbl) - win->tables = tbl->next; - if (tbl->next) - tbl->next->prev = tbl->prev; - if (tbl->prev) - tbl->prev->next = tbl->next; - tbl->next = 0; - tbl->prev = 0; -} - -NK_INTERN nk_uint* -nk_add_value(struct nk_context *ctx, struct nk_window *win, - nk_hash name, nk_uint value) -{ - NK_ASSERT(ctx); - NK_ASSERT(win); - if (!win || !ctx) return 0; - if (!win->tables || win->tables->size >= NK_VALUE_PAGE_CAPACITY) { - struct nk_table *tbl = nk_create_table(ctx); - NK_ASSERT(tbl); - if (!tbl) return 0; - nk_push_table(win, tbl); - } - win->tables->seq = win->seq; - win->tables->keys[win->tables->size] = name; - win->tables->values[win->tables->size] = value; - return &win->tables->values[win->tables->size++]; -} - -NK_INTERN nk_uint* -nk_find_value(struct nk_window *win, nk_hash name) -{ - struct nk_table *iter = win->tables; - while (iter) { - unsigned int i = 0; - unsigned int size = iter->size; - for (i = 0; i < size; ++i) { - if (iter->keys[i] == name) { - iter->seq = win->seq; - return &iter->values[i]; - } - } size = NK_VALUE_PAGE_CAPACITY; - iter = iter->next; - } - return 0; -} - -/* ---------------------------------------------------------------- - * - * WINDOW - * - * ---------------------------------------------------------------*/ -NK_INTERN void* + * ===============================================================*/ +NK_LIB void* nk_create_window(struct nk_context *ctx) { struct nk_page_element *elem; @@ -18440,8 +16046,7 @@ nk_create_window(struct nk_context *ctx) elem->data.win.seq = ctx->seq; return &elem->data.win; } - -NK_INTERN void +NK_LIB void nk_free_window(struct nk_context *ctx, struct nk_window *win) { /* unlink windows from list */ @@ -18468,8 +16073,7 @@ nk_free_window(struct nk_context *ctx, struct nk_window *win) struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); nk_free_page_element(ctx, pe);} } - -NK_INTERN struct nk_window* +NK_LIB struct nk_window* nk_find_window(struct nk_context *ctx, nk_hash hash, const char *name) { struct nk_window *iter; @@ -18485,12 +16089,7 @@ nk_find_window(struct nk_context *ctx, nk_hash hash, const char *name) } return 0; } - -enum nk_window_insert_location { - NK_INSERT_BACK, /* inserts window into the back of list (front of screen) */ - NK_INSERT_FRONT /* inserts window into the front of list (back of screen) */ -}; -NK_INTERN void +NK_LIB void nk_insert_window(struct nk_context *ctx, struct nk_window *win, enum nk_window_insert_location loc) { @@ -18535,8 +16134,7 @@ nk_insert_window(struct nk_context *ctx, struct nk_window *win, } ctx->count++; } - -NK_INTERN void +NK_LIB void nk_remove_window(struct nk_context *ctx, struct nk_window *win) { if (win == ctx->begin || win == ctx->end) { @@ -18565,14 +16163,12 @@ nk_remove_window(struct nk_context *ctx, struct nk_window *win) win->prev = 0; ctx->count--; } - NK_API int nk_begin(struct nk_context *ctx, const char *title, struct nk_rect bounds, nk_flags flags) { return nk_begin_titled(ctx, title, title, bounds, flags); } - NK_API int nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, struct nk_rect bounds, nk_flags flags) @@ -18727,7 +16323,6 @@ nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, win->layout->offset_y = &win->scrollbar.y; return ret; } - NK_API void nk_end(struct nk_context *ctx) { @@ -18746,7 +16341,6 @@ nk_end(struct nk_context *ctx) nk_free_panel(ctx, ctx->current->layout); ctx->current = 0; } - NK_API struct nk_rect nk_window_get_bounds(const struct nk_context *ctx) { @@ -18755,7 +16349,6 @@ nk_window_get_bounds(const struct nk_context *ctx) if (!ctx || !ctx->current) return nk_rect(0,0,0,0); return ctx->current->bounds; } - NK_API struct nk_vec2 nk_window_get_position(const struct nk_context *ctx) { @@ -18764,7 +16357,6 @@ nk_window_get_position(const struct nk_context *ctx) if (!ctx || !ctx->current) return nk_vec2(0,0); return nk_vec2(ctx->current->bounds.x, ctx->current->bounds.y); } - NK_API struct nk_vec2 nk_window_get_size(const struct nk_context *ctx) { @@ -18773,7 +16365,6 @@ nk_window_get_size(const struct nk_context *ctx) if (!ctx || !ctx->current) return nk_vec2(0,0); return nk_vec2(ctx->current->bounds.w, ctx->current->bounds.h); } - NK_API float nk_window_get_width(const struct nk_context *ctx) { @@ -18782,7 +16373,6 @@ nk_window_get_width(const struct nk_context *ctx) if (!ctx || !ctx->current) return 0; return ctx->current->bounds.w; } - NK_API float nk_window_get_height(const struct nk_context *ctx) { @@ -18791,7 +16381,6 @@ nk_window_get_height(const struct nk_context *ctx) if (!ctx || !ctx->current) return 0; return ctx->current->bounds.h; } - NK_API struct nk_rect nk_window_get_content_region(struct nk_context *ctx) { @@ -18800,7 +16389,6 @@ nk_window_get_content_region(struct nk_context *ctx) if (!ctx || !ctx->current) return nk_rect(0,0,0,0); return ctx->current->layout->clip; } - NK_API struct nk_vec2 nk_window_get_content_region_min(struct nk_context *ctx) { @@ -18810,7 +16398,6 @@ nk_window_get_content_region_min(struct nk_context *ctx) if (!ctx || !ctx->current) return nk_vec2(0,0); return nk_vec2(ctx->current->layout->clip.x, ctx->current->layout->clip.y); } - NK_API struct nk_vec2 nk_window_get_content_region_max(struct nk_context *ctx) { @@ -18821,7 +16408,6 @@ nk_window_get_content_region_max(struct nk_context *ctx) return nk_vec2(ctx->current->layout->clip.x + ctx->current->layout->clip.w, ctx->current->layout->clip.y + ctx->current->layout->clip.h); } - NK_API struct nk_vec2 nk_window_get_content_region_size(struct nk_context *ctx) { @@ -18831,7 +16417,6 @@ nk_window_get_content_region_size(struct nk_context *ctx) if (!ctx || !ctx->current) return nk_vec2(0,0); return nk_vec2(ctx->current->layout->clip.w, ctx->current->layout->clip.h); } - NK_API struct nk_command_buffer* nk_window_get_canvas(struct nk_context *ctx) { @@ -18841,7 +16426,6 @@ nk_window_get_canvas(struct nk_context *ctx) if (!ctx || !ctx->current) return 0; return &ctx->current->buffer; } - NK_API struct nk_panel* nk_window_get_panel(struct nk_context *ctx) { @@ -18850,7 +16434,6 @@ nk_window_get_panel(struct nk_context *ctx) if (!ctx || !ctx->current) return 0; return ctx->current->layout; } - NK_API int nk_window_has_focus(const struct nk_context *ctx) { @@ -18860,7 +16443,6 @@ nk_window_has_focus(const struct nk_context *ctx) if (!ctx || !ctx->current) return 0; return ctx->current == ctx->active; } - NK_API int nk_window_is_hovered(struct nk_context *ctx) { @@ -18871,7 +16453,6 @@ nk_window_is_hovered(struct nk_context *ctx) return 0; return nk_input_is_mouse_hovering_rect(&ctx->input, ctx->current->bounds); } - NK_API int nk_window_is_any_hovered(struct nk_context *ctx) { @@ -18899,7 +16480,6 @@ nk_window_is_any_hovered(struct nk_context *ctx) } return 0; } - NK_API int nk_item_is_any_active(struct nk_context *ctx) { @@ -18907,7 +16487,6 @@ nk_item_is_any_active(struct nk_context *ctx) int any_active = (ctx->last_widget_state & NK_WIDGET_STATE_MODIFIED); return any_hovered || any_active; } - NK_API int nk_window_is_collapsed(struct nk_context *ctx, const char *name) { @@ -18923,7 +16502,6 @@ nk_window_is_collapsed(struct nk_context *ctx, const char *name) if (!win) return 0; return win->flags & NK_WINDOW_MINIMIZED; } - NK_API int nk_window_is_closed(struct nk_context *ctx, const char *name) { @@ -18939,7 +16517,6 @@ nk_window_is_closed(struct nk_context *ctx, const char *name) if (!win) return 1; return (win->flags & NK_WINDOW_CLOSED); } - NK_API int nk_window_is_hidden(struct nk_context *ctx, const char *name) { @@ -18955,7 +16532,6 @@ nk_window_is_hidden(struct nk_context *ctx, const char *name) if (!win) return 1; return (win->flags & NK_WINDOW_HIDDEN); } - NK_API int nk_window_is_active(struct nk_context *ctx, const char *name) { @@ -18971,7 +16547,6 @@ nk_window_is_active(struct nk_context *ctx, const char *name) if (!win) return 0; return win == ctx->active; } - NK_API struct nk_window* nk_window_find(struct nk_context *ctx, const char *name) { @@ -18981,7 +16556,6 @@ nk_window_find(struct nk_context *ctx, const char *name) title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); return nk_find_window(ctx, title_hash, name); } - NK_API void nk_window_close(struct nk_context *ctx, const char *name) { @@ -18995,7 +16569,6 @@ nk_window_close(struct nk_context *ctx, const char *name) win->flags |= NK_WINDOW_HIDDEN; win->flags |= NK_WINDOW_CLOSED; } - NK_API void nk_window_set_bounds(struct nk_context *ctx, const char *name, struct nk_rect bounds) @@ -19008,7 +16581,6 @@ nk_window_set_bounds(struct nk_context *ctx, NK_ASSERT(ctx->current != win && "You cannot update a currently in procecss window"); win->bounds = bounds; } - NK_API void nk_window_set_position(struct nk_context *ctx, const char *name, struct nk_vec2 pos) @@ -19018,7 +16590,6 @@ nk_window_set_position(struct nk_context *ctx, win->bounds.x = pos.x; win->bounds.y = pos.y; } - NK_API void nk_window_set_size(struct nk_context *ctx, const char *name, struct nk_vec2 size) @@ -19028,7 +16599,6 @@ nk_window_set_size(struct nk_context *ctx, win->bounds.w = size.x; win->bounds.h = size.y; } - NK_API void nk_window_collapse(struct nk_context *ctx, const char *name, enum nk_collapse_states c) @@ -19047,7 +16617,6 @@ nk_window_collapse(struct nk_context *ctx, const char *name, win->flags |= NK_WINDOW_MINIMIZED; else win->flags &= ~(nk_flags)NK_WINDOW_MINIMIZED; } - NK_API void nk_window_collapse_if(struct nk_context *ctx, const char *name, enum nk_collapse_states c, int cond) @@ -19056,7 +16625,6 @@ nk_window_collapse_if(struct nk_context *ctx, const char *name, if (!ctx || !cond) return; nk_window_collapse(ctx, name, c); } - NK_API void nk_window_show(struct nk_context *ctx, const char *name, enum nk_show_states s) { @@ -19074,7 +16642,6 @@ nk_window_show(struct nk_context *ctx, const char *name, enum nk_show_states s) win->flags |= NK_WINDOW_HIDDEN; } else win->flags &= ~(nk_flags)NK_WINDOW_HIDDEN; } - NK_API void nk_window_show_if(struct nk_context *ctx, const char *name, enum nk_show_states s, int cond) @@ -19103,11 +16670,466 @@ nk_window_set_focus(struct nk_context *ctx, const char *name) ctx->active = win; } -/*---------------------------------------------------------------- + + + + +/* =============================================================== * - * MENUBAR + * POPUP * - * --------------------------------------------------------------*/ + * ===============================================================*/ +NK_API int +nk_popup_begin(struct nk_context *ctx, enum nk_popup_type type, + const char *title, nk_flags flags, struct nk_rect rect) +{ + struct nk_window *popup; + struct nk_window *win; + struct nk_panel *panel; + + int title_len; + nk_hash title_hash; + nk_size allocated; + + NK_ASSERT(ctx); + NK_ASSERT(title); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + panel = win->layout; + NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP) && "popups are not allowed to have popups"); + (void)panel; + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_POPUP); + + popup = win->popup.win; + if (!popup) { + popup = (struct nk_window*)nk_create_window(ctx); + popup->parent = win; + win->popup.win = popup; + win->popup.active = 0; + win->popup.type = NK_PANEL_POPUP; + } + + /* make sure we have correct popup */ + if (win->popup.name != title_hash) { + if (!win->popup.active) { + nk_zero(popup, sizeof(*popup)); + win->popup.name = title_hash; + win->popup.active = 1; + win->popup.type = NK_PANEL_POPUP; + } else return 0; + } + + /* popup position is local to window */ + ctx->current = popup; + rect.x += win->layout->clip.x; + rect.y += win->layout->clip.y; + + /* setup popup data */ + popup->parent = win; + popup->bounds = rect; + popup->seq = ctx->seq; + popup->layout = (struct nk_panel*)nk_create_panel(ctx); + popup->flags = flags; + popup->flags |= NK_WINDOW_BORDER; + if (type == NK_POPUP_DYNAMIC) + popup->flags |= NK_WINDOW_DYNAMIC; + + popup->buffer = win->buffer; + nk_start_popup(ctx, win); + allocated = ctx->memory.allocated; + nk_push_scissor(&popup->buffer, nk_null_rect); + + if (nk_panel_begin(ctx, title, NK_PANEL_POPUP)) { + /* popup is running therefore invalidate parent panels */ + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_ROM; + root->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + win->popup.active = 1; + popup->layout->offset_x = &popup->scrollbar.x; + popup->layout->offset_y = &popup->scrollbar.y; + popup->layout->parent = win->layout; + return 1; + } else { + /* popup was closed/is invalid so cleanup */ + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + win->popup.buf.active = 0; + win->popup.active = 0; + ctx->memory.allocated = allocated; + ctx->current = win; + nk_free_panel(ctx, popup->layout); + popup->layout = 0; + return 0; + } +} +NK_LIB int +nk_nonblock_begin(struct nk_context *ctx, + nk_flags flags, struct nk_rect body, struct nk_rect header, + enum nk_panel_type panel_type) +{ + struct nk_window *popup; + struct nk_window *win; + struct nk_panel *panel; + int is_active = nk_true; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* popups cannot have popups */ + win = ctx->current; + panel = win->layout; + NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP)); + (void)panel; + popup = win->popup.win; + if (!popup) { + /* create window for nonblocking popup */ + popup = (struct nk_window*)nk_create_window(ctx); + popup->parent = win; + win->popup.win = popup; + win->popup.type = panel_type; + nk_command_buffer_init(&popup->buffer, &ctx->memory, NK_CLIPPING_ON); + } else { + /* close the popup if user pressed outside or in the header */ + int pressed, in_body, in_header; + pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT); + in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body); + in_header = nk_input_is_mouse_hovering_rect(&ctx->input, header); + if (pressed && (!in_body || in_header)) + is_active = nk_false; + } + win->popup.header = header; + + if (!is_active) { + /* remove read only mode from all parent panels */ + struct nk_panel *root = win->layout; + while (root) { + root->flags |= NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + return is_active; + } + popup->bounds = body; + popup->parent = win; + popup->layout = (struct nk_panel*)nk_create_panel(ctx); + popup->flags = flags; + popup->flags |= NK_WINDOW_BORDER; + popup->flags |= NK_WINDOW_DYNAMIC; + popup->seq = ctx->seq; + win->popup.active = 1; + NK_ASSERT(popup->layout); + + nk_start_popup(ctx, win); + popup->buffer = win->buffer; + nk_push_scissor(&popup->buffer, nk_null_rect); + ctx->current = popup; + + nk_panel_begin(ctx, 0, panel_type); + win->buffer = popup->buffer; + popup->layout->parent = win->layout; + popup->layout->offset_x = &popup->scrollbar.x; + popup->layout->offset_y = &popup->scrollbar.y; + + /* set read only mode to all parent panels */ + {struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_ROM; + root = root->parent; + }} + return is_active; +} +NK_API void +nk_popup_close(struct nk_context *ctx) +{ + struct nk_window *popup; + NK_ASSERT(ctx); + if (!ctx || !ctx->current) return; + + popup = ctx->current; + NK_ASSERT(popup->parent); + NK_ASSERT(popup->layout->type & NK_PANEL_SET_POPUP); + popup->flags |= NK_WINDOW_HIDDEN; +} +NK_API void +nk_popup_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_window *popup; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return; + + popup = ctx->current; + if (!popup->parent) return; + win = popup->parent; + if (popup->flags & NK_WINDOW_HIDDEN) { + struct nk_panel *root; + root = win->layout; + while (root) { + root->flags |= NK_WINDOW_REMOVE_ROM; + root = root->parent; + } + win->popup.active = 0; + } + nk_push_scissor(&popup->buffer, nk_null_rect); + nk_end(ctx); + + win->buffer = popup->buffer; + nk_finish_popup(ctx, win); + ctx->current = win; + nk_push_scissor(&win->buffer, win->layout->clip); +} + + + + + +/* ============================================================== + * + * CONTEXTUAL + * + * ===============================================================*/ +NK_API int +nk_contextual_begin(struct nk_context *ctx, nk_flags flags, struct nk_vec2 size, + struct nk_rect trigger_bounds) +{ + struct nk_window *win; + struct nk_window *popup; + struct nk_rect body; + + NK_STORAGE const struct nk_rect null_rect = {-1,-1,0,0}; + int is_clicked = 0; + int is_open = 0; + int ret = 0; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + ++win->popup.con_count; + if (ctx->current != ctx->active) + return 0; + + /* check if currently active contextual is active */ + popup = win->popup.win; + is_open = (popup && win->popup.type == NK_PANEL_CONTEXTUAL); + is_clicked = nk_input_mouse_clicked(&ctx->input, NK_BUTTON_RIGHT, trigger_bounds); + if (win->popup.active_con && win->popup.con_count != win->popup.active_con) + return 0; + if (!is_open && win->popup.active_con) + win->popup.active_con = 0; + if ((!is_open && !is_clicked)) + return 0; + + /* calculate contextual position on click */ + win->popup.active_con = win->popup.con_count; + if (is_clicked) { + body.x = ctx->input.mouse.pos.x; + body.y = ctx->input.mouse.pos.y; + } else { + body.x = popup->bounds.x; + body.y = popup->bounds.y; + } + body.w = size.x; + body.h = size.y; + + /* start nonblocking contextual popup */ + ret = nk_nonblock_begin(ctx, flags|NK_WINDOW_NO_SCROLLBAR, body, + null_rect, NK_PANEL_CONTEXTUAL); + if (ret) win->popup.type = NK_PANEL_CONTEXTUAL; + else { + win->popup.active_con = 0; + win->popup.type = NK_PANEL_NONE; + if (win->popup.win) + win->popup.win->flags = 0; + } + return ret; +} +NK_API int +nk_contextual_item_text(struct nk_context *ctx, const char *text, int len, + nk_flags alignment) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, + text, len, alignment, NK_BUTTON_DEFAULT, &style->contextual_button, in, style->font)) { + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} +NK_API int +nk_contextual_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{ + return nk_contextual_item_text(ctx, label, nk_strlen(label), align); +} +NK_API int +nk_contextual_item_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, bounds, + img, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)){ + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} +NK_API int +nk_contextual_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{ + return nk_contextual_item_image_text(ctx, img, label, nk_strlen(label), align); +} +NK_API int +nk_contextual_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *text, int len, nk_flags align) +{ + struct nk_window *win; + const struct nk_input *in; + const struct nk_style *style; + + struct nk_rect bounds; + enum nk_widget_layout_states state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + style = &ctx->style; + state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); + if (!state) return nk_false; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, + symbol, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)) { + nk_contextual_close(ctx); + return nk_true; + } + return nk_false; +} +NK_API int +nk_contextual_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, + const char *text, nk_flags align) +{ + return nk_contextual_item_symbol_text(ctx, symbol, text, nk_strlen(text), align); +} +NK_API void +nk_contextual_close(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + nk_popup_close(ctx); +} +NK_API void +nk_contextual_end(struct nk_context *ctx) +{ + struct nk_window *popup; + struct nk_panel *panel; + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + + popup = ctx->current; + panel = popup->layout; + NK_ASSERT(popup->parent); + NK_ASSERT(panel->type & NK_PANEL_SET_POPUP); + if (panel->flags & NK_WINDOW_DYNAMIC) { + /* Close behavior + This is a bit of a hack solution since we do not know before we end our popup + how big it will be. We therefore do not directly know when a + click outside the non-blocking popup must close it at that direct frame. + Instead it will be closed in the next frame.*/ + struct nk_rect body = {0,0,0,0}; + if (panel->at_y < (panel->bounds.y + panel->bounds.h)) { + struct nk_vec2 padding = nk_panel_get_padding(&ctx->style, panel->type); + body = panel->bounds; + body.y = (panel->at_y + panel->footer_height + panel->border + padding.y + panel->row.height); + body.h = (panel->bounds.y + panel->bounds.h) - body.y; + } + {int pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT); + int in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body); + if (pressed && in_body) + popup->flags |= NK_WINDOW_HIDDEN; + } + } + if (popup->flags & NK_WINDOW_HIDDEN) + popup->seq = 0; + nk_popup_end(ctx); + return; +} + + + + + +/* =============================================================== + * + * MENU + * + * ===============================================================*/ NK_API void nk_menubar_begin(struct nk_context *ctx) { @@ -19145,7 +17167,6 @@ nk_menubar_begin(struct nk_context *ctx) layout->menu.offset.y = *layout->offset_y; *layout->offset_y = 0; } - NK_API void nk_menubar_end(struct nk_context *ctx) { @@ -19177,11 +17198,234 @@ nk_menubar_end(struct nk_context *ctx) layout->clip.h = layout->bounds.h; nk_push_scissor(out, layout->clip); } -/* ------------------------------------------------------------- +NK_INTERN int +nk_menu_begin(struct nk_context *ctx, struct nk_window *win, + const char *id, int is_clicked, struct nk_rect header, struct nk_vec2 size) +{ + int is_open = 0; + int is_active = 0; + struct nk_rect body; + struct nk_window *popup; + nk_hash hash = nk_murmur_hash(id, (int)nk_strlen(id), NK_PANEL_MENU); + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + body.x = header.x; + body.w = size.x; + body.y = header.y + header.h; + body.h = size.y; + + popup = win->popup.win; + is_open = popup ? nk_true : nk_false; + is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_PANEL_MENU); + if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || + (!is_open && !is_active && !is_clicked)) return 0; + if (!nk_nonblock_begin(ctx, NK_WINDOW_NO_SCROLLBAR, body, header, NK_PANEL_MENU)) + return 0; + + win->popup.type = NK_PANEL_MENU; + win->popup.name = hash; + return 1; +} +NK_API int +nk_menu_begin_text(struct nk_context *ctx, const char *title, int len, + nk_flags align, struct nk_vec2 size) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect header; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, header, + title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font)) + is_clicked = nk_true; + return nk_menu_begin(ctx, win, title, is_clicked, header, size); +} +NK_API int nk_menu_begin_label(struct nk_context *ctx, + const char *text, nk_flags align, struct nk_vec2 size) +{ + return nk_menu_begin_text(ctx, text, nk_strlen(text), align, size); +} +NK_API int +nk_menu_begin_image(struct nk_context *ctx, const char *id, struct nk_image img, + struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_image(&ctx->last_widget_state, &win->buffer, header, + img, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in)) + is_clicked = nk_true; + return nk_menu_begin(ctx, win, id, is_clicked, header, size); +} +NK_API int +nk_menu_begin_symbol(struct nk_context *ctx, const char *id, + enum nk_symbol_type sym, struct nk_vec2 size) +{ + struct nk_window *win; + const struct nk_input *in; + struct nk_rect header; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, header, + sym, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font)) + is_clicked = nk_true; + return nk_menu_begin(ctx, win, id, is_clicked, header, size); +} +NK_API int +nk_menu_begin_image_text(struct nk_context *ctx, const char *title, int len, + nk_flags align, struct nk_image img, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, + header, img, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, + ctx->style.font, in)) + is_clicked = nk_true; + return nk_menu_begin(ctx, win, title, is_clicked, header, size); +} +NK_API int +nk_menu_begin_image_label(struct nk_context *ctx, + const char *title, nk_flags align, struct nk_image img, struct nk_vec2 size) +{ + return nk_menu_begin_image_text(ctx, title, nk_strlen(title), align, img, size); +} +NK_API int +nk_menu_begin_symbol_text(struct nk_context *ctx, const char *title, int len, + nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size) +{ + struct nk_window *win; + struct nk_rect header; + const struct nk_input *in; + int is_clicked = nk_false; + nk_flags state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + win = ctx->current; + state = nk_widget(&header, ctx); + if (!state) return 0; + + in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, + header, sym, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, + ctx->style.font, in)) is_clicked = nk_true; + return nk_menu_begin(ctx, win, title, is_clicked, header, size); +} +NK_API int +nk_menu_begin_symbol_label(struct nk_context *ctx, + const char *title, nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size ) +{ + return nk_menu_begin_symbol_text(ctx, title, nk_strlen(title), align,sym,size); +} +NK_API int +nk_menu_item_text(struct nk_context *ctx, const char *title, int len, nk_flags align) +{ + return nk_contextual_item_text(ctx, title, len, align); +} +NK_API int +nk_menu_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{ + return nk_contextual_item_label(ctx, label, align); +} +NK_API int +nk_menu_item_image_label(struct nk_context *ctx, struct nk_image img, + const char *label, nk_flags align) +{ + return nk_contextual_item_image_label(ctx, img, label, align); +} +NK_API int +nk_menu_item_image_text(struct nk_context *ctx, struct nk_image img, + const char *text, int len, nk_flags align) +{ + return nk_contextual_item_image_text(ctx, img, text, len, align); +} +NK_API int nk_menu_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *text, int len, nk_flags align) +{ + return nk_contextual_item_symbol_text(ctx, sym, text, len, align); +} +NK_API int nk_menu_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *label, nk_flags align) +{ + return nk_contextual_item_symbol_label(ctx, sym, label, align); +} +NK_API void nk_menu_close(struct nk_context *ctx) +{ + nk_contextual_close(ctx); +} +NK_API void +nk_menu_end(struct nk_context *ctx) +{ + nk_contextual_end(ctx); +} + + + + + +/* =============================================================== * * LAYOUT * - * --------------------------------------------------------------*/ + * ===============================================================*/ NK_API void nk_layout_set_min_row_height(struct nk_context *ctx, float height) { @@ -19198,7 +17442,6 @@ nk_layout_set_min_row_height(struct nk_context *ctx, float height) layout = win->layout; layout->row.min_height = height; } - NK_API void nk_layout_reset_min_row_height(struct nk_context *ctx) { @@ -19217,8 +17460,7 @@ nk_layout_reset_min_row_height(struct nk_context *ctx) layout->row.min_height += ctx->style.text.padding.y*2; layout->row.min_height += ctx->style.window.min_row_height_padding*2; } - -NK_INTERN float +NK_LIB float nk_layout_row_calculate_usable_space(const struct nk_style *style, enum nk_panel_type type, float total_space, int columns) { @@ -19238,8 +17480,7 @@ nk_layout_row_calculate_usable_space(const struct nk_style *style, enum nk_panel panel_space = total_space - panel_padding - panel_spacing; return panel_space; } - -NK_INTERN void +NK_LIB void nk_panel_layout(const struct nk_context *ctx, struct nk_window *win, float height, int cols) { @@ -19291,8 +17532,7 @@ nk_panel_layout(const struct nk_context *ctx, struct nk_window *win, nk_fill_rect(out, background, 0, color); } } - -NK_INTERN void +NK_LIB void nk_row_layout(struct nk_context *ctx, enum nk_layout_format fmt, float height, int cols, int width) { @@ -19315,7 +17555,6 @@ nk_row_layout(struct nk_context *ctx, enum nk_layout_format fmt, win->layout->row.item_offset = 0; win->layout->row.item_width = (float)width; } - NK_API float nk_layout_ratio_from_pixel(struct nk_context *ctx, float pixel_width) { @@ -19326,19 +17565,16 @@ nk_layout_ratio_from_pixel(struct nk_context *ctx, float pixel_width) win = ctx->current; return NK_CLAMP(0.0f, pixel_width/win->bounds.x, 1.0f); } - NK_API void nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols) { nk_row_layout(ctx, NK_DYNAMIC, height, cols, 0); } - NK_API void nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols) { nk_row_layout(ctx, NK_STATIC, height, cols, item_width); } - NK_API void nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, float row_height, int cols) @@ -19365,7 +17601,6 @@ nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, layout->row.item_offset = 0; layout->row.columns = cols; } - NK_API void nk_layout_row_push(struct nk_context *ctx, float ratio_or_width) { @@ -19392,7 +17627,6 @@ nk_layout_row_push(struct nk_context *ctx, float ratio_or_width) else layout->row.item_width = 1.0f - layout->row.filled; } else layout->row.item_width = ratio_or_width; } - NK_API void nk_layout_row_end(struct nk_context *ctx) { @@ -19413,7 +17647,6 @@ nk_layout_row_end(struct nk_context *ctx) layout->row.item_width = 0; layout->row.item_offset = 0; } - NK_API void nk_layout_row(struct nk_context *ctx, enum nk_layout_format fmt, float height, int cols, const float *ratio) @@ -19453,7 +17686,6 @@ nk_layout_row(struct nk_context *ctx, enum nk_layout_format fmt, layout->row.item_offset = 0; layout->row.filled = 0; } - NK_API void nk_layout_row_template_begin(struct nk_context *ctx, float height) { @@ -19481,7 +17713,6 @@ nk_layout_row_template_begin(struct nk_context *ctx, float height) layout->row.item.w = 0; layout->row.item.h = 0; } - NK_API void nk_layout_row_template_push_dynamic(struct nk_context *ctx) { @@ -19502,7 +17733,6 @@ nk_layout_row_template_push_dynamic(struct nk_context *ctx) if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return; layout->row.templates[layout->row.columns++] = -1.0f; } - NK_API void nk_layout_row_template_push_variable(struct nk_context *ctx, float min_width) { @@ -19523,7 +17753,6 @@ nk_layout_row_template_push_variable(struct nk_context *ctx, float min_width) if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return; layout->row.templates[layout->row.columns++] = -min_width; } - NK_API void nk_layout_row_template_push_static(struct nk_context *ctx, float width) { @@ -19544,7 +17773,6 @@ nk_layout_row_template_push_static(struct nk_context *ctx, float width) if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return; layout->row.templates[layout->row.columns++] = width; } - NK_API void nk_layout_row_template_end(struct nk_context *ctx) { @@ -19596,7 +17824,6 @@ nk_layout_row_template_end(struct nk_context *ctx) } } } - NK_API void nk_layout_space_begin(struct nk_context *ctx, enum nk_layout_format fmt, float height, int widget_count) @@ -19622,7 +17849,6 @@ nk_layout_space_begin(struct nk_context *ctx, enum nk_layout_format fmt, layout->row.item_width = 0; layout->row.item_offset = 0; } - NK_API void nk_layout_space_end(struct nk_context *ctx) { @@ -19642,7 +17868,6 @@ nk_layout_space_end(struct nk_context *ctx) layout->row.item_offset = 0; nk_zero(&layout->row.item, sizeof(layout->row.item)); } - NK_API void nk_layout_space_push(struct nk_context *ctx, struct nk_rect rect) { @@ -19659,7 +17884,6 @@ nk_layout_space_push(struct nk_context *ctx, struct nk_rect rect) layout = win->layout; layout->row.item = rect; } - NK_API struct nk_rect nk_layout_space_bounds(struct nk_context *ctx) { @@ -19679,7 +17903,6 @@ nk_layout_space_bounds(struct nk_context *ctx) ret.h = layout->row.height; return ret; } - NK_API struct nk_rect nk_layout_widget_bounds(struct nk_context *ctx) { @@ -19699,7 +17922,6 @@ nk_layout_widget_bounds(struct nk_context *ctx) ret.h = layout->row.height; return ret; } - NK_API struct nk_vec2 nk_layout_space_to_screen(struct nk_context *ctx, struct nk_vec2 ret) { @@ -19716,7 +17938,6 @@ nk_layout_space_to_screen(struct nk_context *ctx, struct nk_vec2 ret) ret.y += layout->at_y - (float)*layout->offset_y; return ret; } - NK_API struct nk_vec2 nk_layout_space_to_local(struct nk_context *ctx, struct nk_vec2 ret) { @@ -19733,7 +17954,6 @@ nk_layout_space_to_local(struct nk_context *ctx, struct nk_vec2 ret) ret.y += -layout->at_y + (float)*layout->offset_y; return ret; } - NK_API struct nk_rect nk_layout_space_rect_to_screen(struct nk_context *ctx, struct nk_rect ret) { @@ -19750,7 +17970,6 @@ nk_layout_space_rect_to_screen(struct nk_context *ctx, struct nk_rect ret) ret.y += layout->at_y - (float)*layout->offset_y; return ret; } - NK_API struct nk_rect nk_layout_space_rect_to_local(struct nk_context *ctx, struct nk_rect ret) { @@ -19767,8 +17986,7 @@ nk_layout_space_rect_to_local(struct nk_context *ctx, struct nk_rect ret) ret.y += -layout->at_y + (float)*layout->offset_y; return ret; } - -NK_INTERN void +NK_LIB void nk_panel_alloc_row(const struct nk_context *ctx, struct nk_window *win) { struct nk_panel *layout = win->layout; @@ -19776,8 +17994,7 @@ nk_panel_alloc_row(const struct nk_context *ctx, struct nk_window *win) const float row_height = layout->row.height - spacing.y; nk_panel_layout(ctx, win, row_height, layout->row.columns); } - -NK_INTERN void +NK_LIB void nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, struct nk_window *win, int modify) { @@ -19812,7 +18029,7 @@ nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, switch (layout->row.type) { case NK_LAYOUT_DYNAMIC_FIXED: { /* scaling fixed size widgets item width */ - item_width = NK_MAX(1.0f,panel_space-1.0f) / (float)layout->row.columns; + item_width = NK_MAX(1.0f,panel_space) / (float)layout->row.columns; item_offset = (float)layout->row.index * item_width; item_spacing = (float)layout->row.index * spacing.x; } break; @@ -19837,7 +18054,7 @@ nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, bounds->w = layout->bounds.w * layout->row.item.w; bounds->h = layout->row.height * layout->row.item.h; return; - } break; + } case NK_LAYOUT_DYNAMIC: { /* scaling arrays of panel width ratios for every widget */ float ratio; @@ -19878,7 +18095,7 @@ nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, bounds->y -= (float)*layout->offset_y; bounds->h = layout->row.item.h; return; - } break; + } case NK_LAYOUT_STATIC: { /* non-scaling array of panel pixel width for every widget */ item_spacing = (float)layout->row.index * spacing.x; @@ -19907,8 +18124,7 @@ nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, layout->max_x = bounds->x + bounds->w; bounds->x -= (float)*layout->offset_x; } - -NK_INTERN void +NK_LIB void nk_panel_alloc_space(struct nk_rect *bounds, const struct nk_context *ctx) { struct nk_window *win; @@ -19930,8 +18146,7 @@ nk_panel_alloc_space(struct nk_rect *bounds, const struct nk_context *ctx) nk_layout_widget_space(bounds, ctx, win, nk_true); layout->row.index++; } - -NK_INTERN void +NK_LIB void nk_layout_peek(struct nk_rect *bounds, struct nk_context *ctx) { float y; @@ -19961,6 +18176,15 @@ nk_layout_peek(struct nk_rect *bounds, struct nk_context *ctx) layout->row.index = index; } + + + + +/* =============================================================== + * + * TREE + * + * ===============================================================*/ NK_INTERN int nk_tree_state_base(struct nk_context *ctx, enum nk_tree_type type, struct nk_image *img, const char *title, enum nk_collapse_states *state) @@ -20069,7 +18293,6 @@ nk_tree_state_base(struct nk_context *ctx, enum nk_tree_type type, return nk_true; } else return nk_false; } - NK_INTERN int nk_tree_base(struct nk_context *ctx, enum nk_tree_type type, struct nk_image *img, const char *title, enum nk_collapse_states initial_state, @@ -20092,17 +18315,18 @@ nk_tree_base(struct nk_context *ctx, enum nk_tree_type type, } return nk_tree_state_base(ctx, type, img, title, (enum nk_collapse_states*)state); } - NK_API int nk_tree_state_push(struct nk_context *ctx, enum nk_tree_type type, const char *title, enum nk_collapse_states *state) -{return nk_tree_state_base(ctx, type, 0, title, state);} - +{ + return nk_tree_state_base(ctx, type, 0, title, state); +} NK_API int nk_tree_state_image_push(struct nk_context *ctx, enum nk_tree_type type, struct nk_image img, const char *title, enum nk_collapse_states *state) -{return nk_tree_state_base(ctx, type, &img, title, state);} - +{ + return nk_tree_state_base(ctx, type, &img, title, state); +} NK_API void nk_tree_state_pop(struct nk_context *ctx) { @@ -20122,28 +18346,440 @@ nk_tree_state_pop(struct nk_context *ctx) NK_ASSERT(layout->row.tree_depth); layout->row.tree_depth--; } - NK_API int nk_tree_push_hashed(struct nk_context *ctx, enum nk_tree_type type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len, int line) -{return nk_tree_base(ctx, type, 0, title, initial_state, hash, len, line);} - +{ + return nk_tree_base(ctx, type, 0, title, initial_state, hash, len, line); +} NK_API int nk_tree_image_push_hashed(struct nk_context *ctx, enum nk_tree_type type, struct nk_image img, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed) -{return nk_tree_base(ctx, type, &img, title, initial_state, hash, len, seed);} - +{ + return nk_tree_base(ctx, type, &img, title, initial_state, hash, len, seed); +} NK_API void nk_tree_pop(struct nk_context *ctx) -{nk_tree_state_pop(ctx);} +{ + nk_tree_state_pop(ctx); +} +NK_INTERN int +nk_tree_element_image_push_hashed_base(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image *img, const char *title, int title_len, + enum nk_collapse_states *state, int *selected) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_command_buffer *out; + const struct nk_input *in; + const struct nk_style_button *button; + enum nk_symbol_type symbol; + float row_height; + struct nk_vec2 padding; -/*---------------------------------------------------------------- + int text_len; + float text_width; + + struct nk_vec2 item_spacing; + struct nk_rect header = {0,0,0,0}; + struct nk_rect sym = {0,0,0,0}; + struct nk_text text; + + nk_flags ws = 0; + enum nk_widget_layout_states widget_state; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) + return 0; + + /* cache some data */ + win = ctx->current; + layout = win->layout; + out = &win->buffer; + style = &ctx->style; + item_spacing = style->window.spacing; + padding = style->selectable.padding; + + /* calculate header bounds and draw background */ + row_height = style->font->height + 2 * style->tab.padding.y; + nk_layout_set_min_row_height(ctx, row_height); + nk_layout_row_dynamic(ctx, row_height, 1); + nk_layout_reset_min_row_height(ctx); + + widget_state = nk_widget(&header, ctx); + if (type == NK_TREE_TAB) { + const struct nk_style_item *background = &style->tab.background; + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, header, &background->data.image, nk_white); + text.background = nk_rgba(0,0,0,0); + } else { + text.background = background->data.color; + nk_fill_rect(out, header, 0, style->tab.border_color); + nk_fill_rect(out, nk_shrink_rect(header, style->tab.border), + style->tab.rounding, background->data.color); + } + } else text.background = style->window.background; + + in = (!(layout->flags & NK_WINDOW_ROM)) ? &ctx->input: 0; + in = (in && widget_state == NK_WIDGET_VALID) ? &ctx->input : 0; + + /* select correct button style */ + if (*state == NK_MAXIMIZED) { + symbol = style->tab.sym_maximize; + if (type == NK_TREE_TAB) + button = &style->tab.tab_maximize_button; + else button = &style->tab.node_maximize_button; + } else { + symbol = style->tab.sym_minimize; + if (type == NK_TREE_TAB) + button = &style->tab.tab_minimize_button; + else button = &style->tab.node_minimize_button; + } + {/* draw triangle button */ + sym.w = sym.h = style->font->height; + sym.y = header.y + style->tab.padding.y; + sym.x = header.x + style->tab.padding.x; + if (nk_do_button_symbol(&ws, &win->buffer, sym, symbol, NK_BUTTON_DEFAULT, button, in, style->font)) + *state = (*state == NK_MAXIMIZED) ? NK_MINIMIZED : NK_MAXIMIZED;} + + /* draw label */ + {nk_flags dummy = 0; + struct nk_rect label; + /* calculate size of the text and tooltip */ + text_len = nk_strlen(title); + text_width = style->font->width(style->font->userdata, style->font->height, title, text_len); + text_width += (4 * padding.x); + + header.w = NK_MAX(header.w, sym.w + item_spacing.x); + label.x = sym.x + sym.w + item_spacing.x; + label.y = sym.y; + label.w = NK_MIN(header.w - (sym.w + item_spacing.y + style->tab.indent), text_width); + label.h = style->font->height; + + if (img) { + nk_do_selectable_image(&dummy, &win->buffer, label, title, title_len, NK_TEXT_LEFT, + selected, img, &style->selectable, in, style->font); + } else nk_do_selectable(&dummy, &win->buffer, label, title, title_len, NK_TEXT_LEFT, + selected, &style->selectable, in, style->font); + } + /* increase x-axis cursor widget position pointer */ + if (*state == NK_MAXIMIZED) { + layout->at_x = header.x + (float)*layout->offset_x + style->tab.indent; + layout->bounds.w = NK_MAX(layout->bounds.w, style->tab.indent); + layout->bounds.w -= (style->tab.indent + style->window.padding.x); + layout->row.tree_depth++; + return nk_true; + } else return nk_false; +} +NK_INTERN int +nk_tree_element_base(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image *img, const char *title, enum nk_collapse_states initial_state, + int *selected, const char *hash, int len, int line) +{ + struct nk_window *win = ctx->current; + int title_len = 0; + nk_hash tree_hash = 0; + nk_uint *state = 0; + + /* retrieve tree state from internal widget state tables */ + if (!hash) { + title_len = (int)nk_strlen(title); + tree_hash = nk_murmur_hash(title, (int)title_len, (nk_hash)line); + } else tree_hash = nk_murmur_hash(hash, len, (nk_hash)line); + state = nk_find_value(win, tree_hash); + if (!state) { + state = nk_add_value(ctx, win, tree_hash, 0); + *state = initial_state; + } return nk_tree_element_image_push_hashed_base(ctx, type, img, title, + nk_strlen(title), (enum nk_collapse_states*)state, selected); +} +NK_API int +nk_tree_element_push_hashed(struct nk_context *ctx, enum nk_tree_type type, + const char *title, enum nk_collapse_states initial_state, + int *selected, const char *hash, int len, int seed) +{ + return nk_tree_element_base(ctx, type, 0, title, initial_state, selected, hash, len, seed); +} +NK_API int +nk_tree_element_image_push_hashed(struct nk_context *ctx, enum nk_tree_type type, + struct nk_image img, const char *title, enum nk_collapse_states initial_state, + int *selected, const char *hash, int len,int seed) +{ + return nk_tree_element_base(ctx, type, &img, title, initial_state, selected, hash, len, seed); +} +NK_API void +nk_tree_element_pop(struct nk_context *ctx) +{ + nk_tree_state_pop(ctx); +} + + + + + +/* =============================================================== * - * WIDGETS + * GROUP * - * --------------------------------------------------------------*/ + * ===============================================================*/ +NK_API int +nk_group_scrolled_offset_begin(struct nk_context *ctx, + nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags) +{ + struct nk_rect bounds; + struct nk_window panel; + struct nk_window *win; + + win = ctx->current; + nk_panel_alloc_space(&bounds, ctx); + {const struct nk_rect *c = &win->layout->clip; + if (!NK_INTERSECT(c->x, c->y, c->w, c->h, bounds.x, bounds.y, bounds.w, bounds.h) && + !(flags & NK_WINDOW_MOVABLE)) { + return 0; + }} + if (win->flags & NK_WINDOW_ROM) + flags |= NK_WINDOW_ROM; + + /* initialize a fake window to create the panel from */ + nk_zero(&panel, sizeof(panel)); + panel.bounds = bounds; + panel.flags = flags; + panel.scrollbar.x = *x_offset; + panel.scrollbar.y = *y_offset; + panel.buffer = win->buffer; + panel.layout = (struct nk_panel*)nk_create_panel(ctx); + ctx->current = &panel; + nk_panel_begin(ctx, (flags & NK_WINDOW_TITLE) ? title: 0, NK_PANEL_GROUP); + + win->buffer = panel.buffer; + win->buffer.clip = panel.layout->clip; + panel.layout->offset_x = x_offset; + panel.layout->offset_y = y_offset; + panel.layout->parent = win->layout; + win->layout = panel.layout; + + ctx->current = win; + if ((panel.layout->flags & NK_WINDOW_CLOSED) || + (panel.layout->flags & NK_WINDOW_MINIMIZED)) + { + nk_flags f = panel.layout->flags; + nk_group_scrolled_end(ctx); + if (f & NK_WINDOW_CLOSED) + return NK_WINDOW_CLOSED; + if (f & NK_WINDOW_MINIMIZED) + return NK_WINDOW_MINIMIZED; + } + return 1; +} +NK_API void +nk_group_scrolled_end(struct nk_context *ctx) +{ + struct nk_window *win; + struct nk_panel *parent; + struct nk_panel *g; + + struct nk_rect clip; + struct nk_window pan; + struct nk_vec2 panel_padding; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) + return; + + /* make sure nk_group_begin was called correctly */ + NK_ASSERT(ctx->current); + win = ctx->current; + NK_ASSERT(win->layout); + g = win->layout; + NK_ASSERT(g->parent); + parent = g->parent; + + /* dummy window */ + nk_zero_struct(pan); + panel_padding = nk_panel_get_padding(&ctx->style, NK_PANEL_GROUP); + pan.bounds.y = g->bounds.y - (g->header_height + g->menu.h); + pan.bounds.x = g->bounds.x - panel_padding.x; + pan.bounds.w = g->bounds.w + 2 * panel_padding.x; + pan.bounds.h = g->bounds.h + g->header_height + g->menu.h; + if (g->flags & NK_WINDOW_BORDER) { + pan.bounds.x -= g->border; + pan.bounds.y -= g->border; + pan.bounds.w += 2*g->border; + pan.bounds.h += 2*g->border; + } + if (!(g->flags & NK_WINDOW_NO_SCROLLBAR)) { + pan.bounds.w += ctx->style.window.scrollbar_size.x; + pan.bounds.h += ctx->style.window.scrollbar_size.y; + } + pan.scrollbar.x = *g->offset_x; + pan.scrollbar.y = *g->offset_y; + pan.flags = g->flags; + pan.buffer = win->buffer; + pan.layout = g; + pan.parent = win; + ctx->current = &pan; + + /* make sure group has correct clipping rectangle */ + nk_unify(&clip, &parent->clip, pan.bounds.x, pan.bounds.y, + pan.bounds.x + pan.bounds.w, pan.bounds.y + pan.bounds.h + panel_padding.x); + nk_push_scissor(&pan.buffer, clip); + nk_end(ctx); + + win->buffer = pan.buffer; + nk_push_scissor(&win->buffer, parent->clip); + ctx->current = win; + win->layout = parent; + g->bounds = pan.bounds; + return; +} +NK_API int +nk_group_scrolled_begin(struct nk_context *ctx, + struct nk_scroll *scroll, const char *title, nk_flags flags) +{ + return nk_group_scrolled_offset_begin(ctx, &scroll->x, &scroll->y, title, flags); +} +NK_API int +nk_group_begin_titled(struct nk_context *ctx, const char *id, + const char *title, nk_flags flags) +{ + int id_len; + nk_hash id_hash; + struct nk_window *win; + nk_uint *x_offset; + nk_uint *y_offset; + + NK_ASSERT(ctx); + NK_ASSERT(id); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !id) + return 0; + + /* find persistent group scrollbar value */ + win = ctx->current; + id_len = (int)nk_strlen(id); + id_hash = nk_murmur_hash(id, (int)id_len, NK_PANEL_GROUP); + x_offset = nk_find_value(win, id_hash); + if (!x_offset) { + x_offset = nk_add_value(ctx, win, id_hash, 0); + y_offset = nk_add_value(ctx, win, id_hash+1, 0); + + NK_ASSERT(x_offset); + NK_ASSERT(y_offset); + if (!x_offset || !y_offset) return 0; + *x_offset = *y_offset = 0; + } else y_offset = nk_find_value(win, id_hash+1); + return nk_group_scrolled_offset_begin(ctx, x_offset, y_offset, title, flags); +} +NK_API int +nk_group_begin(struct nk_context *ctx, const char *title, nk_flags flags) +{ + return nk_group_begin_titled(ctx, title, title, flags); +} +NK_API void +nk_group_end(struct nk_context *ctx) +{ + nk_group_scrolled_end(ctx); +} + + + + + +/* =============================================================== + * + * LIST VIEW + * + * ===============================================================*/ +NK_API int +nk_list_view_begin(struct nk_context *ctx, struct nk_list_view *view, + const char *title, nk_flags flags, int row_height, int row_count) +{ + int title_len; + nk_hash title_hash; + nk_uint *x_offset; + nk_uint *y_offset; + + int result; + struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *style; + struct nk_vec2 item_spacing; + + NK_ASSERT(ctx); + NK_ASSERT(view); + NK_ASSERT(title); + if (!ctx || !view || !title) return 0; + + win = ctx->current; + style = &ctx->style; + item_spacing = style->window.spacing; + row_height += NK_MAX(0, (int)item_spacing.y); + + /* find persistent list view scrollbar offset */ + title_len = (int)nk_strlen(title); + title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_GROUP); + x_offset = nk_find_value(win, title_hash); + if (!x_offset) { + x_offset = nk_add_value(ctx, win, title_hash, 0); + y_offset = nk_add_value(ctx, win, title_hash+1, 0); + + NK_ASSERT(x_offset); + NK_ASSERT(y_offset); + if (!x_offset || !y_offset) return 0; + *x_offset = *y_offset = 0; + } else y_offset = nk_find_value(win, title_hash+1); + view->scroll_value = *y_offset; + view->scroll_pointer = y_offset; + + *y_offset = 0; + result = nk_group_scrolled_offset_begin(ctx, x_offset, y_offset, title, flags); + win = ctx->current; + layout = win->layout; + + view->total_height = row_height * NK_MAX(row_count,1); + view->begin = (int)NK_MAX(((float)view->scroll_value / (float)row_height), 0.0f); + view->count = (int)NK_MAX(nk_iceilf((layout->clip.h)/(float)row_height),0); + view->count = NK_MIN(view->count, row_count - view->begin); + view->end = view->begin + view->count; + view->ctx = ctx; + return result; +} +NK_API void +nk_list_view_end(struct nk_list_view *view) +{ + struct nk_context *ctx; + struct nk_window *win; + struct nk_panel *layout; + + NK_ASSERT(view); + NK_ASSERT(view->ctx); + NK_ASSERT(view->scroll_pointer); + if (!view || !view->ctx) return; + + ctx = view->ctx; + win = ctx->current; + layout = win->layout; + layout->at_y = layout->bounds.y + (float)view->total_height; + *view->scroll_pointer = *view->scroll_pointer + view->scroll_value; + nk_group_end(view->ctx); +} + + + + + +/* =============================================================== + * + * WIDGET + * + * ===============================================================*/ NK_API struct nk_rect nk_widget_bounds(struct nk_context *ctx) { @@ -20155,7 +18791,6 @@ nk_widget_bounds(struct nk_context *ctx) nk_layout_peek(&bounds, ctx); return bounds; } - NK_API struct nk_vec2 nk_widget_position(struct nk_context *ctx) { @@ -20168,7 +18803,6 @@ nk_widget_position(struct nk_context *ctx) nk_layout_peek(&bounds, ctx); return nk_vec2(bounds.x, bounds.y); } - NK_API struct nk_vec2 nk_widget_size(struct nk_context *ctx) { @@ -20181,7 +18815,6 @@ nk_widget_size(struct nk_context *ctx) nk_layout_peek(&bounds, ctx); return nk_vec2(bounds.w, bounds.h); } - NK_API float nk_widget_width(struct nk_context *ctx) { @@ -20194,7 +18827,6 @@ nk_widget_width(struct nk_context *ctx) nk_layout_peek(&bounds, ctx); return bounds.w; } - NK_API float nk_widget_height(struct nk_context *ctx) { @@ -20207,7 +18839,6 @@ nk_widget_height(struct nk_context *ctx) nk_layout_peek(&bounds, ctx); return bounds.h; } - NK_API int nk_widget_is_hovered(struct nk_context *ctx) { @@ -20230,7 +18861,6 @@ nk_widget_is_hovered(struct nk_context *ctx) return 0; return nk_input_is_mouse_hovering_rect(&ctx->input, bounds); } - NK_API int nk_widget_is_mouse_clicked(struct nk_context *ctx, enum nk_buttons btn) { @@ -20253,7 +18883,6 @@ nk_widget_is_mouse_clicked(struct nk_context *ctx, enum nk_buttons btn) return 0; return nk_input_mouse_clicked(&ctx->input, btn, bounds); } - NK_API int nk_widget_has_mouse_click_down(struct nk_context *ctx, enum nk_buttons btn, int down) { @@ -20276,7 +18905,6 @@ nk_widget_has_mouse_click_down(struct nk_context *ctx, enum nk_buttons btn, int return 0; return nk_input_has_mouse_click_down_in_rect(&ctx->input, btn, bounds, down); } - NK_API enum nk_widget_layout_states nk_widget(struct nk_rect *bounds, const struct nk_context *ctx) { @@ -20325,7 +18953,6 @@ nk_widget(struct nk_rect *bounds, const struct nk_context *ctx) return NK_WIDGET_ROM; return NK_WIDGET_VALID; } - NK_API enum nk_widget_layout_states nk_widget_fitting(struct nk_rect *bounds, struct nk_context *ctx, struct nk_vec2 item_padding) @@ -20359,12 +18986,6 @@ nk_widget_fitting(struct nk_rect *bounds, struct nk_context *ctx, else bounds->w += item_padding.x; return state; } - -/*---------------------------------------------------------------- - * - * MISC - * - * --------------------------------------------------------------*/ NK_API void nk_spacing(struct nk_context *ctx, int cols) { @@ -20394,15 +19015,102 @@ nk_spacing(struct nk_context *ctx, int cols) layout->row.type != NK_LAYOUT_STATIC_FIXED) { for (i = 0; i < cols; ++i) nk_panel_alloc_space(&none, ctx); - } - layout->row.index = index; + } layout->row.index = index; } -/*---------------------------------------------------------------- + + + + +/* =============================================================== * - * TEXT + * TEXT * - * --------------------------------------------------------------*/ + * ===============================================================*/ +NK_LIB void +nk_widget_text(struct nk_command_buffer *o, struct nk_rect b, + const char *string, int len, const struct nk_text *t, + nk_flags a, const struct nk_user_font *f) +{ + struct nk_rect label; + float text_width; + + NK_ASSERT(o); + NK_ASSERT(t); + if (!o || !t) return; + + b.h = NK_MAX(b.h, 2 * t->padding.y); + label.x = 0; label.w = 0; + label.y = b.y + t->padding.y; + label.h = NK_MIN(f->height, b.h - 2 * t->padding.y); + + text_width = f->width(f->userdata, f->height, (const char*)string, len); + text_width += (2.0f * t->padding.x); + + /* align in x-axis */ + if (a & NK_TEXT_ALIGN_LEFT) { + label.x = b.x + t->padding.x; + label.w = NK_MAX(0, b.w - 2 * t->padding.x); + } else if (a & NK_TEXT_ALIGN_CENTERED) { + label.w = NK_MAX(1, 2 * t->padding.x + (float)text_width); + label.x = (b.x + t->padding.x + ((b.w - 2 * t->padding.x) - label.w) / 2); + label.x = NK_MAX(b.x + t->padding.x, label.x); + label.w = NK_MIN(b.x + b.w, label.x + label.w); + if (label.w >= label.x) label.w -= label.x; + } else if (a & NK_TEXT_ALIGN_RIGHT) { + label.x = NK_MAX(b.x + t->padding.x, (b.x + b.w) - (2 * t->padding.x + (float)text_width)); + label.w = (float)text_width + 2 * t->padding.x; + } else return; + + /* align in y-axis */ + if (a & NK_TEXT_ALIGN_MIDDLE) { + label.y = b.y + b.h/2.0f - (float)f->height/2.0f; + label.h = NK_MAX(b.h/2.0f, b.h - (b.h/2.0f + f->height/2.0f)); + } else if (a & NK_TEXT_ALIGN_BOTTOM) { + label.y = b.y + b.h - f->height; + label.h = f->height; + } + nk_draw_text(o, label, (const char*)string, len, f, t->background, t->text); +} +NK_LIB void +nk_widget_text_wrap(struct nk_command_buffer *o, struct nk_rect b, + const char *string, int len, const struct nk_text *t, + const struct nk_user_font *f) +{ + float width; + int glyphs = 0; + int fitting = 0; + int done = 0; + struct nk_rect line; + struct nk_text text; + NK_INTERN nk_rune seperator[] = {' '}; + + NK_ASSERT(o); + NK_ASSERT(t); + if (!o || !t) return; + + text.padding = nk_vec2(0,0); + text.background = t->background; + text.text = t->text; + + b.w = NK_MAX(b.w, 2 * t->padding.x); + b.h = NK_MAX(b.h, 2 * t->padding.y); + b.h = b.h - 2 * t->padding.y; + + line.x = b.x + t->padding.x; + line.y = b.y + t->padding.y; + line.w = b.w - 2 * t->padding.x; + line.h = 2 * t->padding.y + f->height; + + fitting = nk_text_clamp(f, string, len, line.w, &glyphs, &width, seperator,NK_LEN(seperator)); + while (done < len) { + if (!fitting || line.y + line.h >= (b.y + b.h)) break; + nk_widget_text(o, line, &string[done], fitting, &text, NK_TEXT_LEFT, f); + done += fitting; + line.y += f->height + 2 * t->padding.y; + fitting = nk_text_clamp(f, &string[done], len - done, line.w, &glyphs, &width, seperator,NK_LEN(seperator)); + } +} NK_API void nk_text_colored(struct nk_context *ctx, const char *str, int len, nk_flags alignment, struct nk_color color) @@ -20430,7 +19138,6 @@ nk_text_colored(struct nk_context *ctx, const char *str, int len, text.text = color; nk_widget_text(&win->buffer, bounds, str, len, &text, alignment, style->font); } - NK_API void nk_text_wrap_colored(struct nk_context *ctx, const char *str, int len, struct nk_color color) @@ -20458,77 +19165,101 @@ nk_text_wrap_colored(struct nk_context *ctx, const char *str, text.text = color; nk_widget_text_wrap(&win->buffer, bounds, str, len, &text, style->font); } - #ifdef NK_INCLUDE_STANDARD_VARARGS NK_API void nk_labelf_colored(struct nk_context *ctx, nk_flags flags, struct nk_color color, const char *fmt, ...) { - char buf[256]; va_list args; va_start(args, fmt); - nk_strfmt(buf, NK_LEN(buf), fmt, args); - nk_label_colored(ctx, buf, flags, color); + nk_labelfv_colored(ctx, flags, color, fmt, args); va_end(args); } - NK_API void nk_labelf_colored_wrap(struct nk_context *ctx, struct nk_color color, const char *fmt, ...) { - char buf[256]; va_list args; va_start(args, fmt); - nk_strfmt(buf, NK_LEN(buf), fmt, args); - nk_label_colored_wrap(ctx, buf, color); + nk_labelfv_colored_wrap(ctx, color, fmt, args); va_end(args); } - NK_API void nk_labelf(struct nk_context *ctx, nk_flags flags, const char *fmt, ...) { - char buf[256]; va_list args; va_start(args, fmt); - nk_strfmt(buf, NK_LEN(buf), fmt, args); - nk_label(ctx, buf, flags); + nk_labelfv(ctx, flags, fmt, args); va_end(args); } - NK_API void nk_labelf_wrap(struct nk_context *ctx, const char *fmt,...) { - char buf[256]; va_list args; va_start(args, fmt); + nk_labelfv_wrap(ctx, fmt, args); + va_end(args); +} +NK_API void +nk_labelfv_colored(struct nk_context *ctx, nk_flags flags, + struct nk_color color, const char *fmt, va_list args) +{ + char buf[256]; + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label_colored(ctx, buf, flags, color); +} + +NK_API void +nk_labelfv_colored_wrap(struct nk_context *ctx, struct nk_color color, + const char *fmt, va_list args) +{ + char buf[256]; + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label_colored_wrap(ctx, buf, color); +} + +NK_API void +nk_labelfv(struct nk_context *ctx, nk_flags flags, const char *fmt, va_list args) +{ + char buf[256]; + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_label(ctx, buf, flags); +} + +NK_API void +nk_labelfv_wrap(struct nk_context *ctx, const char *fmt, va_list args) +{ + char buf[256]; nk_strfmt(buf, NK_LEN(buf), fmt, args); nk_label_wrap(ctx, buf); - va_end(args); } NK_API void nk_value_bool(struct nk_context *ctx, const char *prefix, int value) -{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, ((value) ? "true": "false"));} - +{ + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, ((value) ? "true": "false")); +} NK_API void nk_value_int(struct nk_context *ctx, const char *prefix, int value) -{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %d", prefix, value);} - +{ + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %d", prefix, value); +} NK_API void nk_value_uint(struct nk_context *ctx, const char *prefix, unsigned int value) -{nk_labelf(ctx, NK_TEXT_LEFT, "%s: %u", prefix, value);} - +{ + nk_labelf(ctx, NK_TEXT_LEFT, "%s: %u", prefix, value); +} NK_API void nk_value_float(struct nk_context *ctx, const char *prefix, float value) { double double_value = (double)value; nk_labelf(ctx, NK_TEXT_LEFT, "%s: %.3f", prefix, double_value); } - NK_API void nk_value_color_byte(struct nk_context *ctx, const char *p, struct nk_color c) -{nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%d, %d, %d, %d)", p, c.r, c.g, c.b, c.a);} - +{ + nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%d, %d, %d, %d)", p, c.r, c.g, c.b, c.a); +} NK_API void nk_value_color_float(struct nk_context *ctx, const char *p, struct nk_color color) { @@ -20536,7 +19267,6 @@ nk_value_color_float(struct nk_context *ctx, const char *p, struct nk_color colo nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%.2f, %.2f, %.2f, %.2f)", p, c[0], c[1], c[2], c[3]); } - NK_API void nk_value_color_hex(struct nk_context *ctx, const char *prefix, struct nk_color color) { @@ -20545,7 +19275,6 @@ nk_value_color_hex(struct nk_context *ctx, const char *prefix, struct nk_color c nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, hex); } #endif - NK_API void nk_text(struct nk_context *ctx, const char *str, int len, nk_flags alignment) { @@ -20553,7 +19282,6 @@ nk_text(struct nk_context *ctx, const char *str, int len, nk_flags alignment) if (!ctx) return; nk_text_colored(ctx, str, len, alignment, ctx->style.text.color); } - NK_API void nk_text_wrap(struct nk_context *ctx, const char *str, int len) { @@ -20561,24 +19289,138 @@ nk_text_wrap(struct nk_context *ctx, const char *str, int len) if (!ctx) return; nk_text_wrap_colored(ctx, str, len, ctx->style.text.color); } - NK_API void nk_label(struct nk_context *ctx, const char *str, nk_flags alignment) -{nk_text(ctx, str, nk_strlen(str), alignment);} - +{ + nk_text(ctx, str, nk_strlen(str), alignment); +} NK_API void nk_label_colored(struct nk_context *ctx, const char *str, nk_flags align, struct nk_color color) -{nk_text_colored(ctx, str, nk_strlen(str), align, color);} - +{ + nk_text_colored(ctx, str, nk_strlen(str), align, color); +} NK_API void nk_label_wrap(struct nk_context *ctx, const char *str) -{nk_text_wrap(ctx, str, nk_strlen(str));} - +{ + nk_text_wrap(ctx, str, nk_strlen(str)); +} NK_API void nk_label_colored_wrap(struct nk_context *ctx, const char *str, struct nk_color color) -{nk_text_wrap_colored(ctx, str, nk_strlen(str), color);} +{ + nk_text_wrap_colored(ctx, str, nk_strlen(str), color); +} + + + + +/* =============================================================== + * + * IMAGE + * + * ===============================================================*/ +NK_API nk_handle +nk_handle_ptr(void *ptr) +{ + nk_handle handle = {0}; + handle.ptr = ptr; + return handle; +} +NK_API nk_handle +nk_handle_id(int id) +{ + nk_handle handle; + nk_zero_struct(handle); + handle.id = id; + return handle; +} +NK_API struct nk_image +nk_subimage_ptr(void *ptr, unsigned short w, unsigned short h, struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.ptr = ptr; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} +NK_API struct nk_image +nk_subimage_id(int id, unsigned short w, unsigned short h, struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.id = id; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} +NK_API struct nk_image +nk_subimage_handle(nk_handle handle, unsigned short w, unsigned short h, + struct nk_rect r) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle = handle; + s.w = w; s.h = h; + s.region[0] = (unsigned short)r.x; + s.region[1] = (unsigned short)r.y; + s.region[2] = (unsigned short)r.w; + s.region[3] = (unsigned short)r.h; + return s; +} +NK_API struct nk_image +nk_image_handle(nk_handle handle) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle = handle; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} +NK_API struct nk_image +nk_image_ptr(void *ptr) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + NK_ASSERT(ptr); + s.handle.ptr = ptr; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} +NK_API struct nk_image +nk_image_id(int id) +{ + struct nk_image s; + nk_zero(&s, sizeof(s)); + s.handle.id = id; + s.w = 0; s.h = 0; + s.region[0] = 0; + s.region[1] = 0; + s.region[2] = 0; + s.region[3] = 0; + return s; +} +NK_API int +nk_image_is_subimage(const struct nk_image* img) +{ + NK_ASSERT(img); + return !(img->w == 0 && img->h == 0); +} NK_API void nk_image(struct nk_context *ctx, struct nk_image img) { @@ -20594,12 +19436,408 @@ nk_image(struct nk_context *ctx, struct nk_image img) if (!nk_widget(&bounds, ctx)) return; nk_draw_image(&win->buffer, bounds, &img, nk_white); } +NK_API void +nk_image_color(struct nk_context *ctx, struct nk_image img, struct nk_color col) +{ + struct nk_window *win; + struct nk_rect bounds; -/*---------------------------------------------------------------- + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout) return; + + win = ctx->current; + if (!nk_widget(&bounds, ctx)) return; + nk_draw_image(&win->buffer, bounds, &img, col); +} + + + + + +/* ============================================================== * * BUTTON * - * --------------------------------------------------------------*/ + * ===============================================================*/ +NK_LIB void +nk_draw_symbol(struct nk_command_buffer *out, enum nk_symbol_type type, + struct nk_rect content, struct nk_color background, struct nk_color foreground, + float border_width, const struct nk_user_font *font) +{ + switch (type) { + case NK_SYMBOL_X: + case NK_SYMBOL_UNDERSCORE: + case NK_SYMBOL_PLUS: + case NK_SYMBOL_MINUS: { + /* single character text symbol */ + const char *X = (type == NK_SYMBOL_X) ? "x": + (type == NK_SYMBOL_UNDERSCORE) ? "_": + (type == NK_SYMBOL_PLUS) ? "+": "-"; + struct nk_text text; + text.padding = nk_vec2(0,0); + text.background = background; + text.text = foreground; + nk_widget_text(out, content, X, 1, &text, NK_TEXT_CENTERED, font); + } break; + case NK_SYMBOL_CIRCLE_SOLID: + case NK_SYMBOL_CIRCLE_OUTLINE: + case NK_SYMBOL_RECT_SOLID: + case NK_SYMBOL_RECT_OUTLINE: { + /* simple empty/filled shapes */ + if (type == NK_SYMBOL_RECT_SOLID || type == NK_SYMBOL_RECT_OUTLINE) { + nk_fill_rect(out, content, 0, foreground); + if (type == NK_SYMBOL_RECT_OUTLINE) + nk_fill_rect(out, nk_shrink_rect(content, border_width), 0, background); + } else { + nk_fill_circle(out, content, foreground); + if (type == NK_SYMBOL_CIRCLE_OUTLINE) + nk_fill_circle(out, nk_shrink_rect(content, 1), background); + } + } break; + case NK_SYMBOL_TRIANGLE_UP: + case NK_SYMBOL_TRIANGLE_DOWN: + case NK_SYMBOL_TRIANGLE_LEFT: + case NK_SYMBOL_TRIANGLE_RIGHT: { + enum nk_heading heading; + struct nk_vec2 points[3]; + heading = (type == NK_SYMBOL_TRIANGLE_RIGHT) ? NK_RIGHT : + (type == NK_SYMBOL_TRIANGLE_LEFT) ? NK_LEFT: + (type == NK_SYMBOL_TRIANGLE_UP) ? NK_UP: NK_DOWN; + nk_triangle_from_direction(points, content, 0, 0, heading); + nk_fill_triangle(out, points[0].x, points[0].y, points[1].x, points[1].y, + points[2].x, points[2].y, foreground); + } break; + default: + case NK_SYMBOL_NONE: + case NK_SYMBOL_MAX: break; + } +} +NK_LIB int +nk_button_behavior(nk_flags *state, struct nk_rect r, + const struct nk_input *i, enum nk_button_behavior behavior) +{ + int ret = 0; + nk_widget_state_reset(state); + if (!i) return 0; + if (nk_input_is_mouse_hovering_rect(i, r)) { + *state = NK_WIDGET_STATE_HOVERED; + if (nk_input_is_mouse_down(i, NK_BUTTON_LEFT)) + *state = NK_WIDGET_STATE_ACTIVE; + if (nk_input_has_mouse_click_in_rect(i, NK_BUTTON_LEFT, r)) { + ret = (behavior != NK_BUTTON_DEFAULT) ? + nk_input_is_mouse_down(i, NK_BUTTON_LEFT): +#ifdef NK_BUTTON_TRIGGER_ON_RELEASE + nk_input_is_mouse_released(i, NK_BUTTON_LEFT); +#else + nk_input_is_mouse_pressed(i, NK_BUTTON_LEFT); +#endif + } + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(i, r)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(i, r)) + *state |= NK_WIDGET_STATE_LEFT; + return ret; +} +NK_LIB const struct nk_style_item* +nk_draw_button(struct nk_command_buffer *out, + const struct nk_rect *bounds, nk_flags state, + const struct nk_style_button *style) +{ + const struct nk_style_item *background; + if (state & NK_WIDGET_STATE_HOVER) + background = &style->hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + background = &style->active; + else background = &style->normal; + + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + } else { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); + } + return background; +} +NK_LIB int +nk_do_button(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, + const struct nk_style_button *style, const struct nk_input *in, + enum nk_button_behavior behavior, struct nk_rect *content) +{ + struct nk_rect bounds; + NK_ASSERT(style); + NK_ASSERT(state); + NK_ASSERT(out); + if (!out || !style) + return nk_false; + + /* calculate button content space */ + content->x = r.x + style->padding.x + style->border + style->rounding; + content->y = r.y + style->padding.y + style->border + style->rounding; + content->w = r.w - (2 * style->padding.x + style->border + style->rounding*2); + content->h = r.h - (2 * style->padding.y + style->border + style->rounding*2); + + /* execute button behavior */ + bounds.x = r.x - style->touch_padding.x; + bounds.y = r.y - style->touch_padding.y; + bounds.w = r.w + 2 * style->touch_padding.x; + bounds.h = r.h + 2 * style->touch_padding.y; + return nk_button_behavior(state, bounds, in, behavior); +} +NK_LIB void +nk_draw_button_text(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, + const struct nk_style_button *style, const char *txt, int len, + nk_flags text_alignment, const struct nk_user_font *font) +{ + struct nk_text text; + const struct nk_style_item *background; + background = nk_draw_button(out, bounds, state, style); + + /* select correct colors/images */ + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + if (state & NK_WIDGET_STATE_HOVER) + text.text = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + text.text = style->text_active; + else text.text = style->text_normal; + + text.padding = nk_vec2(0,0); + nk_widget_text(out, *content, txt, len, &text, text_alignment, font); +} +NK_LIB int +nk_do_button_text(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + const char *string, int len, nk_flags align, enum nk_button_behavior behavior, + const struct nk_style_button *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect content; + int ret = nk_false; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(string); + NK_ASSERT(font); + if (!out || !style || !font || !string) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_text(out, &bounds, &content, *state, style, string, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} +NK_LIB void +nk_draw_button_symbol(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, + nk_flags state, const struct nk_style_button *style, + enum nk_symbol_type type, const struct nk_user_font *font) +{ + struct nk_color sym, bg; + const struct nk_style_item *background; + + /* select correct colors/images */ + background = nk_draw_button(out, bounds, state, style); + if (background->type == NK_STYLE_ITEM_COLOR) + bg = background->data.color; + else bg = style->text_background; + + if (state & NK_WIDGET_STATE_HOVER) + sym = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + sym = style->text_active; + else sym = style->text_normal; + nk_draw_symbol(out, type, *content, bg, sym, 1, font); +} +NK_LIB int +nk_do_button_symbol(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + enum nk_symbol_type symbol, enum nk_button_behavior behavior, + const struct nk_style_button *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int ret; + struct nk_rect content; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(font); + NK_ASSERT(out); + if (!out || !style || !font || !state) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_symbol(out, &bounds, &content, *state, style, symbol, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} +NK_LIB void +nk_draw_button_image(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *content, + nk_flags state, const struct nk_style_button *style, const struct nk_image *img) +{ + nk_draw_button(out, bounds, state, style); + nk_draw_image(out, *content, img, nk_white); +} +NK_LIB int +nk_do_button_image(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + struct nk_image img, enum nk_button_behavior b, + const struct nk_style_button *style, const struct nk_input *in) +{ + int ret; + struct nk_rect content; + + NK_ASSERT(state); + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style || !state) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, b, &content); + content.x += style->image_padding.x; + content.y += style->image_padding.y; + content.w -= 2 * style->image_padding.x; + content.h -= 2 * style->image_padding.y; + + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_image(out, &bounds, &content, *state, style, &img); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} +NK_LIB void +nk_draw_button_text_symbol(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *label, + const struct nk_rect *symbol, nk_flags state, const struct nk_style_button *style, + const char *str, int len, enum nk_symbol_type type, + const struct nk_user_font *font) +{ + struct nk_color sym; + struct nk_text text; + const struct nk_style_item *background; + + /* select correct background colors/images */ + background = nk_draw_button(out, bounds, state, style); + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + + /* select correct text colors */ + if (state & NK_WIDGET_STATE_HOVER) { + sym = style->text_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVED) { + sym = style->text_active; + text.text = style->text_active; + } else { + sym = style->text_normal; + text.text = style->text_normal; + } + + text.padding = nk_vec2(0,0); + nk_draw_symbol(out, type, *symbol, style->text_background, sym, 0, font); + nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); +} +NK_LIB int +nk_do_button_text_symbol(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + enum nk_symbol_type symbol, const char *str, int len, nk_flags align, + enum nk_button_behavior behavior, const struct nk_style_button *style, + const struct nk_user_font *font, const struct nk_input *in) +{ + int ret; + struct nk_rect tri = {0,0,0,0}; + struct nk_rect content; + + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(font); + if (!out || !style || !font) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + tri.y = content.y + (content.h/2) - font->height/2; + tri.w = font->height; tri.h = font->height; + if (align & NK_TEXT_ALIGN_LEFT) { + tri.x = (content.x + content.w) - (2 * style->padding.x + tri.w); + tri.x = NK_MAX(tri.x, 0); + } else tri.x = content.x + 2 * style->padding.x; + + /* draw button */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_text_symbol(out, &bounds, &content, &tri, + *state, style, str, len, symbol, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} +NK_LIB void +nk_draw_button_text_image(struct nk_command_buffer *out, + const struct nk_rect *bounds, const struct nk_rect *label, + const struct nk_rect *image, nk_flags state, const struct nk_style_button *style, + const char *str, int len, const struct nk_user_font *font, + const struct nk_image *img) +{ + struct nk_text text; + const struct nk_style_item *background; + background = nk_draw_button(out, bounds, state, style); + + /* select correct colors */ + if (background->type == NK_STYLE_ITEM_COLOR) + text.background = background->data.color; + else text.background = style->text_background; + if (state & NK_WIDGET_STATE_HOVER) + text.text = style->text_hover; + else if (state & NK_WIDGET_STATE_ACTIVED) + text.text = style->text_active; + else text.text = style->text_normal; + + text.padding = nk_vec2(0,0); + nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); + nk_draw_image(out, *image, img, nk_white); +} +NK_LIB int +nk_do_button_text_image(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + struct nk_image img, const char* str, int len, nk_flags align, + enum nk_button_behavior behavior, const struct nk_style_button *style, + const struct nk_user_font *font, const struct nk_input *in) +{ + int ret; + struct nk_rect icon; + struct nk_rect content; + + NK_ASSERT(style); + NK_ASSERT(state); + NK_ASSERT(font); + NK_ASSERT(out); + if (!out || !font || !style || !str) + return nk_false; + + ret = nk_do_button(state, out, bounds, style, in, behavior, &content); + icon.y = bounds.y + style->padding.y; + icon.w = icon.h = bounds.h - 2 * style->padding.y; + if (align & NK_TEXT_ALIGN_LEFT) { + icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); + icon.x = NK_MAX(icon.x, 0); + } else icon.x = bounds.x + 2 * style->padding.x; + + icon.x += style->image_padding.x; + icon.y += style->image_padding.y; + icon.w -= 2 * style->image_padding.x; + icon.h -= 2 * style->image_padding.y; + + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_button_text_image(out, &bounds, &content, &icon, *state, style, str, len, font, &img); + if (style->draw_end) style->draw_end(out, style->userdata); + return ret; +} NK_API void nk_button_set_behavior(struct nk_context *ctx, enum nk_button_behavior behavior) { @@ -20607,7 +19845,6 @@ nk_button_set_behavior(struct nk_context *ctx, enum nk_button_behavior behavior) if (!ctx) return; ctx->button_behavior = behavior; } - NK_API int nk_button_push_behavior(struct nk_context *ctx, enum nk_button_behavior behavior) { @@ -20628,7 +19865,6 @@ nk_button_push_behavior(struct nk_context *ctx, enum nk_button_behavior behavior ctx->button_behavior = behavior; return 1; } - NK_API int nk_button_pop_behavior(struct nk_context *ctx) { @@ -20647,7 +19883,6 @@ nk_button_pop_behavior(struct nk_context *ctx) *element->address = element->old_value; return 1; } - NK_API int nk_button_text_styled(struct nk_context *ctx, const struct nk_style_button *style, const char *title, int len) @@ -20675,7 +19910,6 @@ nk_button_text_styled(struct nk_context *ctx, title, len, style->text_alignment, ctx->button_behavior, style, in, ctx->style.font); } - NK_API int nk_button_text(struct nk_context *ctx, const char *title, int len) { @@ -20683,14 +19917,15 @@ nk_button_text(struct nk_context *ctx, const char *title, int len) if (!ctx) return 0; return nk_button_text_styled(ctx, &ctx->style.button, title, len); } - NK_API int nk_button_label_styled(struct nk_context *ctx, const struct nk_style_button *style, const char *title) -{return nk_button_text_styled(ctx, style, title, nk_strlen(title));} - +{ + return nk_button_text_styled(ctx, style, title, nk_strlen(title)); +} NK_API int nk_button_label(struct nk_context *ctx, const char *title) -{return nk_button_text(ctx, title, nk_strlen(title));} - +{ + return nk_button_text(ctx, title, nk_strlen(title)); +} NK_API int nk_button_color(struct nk_context *ctx, struct nk_color color) { @@ -20726,7 +19961,6 @@ nk_button_color(struct nk_context *ctx, struct nk_color color) nk_draw_button(&win->buffer, &bounds, ctx->last_widget_state, &button); return ret; } - NK_API int nk_button_symbol_styled(struct nk_context *ctx, const struct nk_style_button *style, enum nk_symbol_type symbol) @@ -20752,7 +19986,6 @@ nk_button_symbol_styled(struct nk_context *ctx, return nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, bounds, symbol, ctx->button_behavior, style, in, ctx->style.font); } - NK_API int nk_button_symbol(struct nk_context *ctx, enum nk_symbol_type symbol) { @@ -20760,7 +19993,6 @@ nk_button_symbol(struct nk_context *ctx, enum nk_symbol_type symbol) if (!ctx) return 0; return nk_button_symbol_styled(ctx, &ctx->style.button, symbol); } - NK_API int nk_button_image_styled(struct nk_context *ctx, const struct nk_style_button *style, struct nk_image img) @@ -20787,7 +20019,6 @@ nk_button_image_styled(struct nk_context *ctx, const struct nk_style_button *sty return nk_do_button_image(&ctx->last_widget_state, &win->buffer, bounds, img, ctx->button_behavior, style, in); } - NK_API int nk_button_image(struct nk_context *ctx, struct nk_image img) { @@ -20795,7 +20026,6 @@ nk_button_image(struct nk_context *ctx, struct nk_image img) if (!ctx) return 0; return nk_button_image_styled(ctx, &ctx->style.button, img); } - NK_API int nk_button_symbol_text_styled(struct nk_context *ctx, const struct nk_style_button *style, enum nk_symbol_type symbol, @@ -20824,7 +20054,6 @@ nk_button_symbol_text_styled(struct nk_context *ctx, symbol, text, len, align, ctx->button_behavior, style, ctx->style.font, in); } - NK_API int nk_button_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, const char* text, int len, nk_flags align) @@ -20833,16 +20062,17 @@ nk_button_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, if (!ctx) return 0; return nk_button_symbol_text_styled(ctx, &ctx->style.button, symbol, text, len, align); } - NK_API int nk_button_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, const char *label, nk_flags align) -{return nk_button_symbol_text(ctx, symbol, label, nk_strlen(label), align);} - +{ + return nk_button_symbol_text(ctx, symbol, label, nk_strlen(label), align); +} NK_API int nk_button_symbol_label_styled(struct nk_context *ctx, const struct nk_style_button *style, enum nk_symbol_type symbol, const char *title, nk_flags align) -{return nk_button_symbol_text_styled(ctx, style, symbol, title, nk_strlen(title), align);} - +{ + return nk_button_symbol_text_styled(ctx, style, symbol, title, nk_strlen(title), align); +} NK_API int nk_button_image_text_styled(struct nk_context *ctx, const struct nk_style_button *style, struct nk_image img, const char *text, @@ -20871,109 +20101,194 @@ nk_button_image_text_styled(struct nk_context *ctx, bounds, img, text, len, align, ctx->button_behavior, style, ctx->style.font, in); } - NK_API int nk_button_image_text(struct nk_context *ctx, struct nk_image img, const char *text, int len, nk_flags align) -{return nk_button_image_text_styled(ctx, &ctx->style.button,img, text, len, align);} - - +{ + return nk_button_image_text_styled(ctx, &ctx->style.button,img, text, len, align); +} NK_API int nk_button_image_label(struct nk_context *ctx, struct nk_image img, const char *label, nk_flags align) -{return nk_button_image_text(ctx, img, label, nk_strlen(label), align);} - +{ + return nk_button_image_text(ctx, img, label, nk_strlen(label), align); +} NK_API int nk_button_image_label_styled(struct nk_context *ctx, const struct nk_style_button *style, struct nk_image img, const char *label, nk_flags text_alignment) -{return nk_button_image_text_styled(ctx, style, img, label, nk_strlen(label), text_alignment);} - -/*---------------------------------------------------------------- - * - * SELECTABLE - * - * --------------------------------------------------------------*/ -NK_API int -nk_selectable_text(struct nk_context *ctx, const char *str, int len, - nk_flags align, int *value) { - struct nk_window *win; - struct nk_panel *layout; - const struct nk_input *in; - const struct nk_style *style; - - enum nk_widget_layout_states state; - struct nk_rect bounds; - - NK_ASSERT(ctx); - NK_ASSERT(value); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout || !value) - return 0; - - win = ctx->current; - layout = win->layout; - style = &ctx->style; - - state = nk_widget(&bounds, ctx); - if (!state) return 0; - in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; - return nk_do_selectable(&ctx->last_widget_state, &win->buffer, bounds, - str, len, align, value, &style->selectable, in, style->font); + return nk_button_image_text_styled(ctx, style, img, label, nk_strlen(label), text_alignment); } -NK_API int -nk_selectable_image_text(struct nk_context *ctx, struct nk_image img, - const char *str, int len, nk_flags align, int *value) + + + + +/* =============================================================== + * + * TOGGLE + * + * ===============================================================*/ +NK_LIB int +nk_toggle_behavior(const struct nk_input *in, struct nk_rect select, + nk_flags *state, int active) { - struct nk_window *win; - struct nk_panel *layout; - const struct nk_input *in; - const struct nk_style *style; + nk_widget_state_reset(state); + if (nk_button_behavior(state, select, in, NK_BUTTON_DEFAULT)) { + *state = NK_WIDGET_STATE_ACTIVE; + active = !active; + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, select)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, select)) + *state |= NK_WIDGET_STATE_LEFT; + return active; +} +NK_LIB void +nk_draw_checkbox(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_toggle *style, int active, + const struct nk_rect *label, const struct nk_rect *selector, + const struct nk_rect *cursors, const char *string, int len, + const struct nk_user_font *font) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + struct nk_text text; - enum nk_widget_layout_states state; + /* select correct colors/images */ + if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_active; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + text.text = style->text_normal; + } + + /* draw background and cursor */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *selector, 0, style->border_color); + nk_fill_rect(out, nk_shrink_rect(*selector, style->border), 0, background->data.color); + } else nk_draw_image(out, *selector, &background->data.image, nk_white); + if (active) { + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *cursors, &cursor->data.image, nk_white); + else nk_fill_rect(out, *cursors, 0, cursor->data.color); + } + + text.padding.x = 0; + text.padding.y = 0; + text.background = style->text_background; + nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); +} +NK_LIB void +nk_draw_option(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_toggle *style, int active, + const struct nk_rect *label, const struct nk_rect *selector, + const struct nk_rect *cursors, const char *string, int len, + const struct nk_user_font *font) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + struct nk_text text; + + /* select correct colors/images */ + if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_hover; + } else if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->hover; + cursor = &style->cursor_hover; + text.text = style->text_active; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + text.text = style->text_normal; + } + + /* draw background and cursor */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_circle(out, *selector, style->border_color); + nk_fill_circle(out, nk_shrink_rect(*selector, style->border), background->data.color); + } else nk_draw_image(out, *selector, &background->data.image, nk_white); + if (active) { + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *cursors, &cursor->data.image, nk_white); + else nk_fill_circle(out, *cursors, cursor->data.color); + } + + text.padding.x = 0; + text.padding.y = 0; + text.background = style->text_background; + nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); +} +NK_LIB int +nk_do_toggle(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect r, + int *active, const char *str, int len, enum nk_toggle_type type, + const struct nk_style_toggle *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int was_active; struct nk_rect bounds; + struct nk_rect select; + struct nk_rect cursor; + struct nk_rect label; - NK_ASSERT(ctx); - NK_ASSERT(value); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout || !value) + NK_ASSERT(style); + NK_ASSERT(out); + NK_ASSERT(font); + if (!out || !style || !font || !active) return 0; - win = ctx->current; - layout = win->layout; - style = &ctx->style; + r.w = NK_MAX(r.w, font->height + 2 * style->padding.x); + r.h = NK_MAX(r.h, font->height + 2 * style->padding.y); - state = nk_widget(&bounds, ctx); - if (!state) return 0; - in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; - return nk_do_selectable_image(&ctx->last_widget_state, &win->buffer, bounds, - str, len, align, value, &img, &style->selectable, in, style->font); + /* add additional touch padding for touch screen devices */ + bounds.x = r.x - style->touch_padding.x; + bounds.y = r.y - style->touch_padding.y; + bounds.w = r.w + 2 * style->touch_padding.x; + bounds.h = r.h + 2 * style->touch_padding.y; + + /* calculate the selector space */ + select.w = font->height; + select.h = select.w; + select.y = r.y + r.h/2.0f - select.h/2.0f; + select.x = r.x; + + /* calculate the bounds of the cursor inside the selector */ + cursor.x = select.x + style->padding.x + style->border; + cursor.y = select.y + style->padding.y + style->border; + cursor.w = select.w - (2 * style->padding.x + 2 * style->border); + cursor.h = select.h - (2 * style->padding.y + 2 * style->border); + + /* label behind the selector */ + label.x = select.x + select.w + style->spacing; + label.y = select.y; + label.w = NK_MAX(r.x + r.w, label.x) - label.x; + label.h = select.w; + + /* update selector */ + was_active = *active; + *active = nk_toggle_behavior(in, bounds, state, *active); + + /* draw selector */ + if (style->draw_begin) + style->draw_begin(out, style->userdata); + if (type == NK_TOGGLE_CHECK) { + nk_draw_checkbox(out, *state, style, *active, &label, &select, &cursor, str, len, font); + } else { + nk_draw_option(out, *state, style, *active, &label, &select, &cursor, str, len, font); + } + if (style->draw_end) + style->draw_end(out, style->userdata); + return (was_active != *active); } - -NK_API int nk_select_text(struct nk_context *ctx, const char *str, int len, - nk_flags align, int value) -{nk_selectable_text(ctx, str, len, align, &value);return value;} - -NK_API int nk_selectable_label(struct nk_context *ctx, const char *str, nk_flags align, int *value) -{return nk_selectable_text(ctx, str, nk_strlen(str), align, value);} - -NK_API int nk_selectable_image_label(struct nk_context *ctx,struct nk_image img, - const char *str, nk_flags align, int *value) -{return nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, value);} - -NK_API int nk_select_label(struct nk_context *ctx, const char *str, nk_flags align, int value) -{nk_selectable_text(ctx, str, nk_strlen(str), align, &value);return value;} - -NK_API int nk_select_image_label(struct nk_context *ctx, struct nk_image img, - const char *str, nk_flags align, int value) -{nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, &value);return value;} - -NK_API int nk_select_image_text(struct nk_context *ctx, struct nk_image img, - const char *str, int len, nk_flags align, int value) -{nk_selectable_image_text(ctx, img, str, len, align, &value);return value;} - /*---------------------------------------------------------------- * * CHECKBOX @@ -21007,7 +20322,6 @@ nk_check_text(struct nk_context *ctx, const char *text, int len, int active) text, len, NK_TOGGLE_CHECK, &style->checkbox, in, style->font); return active; } - NK_API unsigned int nk_check_flags_text(struct nk_context *ctx, const char *text, int len, unsigned int flags, unsigned int value) @@ -21022,7 +20336,6 @@ nk_check_flags_text(struct nk_context *ctx, const char *text, int len, else flags &= ~value; return flags; } - NK_API int nk_checkbox_text(struct nk_context *ctx, const char *text, int len, int *active) { @@ -21035,7 +20348,6 @@ nk_checkbox_text(struct nk_context *ctx, const char *text, int len, int *active) *active = nk_check_text(ctx, text, len, *active); return old_val != *active; } - NK_API int nk_checkbox_flags_text(struct nk_context *ctx, const char *text, int len, unsigned int *flags, unsigned int value) @@ -21054,21 +20366,24 @@ nk_checkbox_flags_text(struct nk_context *ctx, const char *text, int len, } return 0; } - NK_API int nk_check_label(struct nk_context *ctx, const char *label, int active) -{return nk_check_text(ctx, label, nk_strlen(label), active);} - +{ + return nk_check_text(ctx, label, nk_strlen(label), active); +} NK_API unsigned int nk_check_flags_label(struct nk_context *ctx, const char *label, unsigned int flags, unsigned int value) -{return nk_check_flags_text(ctx, label, nk_strlen(label), flags, value);} - +{ + return nk_check_flags_text(ctx, label, nk_strlen(label), flags, value); +} NK_API int nk_checkbox_label(struct nk_context *ctx, const char *label, int *active) -{return nk_checkbox_text(ctx, label, nk_strlen(label), active);} - +{ + return nk_checkbox_text(ctx, label, nk_strlen(label), active); +} NK_API int nk_checkbox_flags_label(struct nk_context *ctx, const char *label, unsigned int *flags, unsigned int value) -{return nk_checkbox_flags_text(ctx, label, nk_strlen(label), flags, value);} - +{ + return nk_checkbox_flags_text(ctx, label, nk_strlen(label), flags, value); +} /*---------------------------------------------------------------- * * OPTION @@ -21096,13 +20411,12 @@ nk_option_text(struct nk_context *ctx, const char *text, int len, int is_active) layout = win->layout; state = nk_widget(&bounds, ctx); - if (!state) return state; + if (!state) return (int)state; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &is_active, text, len, NK_TOGGLE_OPTION, &style->option, in, style->font); return is_active; } - NK_API int nk_radio_text(struct nk_context *ctx, const char *text, int len, int *active) { @@ -21115,20 +20429,539 @@ nk_radio_text(struct nk_context *ctx, const char *text, int len, int *active) *active = nk_option_text(ctx, text, len, old_value); return old_value != *active; } - NK_API int nk_option_label(struct nk_context *ctx, const char *label, int active) -{return nk_option_text(ctx, label, nk_strlen(label), active);} - +{ + return nk_option_text(ctx, label, nk_strlen(label), active); +} NK_API int nk_radio_label(struct nk_context *ctx, const char *label, int *active) -{return nk_radio_text(ctx, label, nk_strlen(label), active);} +{ + return nk_radio_text(ctx, label, nk_strlen(label), active); +} -/*---------------------------------------------------------------- + + + + +/* =============================================================== * - * SLIDER + * SELECTABLE * - * --------------------------------------------------------------*/ + * ===============================================================*/ +NK_LIB void +nk_draw_selectable(struct nk_command_buffer *out, + nk_flags state, const struct nk_style_selectable *style, int active, + const struct nk_rect *bounds, + const struct nk_rect *icon, const struct nk_image *img, enum nk_symbol_type sym, + const char *string, int len, nk_flags align, const struct nk_user_font *font) +{ + const struct nk_style_item *background; + struct nk_text text; + text.padding = style->padding; + + /* select correct colors/images */ + if (!active) { + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->pressed; + text.text = style->text_pressed; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text.text = style->text_hover; + } else { + background = &style->normal; + text.text = style->text_normal; + } + } else { + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->pressed_active; + text.text = style->text_pressed_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover_active; + text.text = style->text_hover_active; + } else { + background = &style->normal_active; + text.text = style->text_normal_active; + } + } + /* draw selectable background and text */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + text.background = nk_rgba(0,0,0,0); + } else { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + text.background = background->data.color; + } + if (icon) { + if (img) nk_draw_image(out, *icon, img, nk_white); + else nk_draw_symbol(out, sym, *icon, text.background, text.text, 1, font); + } + nk_widget_text(out, *bounds, string, len, &text, align, font); +} +NK_LIB int +nk_do_selectable(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, const char *str, int len, nk_flags align, int *value, + const struct nk_style_selectable *style, const struct nk_input *in, + const struct nk_user_font *font) +{ + int old_value; + struct nk_rect touch; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(str); + NK_ASSERT(len); + NK_ASSERT(value); + NK_ASSERT(style); + NK_ASSERT(font); + + if (!state || !out || !str || !len || !value || !style || !font) return 0; + old_value = *value; + + /* remove padding */ + touch.x = bounds.x - style->touch_padding.x; + touch.y = bounds.y - style->touch_padding.y; + touch.w = bounds.w + style->touch_padding.x * 2; + touch.h = bounds.h + style->touch_padding.y * 2; + + /* update button */ + if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) + *value = !(*value); + + /* draw selectable */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_selectable(out, *state, style, *value, &bounds, 0,0,NK_SYMBOL_NONE, str, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return old_value != *value; +} +NK_LIB int +nk_do_selectable_image(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, const char *str, int len, nk_flags align, int *value, + const struct nk_image *img, const struct nk_style_selectable *style, + const struct nk_input *in, const struct nk_user_font *font) +{ + int old_value; + struct nk_rect touch; + struct nk_rect icon; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(str); + NK_ASSERT(len); + NK_ASSERT(value); + NK_ASSERT(style); + NK_ASSERT(font); + + if (!state || !out || !str || !len || !value || !style || !font) return 0; + old_value = *value; + + /* toggle behavior */ + touch.x = bounds.x - style->touch_padding.x; + touch.y = bounds.y - style->touch_padding.y; + touch.w = bounds.w + style->touch_padding.x * 2; + touch.h = bounds.h + style->touch_padding.y * 2; + if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) + *value = !(*value); + + icon.y = bounds.y + style->padding.y; + icon.w = icon.h = bounds.h - 2 * style->padding.y; + if (align & NK_TEXT_ALIGN_LEFT) { + icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); + icon.x = NK_MAX(icon.x, 0); + } else icon.x = bounds.x + 2 * style->padding.x; + + icon.x += style->image_padding.x; + icon.y += style->image_padding.y; + icon.w -= 2 * style->image_padding.x; + icon.h -= 2 * style->image_padding.y; + + /* draw selectable */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_selectable(out, *state, style, *value, &bounds, &icon, img, NK_SYMBOL_NONE, str, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return old_value != *value; +} +NK_LIB int +nk_do_selectable_symbol(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, const char *str, int len, nk_flags align, int *value, + enum nk_symbol_type sym, const struct nk_style_selectable *style, + const struct nk_input *in, const struct nk_user_font *font) +{ + int old_value; + struct nk_rect touch; + struct nk_rect icon; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(str); + NK_ASSERT(len); + NK_ASSERT(value); + NK_ASSERT(style); + NK_ASSERT(font); + + if (!state || !out || !str || !len || !value || !style || !font) return 0; + old_value = *value; + + /* toggle behavior */ + touch.x = bounds.x - style->touch_padding.x; + touch.y = bounds.y - style->touch_padding.y; + touch.w = bounds.w + style->touch_padding.x * 2; + touch.h = bounds.h + style->touch_padding.y * 2; + if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) + *value = !(*value); + + icon.y = bounds.y + style->padding.y; + icon.w = icon.h = bounds.h - 2 * style->padding.y; + if (align & NK_TEXT_ALIGN_LEFT) { + icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); + icon.x = NK_MAX(icon.x, 0); + } else icon.x = bounds.x + 2 * style->padding.x; + + icon.x += style->image_padding.x; + icon.y += style->image_padding.y; + icon.w -= 2 * style->image_padding.x; + icon.h -= 2 * style->image_padding.y; + + /* draw selectable */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_selectable(out, *state, style, *value, &bounds, &icon, 0, sym, str, len, align, font); + if (style->draw_end) style->draw_end(out, style->userdata); + return old_value != *value; +} + +NK_API int +nk_selectable_text(struct nk_context *ctx, const char *str, int len, + nk_flags align, int *value) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(value); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return 0; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_selectable(&ctx->last_widget_state, &win->buffer, bounds, + str, len, align, value, &style->selectable, in, style->font); +} +NK_API int +nk_selectable_image_text(struct nk_context *ctx, struct nk_image img, + const char *str, int len, nk_flags align, int *value) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(value); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return 0; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_selectable_image(&ctx->last_widget_state, &win->buffer, bounds, + str, len, align, value, &img, &style->selectable, in, style->font); +} +NK_API int +nk_selectable_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *str, int len, nk_flags align, int *value) +{ + struct nk_window *win; + struct nk_panel *layout; + const struct nk_input *in; + const struct nk_style *style; + + enum nk_widget_layout_states state; + struct nk_rect bounds; + + NK_ASSERT(ctx); + NK_ASSERT(value); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + if (!ctx || !ctx->current || !ctx->current->layout || !value) + return 0; + + win = ctx->current; + layout = win->layout; + style = &ctx->style; + + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_selectable_symbol(&ctx->last_widget_state, &win->buffer, bounds, + str, len, align, value, sym, &style->selectable, in, style->font); +} +NK_API int +nk_selectable_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *title, nk_flags align, int *value) +{ + return nk_selectable_symbol_text(ctx, sym, title, nk_strlen(title), align, value); +} +NK_API int nk_select_text(struct nk_context *ctx, const char *str, int len, + nk_flags align, int value) +{ + nk_selectable_text(ctx, str, len, align, &value);return value; +} +NK_API int nk_selectable_label(struct nk_context *ctx, const char *str, nk_flags align, int *value) +{ + return nk_selectable_text(ctx, str, nk_strlen(str), align, value); +} +NK_API int nk_selectable_image_label(struct nk_context *ctx,struct nk_image img, + const char *str, nk_flags align, int *value) +{ + return nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, value); +} +NK_API int nk_select_label(struct nk_context *ctx, const char *str, nk_flags align, int value) +{ + nk_selectable_text(ctx, str, nk_strlen(str), align, &value);return value; +} +NK_API int nk_select_image_label(struct nk_context *ctx, struct nk_image img, + const char *str, nk_flags align, int value) +{ + nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, &value);return value; +} +NK_API int nk_select_image_text(struct nk_context *ctx, struct nk_image img, + const char *str, int len, nk_flags align, int value) +{ + nk_selectable_image_text(ctx, img, str, len, align, &value);return value; +} +NK_API int +nk_select_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, + const char *title, int title_len, nk_flags align, int value) +{ + nk_selectable_symbol_text(ctx, sym, title, title_len, align, &value);return value; +} +NK_API int +nk_select_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, + const char *title, nk_flags align, int value) +{ + return nk_select_symbol_text(ctx, sym, title, nk_strlen(title), align, value); +} + + + + + +/* =============================================================== + * + * SLIDER + * + * ===============================================================*/ +NK_LIB float +nk_slider_behavior(nk_flags *state, struct nk_rect *logical_cursor, + struct nk_rect *visual_cursor, struct nk_input *in, + struct nk_rect bounds, float slider_min, float slider_max, float slider_value, + float slider_step, float slider_steps) +{ + int left_mouse_down; + int left_mouse_click_in_cursor; + + /* check if visual cursor is being dragged */ + nk_widget_state_reset(state); + left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_click_in_cursor = in && nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, *visual_cursor, nk_true); + + if (left_mouse_down && left_mouse_click_in_cursor) { + float ratio = 0; + const float d = in->mouse.pos.x - (visual_cursor->x+visual_cursor->w*0.5f); + const float pxstep = bounds.w / slider_steps; + + /* only update value if the next slider step is reached */ + *state = NK_WIDGET_STATE_ACTIVE; + if (NK_ABS(d) >= pxstep) { + const float steps = (float)((int)(NK_ABS(d) / pxstep)); + slider_value += (d > 0) ? (slider_step*steps) : -(slider_step*steps); + slider_value = NK_CLAMP(slider_min, slider_value, slider_max); + ratio = (slider_value - slider_min)/slider_step; + logical_cursor->x = bounds.x + (logical_cursor->w * ratio); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = logical_cursor->x; + } + } + + /* slider widget state */ + if (nk_input_is_mouse_hovering_rect(in, bounds)) + *state = NK_WIDGET_STATE_HOVERED; + if (*state & NK_WIDGET_STATE_HOVER && + !nk_input_is_mouse_prev_hovering_rect(in, bounds)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, bounds)) + *state |= NK_WIDGET_STATE_LEFT; + return slider_value; +} +NK_LIB void +nk_draw_slider(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_slider *style, const struct nk_rect *bounds, + const struct nk_rect *visual_cursor, float min, float value, float max) +{ + struct nk_rect fill; + struct nk_rect bar; + const struct nk_style_item *background; + + /* select correct slider images/colors */ + struct nk_color bar_color; + const struct nk_style_item *cursor; + + NK_UNUSED(min); + NK_UNUSED(max); + NK_UNUSED(value); + + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + bar_color = style->bar_active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + bar_color = style->bar_hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + bar_color = style->bar_normal; + cursor = &style->cursor_normal; + } + /* calculate slider background bar */ + bar.x = bounds->x; + bar.y = (visual_cursor->y + visual_cursor->h/2) - bounds->h/12; + bar.w = bounds->w; + bar.h = bounds->h/6; + + /* filled background bar style */ + fill.w = (visual_cursor->x + (visual_cursor->w/2.0f)) - bar.x; + fill.x = bar.x; + fill.y = bar.y; + fill.h = bar.h; + + /* draw background */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + } else { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); + } + + /* draw slider bar */ + nk_fill_rect(out, bar, style->rounding, bar_color); + nk_fill_rect(out, fill, style->rounding, style->bar_filled); + + /* draw cursor */ + if (cursor->type == NK_STYLE_ITEM_IMAGE) + nk_draw_image(out, *visual_cursor, &cursor->data.image, nk_white); + else nk_fill_circle(out, *visual_cursor, cursor->data.color); +} +NK_LIB float +nk_do_slider(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + float min, float val, float max, float step, + const struct nk_style_slider *style, struct nk_input *in, + const struct nk_user_font *font) +{ + float slider_range; + float slider_min; + float slider_max; + float slider_value; + float slider_steps; + float cursor_offset; + + struct nk_rect visual_cursor; + struct nk_rect logical_cursor; + + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style) + return 0; + + /* remove padding from slider bounds */ + bounds.x = bounds.x + style->padding.x; + bounds.y = bounds.y + style->padding.y; + bounds.h = NK_MAX(bounds.h, 2*style->padding.y); + bounds.w = NK_MAX(bounds.w, 2*style->padding.x + style->cursor_size.x); + bounds.w -= 2 * style->padding.x; + bounds.h -= 2 * style->padding.y; + + /* optional buttons */ + if (style->show_buttons) { + nk_flags ws; + struct nk_rect button; + button.y = bounds.y; + button.w = bounds.h; + button.h = bounds.h; + + /* decrement button */ + button.x = bounds.x; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, NK_BUTTON_DEFAULT, + &style->dec_button, in, font)) + val -= step; + + /* increment button */ + button.x = (bounds.x + bounds.w) - button.w; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, NK_BUTTON_DEFAULT, + &style->inc_button, in, font)) + val += step; + + bounds.x = bounds.x + button.w + style->spacing.x; + bounds.w = bounds.w - (2*button.w + 2*style->spacing.x); + } + + /* remove one cursor size to support visual cursor */ + bounds.x += style->cursor_size.x*0.5f; + bounds.w -= style->cursor_size.x; + + /* make sure the provided values are correct */ + slider_max = NK_MAX(min, max); + slider_min = NK_MIN(min, max); + slider_value = NK_CLAMP(slider_min, val, slider_max); + slider_range = slider_max - slider_min; + slider_steps = slider_range / step; + cursor_offset = (slider_value - slider_min) / step; + + /* calculate cursor + Basically you have two cursors. One for visual representation and interaction + and one for updating the actual cursor value. */ + logical_cursor.h = bounds.h; + logical_cursor.w = bounds.w / slider_steps; + logical_cursor.x = bounds.x + (logical_cursor.w * cursor_offset); + logical_cursor.y = bounds.y; + + visual_cursor.h = style->cursor_size.y; + visual_cursor.w = style->cursor_size.x; + visual_cursor.y = (bounds.y + bounds.h*0.5f) - visual_cursor.h*0.5f; + visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f; + + slider_value = nk_slider_behavior(state, &logical_cursor, &visual_cursor, + in, bounds, slider_min, slider_max, slider_value, step, slider_steps); + visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f; + + /* draw slider */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_slider(out, *state, style, &bounds, &visual_cursor, slider_min, slider_value, slider_max); + if (style->draw_end) style->draw_end(out, style->userdata); + return slider_value; +} NK_API int nk_slider_float(struct nk_context *ctx, float min_value, float *value, float max_value, float value_step) @@ -21156,20 +20989,18 @@ nk_slider_float(struct nk_context *ctx, float min_value, float *value, float max state = nk_widget(&bounds, ctx); if (!state) return ret; - in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + in = (/*state == NK_WIDGET_ROM || */ layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; old_value = *value; *value = nk_do_slider(&ctx->last_widget_state, &win->buffer, bounds, min_value, old_value, max_value, value_step, &style->slider, in, style->font); return (old_value > *value || old_value < *value); } - NK_API float nk_slide_float(struct nk_context *ctx, float min, float val, float max, float step) { nk_slider_float(ctx, min, &val, max, step); return val; } - NK_API int nk_slide_int(struct nk_context *ctx, int min, int val, int max, int step) { @@ -21177,7 +21008,6 @@ nk_slide_int(struct nk_context *ctx, int min, int val, int max, int step) nk_slider_float(ctx, (float)min, &value, (float)max, (float)step); return (int)value; } - NK_API int nk_slider_int(struct nk_context *ctx, int min, int *val, int max, int step) { @@ -21188,18 +21018,118 @@ nk_slider_int(struct nk_context *ctx, int min, int *val, int max, int step) return ret; } -/*---------------------------------------------------------------- + + + + +/* =============================================================== * - * PROGRESSBAR + * PROGRESS * - * --------------------------------------------------------------*/ + * ===============================================================*/ +NK_LIB nk_size +nk_progress_behavior(nk_flags *state, struct nk_input *in, + struct nk_rect r, struct nk_rect cursor, nk_size max, nk_size value, int modifiable) +{ + int left_mouse_down = 0; + int left_mouse_click_in_cursor = 0; + + nk_widget_state_reset(state); + if (!in || !modifiable) return value; + left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_click_in_cursor = in && nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, cursor, nk_true); + if (nk_input_is_mouse_hovering_rect(in, r)) + *state = NK_WIDGET_STATE_HOVERED; + + if (in && left_mouse_down && left_mouse_click_in_cursor) { + if (left_mouse_down && left_mouse_click_in_cursor) { + float ratio = NK_MAX(0, (float)(in->mouse.pos.x - cursor.x)) / (float)cursor.w; + value = (nk_size)NK_CLAMP(0, (float)max * ratio, (float)max); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = cursor.x + cursor.w/2.0f; + *state |= NK_WIDGET_STATE_ACTIVE; + } + } + /* set progressbar widget state */ + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, r)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, r)) + *state |= NK_WIDGET_STATE_LEFT; + return value; +} +NK_LIB void +nk_draw_progress(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_progress *style, const struct nk_rect *bounds, + const struct nk_rect *scursor, nk_size value, nk_size max) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + + NK_UNUSED(max); + NK_UNUSED(value); + + /* select correct colors/images to draw */ + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVER){ + background = &style->hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); + } else nk_draw_image(out, *bounds, &background->data.image, nk_white); + + /* draw cursor */ + if (cursor->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *scursor, style->rounding, cursor->data.color); + nk_stroke_rect(out, *scursor, style->rounding, style->border, style->border_color); + } else nk_draw_image(out, *scursor, &cursor->data.image, nk_white); +} +NK_LIB nk_size +nk_do_progress(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect bounds, + nk_size value, nk_size max, int modifiable, + const struct nk_style_progress *style, struct nk_input *in) +{ + float prog_scale; + nk_size prog_value; + struct nk_rect cursor; + + NK_ASSERT(style); + NK_ASSERT(out); + if (!out || !style) return 0; + + /* calculate progressbar cursor */ + cursor.w = NK_MAX(bounds.w, 2 * style->padding.x + 2 * style->border); + cursor.h = NK_MAX(bounds.h, 2 * style->padding.y + 2 * style->border); + cursor = nk_pad_rect(bounds, nk_vec2(style->padding.x + style->border, style->padding.y + style->border)); + prog_scale = (float)value / (float)max; + + /* update progressbar */ + prog_value = NK_MIN(value, max); + prog_value = nk_progress_behavior(state, in, bounds, cursor,max, prog_value, modifiable); + cursor.w = cursor.w * prog_scale; + + /* draw progressbar */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_progress(out, *state, style, &bounds, &cursor, value, max); + if (style->draw_end) style->draw_end(out, style->userdata); + return prog_value; +} NK_API int nk_progress(struct nk_context *ctx, nk_size *cur, nk_size max, int is_modifyable) { struct nk_window *win; struct nk_panel *layout; const struct nk_style *style; - const struct nk_input *in; + struct nk_input *in; struct nk_rect bounds; enum nk_widget_layout_states state; @@ -21224,15 +21154,1988 @@ nk_progress(struct nk_context *ctx, nk_size *cur, nk_size max, int is_modifyable *cur, max, is_modifyable, &style->progress, in); return (*cur != old_value); } +NK_API nk_size +nk_prog(struct nk_context *ctx, nk_size cur, nk_size max, int modifyable) +{ + nk_progress(ctx, &cur, max, modifyable); + return cur; +} -NK_API nk_size nk_prog(struct nk_context *ctx, nk_size cur, nk_size max, int modifyable) -{nk_progress(ctx, &cur, max, modifyable);return cur;} -/*---------------------------------------------------------------- + + + +/* =============================================================== + * + * SCROLLBAR + * + * ===============================================================*/ +NK_LIB float +nk_scrollbar_behavior(nk_flags *state, struct nk_input *in, + int has_scrolling, const struct nk_rect *scroll, + const struct nk_rect *cursor, const struct nk_rect *empty0, + const struct nk_rect *empty1, float scroll_offset, + float target, float scroll_step, enum nk_orientation o) +{ + nk_flags ws = 0; + int left_mouse_down; + int left_mouse_click_in_cursor; + float scroll_delta; + + nk_widget_state_reset(state); + if (!in) return scroll_offset; + + left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; + left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, + NK_BUTTON_LEFT, *cursor, nk_true); + if (nk_input_is_mouse_hovering_rect(in, *scroll)) + *state = NK_WIDGET_STATE_HOVERED; + + scroll_delta = (o == NK_VERTICAL) ? in->mouse.scroll_delta.y: in->mouse.scroll_delta.x; + if (left_mouse_down && left_mouse_click_in_cursor) { + /* update cursor by mouse dragging */ + float pixel, delta; + *state = NK_WIDGET_STATE_ACTIVE; + if (o == NK_VERTICAL) { + float cursor_y; + pixel = in->mouse.delta.y; + delta = (pixel / scroll->h) * target; + scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->h); + cursor_y = scroll->y + ((scroll_offset/target) * scroll->h); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = cursor_y + cursor->h/2.0f; + } else { + float cursor_x; + pixel = in->mouse.delta.x; + delta = (pixel / scroll->w) * target; + scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->w); + cursor_x = scroll->x + ((scroll_offset/target) * scroll->w); + in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = cursor_x + cursor->w/2.0f; + } + } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_UP) && o == NK_VERTICAL && has_scrolling)|| + nk_button_behavior(&ws, *empty0, in, NK_BUTTON_DEFAULT)) { + /* scroll page up by click on empty space or shortcut */ + if (o == NK_VERTICAL) + scroll_offset = NK_MAX(0, scroll_offset - scroll->h); + else scroll_offset = NK_MAX(0, scroll_offset - scroll->w); + } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_DOWN) && o == NK_VERTICAL && has_scrolling) || + nk_button_behavior(&ws, *empty1, in, NK_BUTTON_DEFAULT)) { + /* scroll page down by click on empty space or shortcut */ + if (o == NK_VERTICAL) + scroll_offset = NK_MIN(scroll_offset + scroll->h, target - scroll->h); + else scroll_offset = NK_MIN(scroll_offset + scroll->w, target - scroll->w); + } else if (has_scrolling) { + if ((scroll_delta < 0 || (scroll_delta > 0))) { + /* update cursor by mouse scrolling */ + scroll_offset = scroll_offset + scroll_step * (-scroll_delta); + if (o == NK_VERTICAL) + scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->h); + else scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->w); + } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_START)) { + /* update cursor to the beginning */ + if (o == NK_VERTICAL) scroll_offset = 0; + } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_END)) { + /* update cursor to the end */ + if (o == NK_VERTICAL) scroll_offset = target - scroll->h; + } + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *scroll)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, *scroll)) + *state |= NK_WIDGET_STATE_LEFT; + return scroll_offset; +} +NK_LIB void +nk_draw_scrollbar(struct nk_command_buffer *out, nk_flags state, + const struct nk_style_scrollbar *style, const struct nk_rect *bounds, + const struct nk_rect *scroll) +{ + const struct nk_style_item *background; + const struct nk_style_item *cursor; + + /* select correct colors/images to draw */ + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + cursor = &style->cursor_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + cursor = &style->cursor_hover; + } else { + background = &style->normal; + cursor = &style->cursor_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); + } else { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + } + + /* draw cursor */ + if (cursor->type == NK_STYLE_ITEM_COLOR) { + nk_fill_rect(out, *scroll, style->rounding_cursor, cursor->data.color); + nk_stroke_rect(out, *scroll, style->rounding_cursor, style->border_cursor, style->cursor_border_color); + } else nk_draw_image(out, *scroll, &cursor->data.image, nk_white); +} +NK_LIB float +nk_do_scrollbarv(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, + float offset, float target, float step, float button_pixel_inc, + const struct nk_style_scrollbar *style, struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect empty_north; + struct nk_rect empty_south; + struct nk_rect cursor; + + float scroll_step; + float scroll_offset; + float scroll_off; + float scroll_ratio; + + NK_ASSERT(out); + NK_ASSERT(style); + NK_ASSERT(state); + if (!out || !style) return 0; + + scroll.w = NK_MAX(scroll.w, 1); + scroll.h = NK_MAX(scroll.h, 0); + if (target <= scroll.h) return 0; + + /* optional scrollbar buttons */ + if (style->show_buttons) { + nk_flags ws; + float scroll_h; + struct nk_rect button; + + button.x = scroll.x; + button.w = scroll.w; + button.h = scroll.w; + + scroll_h = NK_MAX(scroll.h - 2 * button.h,0); + scroll_step = NK_MIN(step, button_pixel_inc); + + /* decrement button */ + button.y = scroll.y; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, + NK_BUTTON_REPEATER, &style->dec_button, in, font)) + offset = offset - scroll_step; + + /* increment button */ + button.y = scroll.y + scroll.h - button.h; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, + NK_BUTTON_REPEATER, &style->inc_button, in, font)) + offset = offset + scroll_step; + + scroll.y = scroll.y + button.h; + scroll.h = scroll_h; + } + + /* calculate scrollbar constants */ + scroll_step = NK_MIN(step, scroll.h); + scroll_offset = NK_CLAMP(0, offset, target - scroll.h); + scroll_ratio = scroll.h / target; + scroll_off = scroll_offset / target; + + /* calculate scrollbar cursor bounds */ + cursor.h = NK_MAX((scroll_ratio * scroll.h) - (2*style->border + 2*style->padding.y), 0); + cursor.y = scroll.y + (scroll_off * scroll.h) + style->border + style->padding.y; + cursor.w = scroll.w - (2 * style->border + 2 * style->padding.x); + cursor.x = scroll.x + style->border + style->padding.x; + + /* calculate empty space around cursor */ + empty_north.x = scroll.x; + empty_north.y = scroll.y; + empty_north.w = scroll.w; + empty_north.h = NK_MAX(cursor.y - scroll.y, 0); + + empty_south.x = scroll.x; + empty_south.y = cursor.y + cursor.h; + empty_south.w = scroll.w; + empty_south.h = NK_MAX((scroll.y + scroll.h) - (cursor.y + cursor.h), 0); + + /* update scrollbar */ + scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor, + &empty_north, &empty_south, scroll_offset, target, scroll_step, NK_VERTICAL); + scroll_off = scroll_offset / target; + cursor.y = scroll.y + (scroll_off * scroll.h) + style->border_cursor + style->padding.y; + + /* draw scrollbar */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_scrollbar(out, *state, style, &scroll, &cursor); + if (style->draw_end) style->draw_end(out, style->userdata); + return scroll_offset; +} +NK_LIB float +nk_do_scrollbarh(nk_flags *state, + struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, + float offset, float target, float step, float button_pixel_inc, + const struct nk_style_scrollbar *style, struct nk_input *in, + const struct nk_user_font *font) +{ + struct nk_rect cursor; + struct nk_rect empty_west; + struct nk_rect empty_east; + + float scroll_step; + float scroll_offset; + float scroll_off; + float scroll_ratio; + + NK_ASSERT(out); + NK_ASSERT(style); + if (!out || !style) return 0; + + /* scrollbar background */ + scroll.h = NK_MAX(scroll.h, 1); + scroll.w = NK_MAX(scroll.w, 2 * scroll.h); + if (target <= scroll.w) return 0; + + /* optional scrollbar buttons */ + if (style->show_buttons) { + nk_flags ws; + float scroll_w; + struct nk_rect button; + button.y = scroll.y; + button.w = scroll.h; + button.h = scroll.h; + + scroll_w = scroll.w - 2 * button.w; + scroll_step = NK_MIN(step, button_pixel_inc); + + /* decrement button */ + button.x = scroll.x; + if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, + NK_BUTTON_REPEATER, &style->dec_button, in, font)) + offset = offset - scroll_step; + + /* increment button */ + button.x = scroll.x + scroll.w - button.w; + if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, + NK_BUTTON_REPEATER, &style->inc_button, in, font)) + offset = offset + scroll_step; + + scroll.x = scroll.x + button.w; + scroll.w = scroll_w; + } + + /* calculate scrollbar constants */ + scroll_step = NK_MIN(step, scroll.w); + scroll_offset = NK_CLAMP(0, offset, target - scroll.w); + scroll_ratio = scroll.w / target; + scroll_off = scroll_offset / target; + + /* calculate cursor bounds */ + cursor.w = (scroll_ratio * scroll.w) - (2*style->border + 2*style->padding.x); + cursor.x = scroll.x + (scroll_off * scroll.w) + style->border + style->padding.x; + cursor.h = scroll.h - (2 * style->border + 2 * style->padding.y); + cursor.y = scroll.y + style->border + style->padding.y; + + /* calculate empty space around cursor */ + empty_west.x = scroll.x; + empty_west.y = scroll.y; + empty_west.w = cursor.x - scroll.x; + empty_west.h = scroll.h; + + empty_east.x = cursor.x + cursor.w; + empty_east.y = scroll.y; + empty_east.w = (scroll.x + scroll.w) - (cursor.x + cursor.w); + empty_east.h = scroll.h; + + /* update scrollbar */ + scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor, + &empty_west, &empty_east, scroll_offset, target, scroll_step, NK_HORIZONTAL); + scroll_off = scroll_offset / target; + cursor.x = scroll.x + (scroll_off * scroll.w); + + /* draw scrollbar */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_scrollbar(out, *state, style, &scroll, &cursor); + if (style->draw_end) style->draw_end(out, style->userdata); + return scroll_offset; +} + + + + + +/* =============================================================== + * + * TEXT EDITOR + * + * ===============================================================*/ +/* stb_textedit.h - v1.8 - public domain - Sean Barrett */ +struct nk_text_find { + float x,y; /* position of n'th character */ + float height; /* height of line */ + int first_char, length; /* first char of row, and length */ + int prev_first; /*_ first char of previous row */ +}; + +struct nk_text_edit_row { + float x0,x1; + /* starting x location, end x location (allows for align=right, etc) */ + float baseline_y_delta; + /* position of baseline relative to previous row's baseline*/ + float ymin,ymax; + /* height of row above and below baseline */ + int num_chars; +}; + +/* forward declarations */ +NK_INTERN void nk_textedit_makeundo_delete(struct nk_text_edit*, int, int); +NK_INTERN void nk_textedit_makeundo_insert(struct nk_text_edit*, int, int); +NK_INTERN void nk_textedit_makeundo_replace(struct nk_text_edit*, int, int, int); +#define NK_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) + +NK_INTERN float +nk_textedit_get_width(const struct nk_text_edit *edit, int line_start, int char_id, + const struct nk_user_font *font) +{ + int len = 0; + nk_rune unicode = 0; + const char *str = nk_str_at_const(&edit->string, line_start + char_id, &unicode, &len); + return font->width(font->userdata, font->height, str, len); +} +NK_INTERN void +nk_textedit_layout_row(struct nk_text_edit_row *r, struct nk_text_edit *edit, + int line_start_id, float row_height, const struct nk_user_font *font) +{ + int l; + int glyphs = 0; + nk_rune unicode; + const char *remaining; + int len = nk_str_len_char(&edit->string); + const char *end = nk_str_get_const(&edit->string) + len; + const char *text = nk_str_at_const(&edit->string, line_start_id, &unicode, &l); + const struct nk_vec2 size = nk_text_calculate_text_bounds(font, + text, (int)(end - text), row_height, &remaining, 0, &glyphs, NK_STOP_ON_NEW_LINE); + + r->x0 = 0.0f; + r->x1 = size.x; + r->baseline_y_delta = size.y; + r->ymin = 0.0f; + r->ymax = size.y; + r->num_chars = glyphs; +} +NK_INTERN int +nk_textedit_locate_coord(struct nk_text_edit *edit, float x, float y, + const struct nk_user_font *font, float row_height) +{ + struct nk_text_edit_row r; + int n = edit->string.len; + float base_y = 0, prev_x; + int i=0, k; + + r.x0 = r.x1 = 0; + r.ymin = r.ymax = 0; + r.num_chars = 0; + + /* search rows to find one that straddles 'y' */ + while (i < n) { + nk_textedit_layout_row(&r, edit, i, row_height, font); + if (r.num_chars <= 0) + return n; + + if (i==0 && y < base_y + r.ymin) + return 0; + + if (y < base_y + r.ymax) + break; + + i += r.num_chars; + base_y += r.baseline_y_delta; + } + + /* below all text, return 'after' last character */ + if (i >= n) + return n; + + /* check if it's before the beginning of the line */ + if (x < r.x0) + return i; + + /* check if it's before the end of the line */ + if (x < r.x1) { + /* search characters in row for one that straddles 'x' */ + k = i; + prev_x = r.x0; + for (i=0; i < r.num_chars; ++i) { + float w = nk_textedit_get_width(edit, k, i, font); + if (x < prev_x+w) { + if (x < prev_x+w/2) + return k+i; + else return k+i+1; + } + prev_x += w; + } + /* shouldn't happen, but if it does, fall through to end-of-line case */ + } + + /* if the last character is a newline, return that. + * otherwise return 'after' the last character */ + if (nk_str_rune_at(&edit->string, i+r.num_chars-1) == '\n') + return i+r.num_chars-1; + else return i+r.num_chars; +} +NK_LIB void +nk_textedit_click(struct nk_text_edit *state, float x, float y, + const struct nk_user_font *font, float row_height) +{ + /* API click: on mouse down, move the cursor to the clicked location, + * and reset the selection */ + state->cursor = nk_textedit_locate_coord(state, x, y, font, row_height); + state->select_start = state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; +} +NK_LIB void +nk_textedit_drag(struct nk_text_edit *state, float x, float y, + const struct nk_user_font *font, float row_height) +{ + /* API drag: on mouse drag, move the cursor and selection endpoint + * to the clicked location */ + int p = nk_textedit_locate_coord(state, x, y, font, row_height); + if (state->select_start == state->select_end) + state->select_start = state->cursor; + state->cursor = state->select_end = p; +} +NK_INTERN void +nk_textedit_find_charpos(struct nk_text_find *find, struct nk_text_edit *state, + int n, int single_line, const struct nk_user_font *font, float row_height) +{ + /* find the x/y location of a character, and remember info about the previous + * row in case we get a move-up event (for page up, we'll have to rescan) */ + struct nk_text_edit_row r; + int prev_start = 0; + int z = state->string.len; + int i=0, first; + + nk_zero_struct(r); + if (n == z) { + /* if it's at the end, then find the last line -- simpler than trying to + explicitly handle this case in the regular code */ + nk_textedit_layout_row(&r, state, 0, row_height, font); + if (single_line) { + find->first_char = 0; + find->length = z; + } else { + while (i < z) { + prev_start = i; + i += r.num_chars; + nk_textedit_layout_row(&r, state, i, row_height, font); + } + + find->first_char = i; + find->length = r.num_chars; + } + find->x = r.x1; + find->y = r.ymin; + find->height = r.ymax - r.ymin; + find->prev_first = prev_start; + return; + } + + /* search rows to find the one that straddles character n */ + find->y = 0; + + for(;;) { + nk_textedit_layout_row(&r, state, i, row_height, font); + if (n < i + r.num_chars) break; + prev_start = i; + i += r.num_chars; + find->y += r.baseline_y_delta; + } + + find->first_char = first = i; + find->length = r.num_chars; + find->height = r.ymax - r.ymin; + find->prev_first = prev_start; + + /* now scan to find xpos */ + find->x = r.x0; + for (i=0; first+i < n; ++i) + find->x += nk_textedit_get_width(state, first, i, font); +} +NK_INTERN void +nk_textedit_clamp(struct nk_text_edit *state) +{ + /* make the selection/cursor state valid if client altered the string */ + int n = state->string.len; + if (NK_TEXT_HAS_SELECTION(state)) { + if (state->select_start > n) state->select_start = n; + if (state->select_end > n) state->select_end = n; + /* if clamping forced them to be equal, move the cursor to match */ + if (state->select_start == state->select_end) + state->cursor = state->select_start; + } + if (state->cursor > n) state->cursor = n; +} +NK_API void +nk_textedit_delete(struct nk_text_edit *state, int where, int len) +{ + /* delete characters while updating undo */ + nk_textedit_makeundo_delete(state, where, len); + nk_str_delete_runes(&state->string, where, len); + state->has_preferred_x = 0; +} +NK_API void +nk_textedit_delete_selection(struct nk_text_edit *state) +{ + /* delete the section */ + nk_textedit_clamp(state); + if (NK_TEXT_HAS_SELECTION(state)) { + if (state->select_start < state->select_end) { + nk_textedit_delete(state, state->select_start, + state->select_end - state->select_start); + state->select_end = state->cursor = state->select_start; + } else { + nk_textedit_delete(state, state->select_end, + state->select_start - state->select_end); + state->select_start = state->cursor = state->select_end; + } + state->has_preferred_x = 0; + } +} +NK_INTERN void +nk_textedit_sortselection(struct nk_text_edit *state) +{ + /* canonicalize the selection so start <= end */ + if (state->select_end < state->select_start) { + int temp = state->select_end; + state->select_end = state->select_start; + state->select_start = temp; + } +} +NK_INTERN void +nk_textedit_move_to_first(struct nk_text_edit *state) +{ + /* move cursor to first character of selection */ + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_sortselection(state); + state->cursor = state->select_start; + state->select_end = state->select_start; + state->has_preferred_x = 0; + } +} +NK_INTERN void +nk_textedit_move_to_last(struct nk_text_edit *state) +{ + /* move cursor to last character of selection */ + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_sortselection(state); + nk_textedit_clamp(state); + state->cursor = state->select_end; + state->select_start = state->select_end; + state->has_preferred_x = 0; + } +} +NK_INTERN int +nk_is_word_boundary( struct nk_text_edit *state, int idx) +{ + int len; + nk_rune c; + if (idx <= 0) return 1; + if (!nk_str_at_rune(&state->string, idx, &c, &len)) return 1; + return (c == ' ' || c == '\t' ||c == 0x3000 || c == ',' || c == ';' || + c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || + c == '|'); +} +NK_INTERN int +nk_textedit_move_to_word_previous(struct nk_text_edit *state) +{ + int c = state->cursor - 1; + while( c >= 0 && !nk_is_word_boundary(state, c)) + --c; + + if( c < 0 ) + c = 0; + + return c; +} +NK_INTERN int +nk_textedit_move_to_word_next(struct nk_text_edit *state) +{ + const int len = state->string.len; + int c = state->cursor+1; + while( c < len && !nk_is_word_boundary(state, c)) + ++c; + + if( c > len ) + c = len; + + return c; +} +NK_INTERN void +nk_textedit_prep_selection_at_cursor(struct nk_text_edit *state) +{ + /* update selection and cursor to match each other */ + if (!NK_TEXT_HAS_SELECTION(state)) + state->select_start = state->select_end = state->cursor; + else state->cursor = state->select_end; +} +NK_API int +nk_textedit_cut(struct nk_text_edit *state) +{ + /* API cut: delete selection */ + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + return 0; + if (NK_TEXT_HAS_SELECTION(state)) { + nk_textedit_delete_selection(state); /* implicitly clamps */ + state->has_preferred_x = 0; + return 1; + } + return 0; +} +NK_API int +nk_textedit_paste(struct nk_text_edit *state, char const *ctext, int len) +{ + /* API paste: replace existing selection with passed-in text */ + int glyphs; + const char *text = (const char *) ctext; + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) return 0; + + /* if there's a selection, the paste should delete it */ + nk_textedit_clamp(state); + nk_textedit_delete_selection(state); + + /* try to insert the characters */ + glyphs = nk_utf_len(ctext, len); + if (nk_str_insert_text_char(&state->string, state->cursor, text, len)) { + nk_textedit_makeundo_insert(state, state->cursor, glyphs); + state->cursor += len; + state->has_preferred_x = 0; + return 1; + } + /* remove the undo since we didn't actually insert the characters */ + if (state->undo.undo_point) + --state->undo.undo_point; + return 0; +} +NK_API void +nk_textedit_text(struct nk_text_edit *state, const char *text, int total_len) +{ + nk_rune unicode; + int glyph_len; + int text_len = 0; + + NK_ASSERT(state); + NK_ASSERT(text); + if (!text || !total_len || state->mode == NK_TEXT_EDIT_MODE_VIEW) return; + + glyph_len = nk_utf_decode(text, &unicode, total_len); + while ((text_len < total_len) && glyph_len) + { + /* don't insert a backward delete, just process the event */ + if (unicode == 127) goto next; + /* can't add newline in single-line mode */ + if (unicode == '\n' && state->single_line) goto next; + /* filter incoming text */ + if (state->filter && !state->filter(state, unicode)) goto next; + + if (!NK_TEXT_HAS_SELECTION(state) && + state->cursor < state->string.len) + { + if (state->mode == NK_TEXT_EDIT_MODE_REPLACE) { + nk_textedit_makeundo_replace(state, state->cursor, 1, 1); + nk_str_delete_runes(&state->string, state->cursor, 1); + } + if (nk_str_insert_text_utf8(&state->string, state->cursor, + text+text_len, 1)) + { + ++state->cursor; + state->has_preferred_x = 0; + } + } else { + nk_textedit_delete_selection(state); /* implicitly clamps */ + if (nk_str_insert_text_utf8(&state->string, state->cursor, + text+text_len, 1)) + { + nk_textedit_makeundo_insert(state, state->cursor, 1); + ++state->cursor; + state->has_preferred_x = 0; + } + } + next: + text_len += glyph_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, total_len-text_len); + } +} +NK_LIB void +nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod, + const struct nk_user_font *font, float row_height) +{ +retry: + switch (key) + { + case NK_KEY_NONE: + case NK_KEY_CTRL: + case NK_KEY_ENTER: + case NK_KEY_SHIFT: + case NK_KEY_TAB: + case NK_KEY_COPY: + case NK_KEY_CUT: + case NK_KEY_PASTE: + case NK_KEY_MAX: + default: break; + case NK_KEY_TEXT_UNDO: + nk_textedit_undo(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_REDO: + nk_textedit_redo(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_SELECT_ALL: + nk_textedit_select_all(state); + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_INSERT_MODE: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + state->mode = NK_TEXT_EDIT_MODE_INSERT; + break; + case NK_KEY_TEXT_REPLACE_MODE: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + state->mode = NK_TEXT_EDIT_MODE_REPLACE; + break; + case NK_KEY_TEXT_RESET_MODE: + if (state->mode == NK_TEXT_EDIT_MODE_INSERT || + state->mode == NK_TEXT_EDIT_MODE_REPLACE) + state->mode = NK_TEXT_EDIT_MODE_VIEW; + break; + + case NK_KEY_LEFT: + if (shift_mod) { + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + /* move selection left */ + if (state->select_end > 0) + --state->select_end; + state->cursor = state->select_end; + state->has_preferred_x = 0; + } else { + /* if currently there's a selection, + * move cursor to start of selection */ + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + else if (state->cursor > 0) + --state->cursor; + state->has_preferred_x = 0; + } break; + + case NK_KEY_RIGHT: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + /* move selection right */ + ++state->select_end; + nk_textedit_clamp(state); + state->cursor = state->select_end; + state->has_preferred_x = 0; + } else { + /* if currently there's a selection, + * move cursor to end of selection */ + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + else ++state->cursor; + nk_textedit_clamp(state); + state->has_preferred_x = 0; + } break; + + case NK_KEY_TEXT_WORD_LEFT: + if (shift_mod) { + if( !NK_TEXT_HAS_SELECTION( state ) ) + nk_textedit_prep_selection_at_cursor(state); + state->cursor = nk_textedit_move_to_word_previous(state); + state->select_end = state->cursor; + nk_textedit_clamp(state ); + } else { + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + else { + state->cursor = nk_textedit_move_to_word_previous(state); + nk_textedit_clamp(state ); + } + } break; + + case NK_KEY_TEXT_WORD_RIGHT: + if (shift_mod) { + if( !NK_TEXT_HAS_SELECTION( state ) ) + nk_textedit_prep_selection_at_cursor(state); + state->cursor = nk_textedit_move_to_word_next(state); + state->select_end = state->cursor; + nk_textedit_clamp(state); + } else { + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + else { + state->cursor = nk_textedit_move_to_word_next(state); + nk_textedit_clamp(state ); + } + } break; + + case NK_KEY_DOWN: { + struct nk_text_find find; + struct nk_text_edit_row row; + int i, sel = shift_mod; + + if (state->single_line) { + /* on windows, up&down in single-line behave like left&right */ + key = NK_KEY_RIGHT; + goto retry; + } + + if (sel) + nk_textedit_prep_selection_at_cursor(state); + else if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_last(state); + + /* compute current position of cursor point */ + nk_textedit_clamp(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + /* now find character position down a row */ + if (find.length) + { + float x; + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + int start = find.first_char + find.length; + + state->cursor = start; + nk_textedit_layout_row(&row, state, state->cursor, row_height, font); + x = row.x0; + + for (i=0; i < row.num_chars && x < row.x1; ++i) { + float dx = nk_textedit_get_width(state, start, i, font); + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + nk_textedit_clamp(state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + if (sel) + state->select_end = state->cursor; + } + } break; + + case NK_KEY_UP: { + struct nk_text_find find; + struct nk_text_edit_row row; + int i, sel = shift_mod; + + if (state->single_line) { + /* on windows, up&down become left&right */ + key = NK_KEY_LEFT; + goto retry; + } + + if (sel) + nk_textedit_prep_selection_at_cursor(state); + else if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_move_to_first(state); + + /* compute current position of cursor point */ + nk_textedit_clamp(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + /* can only go up if there's a previous row */ + if (find.prev_first != find.first_char) { + /* now find character position up a row */ + float x; + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + + state->cursor = find.prev_first; + nk_textedit_layout_row(&row, state, state->cursor, row_height, font); + x = row.x0; + + for (i=0; i < row.num_chars && x < row.x1; ++i) { + float dx = nk_textedit_get_width(state, find.prev_first, i, font); + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + nk_textedit_clamp(state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + if (sel) state->select_end = state->cursor; + } + } break; + + case NK_KEY_DEL: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + break; + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_delete_selection(state); + else { + int n = state->string.len; + if (state->cursor < n) + nk_textedit_delete(state, state->cursor, 1); + } + state->has_preferred_x = 0; + break; + + case NK_KEY_BACKSPACE: + if (state->mode == NK_TEXT_EDIT_MODE_VIEW) + break; + if (NK_TEXT_HAS_SELECTION(state)) + nk_textedit_delete_selection(state); + else { + nk_textedit_clamp(state); + if (state->cursor > 0) { + nk_textedit_delete(state, state->cursor-1, 1); + --state->cursor; + } + } + state->has_preferred_x = 0; + break; + + case NK_KEY_TEXT_START: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = 0; + state->has_preferred_x = 0; + } else { + state->cursor = state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + } + break; + + case NK_KEY_TEXT_END: + if (shift_mod) { + nk_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = state->string.len; + state->has_preferred_x = 0; + } else { + state->cursor = state->string.len; + state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + } + break; + + case NK_KEY_TEXT_LINE_START: { + if (shift_mod) { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + if (state->string.len && state->cursor == state->string.len) + --state->cursor; + nk_textedit_find_charpos(&find, state,state->cursor, state->single_line, + font, row_height); + state->cursor = state->select_end = find.first_char; + state->has_preferred_x = 0; + } else { + struct nk_text_find find; + if (state->string.len && state->cursor == state->string.len) + --state->cursor; + nk_textedit_clamp(state); + nk_textedit_move_to_first(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + state->cursor = find.first_char; + state->has_preferred_x = 0; + } + } break; + + case NK_KEY_TEXT_LINE_END: { + if (shift_mod) { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_prep_selection_at_cursor(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') + --state->cursor; + state->select_end = state->cursor; + } else { + struct nk_text_find find; + nk_textedit_clamp(state); + nk_textedit_move_to_first(state); + nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, + font, row_height); + + state->has_preferred_x = 0; + state->cursor = find.first_char + find.length; + if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') + --state->cursor; + }} break; + } +} +NK_INTERN void +nk_textedit_flush_redo(struct nk_text_undo_state *state) +{ + state->redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; + state->redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; +} +NK_INTERN void +nk_textedit_discard_undo(struct nk_text_undo_state *state) +{ + /* discard the oldest entry in the undo list */ + if (state->undo_point > 0) { + /* if the 0th undo state has characters, clean those up */ + if (state->undo_rec[0].char_storage >= 0) { + int n = state->undo_rec[0].insert_length, i; + /* delete n characters from all other records */ + state->undo_char_point = (short)(state->undo_char_point - n); + NK_MEMCPY(state->undo_char, state->undo_char + n, + (nk_size)state->undo_char_point*sizeof(nk_rune)); + for (i=0; i < state->undo_point; ++i) { + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage = (short) + (state->undo_rec[i].char_storage - n); + } + } + --state->undo_point; + NK_MEMCPY(state->undo_rec, state->undo_rec+1, + (nk_size)((nk_size)state->undo_point * sizeof(state->undo_rec[0]))); + } +} +NK_INTERN void +nk_textedit_discard_redo(struct nk_text_undo_state *state) +{ +/* discard the oldest entry in the redo list--it's bad if this + ever happens, but because undo & redo have to store the actual + characters in different cases, the redo character buffer can + fill up even though the undo buffer didn't */ + nk_size num; + int k = NK_TEXTEDIT_UNDOSTATECOUNT-1; + if (state->redo_point <= k) { + /* if the k'th undo state has characters, clean those up */ + if (state->undo_rec[k].char_storage >= 0) { + int n = state->undo_rec[k].insert_length, i; + /* delete n characters from all other records */ + state->redo_char_point = (short)(state->redo_char_point + n); + num = (nk_size)(NK_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point); + NK_MEMCPY(state->undo_char + state->redo_char_point, + state->undo_char + state->redo_char_point-n, num * sizeof(char)); + for (i = state->redo_point; i < k; ++i) { + if (state->undo_rec[i].char_storage >= 0) { + state->undo_rec[i].char_storage = (short) + (state->undo_rec[i].char_storage + n); + } + } + } + ++state->redo_point; + num = (nk_size)(NK_TEXTEDIT_UNDOSTATECOUNT - state->redo_point); + if (num) NK_MEMCPY(state->undo_rec + state->redo_point-1, + state->undo_rec + state->redo_point, num * sizeof(state->undo_rec[0])); + } +} +NK_INTERN struct nk_text_undo_record* +nk_textedit_create_undo_record(struct nk_text_undo_state *state, int numchars) +{ + /* any time we create a new undo record, we discard redo*/ + nk_textedit_flush_redo(state); + + /* if we have no free records, we have to make room, + * by sliding the existing records down */ + if (state->undo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + nk_textedit_discard_undo(state); + + /* if the characters to store won't possibly fit in the buffer, + * we can't undo */ + if (numchars > NK_TEXTEDIT_UNDOCHARCOUNT) { + state->undo_point = 0; + state->undo_char_point = 0; + return 0; + } + + /* if we don't have enough free characters in the buffer, + * we have to make room */ + while (state->undo_char_point + numchars > NK_TEXTEDIT_UNDOCHARCOUNT) + nk_textedit_discard_undo(state); + return &state->undo_rec[state->undo_point++]; +} +NK_INTERN nk_rune* +nk_textedit_createundo(struct nk_text_undo_state *state, int pos, + int insert_len, int delete_len) +{ + struct nk_text_undo_record *r = nk_textedit_create_undo_record(state, insert_len); + if (r == 0) + return 0; + + r->where = pos; + r->insert_length = (short) insert_len; + r->delete_length = (short) delete_len; + + if (insert_len == 0) { + r->char_storage = -1; + return 0; + } else { + r->char_storage = state->undo_char_point; + state->undo_char_point = (short)(state->undo_char_point + insert_len); + return &state->undo_char[r->char_storage]; + } +} +NK_API void +nk_textedit_undo(struct nk_text_edit *state) +{ + struct nk_text_undo_state *s = &state->undo; + struct nk_text_undo_record u, *r; + if (s->undo_point == 0) + return; + + /* we need to do two things: apply the undo record, and create a redo record */ + u = s->undo_rec[s->undo_point-1]; + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = -1; + + r->insert_length = u.delete_length; + r->delete_length = u.insert_length; + r->where = u.where; + + if (u.delete_length) + { + /* if the undo record says to delete characters, then the redo record will + need to re-insert the characters that get deleted, so we need to store + them. + there are three cases: + - there's enough room to store the characters + - characters stored for *redoing* don't leave room for redo + - characters stored for *undoing* don't leave room for redo + if the last is true, we have to bail */ + if (s->undo_char_point + u.delete_length >= NK_TEXTEDIT_UNDOCHARCOUNT) { + /* the undo records take up too much character space; there's no space + * to store the redo characters */ + r->insert_length = 0; + } else { + int i; + /* there's definitely room to store the characters eventually */ + while (s->undo_char_point + u.delete_length > s->redo_char_point) { + /* there's currently not enough room, so discard a redo record */ + nk_textedit_discard_redo(s); + /* should never happen: */ + if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + return; + } + + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = (short)(s->redo_char_point - u.delete_length); + s->redo_char_point = (short)(s->redo_char_point - u.delete_length); + + /* now save the characters */ + for (i=0; i < u.delete_length; ++i) + s->undo_char[r->char_storage + i] = + nk_str_rune_at(&state->string, u.where + i); + } + /* now we can carry out the deletion */ + nk_str_delete_runes(&state->string, u.where, u.delete_length); + } + + /* check type of recorded action: */ + if (u.insert_length) { + /* easy case: was a deletion, so we need to insert n characters */ + nk_str_insert_text_runes(&state->string, u.where, + &s->undo_char[u.char_storage], u.insert_length); + s->undo_char_point = (short)(s->undo_char_point - u.insert_length); + } + state->cursor = (short)(u.where + u.insert_length); + + s->undo_point--; + s->redo_point--; +} +NK_API void +nk_textedit_redo(struct nk_text_edit *state) +{ + struct nk_text_undo_state *s = &state->undo; + struct nk_text_undo_record *u, r; + if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) + return; + + /* we need to do two things: apply the redo record, and create an undo record */ + u = &s->undo_rec[s->undo_point]; + r = s->undo_rec[s->redo_point]; + + /* we KNOW there must be room for the undo record, because the redo record + was derived from an undo record */ + u->delete_length = r.insert_length; + u->insert_length = r.delete_length; + u->where = r.where; + u->char_storage = -1; + + if (r.delete_length) { + /* the redo record requires us to delete characters, so the undo record + needs to store the characters */ + if (s->undo_char_point + u->insert_length > s->redo_char_point) { + u->insert_length = 0; + u->delete_length = 0; + } else { + int i; + u->char_storage = s->undo_char_point; + s->undo_char_point = (short)(s->undo_char_point + u->insert_length); + + /* now save the characters */ + for (i=0; i < u->insert_length; ++i) { + s->undo_char[u->char_storage + i] = + nk_str_rune_at(&state->string, u->where + i); + } + } + nk_str_delete_runes(&state->string, r.where, r.delete_length); + } + + if (r.insert_length) { + /* easy case: need to insert n characters */ + nk_str_insert_text_runes(&state->string, r.where, + &s->undo_char[r.char_storage], r.insert_length); + } + state->cursor = r.where + r.insert_length; + + s->undo_point++; + s->redo_point++; +} +NK_INTERN void +nk_textedit_makeundo_insert(struct nk_text_edit *state, int where, int length) +{ + nk_textedit_createundo(&state->undo, where, 0, length); +} +NK_INTERN void +nk_textedit_makeundo_delete(struct nk_text_edit *state, int where, int length) +{ + int i; + nk_rune *p = nk_textedit_createundo(&state->undo, where, length, 0); + if (p) { + for (i=0; i < length; ++i) + p[i] = nk_str_rune_at(&state->string, where+i); + } +} +NK_INTERN void +nk_textedit_makeundo_replace(struct nk_text_edit *state, int where, + int old_length, int new_length) +{ + int i; + nk_rune *p = nk_textedit_createundo(&state->undo, where, old_length, new_length); + if (p) { + for (i=0; i < old_length; ++i) + p[i] = nk_str_rune_at(&state->string, where+i); + } +} +NK_LIB void +nk_textedit_clear_state(struct nk_text_edit *state, enum nk_text_edit_type type, + nk_plugin_filter filter) +{ + /* reset the state to default */ + state->undo.undo_point = 0; + state->undo.undo_char_point = 0; + state->undo.redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; + state->undo.redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; + state->select_end = state->select_start = 0; + state->cursor = 0; + state->has_preferred_x = 0; + state->preferred_x = 0; + state->cursor_at_end_of_line = 0; + state->initialized = 1; + state->single_line = (unsigned char)(type == NK_TEXT_EDIT_SINGLE_LINE); + state->mode = NK_TEXT_EDIT_MODE_VIEW; + state->filter = filter; + state->scrollbar = nk_vec2(0,0); +} +NK_API void +nk_textedit_init_fixed(struct nk_text_edit *state, void *memory, nk_size size) +{ + NK_ASSERT(state); + NK_ASSERT(memory); + if (!state || !memory || !size) return; + NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init_fixed(&state->string, memory, size); +} +NK_API void +nk_textedit_init(struct nk_text_edit *state, struct nk_allocator *alloc, nk_size size) +{ + NK_ASSERT(state); + NK_ASSERT(alloc); + if (!state || !alloc) return; + NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init(&state->string, alloc, size); +} +#ifdef NK_INCLUDE_DEFAULT_ALLOCATOR +NK_API void +nk_textedit_init_default(struct nk_text_edit *state) +{ + NK_ASSERT(state); + if (!state) return; + NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); + nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); + nk_str_init_default(&state->string); +} +#endif +NK_API void +nk_textedit_select_all(struct nk_text_edit *state) +{ + NK_ASSERT(state); + state->select_start = 0; + state->select_end = state->string.len; +} +NK_API void +nk_textedit_free(struct nk_text_edit *state) +{ + NK_ASSERT(state); + if (!state) return; + nk_str_free(&state->string); +} + + + + + +/* =============================================================== + * + * FILTER + * + * ===============================================================*/ +NK_API int +nk_filter_default(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(unicode); + NK_UNUSED(box); + return nk_true; +} +NK_API int +nk_filter_ascii(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode > 128) return nk_false; + else return nk_true; +} +NK_API int +nk_filter_float(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && unicode != '.' && unicode != '-') + return nk_false; + else return nk_true; +} +NK_API int +nk_filter_decimal(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && unicode != '-') + return nk_false; + else return nk_true; +} +NK_API int +nk_filter_hex(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if ((unicode < '0' || unicode > '9') && + (unicode < 'a' || unicode > 'f') && + (unicode < 'A' || unicode > 'F')) + return nk_false; + else return nk_true; +} +NK_API int +nk_filter_oct(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode < '0' || unicode > '7') + return nk_false; + else return nk_true; +} +NK_API int +nk_filter_binary(const struct nk_text_edit *box, nk_rune unicode) +{ + NK_UNUSED(box); + if (unicode != '0' && unicode != '1') + return nk_false; + else return nk_true; +} + +/* =============================================================== * * EDIT * - * --------------------------------------------------------------*/ + * ===============================================================*/ +NK_LIB void +nk_edit_draw_text(struct nk_command_buffer *out, + const struct nk_style_edit *style, float pos_x, float pos_y, + float x_offset, const char *text, int byte_len, float row_height, + const struct nk_user_font *font, struct nk_color background, + struct nk_color foreground, int is_selected) +{ + NK_ASSERT(out); + NK_ASSERT(font); + NK_ASSERT(style); + if (!text || !byte_len || !out || !style) return; + + {int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + float line_width = 0; + float glyph_width; + const char *line = text; + float line_offset = 0; + int line_count = 0; + + struct nk_text txt; + txt.padding = nk_vec2(0,0); + txt.background = background; + txt.text = foreground; + + glyph_len = nk_utf_decode(text+text_len, &unicode, byte_len-text_len); + if (!glyph_len) return; + while ((text_len < byte_len) && glyph_len) + { + if (unicode == '\n') { + /* new line separator so draw previous line */ + struct nk_rect label; + label.y = pos_y + line_offset; + label.h = row_height; + label.w = line_width; + label.x = pos_x; + if (!line_count) + label.x += x_offset; + + if (is_selected) /* selection needs to draw different background color */ + nk_fill_rect(out, label, 0, background); + nk_widget_text(out, label, line, (int)((text + text_len) - line), + &txt, NK_TEXT_CENTERED, font); + + text_len++; + line_count++; + line_width = 0; + line = text + text_len; + line_offset += row_height; + glyph_len = nk_utf_decode(text + text_len, &unicode, (int)(byte_len-text_len)); + continue; + } + if (unicode == '\r') { + text_len++; + glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); + continue; + } + glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); + line_width += (float)glyph_width; + text_len += glyph_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); + continue; + } + if (line_width > 0) { + /* draw last line */ + struct nk_rect label; + label.y = pos_y + line_offset; + label.h = row_height; + label.w = line_width; + label.x = pos_x; + if (!line_count) + label.x += x_offset; + + if (is_selected) + nk_fill_rect(out, label, 0, background); + nk_widget_text(out, label, line, (int)((text + text_len) - line), + &txt, NK_TEXT_LEFT, font); + }} +} +NK_LIB nk_flags +nk_do_edit(nk_flags *state, struct nk_command_buffer *out, + struct nk_rect bounds, nk_flags flags, nk_plugin_filter filter, + struct nk_text_edit *edit, const struct nk_style_edit *style, + struct nk_input *in, const struct nk_user_font *font) +{ + struct nk_rect area; + nk_flags ret = 0; + float row_height; + char prev_state = 0; + char is_hovered = 0; + char select_all = 0; + char cursor_follow = 0; + struct nk_rect old_clip; + struct nk_rect clip; + + NK_ASSERT(state); + NK_ASSERT(out); + NK_ASSERT(style); + if (!state || !out || !style) + return ret; + + /* visible text area calculation */ + area.x = bounds.x + style->padding.x + style->border; + area.y = bounds.y + style->padding.y + style->border; + area.w = bounds.w - (2.0f * style->padding.x + 2 * style->border); + area.h = bounds.h - (2.0f * style->padding.y + 2 * style->border); + if (flags & NK_EDIT_MULTILINE) + area.w = NK_MAX(0, area.w - style->scrollbar_size.x); + row_height = (flags & NK_EDIT_MULTILINE)? font->height + style->row_padding: area.h; + + /* calculate clipping rectangle */ + old_clip = out->clip; + nk_unify(&clip, &old_clip, area.x, area.y, area.x + area.w, area.y + area.h); + + /* update edit state */ + prev_state = (char)edit->active; + is_hovered = (char)nk_input_is_mouse_hovering_rect(in, bounds); + if (in && in->mouse.buttons[NK_BUTTON_LEFT].clicked && in->mouse.buttons[NK_BUTTON_LEFT].down) { + edit->active = NK_INBOX(in->mouse.pos.x, in->mouse.pos.y, + bounds.x, bounds.y, bounds.w, bounds.h); + } + + /* (de)activate text editor */ + if (!prev_state && edit->active) { + const enum nk_text_edit_type type = (flags & NK_EDIT_MULTILINE) ? + NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE; + nk_textedit_clear_state(edit, type, filter); + if (flags & NK_EDIT_AUTO_SELECT) + select_all = nk_true; + if (flags & NK_EDIT_GOTO_END_ON_ACTIVATE) { + edit->cursor = edit->string.len; + in = 0; + } + } else if (!edit->active) edit->mode = NK_TEXT_EDIT_MODE_VIEW; + if (flags & NK_EDIT_READ_ONLY) + edit->mode = NK_TEXT_EDIT_MODE_VIEW; + else if (flags & NK_EDIT_ALWAYS_INSERT_MODE) + edit->mode = NK_TEXT_EDIT_MODE_INSERT; + + ret = (edit->active) ? NK_EDIT_ACTIVE: NK_EDIT_INACTIVE; + if (prev_state != edit->active) + ret |= (edit->active) ? NK_EDIT_ACTIVATED: NK_EDIT_DEACTIVATED; + + /* handle user input */ + if (edit->active && in) + { + int shift_mod = in->keyboard.keys[NK_KEY_SHIFT].down; + const float mouse_x = (in->mouse.pos.x - area.x) + edit->scrollbar.x; + const float mouse_y = (in->mouse.pos.y - area.y) + edit->scrollbar.y; + + /* mouse click handler */ + is_hovered = (char)nk_input_is_mouse_hovering_rect(in, area); + if (select_all) { + nk_textedit_select_all(edit); + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && + in->mouse.buttons[NK_BUTTON_LEFT].clicked) { + nk_textedit_click(edit, mouse_x, mouse_y, font, row_height); + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && + (in->mouse.delta.x != 0.0f || in->mouse.delta.y != 0.0f)) { + nk_textedit_drag(edit, mouse_x, mouse_y, font, row_height); + cursor_follow = nk_true; + } else if (is_hovered && in->mouse.buttons[NK_BUTTON_RIGHT].clicked && + in->mouse.buttons[NK_BUTTON_RIGHT].down) { + nk_textedit_key(edit, NK_KEY_TEXT_WORD_LEFT, nk_false, font, row_height); + nk_textedit_key(edit, NK_KEY_TEXT_WORD_RIGHT, nk_true, font, row_height); + cursor_follow = nk_true; + } + + {int i; /* keyboard input */ + int old_mode = edit->mode; + for (i = 0; i < NK_KEY_MAX; ++i) { + if (i == NK_KEY_ENTER || i == NK_KEY_TAB) continue; /* special case */ + if (nk_input_is_key_pressed(in, (enum nk_keys)i)) { + nk_textedit_key(edit, (enum nk_keys)i, shift_mod, font, row_height); + cursor_follow = nk_true; + } + } + if (old_mode != edit->mode) { + in->keyboard.text_len = 0; + }} + + /* text input */ + edit->filter = filter; + if (in->keyboard.text_len) { + nk_textedit_text(edit, in->keyboard.text, in->keyboard.text_len); + cursor_follow = nk_true; + in->keyboard.text_len = 0; + } + + /* enter key handler */ + if (nk_input_is_key_pressed(in, NK_KEY_ENTER)) { + cursor_follow = nk_true; + if (flags & NK_EDIT_CTRL_ENTER_NEWLINE && shift_mod) + nk_textedit_text(edit, "\n", 1); + else if (flags & NK_EDIT_SIG_ENTER) + ret |= NK_EDIT_COMMITED; + else nk_textedit_text(edit, "\n", 1); + } + + /* cut & copy handler */ + {int copy= nk_input_is_key_pressed(in, NK_KEY_COPY); + int cut = nk_input_is_key_pressed(in, NK_KEY_CUT); + if ((copy || cut) && (flags & NK_EDIT_CLIPBOARD)) + { + int glyph_len; + nk_rune unicode; + const char *text; + int b = edit->select_start; + int e = edit->select_end; + + int begin = NK_MIN(b, e); + int end = NK_MAX(b, e); + text = nk_str_at_const(&edit->string, begin, &unicode, &glyph_len); + if (edit->clip.copy) + edit->clip.copy(edit->clip.userdata, text, end - begin); + if (cut && !(flags & NK_EDIT_READ_ONLY)){ + nk_textedit_cut(edit); + cursor_follow = nk_true; + } + }} + + /* paste handler */ + {int paste = nk_input_is_key_pressed(in, NK_KEY_PASTE); + if (paste && (flags & NK_EDIT_CLIPBOARD) && edit->clip.paste) { + edit->clip.paste(edit->clip.userdata, edit); + cursor_follow = nk_true; + }} + + /* tab handler */ + {int tab = nk_input_is_key_pressed(in, NK_KEY_TAB); + if (tab && (flags & NK_EDIT_ALLOW_TAB)) { + nk_textedit_text(edit, " ", 4); + cursor_follow = nk_true; + }} + } + + /* set widget state */ + if (edit->active) + *state = NK_WIDGET_STATE_ACTIVE; + else nk_widget_state_reset(state); + + if (is_hovered) + *state |= NK_WIDGET_STATE_HOVERED; + + /* DRAW EDIT */ + {const char *text = nk_str_get_const(&edit->string); + int len = nk_str_len_char(&edit->string); + + {/* select background colors/images */ + const struct nk_style_item *background; + if (*state & NK_WIDGET_STATE_ACTIVED) + background = &style->active; + else if (*state & NK_WIDGET_STATE_HOVER) + background = &style->hover; + else background = &style->normal; + + /* draw background frame */ + if (background->type == NK_STYLE_ITEM_COLOR) { + nk_stroke_rect(out, bounds, style->rounding, style->border, style->border_color); + nk_fill_rect(out, bounds, style->rounding, background->data.color); + } else nk_draw_image(out, bounds, &background->data.image, nk_white);} + + area.w = NK_MAX(0, area.w - style->cursor_size); + if (edit->active) + { + int total_lines = 1; + struct nk_vec2 text_size = nk_vec2(0,0); + + /* text pointer positions */ + const char *cursor_ptr = 0; + const char *select_begin_ptr = 0; + const char *select_end_ptr = 0; + + /* 2D pixel positions */ + struct nk_vec2 cursor_pos = nk_vec2(0,0); + struct nk_vec2 selection_offset_start = nk_vec2(0,0); + struct nk_vec2 selection_offset_end = nk_vec2(0,0); + + int selection_begin = NK_MIN(edit->select_start, edit->select_end); + int selection_end = NK_MAX(edit->select_start, edit->select_end); + + /* calculate total line count + total space + cursor/selection position */ + float line_width = 0.0f; + if (text && len) + { + /* utf8 encoding */ + float glyph_width; + int glyph_len = 0; + nk_rune unicode = 0; + int text_len = 0; + int glyphs = 0; + int row_begin = 0; + + glyph_len = nk_utf_decode(text, &unicode, len); + glyph_width = font->width(font->userdata, font->height, text, glyph_len); + line_width = 0; + + /* iterate all lines */ + while ((text_len < len) && glyph_len) + { + /* set cursor 2D position and line */ + if (!cursor_ptr && glyphs == edit->cursor) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + cursor_pos.y = (float)(total_lines-1) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + cursor_pos.x = row_size.x; + cursor_ptr = text + text_len; + } + + /* set start selection 2D position and line */ + if (!select_begin_ptr && edit->select_start != edit->select_end && + glyphs == selection_begin) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + selection_offset_start.y = (float)(NK_MAX(total_lines-1,0)) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + selection_offset_start.x = row_size.x; + select_begin_ptr = text + text_len; + } + + /* set end selection 2D position and line */ + if (!select_end_ptr && edit->select_start != edit->select_end && + glyphs == selection_end) + { + int glyph_offset; + struct nk_vec2 out_offset; + struct nk_vec2 row_size; + const char *remaining; + + /* calculate 2d position */ + selection_offset_end.y = (float)(total_lines-1) * row_height; + row_size = nk_text_calculate_text_bounds(font, text+row_begin, + text_len-row_begin, row_height, &remaining, + &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); + selection_offset_end.x = row_size.x; + select_end_ptr = text + text_len; + } + if (unicode == '\n') { + text_size.x = NK_MAX(text_size.x, line_width); + total_lines++; + line_width = 0; + text_len++; + glyphs++; + row_begin = text_len; + glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); + glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); + continue; + } + + glyphs++; + text_len += glyph_len; + line_width += (float)glyph_width; + + glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); + glyph_width = font->width(font->userdata, font->height, + text+text_len, glyph_len); + continue; + } + text_size.y = (float)total_lines * row_height; + + /* handle case when cursor is at end of text buffer */ + if (!cursor_ptr && edit->cursor == edit->string.len) { + cursor_pos.x = line_width; + cursor_pos.y = text_size.y - row_height; + } + } + { + /* scrollbar */ + if (cursor_follow) + { + /* update scrollbar to follow cursor */ + if (!(flags & NK_EDIT_NO_HORIZONTAL_SCROLL)) { + /* horizontal scroll */ + const float scroll_increment = area.w * 0.25f; + if (cursor_pos.x < edit->scrollbar.x) + edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x - scroll_increment); + if (cursor_pos.x >= edit->scrollbar.x + area.w) + edit->scrollbar.x = (float)(int)NK_MAX(0.0f, edit->scrollbar.x + scroll_increment); + } else edit->scrollbar.x = 0; + + if (flags & NK_EDIT_MULTILINE) { + /* vertical scroll */ + if (cursor_pos.y < edit->scrollbar.y) + edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y - row_height); + if (cursor_pos.y >= edit->scrollbar.y + area.h) + edit->scrollbar.y = edit->scrollbar.y + row_height; + } else edit->scrollbar.y = 0; + } + + /* scrollbar widget */ + if (flags & NK_EDIT_MULTILINE) + { + nk_flags ws; + struct nk_rect scroll; + float scroll_target; + float scroll_offset; + float scroll_step; + float scroll_inc; + + scroll = area; + scroll.x = (bounds.x + bounds.w - style->border) - style->scrollbar_size.x; + scroll.w = style->scrollbar_size.x; + + scroll_offset = edit->scrollbar.y; + scroll_step = scroll.h * 0.10f; + scroll_inc = scroll.h * 0.01f; + scroll_target = text_size.y; + edit->scrollbar.y = nk_do_scrollbarv(&ws, out, scroll, 0, + scroll_offset, scroll_target, scroll_step, scroll_inc, + &style->scrollbar, in, font); + } + } + + /* draw text */ + {struct nk_color background_color; + struct nk_color text_color; + struct nk_color sel_background_color; + struct nk_color sel_text_color; + struct nk_color cursor_color; + struct nk_color cursor_text_color; + const struct nk_style_item *background; + nk_push_scissor(out, clip); + + /* select correct colors to draw */ + if (*state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + text_color = style->text_active; + sel_text_color = style->selected_text_hover; + sel_background_color = style->selected_hover; + cursor_color = style->cursor_hover; + cursor_text_color = style->cursor_text_hover; + } else if (*state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text_color = style->text_hover; + sel_text_color = style->selected_text_hover; + sel_background_color = style->selected_hover; + cursor_text_color = style->cursor_text_hover; + cursor_color = style->cursor_hover; + } else { + background = &style->normal; + text_color = style->text_normal; + sel_text_color = style->selected_text_normal; + sel_background_color = style->selected_normal; + cursor_color = style->cursor_normal; + cursor_text_color = style->cursor_text_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) + background_color = nk_rgba(0,0,0,0); + else background_color = background->data.color; + + + if (edit->select_start == edit->select_end) { + /* no selection so just draw the complete text */ + const char *begin = nk_str_get_const(&edit->string); + int l = nk_str_len_char(&edit->string); + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, l, row_height, font, + background_color, text_color, nk_false); + } else { + /* edit has selection so draw 1-3 text chunks */ + if (edit->select_start != edit->select_end && selection_begin > 0){ + /* draw unselected text before selection */ + const char *begin = nk_str_get_const(&edit->string); + NK_ASSERT(select_begin_ptr); + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, (int)(select_begin_ptr - begin), + row_height, font, background_color, text_color, nk_false); + } + if (edit->select_start != edit->select_end) { + /* draw selected text */ + NK_ASSERT(select_begin_ptr); + if (!select_end_ptr) { + const char *begin = nk_str_get_const(&edit->string); + select_end_ptr = begin + nk_str_len_char(&edit->string); + } + nk_edit_draw_text(out, style, + area.x - edit->scrollbar.x, + area.y + selection_offset_start.y - edit->scrollbar.y, + selection_offset_start.x, + select_begin_ptr, (int)(select_end_ptr - select_begin_ptr), + row_height, font, sel_background_color, sel_text_color, nk_true); + } + if ((edit->select_start != edit->select_end && + selection_end < edit->string.len)) + { + /* draw unselected text after selected text */ + const char *begin = select_end_ptr; + const char *end = nk_str_get_const(&edit->string) + + nk_str_len_char(&edit->string); + NK_ASSERT(select_end_ptr); + nk_edit_draw_text(out, style, + area.x - edit->scrollbar.x, + area.y + selection_offset_end.y - edit->scrollbar.y, + selection_offset_end.x, + begin, (int)(end - begin), row_height, font, + background_color, text_color, nk_true); + } + } + + /* cursor */ + if (edit->select_start == edit->select_end) + { + if (edit->cursor >= nk_str_len(&edit->string) || + (cursor_ptr && *cursor_ptr == '\n')) { + /* draw cursor at end of line */ + struct nk_rect cursor; + cursor.w = style->cursor_size; + cursor.h = font->height; + cursor.x = area.x + cursor_pos.x - edit->scrollbar.x; + cursor.y = area.y + cursor_pos.y + row_height/2.0f - cursor.h/2.0f; + cursor.y -= edit->scrollbar.y; + nk_fill_rect(out, cursor, 0, cursor_color); + } else { + /* draw cursor inside text */ + int glyph_len; + struct nk_rect label; + struct nk_text txt; + + nk_rune unicode; + NK_ASSERT(cursor_ptr); + glyph_len = nk_utf_decode(cursor_ptr, &unicode, 4); + + label.x = area.x + cursor_pos.x - edit->scrollbar.x; + label.y = area.y + cursor_pos.y - edit->scrollbar.y; + label.w = font->width(font->userdata, font->height, cursor_ptr, glyph_len); + label.h = row_height; + + txt.padding = nk_vec2(0,0); + txt.background = cursor_color;; + txt.text = cursor_text_color; + nk_fill_rect(out, label, 0, cursor_color); + nk_widget_text(out, label, cursor_ptr, glyph_len, &txt, NK_TEXT_LEFT, font); + } + }} + } else { + /* not active so just draw text */ + int l = nk_str_len_char(&edit->string); + const char *begin = nk_str_get_const(&edit->string); + + const struct nk_style_item *background; + struct nk_color background_color; + struct nk_color text_color; + nk_push_scissor(out, clip); + if (*state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + text_color = style->text_active; + } else if (*state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text_color = style->text_hover; + } else { + background = &style->normal; + text_color = style->text_normal; + } + if (background->type == NK_STYLE_ITEM_IMAGE) + background_color = nk_rgba(0,0,0,0); + else background_color = background->data.color; + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, + area.y - edit->scrollbar.y, 0, begin, l, row_height, font, + background_color, text_color, nk_false); + } + nk_push_scissor(out, old_clip);} + return ret; +} NK_API void nk_edit_focus(struct nk_context *ctx, nk_flags flags) { @@ -21250,7 +23153,6 @@ nk_edit_focus(struct nk_context *ctx, nk_flags flags) if (flags & NK_EDIT_ALWAYS_INSERT_MODE) win->edit.mode = NK_TEXT_EDIT_MODE_INSERT; } - NK_API void nk_edit_unfocus(struct nk_context *ctx) { @@ -21263,7 +23165,6 @@ nk_edit_unfocus(struct nk_context *ctx) win->edit.active = nk_false; win->edit.name = 0; } - NK_API nk_flags nk_edit_string(struct nk_context *ctx, nk_flags flags, char *memory, int *len, int max, nk_plugin_filter filter) @@ -21318,10 +23219,8 @@ nk_edit_string(struct nk_context *ctx, nk_flags flags, win->edit.mode = edit->mode; win->edit.scrollbar.x = (nk_uint)edit->scrollbar.x; win->edit.scrollbar.y = (nk_uint)edit->scrollbar.y; - } - return state; + } return state; } - NK_API nk_flags nk_edit_buffer(struct nk_context *ctx, nk_flags flags, struct nk_text_edit *edit, nk_plugin_filter filter) @@ -21381,10 +23280,8 @@ nk_edit_buffer(struct nk_context *ctx, nk_flags flags, } else if (prev_state && !edit->active) { /* current edit is now cold */ win->edit.active = nk_false; - } - return ret_flags; + } return ret_flags; } - NK_API nk_flags nk_edit_string_zero_terminated(struct nk_context *ctx, nk_flags flags, char *buffer, int max, nk_plugin_filter filter) @@ -21396,12 +23293,284 @@ nk_edit_string_zero_terminated(struct nk_context *ctx, nk_flags flags, return result; } -/*---------------------------------------------------------------- + + + + +/* =============================================================== * - * PROPERTY + * PROPERTY * - * --------------------------------------------------------------*/ -NK_INTERN struct nk_property_variant + * ===============================================================*/ +NK_LIB void +nk_drag_behavior(nk_flags *state, const struct nk_input *in, + struct nk_rect drag, struct nk_property_variant *variant, + float inc_per_pixel) +{ + int left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; + int left_mouse_click_in_cursor = in && + nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, drag, nk_true); + + nk_widget_state_reset(state); + if (nk_input_is_mouse_hovering_rect(in, drag)) + *state = NK_WIDGET_STATE_HOVERED; + + if (left_mouse_down && left_mouse_click_in_cursor) { + float delta, pixels; + pixels = in->mouse.delta.x; + delta = pixels * inc_per_pixel; + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = variant->value.i + (int)delta; + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i); + break; + case NK_PROPERTY_FLOAT: + variant->value.f = variant->value.f + (float)delta; + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f); + break; + case NK_PROPERTY_DOUBLE: + variant->value.d = variant->value.d + (double)delta; + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d); + break; + } + *state = NK_WIDGET_STATE_ACTIVE; + } + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, drag)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, drag)) + *state |= NK_WIDGET_STATE_LEFT; +} +NK_LIB void +nk_property_behavior(nk_flags *ws, const struct nk_input *in, + struct nk_rect property, struct nk_rect label, struct nk_rect edit, + struct nk_rect empty, int *state, struct nk_property_variant *variant, + float inc_per_pixel) +{ + if (in && *state == NK_PROPERTY_DEFAULT) { + if (nk_button_behavior(ws, edit, in, NK_BUTTON_DEFAULT)) + *state = NK_PROPERTY_EDIT; + else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, label, nk_true)) + *state = NK_PROPERTY_DRAG; + else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, empty, nk_true)) + *state = NK_PROPERTY_DRAG; + } + if (*state == NK_PROPERTY_DRAG) { + nk_drag_behavior(ws, in, property, variant, inc_per_pixel); + if (!(*ws & NK_WIDGET_STATE_ACTIVED)) *state = NK_PROPERTY_DEFAULT; + } +} +NK_LIB void +nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *style, + const struct nk_rect *bounds, const struct nk_rect *label, nk_flags state, + const char *name, int len, const struct nk_user_font *font) +{ + struct nk_text text; + const struct nk_style_item *background; + + /* select correct background and text color */ + if (state & NK_WIDGET_STATE_ACTIVED) { + background = &style->active; + text.text = style->label_active; + } else if (state & NK_WIDGET_STATE_HOVER) { + background = &style->hover; + text.text = style->label_hover; + } else { + background = &style->normal; + text.text = style->label_normal; + } + + /* draw background */ + if (background->type == NK_STYLE_ITEM_IMAGE) { + nk_draw_image(out, *bounds, &background->data.image, nk_white); + text.background = nk_rgba(0,0,0,0); + } else { + text.background = background->data.color; + nk_fill_rect(out, *bounds, style->rounding, background->data.color); + nk_stroke_rect(out, *bounds, style->rounding, style->border, background->data.color); + } + + /* draw label */ + text.padding = nk_vec2(0,0); + nk_widget_text(out, *label, name, len, &text, NK_TEXT_CENTERED, font); +} +NK_LIB void +nk_do_property(nk_flags *ws, + struct nk_command_buffer *out, struct nk_rect property, + const char *name, struct nk_property_variant *variant, + float inc_per_pixel, char *buffer, int *len, + int *state, int *cursor, int *select_begin, int *select_end, + const struct nk_style_property *style, + enum nk_property_filter filter, struct nk_input *in, + const struct nk_user_font *font, struct nk_text_edit *text_edit, + enum nk_button_behavior behavior) +{ + const nk_plugin_filter filters[] = { + nk_filter_decimal, + nk_filter_float + }; + int active, old; + int num_len, name_len; + char string[NK_MAX_NUMBER_BUFFER]; + float size; + + char *dst = 0; + int *length; + + struct nk_rect left; + struct nk_rect right; + struct nk_rect label; + struct nk_rect edit; + struct nk_rect empty; + + /* left decrement button */ + left.h = font->height/2; + left.w = left.h; + left.x = property.x + style->border + style->padding.x; + left.y = property.y + style->border + property.h/2.0f - left.h/2; + + /* text label */ + name_len = nk_strlen(name); + size = font->width(font->userdata, font->height, name, name_len); + label.x = left.x + left.w + style->padding.x; + label.w = (float)size + 2 * style->padding.x; + label.y = property.y + style->border + style->padding.y; + label.h = property.h - (2 * style->border + 2 * style->padding.y); + + /* right increment button */ + right.y = left.y; + right.w = left.w; + right.h = left.h; + right.x = property.x + property.w - (right.w + style->padding.x); + + /* edit */ + if (*state == NK_PROPERTY_EDIT) { + size = font->width(font->userdata, font->height, buffer, *len); + size += style->edit.cursor_size; + length = len; + dst = buffer; + } else { + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + nk_itoa(string, variant->value.i); + num_len = nk_strlen(string); + break; + case NK_PROPERTY_FLOAT: + NK_DTOA(string, (double)variant->value.f); + num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); + break; + case NK_PROPERTY_DOUBLE: + NK_DTOA(string, variant->value.d); + num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); + break; + } + size = font->width(font->userdata, font->height, string, num_len); + dst = string; + length = &num_len; + } + + edit.w = (float)size + 2 * style->padding.x; + edit.w = NK_MIN(edit.w, right.x - (label.x + label.w)); + edit.x = right.x - (edit.w + style->padding.x); + edit.y = property.y + style->border; + edit.h = property.h - (2 * style->border); + + /* empty left space activator */ + empty.w = edit.x - (label.x + label.w); + empty.x = label.x + label.w; + empty.y = property.y; + empty.h = property.h; + + /* update property */ + old = (*state == NK_PROPERTY_EDIT); + nk_property_behavior(ws, in, property, label, edit, empty, state, variant, inc_per_pixel); + + /* draw property */ + if (style->draw_begin) style->draw_begin(out, style->userdata); + nk_draw_property(out, style, &property, &label, *ws, name, name_len, font); + if (style->draw_end) style->draw_end(out, style->userdata); + + /* execute right button */ + if (nk_do_button_symbol(ws, out, left, style->sym_left, behavior, &style->dec_button, in, font)) { + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i - variant->step.i, variant->max_value.i); break; + case NK_PROPERTY_FLOAT: + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f - variant->step.f, variant->max_value.f); break; + case NK_PROPERTY_DOUBLE: + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d - variant->step.d, variant->max_value.d); break; + } + } + /* execute left button */ + if (nk_do_button_symbol(ws, out, right, style->sym_right, behavior, &style->inc_button, in, font)) { + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i + variant->step.i, variant->max_value.i); break; + case NK_PROPERTY_FLOAT: + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f + variant->step.f, variant->max_value.f); break; + case NK_PROPERTY_DOUBLE: + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d + variant->step.d, variant->max_value.d); break; + } + } + if (old != NK_PROPERTY_EDIT && (*state == NK_PROPERTY_EDIT)) { + /* property has been activated so setup buffer */ + NK_MEMCPY(buffer, dst, (nk_size)*length); + *cursor = nk_utf_len(buffer, *length); + *len = *length; + length = len; + dst = buffer; + active = 0; + } else active = (*state == NK_PROPERTY_EDIT); + + /* execute and run text edit field */ + nk_textedit_clear_state(text_edit, NK_TEXT_EDIT_SINGLE_LINE, filters[filter]); + text_edit->active = (unsigned char)active; + text_edit->string.len = *length; + text_edit->cursor = NK_CLAMP(0, *cursor, *length); + text_edit->select_start = NK_CLAMP(0,*select_begin, *length); + text_edit->select_end = NK_CLAMP(0,*select_end, *length); + text_edit->string.buffer.allocated = (nk_size)*length; + text_edit->string.buffer.memory.size = NK_MAX_NUMBER_BUFFER; + text_edit->string.buffer.memory.ptr = dst; + text_edit->string.buffer.size = NK_MAX_NUMBER_BUFFER; + text_edit->mode = NK_TEXT_EDIT_MODE_INSERT; + nk_do_edit(ws, out, edit, NK_EDIT_FIELD|NK_EDIT_AUTO_SELECT, + filters[filter], text_edit, &style->edit, (*state == NK_PROPERTY_EDIT) ? in: 0, font); + + *length = text_edit->string.len; + *cursor = text_edit->cursor; + *select_begin = text_edit->select_start; + *select_end = text_edit->select_end; + if (text_edit->active && nk_input_is_key_pressed(in, NK_KEY_ENTER)) + text_edit->active = nk_false; + + if (active && !text_edit->active) { + /* property is now not active so convert edit text to value*/ + *state = NK_PROPERTY_DEFAULT; + buffer[*len] = '\0'; + switch (variant->kind) { + default: break; + case NK_PROPERTY_INT: + variant->value.i = nk_strtoi(buffer, 0); + variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i); + break; + case NK_PROPERTY_FLOAT: + nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); + variant->value.f = nk_strtof(buffer, 0); + variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f); + break; + case NK_PROPERTY_DOUBLE: + nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); + variant->value.d = nk_strtod(buffer, 0); + variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d); + break; + } + } +} +NK_LIB struct nk_property_variant nk_property_variant_int(int value, int min_value, int max_value, int step) { struct nk_property_variant result; @@ -21412,8 +23581,7 @@ nk_property_variant_int(int value, int min_value, int max_value, int step) result.step.i = step; return result; } - -NK_INTERN struct nk_property_variant +NK_LIB struct nk_property_variant nk_property_variant_float(float value, float min_value, float max_value, float step) { struct nk_property_variant result; @@ -21424,8 +23592,7 @@ nk_property_variant_float(float value, float min_value, float max_value, float s result.step.f = step; return result; } - -NK_INTERN struct nk_property_variant +NK_LIB struct nk_property_variant nk_property_variant_double(double value, double min_value, double max_value, double step) { @@ -21437,8 +23604,7 @@ nk_property_variant_double(double value, double min_value, double max_value, result.step.d = step; return result; } - -NK_INTERN void +NK_LIB void nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant *variant, float inc_per_pixel, const enum nk_property_filter filter) { @@ -21538,7 +23704,6 @@ nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant win->property.active = 0; } } - NK_API void nk_property_int(struct nk_context *ctx, const char *name, int min, int *val, int max, int step, float inc_per_pixel) @@ -21553,7 +23718,6 @@ nk_property_int(struct nk_context *ctx, const char *name, nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT); *val = variant.value.i; } - NK_API void nk_property_float(struct nk_context *ctx, const char *name, float min, float *val, float max, float step, float inc_per_pixel) @@ -21568,7 +23732,6 @@ nk_property_float(struct nk_context *ctx, const char *name, nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); *val = variant.value.f; } - NK_API void nk_property_double(struct nk_context *ctx, const char *name, double min, double *val, double max, double step, float inc_per_pixel) @@ -21583,7 +23746,6 @@ nk_property_double(struct nk_context *ctx, const char *name, nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); *val = variant.value.d; } - NK_API int nk_propertyi(struct nk_context *ctx, const char *name, int min, int val, int max, int step, float inc_per_pixel) @@ -21598,7 +23760,6 @@ nk_propertyi(struct nk_context *ctx, const char *name, int min, int val, val = variant.value.i; return val; } - NK_API float nk_propertyf(struct nk_context *ctx, const char *name, float min, float val, float max, float step, float inc_per_pixel) @@ -21613,7 +23774,6 @@ nk_propertyf(struct nk_context *ctx, const char *name, float min, val = variant.value.f; return val; } - NK_API double nk_propertyd(struct nk_context *ctx, const char *name, double min, double val, double max, double step, float inc_per_pixel) @@ -21629,53 +23789,15 @@ nk_propertyd(struct nk_context *ctx, const char *name, double min, return val; } -/*---------------------------------------------------------------- - * - * COLOR PICKER - * - * --------------------------------------------------------------*/ -NK_API int -nk_color_pick(struct nk_context * ctx, struct nk_color *color, - enum nk_color_format fmt) -{ - struct nk_window *win; - struct nk_panel *layout; - const struct nk_style *config; - const struct nk_input *in; - enum nk_widget_layout_states state; - struct nk_rect bounds; - NK_ASSERT(ctx); - NK_ASSERT(color); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout || !color) - return 0; - win = ctx->current; - config = &ctx->style; - layout = win->layout; - state = nk_widget(&bounds, ctx); - if (!state) return 0; - in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; - return nk_do_color_picker(&ctx->last_widget_state, &win->buffer, color, fmt, bounds, - nk_vec2(0,0), in, config->font); -} -NK_API struct nk_color -nk_color_picker(struct nk_context *ctx, struct nk_color color, - enum nk_color_format fmt) -{ - nk_color_pick(ctx, &color, fmt); - return color; -} - -/* ------------------------------------------------------------- +/* ============================================================== * * CHART * - * --------------------------------------------------------------*/ + * ===============================================================*/ NK_API int nk_chart_begin_colored(struct nk_context *ctx, enum nk_chart_type type, struct nk_color color, struct nk_color highlight, @@ -21735,12 +23857,13 @@ nk_chart_begin_colored(struct nk_context *ctx, enum nk_chart_type type, } return 1; } - NK_API int nk_chart_begin(struct nk_context *ctx, const enum nk_chart_type type, int count, float min_value, float max_value) -{return nk_chart_begin_colored(ctx, type, ctx->style.chart.color, ctx->style.chart.selected_color, count, min_value, max_value);} - +{ + return nk_chart_begin_colored(ctx, type, ctx->style.chart.color, + ctx->style.chart.selected_color, count, min_value, max_value); +} NK_API void nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type type, struct nk_color color, struct nk_color highlight, @@ -21764,12 +23887,13 @@ nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type type, slot->max = NK_MAX(min_value, max_value); slot->range = slot->max - slot->min;} } - NK_API void nk_chart_add_slot(struct nk_context *ctx, const enum nk_chart_type type, int count, float min_value, float max_value) -{nk_chart_add_slot_colored(ctx, type, ctx->style.chart.color, ctx->style.chart.selected_color, count, min_value, max_value);} - +{ + nk_chart_add_slot_colored(ctx, type, ctx->style.chart.color, + ctx->style.chart.selected_color, count, min_value, max_value); +} NK_INTERN nk_flags nk_chart_push_line(struct nk_context *ctx, struct nk_window *win, struct nk_chart *g, float value, int slot) @@ -21840,7 +23964,6 @@ nk_chart_push_line(struct nk_context *ctx, struct nk_window *win, g->slots[slot].index += 1; return ret; } - NK_INTERN nk_flags nk_chart_push_column(const struct nk_context *ctx, struct nk_window *win, struct nk_chart *chart, float value, int slot) @@ -21887,7 +24010,6 @@ nk_chart_push_column(const struct nk_context *ctx, struct nk_window *win, chart->slots[slot].index += 1; return ret; } - NK_API nk_flags nk_chart_push_slot(struct nk_context *ctx, float value, int slot) { @@ -21914,11 +24036,11 @@ nk_chart_push_slot(struct nk_context *ctx, float value, int slot) } return flags; } - NK_API nk_flags nk_chart_push(struct nk_context *ctx, float value) -{return nk_chart_push_slot(ctx, value, 0);} - +{ + return nk_chart_push_slot(ctx, value, 0); +} NK_API void nk_chart_end(struct nk_context *ctx) { @@ -21935,7 +24057,6 @@ nk_chart_end(struct nk_context *ctx) NK_MEMSET(chart, 0, sizeof(*chart)); return; } - NK_API void nk_plot(struct nk_context *ctx, enum nk_chart_type type, const float *values, int count, int offset) @@ -21961,7 +24082,6 @@ nk_plot(struct nk_context *ctx, enum nk_chart_type type, const float *values, nk_chart_end(ctx); } } - NK_API void nk_plot_function(struct nk_context *ctx, enum nk_chart_type type, void *userdata, float(*value_getter)(void* user, int index), int count, int offset) @@ -21988,782 +24108,217 @@ nk_plot_function(struct nk_context *ctx, enum nk_chart_type type, void *userdata } } -/* ------------------------------------------------------------- + + + + +/* ============================================================== * - * GROUP + * COLOR PICKER * - * --------------------------------------------------------------*/ -NK_API int -nk_group_scrolled_offset_begin(struct nk_context *ctx, - nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags) + * ===============================================================*/ +NK_LIB int +nk_color_picker_behavior(nk_flags *state, + const struct nk_rect *bounds, const struct nk_rect *matrix, + const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, + struct nk_colorf *color, const struct nk_input *in) { - struct nk_rect bounds; - struct nk_window panel; - struct nk_window *win; + float hsva[4]; + int value_changed = 0; + int hsv_changed = 0; - win = ctx->current; - nk_panel_alloc_space(&bounds, ctx); - {const struct nk_rect *c = &win->layout->clip; - if (!NK_INTERSECT(c->x, c->y, c->w, c->h, bounds.x, bounds.y, bounds.w, bounds.h) && - !(flags & NK_WINDOW_MOVABLE)) { - return 0; - }} - if (win->flags & NK_WINDOW_ROM) - flags |= NK_WINDOW_ROM; + NK_ASSERT(state); + NK_ASSERT(matrix); + NK_ASSERT(hue_bar); + NK_ASSERT(color); - /* initialize a fake window to create the panel from */ - nk_zero(&panel, sizeof(panel)); - panel.bounds = bounds; - panel.flags = flags; - panel.scrollbar.x = *x_offset; - panel.scrollbar.y = *y_offset; - panel.buffer = win->buffer; - panel.layout = (struct nk_panel*)nk_create_panel(ctx); - ctx->current = &panel; - nk_panel_begin(ctx, (flags & NK_WINDOW_TITLE) ? title: 0, NK_PANEL_GROUP); - - win->buffer = panel.buffer; - win->buffer.clip = panel.layout->clip; - panel.layout->offset_x = x_offset; - panel.layout->offset_y = y_offset; - panel.layout->parent = win->layout; - win->layout = panel.layout; - - ctx->current = win; - if ((panel.layout->flags & NK_WINDOW_CLOSED) || - (panel.layout->flags & NK_WINDOW_MINIMIZED)) - { - nk_flags f = panel.layout->flags; - nk_group_scrolled_end(ctx); - if (f & NK_WINDOW_CLOSED) - return NK_WINDOW_CLOSED; - if (f & NK_WINDOW_MINIMIZED) - return NK_WINDOW_MINIMIZED; + /* color matrix */ + nk_colorf_hsva_fv(hsva, *color); + if (nk_button_behavior(state, *matrix, in, NK_BUTTON_REPEATER)) { + hsva[1] = NK_SATURATE((in->mouse.pos.x - matrix->x) / (matrix->w-1)); + hsva[2] = 1.0f - NK_SATURATE((in->mouse.pos.y - matrix->y) / (matrix->h-1)); + value_changed = hsv_changed = 1; } - return 1; -} - -NK_API void -nk_group_scrolled_end(struct nk_context *ctx) -{ - struct nk_window *win; - struct nk_panel *parent; - struct nk_panel *g; - - struct nk_rect clip; - struct nk_window pan; - struct nk_vec2 panel_padding; - - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - if (!ctx || !ctx->current) - return; - - /* make sure nk_group_begin was called correctly */ - NK_ASSERT(ctx->current); - win = ctx->current; - NK_ASSERT(win->layout); - g = win->layout; - NK_ASSERT(g->parent); - parent = g->parent; - - /* dummy window */ - nk_zero_struct(pan); - panel_padding = nk_panel_get_padding(&ctx->style, NK_PANEL_GROUP); - pan.bounds.y = g->bounds.y - (g->header_height + g->menu.h); - pan.bounds.x = g->bounds.x - panel_padding.x; - pan.bounds.w = g->bounds.w + 2 * panel_padding.x; - pan.bounds.h = g->bounds.h + g->header_height + g->menu.h; - if (g->flags & NK_WINDOW_BORDER) { - pan.bounds.x -= g->border; - pan.bounds.y -= g->border; - pan.bounds.w += 2*g->border; - pan.bounds.h += 2*g->border; + /* hue bar */ + if (nk_button_behavior(state, *hue_bar, in, NK_BUTTON_REPEATER)) { + hsva[0] = NK_SATURATE((in->mouse.pos.y - hue_bar->y) / (hue_bar->h-1)); + value_changed = hsv_changed = 1; } - if (!(g->flags & NK_WINDOW_NO_SCROLLBAR)) { - pan.bounds.w += ctx->style.window.scrollbar_size.x; - pan.bounds.h += ctx->style.window.scrollbar_size.y; - } - pan.scrollbar.x = *g->offset_x; - pan.scrollbar.y = *g->offset_y; - pan.flags = g->flags; - pan.buffer = win->buffer; - pan.layout = g; - pan.parent = win; - ctx->current = &pan; - - /* make sure group has correct clipping rectangle */ - nk_unify(&clip, &parent->clip, pan.bounds.x, pan.bounds.y, - pan.bounds.x + pan.bounds.w, pan.bounds.y + pan.bounds.h + panel_padding.x); - nk_push_scissor(&pan.buffer, clip); - nk_end(ctx); - - win->buffer = pan.buffer; - nk_push_scissor(&win->buffer, parent->clip); - ctx->current = win; - win->layout = parent; - g->bounds = pan.bounds; - return; -} - -NK_API int -nk_group_scrolled_begin(struct nk_context *ctx, - struct nk_scroll *scroll, const char *title, nk_flags flags) -{return nk_group_scrolled_offset_begin(ctx, &scroll->x, &scroll->y, title, flags);} - -NK_API int -nk_group_begin(struct nk_context *ctx, const char *title, nk_flags flags) -{ - int title_len; - nk_hash title_hash; - struct nk_window *win; - nk_uint *x_offset; - nk_uint *y_offset; - - NK_ASSERT(ctx); - NK_ASSERT(title); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout || !title) - return 0; - - /* find persistent group scrollbar value */ - win = ctx->current; - title_len = (int)nk_strlen(title); - title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_GROUP); - x_offset = nk_find_value(win, title_hash); - if (!x_offset) { - x_offset = nk_add_value(ctx, win, title_hash, 0); - y_offset = nk_add_value(ctx, win, title_hash+1, 0); - - NK_ASSERT(x_offset); - NK_ASSERT(y_offset); - if (!x_offset || !y_offset) return 0; - *x_offset = *y_offset = 0; - } else y_offset = nk_find_value(win, title_hash+1); - return nk_group_scrolled_offset_begin(ctx, x_offset, y_offset, title, flags); -} - -NK_API void -nk_group_end(struct nk_context *ctx) -{nk_group_scrolled_end(ctx);} - -NK_API int -nk_list_view_begin(struct nk_context *ctx, struct nk_list_view *view, - const char *title, nk_flags flags, int row_height, int row_count) -{ - int title_len; - nk_hash title_hash; - nk_uint *x_offset; - nk_uint *y_offset; - - int result; - struct nk_window *win; - struct nk_panel *layout; - const struct nk_style *style; - struct nk_vec2 item_spacing; - - NK_ASSERT(ctx); - NK_ASSERT(view); - NK_ASSERT(title); - if (!ctx || !view || !title) return 0; - - win = ctx->current; - style = &ctx->style; - item_spacing = style->window.spacing; - row_height += NK_MAX(0, (int)item_spacing.y); - - /* find persistent list view scrollbar offset */ - title_len = (int)nk_strlen(title); - title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_GROUP); - x_offset = nk_find_value(win, title_hash); - if (!x_offset) { - x_offset = nk_add_value(ctx, win, title_hash, 0); - y_offset = nk_add_value(ctx, win, title_hash+1, 0); - - NK_ASSERT(x_offset); - NK_ASSERT(y_offset); - if (!x_offset || !y_offset) return 0; - *x_offset = *y_offset = 0; - } else y_offset = nk_find_value(win, title_hash+1); - view->scroll_value = *y_offset; - view->scroll_pointer = y_offset; - - *y_offset = 0; - result = nk_group_scrolled_offset_begin(ctx, x_offset, y_offset, title, flags); - win = ctx->current; - layout = win->layout; - - view->total_height = row_height * NK_MAX(row_count,1); - view->begin = (int)NK_MAX(((float)view->scroll_value / (float)row_height), 0.0f); - view->count = (int)NK_MAX(nk_iceilf((layout->clip.h)/(float)row_height), 0); - view->end = view->begin + view->count; - view->ctx = ctx; - return result; -} - -NK_API void -nk_list_view_end(struct nk_list_view *view) -{ - struct nk_context *ctx; - struct nk_window *win; - struct nk_panel *layout; - - NK_ASSERT(view); - NK_ASSERT(view->ctx); - NK_ASSERT(view->scroll_pointer); - if (!view || !view->ctx) return; - - ctx = view->ctx; - win = ctx->current; - layout = win->layout; - layout->at_y = layout->bounds.y + (float)view->total_height; - *view->scroll_pointer = *view->scroll_pointer + view->scroll_value; - nk_group_end(view->ctx); -} - -/* -------------------------------------------------------------- - * - * POPUP - * - * --------------------------------------------------------------*/ -NK_API int -nk_popup_begin(struct nk_context *ctx, enum nk_popup_type type, - const char *title, nk_flags flags, struct nk_rect rect) -{ - struct nk_window *popup; - struct nk_window *win; - struct nk_panel *panel; - - int title_len; - nk_hash title_hash; - nk_size allocated; - - NK_ASSERT(ctx); - NK_ASSERT(title); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - panel = win->layout; - NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP) && "popups are not allowed to have popups"); - (void)panel; - title_len = (int)nk_strlen(title); - title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_POPUP); - - popup = win->popup.win; - if (!popup) { - popup = (struct nk_window*)nk_create_window(ctx); - popup->parent = win; - win->popup.win = popup; - win->popup.active = 0; - win->popup.type = NK_PANEL_POPUP; - } - - /* make sure we have correct popup */ - if (win->popup.name != title_hash) { - if (!win->popup.active) { - nk_zero(popup, sizeof(*popup)); - win->popup.name = title_hash; - win->popup.active = 1; - win->popup.type = NK_PANEL_POPUP; - } else return 0; - } - - /* popup position is local to window */ - ctx->current = popup; - rect.x += win->layout->clip.x; - rect.y += win->layout->clip.y; - - /* setup popup data */ - popup->parent = win; - popup->bounds = rect; - popup->seq = ctx->seq; - popup->layout = (struct nk_panel*)nk_create_panel(ctx); - popup->flags = flags; - popup->flags |= NK_WINDOW_BORDER; - if (type == NK_POPUP_DYNAMIC) - popup->flags |= NK_WINDOW_DYNAMIC; - - popup->buffer = win->buffer; - nk_start_popup(ctx, win); - allocated = ctx->memory.allocated; - nk_push_scissor(&popup->buffer, nk_null_rect); - - if (nk_panel_begin(ctx, title, NK_PANEL_POPUP)) { - /* popup is running therefore invalidate parent panels */ - struct nk_panel *root; - root = win->layout; - while (root) { - root->flags |= NK_WINDOW_ROM; - root->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM; - root = root->parent; + /* alpha bar */ + if (alpha_bar) { + if (nk_button_behavior(state, *alpha_bar, in, NK_BUTTON_REPEATER)) { + hsva[3] = 1.0f - NK_SATURATE((in->mouse.pos.y - alpha_bar->y) / (alpha_bar->h-1)); + value_changed = 1; } - win->popup.active = 1; - popup->layout->offset_x = &popup->scrollbar.x; - popup->layout->offset_y = &popup->scrollbar.y; - popup->layout->parent = win->layout; - return 1; - } else { - /* popup was closed/is invalid so cleanup */ - struct nk_panel *root; - root = win->layout; - while (root) { - root->flags |= NK_WINDOW_REMOVE_ROM; - root = root->parent; - } - win->popup.buf.active = 0; - win->popup.active = 0; - ctx->memory.allocated = allocated; - ctx->current = win; - nk_free_panel(ctx, popup->layout); - popup->layout = 0; - return 0; } + nk_widget_state_reset(state); + if (hsv_changed) { + *color = nk_hsva_colorfv(hsva); + *state = NK_WIDGET_STATE_ACTIVE; + } + if (value_changed) { + color->a = hsva[3]; + *state = NK_WIDGET_STATE_ACTIVE; + } + /* set color picker widget state */ + if (nk_input_is_mouse_hovering_rect(in, *bounds)) + *state = NK_WIDGET_STATE_HOVERED; + if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *bounds)) + *state |= NK_WIDGET_STATE_ENTERED; + else if (nk_input_is_mouse_prev_hovering_rect(in, *bounds)) + *state |= NK_WIDGET_STATE_LEFT; + return value_changed; } - -NK_INTERN int -nk_nonblock_begin(struct nk_context *ctx, - nk_flags flags, struct nk_rect body, struct nk_rect header, - enum nk_panel_type panel_type) +NK_LIB void +nk_draw_color_picker(struct nk_command_buffer *o, const struct nk_rect *matrix, + const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, + struct nk_colorf col) { - struct nk_window *popup; - struct nk_window *win; - struct nk_panel *panel; - int is_active = nk_true; + NK_STORAGE const struct nk_color black = {0,0,0,255}; + NK_STORAGE const struct nk_color white = {255, 255, 255, 255}; + NK_STORAGE const struct nk_color black_trans = {0,0,0,0}; - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; + const float crosshair_size = 7.0f; + struct nk_color temp; + float hsva[4]; + float line_y; + int i; - /* popups cannot have popups */ - win = ctx->current; - panel = win->layout; - NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP)); - (void)panel; - popup = win->popup.win; - if (!popup) { - /* create window for nonblocking popup */ - popup = (struct nk_window*)nk_create_window(ctx); - popup->parent = win; - win->popup.win = popup; - win->popup.type = panel_type; - nk_command_buffer_init(&popup->buffer, &ctx->memory, NK_CLIPPING_ON); - } else { - /* close the popup if user pressed outside or in the header */ - int pressed, in_body, in_header; - pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT); - in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body); - in_header = nk_input_is_mouse_hovering_rect(&ctx->input, header); - if (pressed && (!in_body || in_header)) - is_active = nk_false; + NK_ASSERT(o); + NK_ASSERT(matrix); + NK_ASSERT(hue_bar); + + /* draw hue bar */ + nk_colorf_hsva_fv(hsva, col); + for (i = 0; i < 6; ++i) { + NK_GLOBAL const struct nk_color hue_colors[] = { + {255, 0, 0, 255}, {255,255,0,255}, {0,255,0,255}, {0, 255,255,255}, + {0,0,255,255}, {255, 0, 255, 255}, {255, 0, 0, 255} + }; + nk_fill_rect_multi_color(o, + nk_rect(hue_bar->x, hue_bar->y + (float)i * (hue_bar->h/6.0f) + 0.5f, + hue_bar->w, (hue_bar->h/6.0f) + 0.5f), hue_colors[i], hue_colors[i], + hue_colors[i+1], hue_colors[i+1]); } - win->popup.header = header; + line_y = (float)(int)(hue_bar->y + hsva[0] * matrix->h + 0.5f); + nk_stroke_line(o, hue_bar->x-1, line_y, hue_bar->x + hue_bar->w + 2, + line_y, 1, nk_rgb(255,255,255)); - if (!is_active) { - /* remove read only mode from all parent panels */ - struct nk_panel *root = win->layout; - while (root) { - root->flags |= NK_WINDOW_REMOVE_ROM; - root = root->parent; - } - return is_active; + /* draw alpha bar */ + if (alpha_bar) { + float alpha = NK_SATURATE(col.a); + line_y = (float)(int)(alpha_bar->y + (1.0f - alpha) * matrix->h + 0.5f); + + nk_fill_rect_multi_color(o, *alpha_bar, white, white, black, black); + nk_stroke_line(o, alpha_bar->x-1, line_y, alpha_bar->x + alpha_bar->w + 2, + line_y, 1, nk_rgb(255,255,255)); } - popup->bounds = body; - popup->parent = win; - popup->layout = (struct nk_panel*)nk_create_panel(ctx); - popup->flags = flags; - popup->flags |= NK_WINDOW_BORDER; - popup->flags |= NK_WINDOW_DYNAMIC; - popup->seq = ctx->seq; - win->popup.active = 1; - NK_ASSERT(popup->layout); + /* draw color matrix */ + temp = nk_hsv_f(hsva[0], 1.0f, 1.0f); + nk_fill_rect_multi_color(o, *matrix, white, temp, temp, white); + nk_fill_rect_multi_color(o, *matrix, black_trans, black_trans, black, black); - nk_start_popup(ctx, win); - popup->buffer = win->buffer; - nk_push_scissor(&popup->buffer, nk_null_rect); - ctx->current = popup; - - nk_panel_begin(ctx, 0, panel_type); - win->buffer = popup->buffer; - popup->layout->parent = win->layout; - popup->layout->offset_x = &popup->scrollbar.x; - popup->layout->offset_y = &popup->scrollbar.y; - - /* set read only mode to all parent panels */ - {struct nk_panel *root; - root = win->layout; - while (root) { - root->flags |= NK_WINDOW_ROM; - root = root->parent; - }} - return is_active; + /* draw cross-hair */ + {struct nk_vec2 p; float S = hsva[1]; float V = hsva[2]; + p.x = (float)(int)(matrix->x + S * matrix->w); + p.y = (float)(int)(matrix->y + (1.0f - V) * matrix->h); + nk_stroke_line(o, p.x - crosshair_size, p.y, p.x-2, p.y, 1.0f, white); + nk_stroke_line(o, p.x + crosshair_size + 1, p.y, p.x+3, p.y, 1.0f, white); + nk_stroke_line(o, p.x, p.y + crosshair_size + 1, p.x, p.y+3, 1.0f, white); + nk_stroke_line(o, p.x, p.y - crosshair_size, p.x, p.y-2, 1.0f, white);} } - -NK_API void -nk_popup_close(struct nk_context *ctx) +NK_LIB int +nk_do_color_picker(nk_flags *state, + struct nk_command_buffer *out, struct nk_colorf *col, + enum nk_color_format fmt, struct nk_rect bounds, + struct nk_vec2 padding, const struct nk_input *in, + const struct nk_user_font *font) { - struct nk_window *popup; - NK_ASSERT(ctx); - if (!ctx || !ctx->current) return; - - popup = ctx->current; - NK_ASSERT(popup->parent); - NK_ASSERT(popup->layout->type & NK_PANEL_SET_POPUP); - popup->flags |= NK_WINDOW_HIDDEN; -} - -NK_API void -nk_popup_end(struct nk_context *ctx) -{ - struct nk_window *win; - struct nk_window *popup; - - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return; - - popup = ctx->current; - if (!popup->parent) return; - win = popup->parent; - if (popup->flags & NK_WINDOW_HIDDEN) { - struct nk_panel *root; - root = win->layout; - while (root) { - root->flags |= NK_WINDOW_REMOVE_ROM; - root = root->parent; - } - win->popup.active = 0; - } - nk_push_scissor(&popup->buffer, nk_null_rect); - nk_end(ctx); - - win->buffer = popup->buffer; - nk_finish_popup(ctx, win); - ctx->current = win; - nk_push_scissor(&win->buffer, win->layout->clip); -} -/* ------------------------------------------------------------- - * - * TOOLTIP - * - * -------------------------------------------------------------- */ -NK_API int -nk_tooltip_begin(struct nk_context *ctx, float width) -{ - struct nk_window *win; - const struct nk_input *in; - struct nk_rect bounds; - int ret; - - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - /* make sure that no nonblocking popup is currently active */ - win = ctx->current; - in = &ctx->input; - if (win->popup.win && (win->popup.type & NK_PANEL_SET_NONBLOCK)) - return 0; - - bounds.w = (float) nk_iceilf(width); - bounds.h = (float) nk_iceilf(nk_null_rect.h); - bounds.x = nk_ifloorf(in->mouse.pos.x + 1) - win->layout->clip.x; - bounds.y = nk_ifloorf(in->mouse.pos.y + 1) - win->layout->clip.y; - - ret = nk_popup_begin(ctx, NK_POPUP_DYNAMIC, - "__##Tooltip##__", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER, bounds); - if (ret) win->layout->flags &= ~(nk_flags)NK_WINDOW_ROM; - win->popup.type = NK_PANEL_TOOLTIP; - ctx->current->layout->type = NK_PANEL_TOOLTIP; - return ret; -} - -NK_API void -nk_tooltip_end(struct nk_context *ctx) -{ - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - if (!ctx || !ctx->current) return; - ctx->current->seq--; - nk_popup_close(ctx); - nk_popup_end(ctx); -} - -NK_API void -nk_tooltip(struct nk_context *ctx, const char *text) -{ - const struct nk_style *style; - struct nk_vec2 padding; - - int text_len; - float text_width; - float text_height; - - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - NK_ASSERT(text); - if (!ctx || !ctx->current || !ctx->current->layout || !text) - return; - - /* fetch configuration data */ - style = &ctx->style; - padding = style->window.padding; - - /* calculate size of the text and tooltip */ - text_len = nk_strlen(text); - text_width = style->font->width(style->font->userdata, - style->font->height, text, text_len); - text_width += (4 * padding.x); - text_height = (style->font->height + 2 * padding.y); - - /* execute tooltip and fill with text */ - if (nk_tooltip_begin(ctx, (float)text_width)) { - nk_layout_row_dynamic(ctx, (float)text_height, 1); - nk_text(ctx, text, text_len, NK_TEXT_LEFT); - nk_tooltip_end(ctx); - } -} -#ifdef NK_INCLUDE_STANDARD_VARARGS -NK_API void -nk_tooltipf(struct nk_context *ctx, const char *fmt, ...) -{ - char buf[256]; - va_list args; - va_start(args, fmt); - nk_strfmt(buf, NK_LEN(buf), fmt, args); - va_end(args); - nk_tooltip(ctx, buf); -} -#endif - -/* ------------------------------------------------------------- - * - * CONTEXTUAL - * - * -------------------------------------------------------------- */ -NK_API int -nk_contextual_begin(struct nk_context *ctx, nk_flags flags, struct nk_vec2 size, - struct nk_rect trigger_bounds) -{ - struct nk_window *win; - struct nk_window *popup; - struct nk_rect body; - - NK_STORAGE const struct nk_rect null_rect = {0,0,0,0}; - int is_clicked = 0; - int is_active = 0; - int is_open = 0; int ret = 0; + struct nk_rect matrix; + struct nk_rect hue_bar; + struct nk_rect alpha_bar; + float bar_w; - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; + NK_ASSERT(out); + NK_ASSERT(col); + NK_ASSERT(state); + NK_ASSERT(font); + if (!out || !col || !state || !font) + return ret; - win = ctx->current; - ++win->popup.con_count; + bar_w = font->height; + bounds.x += padding.x; + bounds.y += padding.x; + bounds.w -= 2 * padding.x; + bounds.h -= 2 * padding.y; - /* check if currently active contextual is active */ - popup = win->popup.win; - is_open = (popup && win->popup.type == NK_PANEL_CONTEXTUAL); - is_clicked = nk_input_mouse_clicked(&ctx->input, NK_BUTTON_RIGHT, trigger_bounds); - if (win->popup.active_con && win->popup.con_count != win->popup.active_con) - return 0; - if ((is_clicked && is_open && !is_active) || (!is_open && !is_active && !is_clicked)) - return 0; + matrix.x = bounds.x; + matrix.y = bounds.y; + matrix.h = bounds.h; + matrix.w = bounds.w - (3 * padding.x + 2 * bar_w); - /* calculate contextual position on click */ - win->popup.active_con = win->popup.con_count; - if (is_clicked) { - body.x = ctx->input.mouse.pos.x; - body.y = ctx->input.mouse.pos.y; - } else { - body.x = popup->bounds.x; - body.y = popup->bounds.y; - } - body.w = size.x; - body.h = size.y; + hue_bar.w = bar_w; + hue_bar.y = bounds.y; + hue_bar.h = matrix.h; + hue_bar.x = matrix.x + matrix.w + padding.x; - /* start nonblocking contextual popup */ - ret = nk_nonblock_begin(ctx, flags|NK_WINDOW_NO_SCROLLBAR, body, - null_rect, NK_PANEL_CONTEXTUAL); - if (ret) win->popup.type = NK_PANEL_CONTEXTUAL; - else { - win->popup.active_con = 0; - if (win->popup.win) - win->popup.win->flags = 0; - } + alpha_bar.x = hue_bar.x + hue_bar.w + padding.x; + alpha_bar.y = bounds.y; + alpha_bar.w = bar_w; + alpha_bar.h = matrix.h; + + ret = nk_color_picker_behavior(state, &bounds, &matrix, &hue_bar, + (fmt == NK_RGBA) ? &alpha_bar:0, col, in); + nk_draw_color_picker(out, &matrix, &hue_bar, (fmt == NK_RGBA) ? &alpha_bar:0, *col); return ret; } - NK_API int -nk_contextual_item_text(struct nk_context *ctx, const char *text, int len, - nk_flags alignment) +nk_color_pick(struct nk_context * ctx, struct nk_colorf *color, + enum nk_color_format fmt) { struct nk_window *win; + struct nk_panel *layout; + const struct nk_style *config; const struct nk_input *in; - const struct nk_style *style; - struct nk_rect bounds; enum nk_widget_layout_states state; + struct nk_rect bounds; NK_ASSERT(ctx); + NK_ASSERT(color); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) + if (!ctx || !ctx->current || !ctx->current->layout || !color) return 0; win = ctx->current; - style = &ctx->style; - state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); - if (!state) return nk_false; - - in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; - if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, - text, len, alignment, NK_BUTTON_DEFAULT, &style->contextual_button, in, style->font)) { - nk_contextual_close(ctx); - return nk_true; - } - return nk_false; + config = &ctx->style; + layout = win->layout; + state = nk_widget(&bounds, ctx); + if (!state) return 0; + in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; + return nk_do_color_picker(&ctx->last_widget_state, &win->buffer, color, fmt, bounds, + nk_vec2(0,0), in, config->font); } - -NK_API int nk_contextual_item_label(struct nk_context *ctx, const char *label, nk_flags align) -{return nk_contextual_item_text(ctx, label, nk_strlen(label), align);} - -NK_API int -nk_contextual_item_image_text(struct nk_context *ctx, struct nk_image img, - const char *text, int len, nk_flags align) +NK_API struct nk_colorf +nk_color_picker(struct nk_context *ctx, struct nk_colorf color, + enum nk_color_format fmt) { - struct nk_window *win; - const struct nk_input *in; - const struct nk_style *style; - - struct nk_rect bounds; - enum nk_widget_layout_states state; - - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - style = &ctx->style; - state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); - if (!state) return nk_false; - - in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; - if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, bounds, - img, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)){ - nk_contextual_close(ctx); - return nk_true; - } - return nk_false; + nk_color_pick(ctx, &color, fmt); + return color; } -NK_API int nk_contextual_item_image_label(struct nk_context *ctx, struct nk_image img, - const char *label, nk_flags align) -{return nk_contextual_item_image_text(ctx, img, label, nk_strlen(label), align);} -NK_API int -nk_contextual_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, - const char *text, int len, nk_flags align) -{ - struct nk_window *win; - const struct nk_input *in; - const struct nk_style *style; - struct nk_rect bounds; - enum nk_widget_layout_states state; - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - win = ctx->current; - style = &ctx->style; - state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); - if (!state) return nk_false; - - in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; - if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, - symbol, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)) { - nk_contextual_close(ctx); - return nk_true; - } - return nk_false; -} - -NK_API int nk_contextual_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, - const char *text, nk_flags align) -{return nk_contextual_item_symbol_text(ctx, symbol, text, nk_strlen(text), align);} - -NK_API void -nk_contextual_close(struct nk_context *ctx) -{ - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) return; - nk_popup_close(ctx); -} - -NK_API void -nk_contextual_end(struct nk_context *ctx) -{ - struct nk_window *popup; - struct nk_panel *panel; - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - if (!ctx || !ctx->current) return; - - popup = ctx->current; - panel = popup->layout; - NK_ASSERT(popup->parent); - NK_ASSERT(panel->type & NK_PANEL_SET_POPUP); - if (panel->flags & NK_WINDOW_DYNAMIC) { - /* Close behavior - This is a bit of a hack solution since we do not know before we end our popup - how big it will be. We therefore do not directly know when a - click outside the non-blocking popup must close it at that direct frame. - Instead it will be closed in the next frame.*/ - struct nk_rect body = {0,0,0,0}; - if (panel->at_y < (panel->bounds.y + panel->bounds.h)) { - struct nk_vec2 padding = nk_panel_get_padding(&ctx->style, panel->type); - body = panel->bounds; - body.y = (panel->at_y + panel->footer_height + panel->border + padding.y + panel->row.height); - body.h = (panel->bounds.y + panel->bounds.h) - body.y; - } - {int pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT); - int in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body); - if (pressed && in_body) - popup->flags |= NK_WINDOW_HIDDEN; - } - } - if (popup->flags & NK_WINDOW_HIDDEN) - popup->seq = 0; - nk_popup_end(ctx); - return; -} -/* ------------------------------------------------------------- +/* ============================================================== * * COMBO * - * --------------------------------------------------------------*/ + * ===============================================================*/ NK_INTERN int nk_combo_begin(struct nk_context *ctx, struct nk_window *win, struct nk_vec2 size, int is_clicked, struct nk_rect header) @@ -22798,7 +24353,6 @@ nk_combo_begin(struct nk_context *ctx, struct nk_window *win, win->popup.name = hash; return 1; } - NK_API int nk_combo_begin_text(struct nk_context *ctx, const char *selected, int len, struct nk_vec2 size) @@ -22888,10 +24442,11 @@ nk_combo_begin_text(struct nk_context *ctx, const char *selected, int len, } return nk_combo_begin(ctx, win, size, is_clicked, header); } - -NK_API int nk_combo_begin_label(struct nk_context *ctx, const char *selected, struct nk_vec2 size) -{return nk_combo_begin_text(ctx, selected, nk_strlen(selected), size);} - +NK_API int +nk_combo_begin_label(struct nk_context *ctx, const char *selected, struct nk_vec2 size) +{ + return nk_combo_begin_text(ctx, selected, nk_strlen(selected), size); +} NK_API int nk_combo_begin_color(struct nk_context *ctx, struct nk_color color, struct nk_vec2 size) { @@ -22969,7 +24524,6 @@ nk_combo_begin_color(struct nk_context *ctx, struct nk_color color, struct nk_ve } return nk_combo_begin(ctx, win, size, is_clicked, header); } - NK_API int nk_combo_begin_symbol(struct nk_context *ctx, enum nk_symbol_type symbol, struct nk_vec2 size) { @@ -23057,7 +24611,6 @@ nk_combo_begin_symbol(struct nk_context *ctx, enum nk_symbol_type symbol, struct } return nk_combo_begin(ctx, win, size, is_clicked, header); } - NK_API int nk_combo_begin_symbol_text(struct nk_context *ctx, const char *selected, int len, enum nk_symbol_type symbol, struct nk_vec2 size) @@ -23154,7 +24707,6 @@ nk_combo_begin_symbol_text(struct nk_context *ctx, const char *selected, int len } return nk_combo_begin(ctx, win, size, is_clicked, header); } - NK_API int nk_combo_begin_image(struct nk_context *ctx, struct nk_image img, struct nk_vec2 size) { @@ -23232,7 +24784,6 @@ nk_combo_begin_image(struct nk_context *ctx, struct nk_image img, struct nk_vec2 } return nk_combo_begin(ctx, win, size, is_clicked, header); } - NK_API int nk_combo_begin_image_text(struct nk_context *ctx, const char *selected, int len, struct nk_image img, struct nk_vec2 size) @@ -23324,43 +24875,60 @@ nk_combo_begin_image_text(struct nk_context *ctx, const char *selected, int len, } return nk_combo_begin(ctx, win, size, is_clicked, header); } - -NK_API int nk_combo_begin_symbol_label(struct nk_context *ctx, +NK_API int +nk_combo_begin_symbol_label(struct nk_context *ctx, const char *selected, enum nk_symbol_type type, struct nk_vec2 size) -{return nk_combo_begin_symbol_text(ctx, selected, nk_strlen(selected), type, size);} - -NK_API int nk_combo_begin_image_label(struct nk_context *ctx, +{ + return nk_combo_begin_symbol_text(ctx, selected, nk_strlen(selected), type, size); +} +NK_API int +nk_combo_begin_image_label(struct nk_context *ctx, const char *selected, struct nk_image img, struct nk_vec2 size) -{return nk_combo_begin_image_text(ctx, selected, nk_strlen(selected), img, size);} - -NK_API int nk_combo_item_text(struct nk_context *ctx, const char *text, int len,nk_flags align) -{return nk_contextual_item_text(ctx, text, len, align);} - -NK_API int nk_combo_item_label(struct nk_context *ctx, const char *label, nk_flags align) -{return nk_contextual_item_label(ctx, label, align);} - -NK_API int nk_combo_item_image_text(struct nk_context *ctx, struct nk_image img, const char *text, +{ + return nk_combo_begin_image_text(ctx, selected, nk_strlen(selected), img, size); +} +NK_API int +nk_combo_item_text(struct nk_context *ctx, const char *text, int len,nk_flags align) +{ + return nk_contextual_item_text(ctx, text, len, align); +} +NK_API int +nk_combo_item_label(struct nk_context *ctx, const char *label, nk_flags align) +{ + return nk_contextual_item_label(ctx, label, align); +} +NK_API int +nk_combo_item_image_text(struct nk_context *ctx, struct nk_image img, const char *text, int len, nk_flags alignment) -{return nk_contextual_item_image_text(ctx, img, text, len, alignment);} - -NK_API int nk_combo_item_image_label(struct nk_context *ctx, struct nk_image img, +{ + return nk_contextual_item_image_text(ctx, img, text, len, alignment); +} +NK_API int +nk_combo_item_image_label(struct nk_context *ctx, struct nk_image img, const char *text, nk_flags alignment) -{return nk_contextual_item_image_label(ctx, img, text, alignment);} - -NK_API int nk_combo_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, +{ + return nk_contextual_item_image_label(ctx, img, text, alignment); +} +NK_API int +nk_combo_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, const char *text, int len, nk_flags alignment) -{return nk_contextual_item_symbol_text(ctx, sym, text, len, alignment);} - -NK_API int nk_combo_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, +{ + return nk_contextual_item_symbol_text(ctx, sym, text, len, alignment); +} +NK_API int +nk_combo_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, const char *label, nk_flags alignment) -{return nk_contextual_item_symbol_label(ctx, sym, label, alignment);} - +{ + return nk_contextual_item_symbol_label(ctx, sym, label, alignment); +} NK_API void nk_combo_end(struct nk_context *ctx) -{nk_contextual_end(ctx);} - +{ + nk_contextual_end(ctx); +} NK_API void nk_combo_close(struct nk_context *ctx) -{nk_contextual_close(ctx);} - +{ + nk_contextual_close(ctx); +} NK_API int nk_combo(struct nk_context *ctx, const char **items, int count, int selected, int item_height, struct nk_vec2 size) @@ -23391,7 +24959,6 @@ nk_combo(struct nk_context *ctx, const char **items, int count, } return selected; } - NK_API int nk_combo_separator(struct nk_context *ctx, const char *items_separated_by_separator, int separator, int selected, int count, int item_height, struct nk_vec2 size) @@ -23441,12 +25008,12 @@ nk_combo_separator(struct nk_context *ctx, const char *items_separated_by_separa } return selected; } - NK_API int nk_combo_string(struct nk_context *ctx, const char *items_separated_by_zeros, int selected, int count, int item_height, struct nk_vec2 size) -{return nk_combo_separator(ctx, items_separated_by_zeros, '\0', selected, count, item_height, size);} - +{ + return nk_combo_separator(ctx, items_separated_by_zeros, '\0', selected, count, item_height, size); +} NK_API int nk_combo_callback(struct nk_context *ctx, void(*item_getter)(void*, int, const char**), void *userdata, int selected, int count, int item_height, struct nk_vec2 size) @@ -23478,78 +25045,52 @@ nk_combo_callback(struct nk_context *ctx, void(*item_getter)(void*, int, const c selected = i; } nk_combo_end(ctx); - } - return selected; + } return selected; } - -NK_API void nk_combobox(struct nk_context *ctx, const char **items, int count, +NK_API void +nk_combobox(struct nk_context *ctx, const char **items, int count, int *selected, int item_height, struct nk_vec2 size) -{*selected = nk_combo(ctx, items, count, *selected, item_height, size);} - -NK_API void nk_combobox_string(struct nk_context *ctx, const char *items_separated_by_zeros, +{ + *selected = nk_combo(ctx, items, count, *selected, item_height, size); +} +NK_API void +nk_combobox_string(struct nk_context *ctx, const char *items_separated_by_zeros, int *selected, int count, int item_height, struct nk_vec2 size) -{*selected = nk_combo_string(ctx, items_separated_by_zeros, *selected, count, item_height, size);} - -NK_API void nk_combobox_separator(struct nk_context *ctx, const char *items_separated_by_separator, +{ + *selected = nk_combo_string(ctx, items_separated_by_zeros, *selected, count, item_height, size); +} +NK_API void +nk_combobox_separator(struct nk_context *ctx, const char *items_separated_by_separator, int separator,int *selected, int count, int item_height, struct nk_vec2 size) -{*selected = nk_combo_separator(ctx, items_separated_by_separator, separator, - *selected, count, item_height, size);} - -NK_API void nk_combobox_callback(struct nk_context *ctx, +{ + *selected = nk_combo_separator(ctx, items_separated_by_separator, separator, + *selected, count, item_height, size); +} +NK_API void +nk_combobox_callback(struct nk_context *ctx, void(*item_getter)(void* data, int id, const char **out_text), void *userdata, int *selected, int count, int item_height, struct nk_vec2 size) -{*selected = nk_combo_callback(ctx, item_getter, userdata, *selected, count, item_height, size);} +{ + *selected = nk_combo_callback(ctx, item_getter, userdata, *selected, count, item_height, size); +} -/* - * ------------------------------------------------------------- + + + + +/* =============================================================== * - * MENU + * TOOLTIP * - * -------------------------------------------------------------- - */ -NK_INTERN int -nk_menu_begin(struct nk_context *ctx, struct nk_window *win, - const char *id, int is_clicked, struct nk_rect header, struct nk_vec2 size) -{ - int is_open = 0; - int is_active = 0; - struct nk_rect body; - struct nk_window *popup; - nk_hash hash = nk_murmur_hash(id, (int)nk_strlen(id), NK_PANEL_MENU); - - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - body.x = header.x; - body.w = size.x; - body.y = header.y + header.h; - body.h = size.y; - - popup = win->popup.win; - is_open = popup ? nk_true : nk_false; - is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_PANEL_MENU); - if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || - (!is_open && !is_active && !is_clicked)) return 0; - if (!nk_nonblock_begin(ctx, NK_WINDOW_NO_SCROLLBAR, body, header, NK_PANEL_MENU)) - return 0; - - win->popup.type = NK_PANEL_MENU; - win->popup.name = hash; - return 1; -} - + * ===============================================================*/ NK_API int -nk_menu_begin_text(struct nk_context *ctx, const char *title, int len, - nk_flags align, struct nk_vec2 size) +nk_tooltip_begin(struct nk_context *ctx, float width) { + int x,y,w,h; struct nk_window *win; const struct nk_input *in; - struct nk_rect header; - int is_clicked = nk_false; - nk_flags state; + struct nk_rect bounds; + int ret; NK_ASSERT(ctx); NK_ASSERT(ctx->current); @@ -23557,161 +25098,442 @@ nk_menu_begin_text(struct nk_context *ctx, const char *title, int len, if (!ctx || !ctx->current || !ctx->current->layout) return 0; + /* make sure that no nonblocking popup is currently active */ win = ctx->current; - state = nk_widget(&header, ctx); - if (!state) return 0; - in = (state == NK_WIDGET_ROM || win->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; - if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, header, - title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font)) - is_clicked = nk_true; - return nk_menu_begin(ctx, win, title, is_clicked, header, size); -} - -NK_API int nk_menu_begin_label(struct nk_context *ctx, - const char *text, nk_flags align, struct nk_vec2 size) -{return nk_menu_begin_text(ctx, text, nk_strlen(text), align, size);} - -NK_API int -nk_menu_begin_image(struct nk_context *ctx, const char *id, struct nk_image img, - struct nk_vec2 size) -{ - struct nk_window *win; - struct nk_rect header; - const struct nk_input *in; - int is_clicked = nk_false; - nk_flags state; - - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) + in = &ctx->input; + if (win->popup.win && (win->popup.type & NK_PANEL_SET_NONBLOCK)) return 0; - win = ctx->current; - state = nk_widget(&header, ctx); - if (!state) return 0; - in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; - if (nk_do_button_image(&ctx->last_widget_state, &win->buffer, header, - img, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in)) - is_clicked = nk_true; - return nk_menu_begin(ctx, win, id, is_clicked, header, size); + w = nk_iceilf(width); + h = nk_iceilf(nk_null_rect.h); + x = nk_ifloorf(in->mouse.pos.x + 1) - (int)win->layout->clip.x; + y = nk_ifloorf(in->mouse.pos.y + 1) - (int)win->layout->clip.y; + + bounds.x = (float)x; + bounds.y = (float)y; + bounds.w = (float)w; + bounds.h = (float)h; + + ret = nk_popup_begin(ctx, NK_POPUP_DYNAMIC, + "__##Tooltip##__", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER, bounds); + if (ret) win->layout->flags &= ~(nk_flags)NK_WINDOW_ROM; + win->popup.type = NK_PANEL_TOOLTIP; + ctx->current->layout->type = NK_PANEL_TOOLTIP; + return ret; } -NK_API int -nk_menu_begin_symbol(struct nk_context *ctx, const char *id, - enum nk_symbol_type sym, struct nk_vec2 size) -{ - struct nk_window *win; - const struct nk_input *in; - struct nk_rect header; - int is_clicked = nk_false; - nk_flags state; - - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - state = nk_widget(&header, ctx); - if (!state) return 0; - in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; - if (nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, header, - sym, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font)) - is_clicked = nk_true; - return nk_menu_begin(ctx, win, id, is_clicked, header, size); -} - -NK_API int -nk_menu_begin_image_text(struct nk_context *ctx, const char *title, int len, - nk_flags align, struct nk_image img, struct nk_vec2 size) -{ - struct nk_window *win; - struct nk_rect header; - const struct nk_input *in; - int is_clicked = nk_false; - nk_flags state; - - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - state = nk_widget(&header, ctx); - if (!state) return 0; - in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; - if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, - header, img, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, - ctx->style.font, in)) - is_clicked = nk_true; - return nk_menu_begin(ctx, win, title, is_clicked, header, size); -} - -NK_API int nk_menu_begin_image_label(struct nk_context *ctx, - const char *title, nk_flags align, struct nk_image img, struct nk_vec2 size) -{return nk_menu_begin_image_text(ctx, title, nk_strlen(title), align, img, size);} - -NK_API int -nk_menu_begin_symbol_text(struct nk_context *ctx, const char *title, int len, - nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size) -{ - struct nk_window *win; - struct nk_rect header; - const struct nk_input *in; - int is_clicked = nk_false; - nk_flags state; - - NK_ASSERT(ctx); - NK_ASSERT(ctx->current); - NK_ASSERT(ctx->current->layout); - if (!ctx || !ctx->current || !ctx->current->layout) - return 0; - - win = ctx->current; - state = nk_widget(&header, ctx); - if (!state) return 0; - - in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; - if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, - header, sym, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, - ctx->style.font, in)) is_clicked = nk_true; - return nk_menu_begin(ctx, win, title, is_clicked, header, size); -} - -NK_API int nk_menu_begin_symbol_label(struct nk_context *ctx, - const char *title, nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size ) -{return nk_menu_begin_symbol_text(ctx, title, nk_strlen(title), align,sym,size);} - -NK_API int nk_menu_item_text(struct nk_context *ctx, const char *title, int len, nk_flags align) -{return nk_contextual_item_text(ctx, title, len, align);} - -NK_API int nk_menu_item_label(struct nk_context *ctx, const char *label, nk_flags align) -{return nk_contextual_item_label(ctx, label, align);} - -NK_API int nk_menu_item_image_label(struct nk_context *ctx, struct nk_image img, - const char *label, nk_flags align) -{return nk_contextual_item_image_label(ctx, img, label, align);} - -NK_API int nk_menu_item_image_text(struct nk_context *ctx, struct nk_image img, - const char *text, int len, nk_flags align) -{return nk_contextual_item_image_text(ctx, img, text, len, align);} - -NK_API int nk_menu_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, - const char *text, int len, nk_flags align) -{return nk_contextual_item_symbol_text(ctx, sym, text, len, align);} - -NK_API int nk_menu_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, - const char *label, nk_flags align) -{return nk_contextual_item_symbol_label(ctx, sym, label, align);} - -NK_API void nk_menu_close(struct nk_context *ctx) -{nk_contextual_close(ctx);} - NK_API void -nk_menu_end(struct nk_context *ctx) -{nk_contextual_end(ctx);} +nk_tooltip_end(struct nk_context *ctx) +{ + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + if (!ctx || !ctx->current) return; + ctx->current->seq--; + nk_popup_close(ctx); + nk_popup_end(ctx); +} +NK_API void +nk_tooltip(struct nk_context *ctx, const char *text) +{ + const struct nk_style *style; + struct nk_vec2 padding; + + int text_len; + float text_width; + float text_height; + + NK_ASSERT(ctx); + NK_ASSERT(ctx->current); + NK_ASSERT(ctx->current->layout); + NK_ASSERT(text); + if (!ctx || !ctx->current || !ctx->current->layout || !text) + return; + + /* fetch configuration data */ + style = &ctx->style; + padding = style->window.padding; + + /* calculate size of the text and tooltip */ + text_len = nk_strlen(text); + text_width = style->font->width(style->font->userdata, + style->font->height, text, text_len); + text_width += (4 * padding.x); + text_height = (style->font->height + 2 * padding.y); + + /* execute tooltip and fill with text */ + if (nk_tooltip_begin(ctx, (float)text_width)) { + nk_layout_row_dynamic(ctx, (float)text_height, 1); + nk_text(ctx, text, text_len, NK_TEXT_LEFT); + nk_tooltip_end(ctx); + } +} +#ifdef NK_INCLUDE_STANDARD_VARARGS +NK_API void +nk_tooltipf(struct nk_context *ctx, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + nk_tooltipfv(ctx, fmt, args); + va_end(args); +} +NK_API void +nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args) +{ + char buf[256]; + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_tooltip(ctx, buf); +} +#endif + + #endif /* NK_IMPLEMENTATION */ + +/* +/// ## License +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~none +/// ------------------------------------------------------------------------------ +/// This software is available under 2 licenses -- choose whichever you prefer. +/// ------------------------------------------------------------------------------ +/// ALTERNATIVE A - MIT License +/// Copyright (c) 2016-2018 Micha Mettke +/// Permission is hereby granted, free of charge, to any person obtaining a copy of +/// this software and associated documentation files (the "Software"), to deal in +/// the Software without restriction, including without limitation the rights to +/// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +/// of the Software, and to permit persons to whom the Software is furnished to do +/// so, subject to the following conditions: +/// The above copyright notice and this permission notice shall be included in all +/// copies or substantial portions of the Software. +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +/// SOFTWARE. +/// ------------------------------------------------------------------------------ +/// ALTERNATIVE B - Public Domain (www.unlicense.org) +/// This is free and unencumbered software released into the public domain. +/// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +/// software, either in source code form or as a compiled binary, for any purpose, +/// commercial or non-commercial, and by any means. +/// In jurisdictions that recognize copyright laws, the author or authors of this +/// software dedicate any and all copyright interest in the software to the public +/// domain. We make this dedication for the benefit of the public at large and to +/// the detriment of our heirs and successors. We intend this dedication to be an +/// overt act of relinquishment in perpetuity of all present and future rights to +/// this software under copyright law. +/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +/// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +/// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +/// ------------------------------------------------------------------------------ +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +/// ## Changelog +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~none +/// [date][x.yy.zz]-[description] +/// -[date]: date on which the change has been pushed +/// -[x.yy.zz]: Numerical version string representation. Each version number on the right +/// resets back to zero if version on the left is incremented. +/// - [x]: Major version with API and library breaking changes +/// - [yy]: Minor version with non-breaking API and library changes +/// - [zz]: Bug fix version with no direct changes to API +/// +/// - 2018/04/01 (4.00.1) - Fixed calling `nk_convert` multiple time per single frame +/// - 2018/04/01 (4.00.0) - BREAKING CHANGE: nk_draw_list_clear no longer tries to +/// clear provided buffers. So make sure to either free +/// or clear each passed buffer after calling nk_convert. +/// - 2018/02/23 (3.00.6) - Fixed slider dragging behavior +/// - 2018/01/31 (3.00.5) - Fixed overcalculation of cursor data in font baking process +/// - 2018/01/31 (3.00.4) - Removed name collision with stb_truetype +/// - 2018/01/28 (3.00.3) - Fixed panel window border drawing bug +/// - 2018/01/12 (3.00.2) - Added `nk_group_begin_titled` for separed group identifier and title +/// - 2018/01/07 (3.00.1) - Started to change documentation style +/// - 2018/01/05 (3.00.0) - BREAKING CHANGE: The previous color picker API was broken +/// because of conversions between float and byte color representation. +/// Color pickers now use floating point values to represent +/// HSV values. To get back the old behavior I added some additional +/// color conversion functions to cast between nk_color and +/// nk_colorf. +/// - 2017/12/23 (2.00.7) - Fixed small warning +/// - 2017/12/23 (2.00.7) - Fixed nk_edit_buffer behavior if activated to allow input +/// - 2017/12/23 (2.00.7) - Fixed modifyable progressbar dragging visuals and input behavior +/// - 2017/12/04 (2.00.6) - Added formated string tooltip widget +/// - 2017/11/18 (2.00.5) - Fixed window becoming hidden with flag NK_WINDOW_NO_INPUT +/// - 2017/11/15 (2.00.4) - Fixed font merging +/// - 2017/11/07 (2.00.3) - Fixed window size and position modifier functions +/// - 2017/09/14 (2.00.2) - Fixed nk_edit_buffer and nk_edit_focus behavior +/// - 2017/09/14 (2.00.1) - Fixed window closing behavior +/// - 2017/09/14 (2.00.0) - BREAKING CHANGE: Modifing window position and size funtions now +/// require the name of the window and must happen outside the window +/// building process (between function call nk_begin and nk_end). +/// - 2017/09/11 (1.40.9) - Fixed window background flag if background window is declared last +/// - 2017/08/27 (1.40.8) - Fixed `nk_item_is_any_active` for hidden windows +/// - 2017/08/27 (1.40.7) - Fixed window background flag +/// - 2017/07/07 (1.40.6) - Fixed missing clipping rect check for hovering/clicked +/// query for widgets +/// - 2017/07/07 (1.40.5) - Fixed drawing bug for vertex output for lines and stroked +/// and filled rectangles +/// - 2017/07/07 (1.40.4) - Fixed bug in nk_convert trying to add windows that are in +/// process of being destroyed. +/// - 2017/07/07 (1.40.3) - Fixed table internal bug caused by storing table size in +/// window instead of directly in table. +/// - 2017/06/30 (1.40.2) - Removed unneeded semicolon in C++ NK_ALIGNOF macro +/// - 2017/06/30 (1.40.1) - Fixed drawing lines smaller or equal zero +/// - 2017/06/08 (1.40.0) - Removed the breaking part of last commit. Auto layout now only +/// comes in effect if you pass in zero was row height argument +/// - 2017/06/08 (1.40.0) - BREAKING CHANGE: while not directly API breaking it will change +/// how layouting works. From now there will be an internal minimum +/// row height derived from font height. If you need a row smaller than +/// that you can directly set it by `nk_layout_set_min_row_height` and +/// reset the value back by calling `nk_layout_reset_min_row_height. +/// - 2017/06/08 (1.39.1) - Fixed property text edit handling bug caused by past `nk_widget` fix +/// - 2017/06/08 (1.39.0) - Added function to retrieve window space without calling a nk_layout_xxx function +/// - 2017/06/06 (1.38.5) - Fixed `nk_convert` return flag for command buffer +/// - 2017/05/23 (1.38.4) - Fixed activation behavior for widgets partially clipped +/// - 2017/05/10 (1.38.3) - Fixed wrong min window size mouse scaling over boundries +/// - 2017/05/09 (1.38.2) - Fixed vertical scrollbar drawing with not enough space +/// - 2017/05/09 (1.38.1) - Fixed scaler dragging behavior if window size hits minimum size +/// - 2017/05/06 (1.38.0) - Added platform double-click support +/// - 2017/04/20 (1.37.1) - Fixed key repeat found inside glfw demo backends +/// - 2017/04/20 (1.37.0) - Extended properties with selection and clipbard support +/// - 2017/04/20 (1.36.2) - Fixed #405 overlapping rows with zero padding and spacing +/// - 2017/04/09 (1.36.1) - Fixed #403 with another widget float error +/// - 2017/04/09 (1.36.0) - Added window `NK_WINDOW_NO_INPUT` and `NK_WINDOW_NOT_INTERACTIVE` flags +/// - 2017/04/09 (1.35.3) - Fixed buffer heap corruption +/// - 2017/03/25 (1.35.2) - Fixed popup overlapping for `NK_WINDOW_BACKGROUND` windows +/// - 2017/03/25 (1.35.1) - Fixed windows closing behavior +/// - 2017/03/18 (1.35.0) - Added horizontal scroll requested in #377 +/// - 2017/03/18 (1.34.3) - Fixed long window header titles +/// - 2017/03/04 (1.34.2) - Fixed text edit filtering +/// - 2017/03/04 (1.34.1) - Fixed group closable flag +/// - 2017/02/25 (1.34.0) - Added custom draw command for better language binding support +/// - 2017/01/24 (1.33.0) - Added programatic way of remove edit focus +/// - 2017/01/24 (1.32.3) - Fixed wrong define for basic type definitions for windows +/// - 2017/01/21 (1.32.2) - Fixed input capture from hidden or closed windows +/// - 2017/01/21 (1.32.1) - Fixed slider behavior and drawing +/// - 2017/01/13 (1.32.0) - Added flag to put scaler into the bottom left corner +/// - 2017/01/13 (1.31.0) - Added additional row layouting method to combine both +/// dynamic and static widgets. +/// - 2016/12/31 (1.30.0) - Extended scrollbar offset from 16-bit to 32-bit +/// - 2016/12/31 (1.29.2)- Fixed closing window bug of minimized windows +/// - 2016/12/03 (1.29.1)- Fixed wrapped text with no seperator and C89 error +/// - 2016/12/03 (1.29.0) - Changed text wrapping to process words not characters +/// - 2016/11/22 (1.28.6)- Fixed window minimized closing bug +/// - 2016/11/19 (1.28.5)- Fixed abstract combo box closing behavior +/// - 2016/11/19 (1.28.4)- Fixed tooltip flickering +/// - 2016/11/19 (1.28.3)- Fixed memory leak caused by popup repeated closing +/// - 2016/11/18 (1.28.2)- Fixed memory leak caused by popup panel allocation +/// - 2016/11/10 (1.28.1)- Fixed some warnings and C++ error +/// - 2016/11/10 (1.28.0)- Added additional `nk_button` versions which allows to directly +/// pass in a style struct to change buttons visual. +/// - 2016/11/10 (1.27.0)- Added additional 'nk_tree' versions to support external state +/// storage. Just like last the `nk_group` commit the main +/// advantage is that you optionally can minimize nuklears runtime +/// memory consumption or handle hash collisions. +/// - 2016/11/09 (1.26.0)- Added additional `nk_group` version to support external scrollbar +/// offset storage. Main advantage is that you can externalize +/// the memory management for the offset. It could also be helpful +/// if you have a hash collision in `nk_group_begin` but really +/// want the name. In addition I added `nk_list_view` which allows +/// to draw big lists inside a group without actually having to +/// commit the whole list to nuklear (issue #269). +/// - 2016/10/30 (1.25.1)- Fixed clipping rectangle bug inside `nk_draw_list` +/// - 2016/10/29 (1.25.0)- Pulled `nk_panel` memory management into nuklear and out of +/// the hands of the user. From now on users don't have to care +/// about panels unless they care about some information. If you +/// still need the panel just call `nk_window_get_panel`. +/// - 2016/10/21 (1.24.0)- Changed widget border drawing to stroked rectangle from filled +/// rectangle for less overdraw and widget background transparency. +/// - 2016/10/18 (1.23.0)- Added `nk_edit_focus` for manually edit widget focus control +/// - 2016/09/29 (1.22.7)- Fixed deduction of basic type in non `` compilation +/// - 2016/09/29 (1.22.6)- Fixed edit widget UTF-8 text cursor drawing bug +/// - 2016/09/28 (1.22.5)- Fixed edit widget UTF-8 text appending/inserting/removing +/// - 2016/09/28 (1.22.4)- Fixed drawing bug inside edit widgets which offset all text +/// text in every edit widget if one of them is scrolled. +/// - 2016/09/28 (1.22.3)- Fixed small bug in edit widgets if not active. The wrong +/// text length is passed. It should have been in bytes but +/// was passed as glyphes. +/// - 2016/09/20 (1.22.2)- Fixed color button size calculation +/// - 2016/09/20 (1.22.1)- Fixed some `nk_vsnprintf` behavior bugs and removed +/// `` again from `NK_INCLUDE_STANDARD_VARARGS`. +/// - 2016/09/18 (1.22.0)- C89 does not support vsnprintf only C99 and newer as well +/// as C++11 and newer. In addition to use vsnprintf you have +/// to include . So just defining `NK_INCLUDE_STD_VAR_ARGS` +/// is not enough. That behavior is now fixed. By default if +/// both varargs as well as stdio is selected I try to use +/// vsnprintf if not possible I will revert to vsprintf. If +/// varargs but not stdio was defined I will use my own function. +/// - 2016/09/15 (1.21.2)- Fixed panel `close` behavior for deeper panel levels +/// - 2016/09/15 (1.21.1)- Fixed C++ errors and wrong argument to `nk_panel_get_xxxx` +/// - 2016/09/13 (1.21.0) - !BREAKING! Fixed nonblocking popup behavior in menu, combo, +/// and contextual which prevented closing in y-direction if +/// popup did not reach max height. +/// In addition the height parameter was changed into vec2 +/// for width and height to have more control over the popup size. +/// - 2016/09/13 (1.20.3) - Cleaned up and extended type selection +/// - 2016/09/13 (1.20.2)- Fixed slider behavior hopefully for the last time. This time +/// all calculation are correct so no more hackery. +/// - 2016/09/13 (1.20.1)- Internal change to divide window/panel flags into panel flags and types. +/// Suprisinly spend years in C and still happened to confuse types +/// with flags. Probably something to take note. +/// - 2016/09/08 (1.20.0)- Added additional helper function to make it easier to just +/// take the produced buffers from `nk_convert` and unplug the +/// iteration process from `nk_context`. So now you can +/// just use the vertex,element and command buffer + two pointer +/// inside the command buffer retrieved by calls `nk__draw_begin` +/// and `nk__draw_end` and macro `nk_draw_foreach_bounded`. +/// - 2016/09/08 (1.19.0)- Added additional asserts to make sure every `nk_xxx_begin` call +/// for windows, popups, combobox, menu and contextual is guarded by +/// `if` condition and does not produce false drawing output. +/// - 2016/09/08 (1.18.0)- Changed confusing name for `NK_SYMBOL_RECT_FILLED`, `NK_SYMBOL_RECT` +/// to hopefully easier to understand `NK_SYMBOL_RECT_FILLED` and +/// `NK_SYMBOL_RECT_OUTLINE`. +/// - 2016/09/08 (1.17.0)- Changed confusing name for `NK_SYMBOL_CIRLCE_FILLED`, `NK_SYMBOL_CIRCLE` +/// to hopefully easier to understand `NK_SYMBOL_CIRCLE_FILLED` and +/// `NK_SYMBOL_CIRCLE_OUTLINE`. +/// - 2016/09/08 (1.16.0)- Added additional checks to select correct types if `NK_INCLUDE_FIXED_TYPES` +/// is not defined by supporting the biggest compiler GCC, clang and MSVC. +/// - 2016/09/07 (1.15.3)- Fixed `NK_INCLUDE_COMMAND_USERDATA` define to not cause an error +/// - 2016/09/04 (1.15.2)- Fixed wrong combobox height calculation +/// - 2016/09/03 (1.15.1)- Fixed gaps inside combo boxes in OpenGL +/// - 2016/09/02 (1.15.0) - Changed nuklear to not have any default vertex layout and +/// instead made it user provided. The range of types to convert +/// to is quite limited at the moment, but I would be more than +/// happy to accept PRs to add additional. +/// - 2016/08/30 (1.14.2) - Removed unused variables +/// - 2016/08/30 (1.14.1) - Fixed C++ build errors +/// - 2016/08/30 (1.14.0) - Removed mouse dragging from SDL demo since it does not work correctly +/// - 2016/08/30 (1.13.4) - Tweaked some default styling variables +/// - 2016/08/30 (1.13.3) - Hopefully fixed drawing bug in slider, in general I would +/// refrain from using slider with a big number of steps. +/// - 2016/08/30 (1.13.2) - Fixed close and minimize button which would fire even if the +/// window was in Read Only Mode. +/// - 2016/08/30 (1.13.1) - Fixed popup panel padding handling which was previously just +/// a hack for combo box and menu. +/// - 2016/08/30 (1.13.0) - Removed `NK_WINDOW_DYNAMIC` flag from public API since +/// it is bugged and causes issues in window selection. +/// - 2016/08/30 (1.12.0) - Removed scaler size. The size of the scaler is now +/// determined by the scrollbar size +/// - 2016/08/30 (1.11.2) - Fixed some drawing bugs caused by changes from 1.11 +/// - 2016/08/30 (1.11.1) - Fixed overlapping minimized window selection +/// - 2016/08/30 (1.11.0) - Removed some internal complexity and overly complex code +/// handling panel padding and panel border. +/// - 2016/08/29 (1.10.0) - Added additional height parameter to `nk_combobox_xxx` +/// - 2016/08/29 (1.10.0) - Fixed drawing bug in dynamic popups +/// - 2016/08/29 (1.10.0) - Added experimental mouse scrolling to popups, menus and comboboxes +/// - 2016/08/26 (1.10.0) - Added window name string prepresentation to account for +/// hash collisions. Currently limited to NK_WINDOW_MAX_NAME +/// which in term can be redefined if not big enough. +/// - 2016/08/26 (1.10.0) - Added stacks for temporary style/UI changes in code +/// - 2016/08/25 (1.10.0) - Changed `nk_input_is_key_pressed` and 'nk_input_is_key_released' +/// to account for key press and release happening in one frame. +/// - 2016/08/25 (1.10.0) - Added additional nk_edit flag to directly jump to the end on activate +/// - 2016/08/17 (1.09.6)- Removed invalid check for value zero in nk_propertyx +/// - 2016/08/16 (1.09.5)- Fixed ROM mode for deeper levels of popup windows parents. +/// - 2016/08/15 (1.09.4)- Editbox are now still active if enter was pressed with flag +/// `NK_EDIT_SIG_ENTER`. Main reasoning is to be able to keep +/// typing after commiting. +/// - 2016/08/15 (1.09.4)- Removed redundant code +/// - 2016/08/15 (1.09.4)- Fixed negative numbers in `nk_strtoi` and remove unused variable +/// - 2016/08/15 (1.09.3)- Fixed `NK_WINDOW_BACKGROUND` flag behavior to select a background +/// window only as selected by hovering and not by clicking. +/// - 2016/08/14 (1.09.2)- Fixed a bug in font atlas which caused wrong loading +/// of glyphes for font with multiple ranges. +/// - 2016/08/12 (1.09.1)- Added additional function to check if window is currently +/// hidden and therefore not visible. +/// - 2016/08/12 (1.09.1)- nk_window_is_closed now queries the correct flag `NK_WINDOW_CLOSED` +/// instead of the old flag `NK_WINDOW_HIDDEN` +/// - 2016/08/09 (1.09.0) - Added additional double version to nk_property and changed +/// the underlying implementation to not cast to float and instead +/// work directly on the given values. +/// - 2016/08/09 (1.08.0) - Added additional define to overwrite library internal +/// floating pointer number to string conversion for additional +/// precision. +/// - 2016/08/09 (1.08.0) - Added additional define to overwrite library internal +/// string to floating point number conversion for additional +/// precision. +/// - 2016/08/08 (1.07.2)- Fixed compiling error without define NK_INCLUDE_FIXED_TYPE +/// - 2016/08/08 (1.07.1)- Fixed possible floating point error inside `nk_widget` leading +/// to wrong wiget width calculation which results in widgets falsly +/// becomming tagged as not inside window and cannot be accessed. +/// - 2016/08/08 (1.07.0) - Nuklear now differentiates between hiding a window (NK_WINDOW_HIDDEN) and +/// closing a window (NK_WINDOW_CLOSED). A window can be hidden/shown +/// by using `nk_window_show` and closed by either clicking the close +/// icon in a window or by calling `nk_window_close`. Only closed +/// windows get removed at the end of the frame while hidden windows +/// remain. +/// - 2016/08/08 (1.06.0) - Added `nk_edit_string_zero_terminated` as a second option to +/// `nk_edit_string` which takes, edits and outputs a '\0' terminated string. +/// - 2016/08/08 (1.05.4)- Fixed scrollbar auto hiding behavior +/// - 2016/08/08 (1.05.3)- Fixed wrong panel padding selection in `nk_layout_widget_space` +/// - 2016/08/07 (1.05.2)- Fixed old bug in dynamic immediate mode layout API, calculating +/// wrong item spacing and panel width. +///- 2016/08/07 (1.05.1)- Hopefully finally fixed combobox popup drawing bug +///- 2016/08/07 (1.05.0) - Split varargs away from NK_INCLUDE_STANDARD_IO into own +/// define NK_INCLUDE_STANDARD_VARARGS to allow more fine +/// grained controlled over library includes. +/// - 2016/08/06 (1.04.5)- Changed memset calls to NK_MEMSET +/// - 2016/08/04 (1.04.4)- Fixed fast window scaling behavior +/// - 2016/08/04 (1.04.3)- Fixed window scaling, movement bug which appears if you +/// move/scale a window and another window is behind it. +/// If you are fast enough then the window behind gets activated +/// and the operation is blocked. I now require activating +/// by hovering only if mouse is not pressed. +/// - 2016/08/04 (1.04.2)- Fixed changing fonts +/// - 2016/08/03 (1.04.1)- Fixed `NK_WINDOW_BACKGROUND` behavior +/// - 2016/08/03 (1.04.0) - Added color parameter to `nk_draw_image` +/// - 2016/08/03 (1.04.0) - Added additional window padding style attributes for +/// sub windows (combo, menu, ...) +/// - 2016/08/03 (1.04.0) - Added functions to show/hide software cursor +/// - 2016/08/03 (1.04.0) - Added `NK_WINDOW_BACKGROUND` flag to force a window +/// to be always in the background of the screen +/// - 2016/08/03 (1.03.2)- Removed invalid assert macro for NK_RGB color picker +/// - 2016/08/01 (1.03.1)- Added helper macros into header include guard +/// - 2016/07/29 (1.03.0) - Moved the window/table pool into the header part to +/// simplify memory management by removing the need to +/// allocate the pool. +/// - 2016/07/29 (1.02.0) - Added auto scrollbar hiding window flag which if enabled +/// will hide the window scrollbar after NK_SCROLLBAR_HIDING_TIMEOUT +/// seconds without window interaction. To make it work +/// you have to also set a delta time inside the `nk_context`. +/// - 2016/07/25 (1.01.1) - Fixed small panel and panel border drawing bugs +/// - 2016/07/15 (1.01.0) - Added software cursor to `nk_style` and `nk_context` +/// - 2016/07/15 (1.01.0) - Added const correctness to `nk_buffer_push' data argument +/// - 2016/07/15 (1.01.0) - Removed internal font baking API and simplified +/// font atlas memory management by converting pointer +/// arrays for fonts and font configurations to lists. +/// - 2016/07/15 (1.00.0) - Changed button API to use context dependend button +/// behavior instead of passing it for every function call. +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +/// ## Gallery +/// ![Figure [blue]: Feature overview with blue color styling](https://cloud.githubusercontent.com/assets/8057201/13538240/acd96876-e249-11e5-9547-5ac0b19667a0.png) +/// ![Figure [red]: Feature overview with red color styling](https://cloud.githubusercontent.com/assets/8057201/13538243/b04acd4c-e249-11e5-8fd2-ad7744a5b446.png) +/// ![Figure [widgets]: Widget overview](https://cloud.githubusercontent.com/assets/8057201/11282359/3325e3c6-8eff-11e5-86cb-cf02b0596087.png) +/// ![Figure [blackwhite]: Black and white](https://cloud.githubusercontent.com/assets/8057201/11033668/59ab5d04-86e5-11e5-8091-c56f16411565.png) +/// ![Figure [filexp]: File explorer](https://cloud.githubusercontent.com/assets/8057201/10718115/02a9ba08-7b6b-11e5-950f-adacdd637739.png) +/// ![Figure [opengl]: OpenGL Editor](https://cloud.githubusercontent.com/assets/8057201/12779619/2a20d72c-ca69-11e5-95fe-4edecf820d5c.png) +/// ![Figure [nodedit]: Node Editor](https://cloud.githubusercontent.com/assets/8057201/9976995/e81ac04a-5ef7-11e5-872b-acd54fbeee03.gif) +/// ![Figure [skinning]: Using skinning in Nuklear](https://cloud.githubusercontent.com/assets/8057201/15991632/76494854-30b8-11e6-9555-a69840d0d50b.png) +/// ![Figure [bf]: Heavy modified version](https://cloud.githubusercontent.com/assets/8057201/14902576/339926a8-0d9c-11e6-9fee-a8b73af04473.png) +/// +/// ## Credits +/// Developed by Micha Mettke and every direct or indirect github contributor.

+/// +/// Embeds [stb_texedit](https://github.com/nothings/stb/blob/master/stb_textedit.h), [stb_truetype](https://github.com/nothings/stb/blob/master/stb_truetype.h) and [stb_rectpack](https://github.com/nothings/stb/blob/master/stb_rect_pack.h) by Sean Barret (public domain)
+/// Uses [stddoc.c](https://github.com/r-lyeh/stddoc.c) from r-lyeh@github.com for documentation generation

+/// Embeds ProggyClean.ttf font by Tristan Grimmer (MIT license).
+/// +/// Big thank you to Omar Cornut (ocornut@github) for his [imgui library](https://github.com/ocornut/imgui) and +/// giving me the inspiration for this library, Casey Muratori for handmade hero +/// and his original immediate mode graphical user interface idea and Sean +/// Barret for his amazing single header libraries which restored my faith +/// in libraries and brought me to create some of my own. Finally Apoorva Joshi +/// for his single header file packer. +*/ + diff --git a/src/external/glfw/deps/vulkan/vulkan.h b/src/external/glfw/deps/vulkan/vulkan.h index bf35a3a7..4ffdeb9d 100644 --- a/src/external/glfw/deps/vulkan/vulkan.h +++ b/src/external/glfw/deps/vulkan/vulkan.h @@ -35,12 +35,6 @@ #endif -#ifdef VK_USE_PLATFORM_MIR_KHR -#include -#include "vulkan_mir.h" -#endif - - #ifdef VK_USE_PLATFORM_VI_NN #include "vulkan_vi.h" #endif diff --git a/src/external/glfw/include/GLFW/glfw3.h b/src/external/glfw/include/GLFW/glfw3.h index 990fe3f7..099cd8e6 100644 --- a/src/external/glfw/include/GLFW/glfw3.h +++ b/src/external/glfw/include/GLFW/glfw3.h @@ -269,23 +269,22 @@ extern "C" { #define GLFW_VERSION_REVISION 0 /*! @} */ -/*! @name Boolean values - * @{ */ /*! @brief One. * - * One. Seriously. You don't _need_ to use this symbol in your code. It's - * semantic sugar for the number 1. You can also use `1` or `true` or `_True` - * or `GL_TRUE` or whatever you want. + * This is only semantic sugar for the number 1. You can instead use `1` or + * `true` or `_True` or `GL_TRUE` or anything else that is equal to one. + * + * @ingroup init */ #define GLFW_TRUE 1 /*! @brief Zero. * - * Zero. Seriously. You don't _need_ to use this symbol in your code. It's - * semantic sugar for the number 0. You can also use `0` or `false` or - * `_False` or `GL_FALSE` or whatever you want. + * This is only semantic sugar for the number 0. You can instead use `0` or + * `false` or `_False` or `GL_FALSE` or anything else that is equal to zero. + * + * @ingroup init */ #define GLFW_FALSE 0 -/*! @} */ /*! @name Key and button actions * @{ */ @@ -313,6 +312,7 @@ extern "C" { /*! @} */ /*! @defgroup hat_state Joystick hat states + * @brief Joystick hat states. * * See [joystick hat input](@ref joystick_hat) for how these are used. * @@ -973,6 +973,10 @@ extern "C" { * [attribute](@ref GLFW_CLIENT_API_attrib). */ #define GLFW_CONTEXT_CREATION_API 0x0002200B +/*! @brief Window content area scaling window + * [window hint](@ref GLFW_SCALE_TO_MONITOR). + */ +#define GLFW_SCALE_TO_MONITOR 0x0002200C #define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 #define GLFW_COCOA_FRAME_NAME 0x00023002 @@ -1056,9 +1060,20 @@ extern "C" { /*! @addtogroup init * @{ */ +/*! @brief Joystick hat buttons init hint. + * + * Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS) + */ #define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 - +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES) + */ #define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR) + */ #define GLFW_COCOA_MENUBAR 0x00051002 /*! @} */ @@ -1129,7 +1144,7 @@ typedef struct GLFWwindow GLFWwindow; * * @since Added in version 3.1. * - * @ingroup cursor + * @ingroup input */ typedef struct GLFWcursor GLFWcursor; @@ -1567,6 +1582,8 @@ typedef struct GLFWgammaramp * * @since Added in version 2.1. * @glfw3 Removed format and bytes-per-pixel members. + * + * @ingroup window */ typedef struct GLFWimage { @@ -1589,6 +1606,8 @@ typedef struct GLFWimage * @sa @ref glfwGetGamepadState * * @since Added in version 3.3. + * + * @ingroup input */ typedef struct GLFWgamepadstate { @@ -2137,9 +2156,9 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); /*! @brief Generates a gamma ramp and sets it for the specified monitor. * - * This function generates a 256-element gamma ramp from the specified exponent - * and then calls @ref glfwSetGammaRamp with it. The value must be a finite - * number greater than zero. + * This function generates an appropriately sized gamma ramp from the specified + * exponent and then calls @ref glfwSetGammaRamp with it. The value must be + * a finite number greater than zero. * * The software controlled gamma ramp is applied _in addition_ to the hardware * gamma correction, which today is usually an approximation of sRGB gamma. @@ -2218,8 +2237,8 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark Gamma ramp sizes other than 256 are not supported by all platforms - * or graphics hardware. + * @remark The size of the specified gamma ramp should match the size of the + * current ramp for that monitor. * * @remark @win32 The gamma ramp size must be 256. * @@ -3758,6 +3777,9 @@ GLFWAPI void glfwWaitEvents(void); * * @param[in] timeout The maximum amount of time, in seconds, to wait. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * * @reentrancy This function must not be called from a callback. * * @thread_safety This function must only be called from the main thread. @@ -4011,8 +4033,8 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); * `GLFW_RELEASE`. * * If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function - * `GLFW_PRESS` the first time you call it for a mouse button that was pressed, - * even if that mouse button has already been released. + * returns `GLFW_PRESS` the first time you call it for a mouse button that was + * pressed, even if that mouse button has already been released. * * @param[in] window The desired window. * @param[in] button The desired [mouse button](@ref buttons). @@ -4283,9 +4305,7 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * The character callback behaves as system text input normally does and will * not be called if modifier keys are held down that would prevent normal text * input on that platform, for example a Super (Command) key on macOS or Alt key - * on Windows. There is a - * [character with modifiers callback](@ref glfwSetCharModsCallback) that - * receives these events. + * on Windows. * * @param[in] window The window whose callback to set. * @param[in] cbfun The new callback, or `NULL` to remove the currently set @@ -4581,7 +4601,7 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); * Each element in the array is one of the following values: * * Name | Value - * --------------------- | -------------------------------- + * ---- | ----- * `GLFW_HAT_CENTERED` | 0 * `GLFW_HAT_UP` | 1 * `GLFW_HAT_RIGHT` | 2 @@ -4901,6 +4921,8 @@ GLFWAPI const char* glfwGetGamepadName(int jid); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. * + * @thread_safety This function must only be called from the main thread. + * * @sa @ref gamepad * @sa @ref glfwUpdateGamepadMappings * @sa @ref glfwJoystickIsGamepad @@ -4922,8 +4944,6 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @wayland Clipboard is currently unimplemented. - * * @pointer_lifetime The specified string is copied before this function * returns. * @@ -4952,8 +4972,6 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @wayland Clipboard is currently unimplemented. - * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the next call to @ref * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library diff --git a/src/external/glfw/include/GLFW/glfw3native.h b/src/external/glfw/include/GLFW/glfw3native.h index d585496c..81f48d0e 100644 --- a/src/external/glfw/include/GLFW/glfw3native.h +++ b/src/external/glfw/include/GLFW/glfw3native.h @@ -62,7 +62,6 @@ extern "C" { * * `GLFW_EXPOSE_NATIVE_COCOA` * * `GLFW_EXPOSE_NATIVE_X11` * * `GLFW_EXPOSE_NATIVE_WAYLAND` - * * `GLFW_EXPOSE_NATIVE_MIR` * * The available context API macros are: * * `GLFW_EXPOSE_NATIVE_WGL` @@ -82,7 +81,7 @@ extern "C" { * System headers and types *************************************************************************/ -#if defined(GLFW_EXPOSE_NATIVE_WIN32) +#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for // example to allow applications to correctly declare a GL_ARB_debug_output // callback) but windows.h assumes no one will define APIENTRY before it does @@ -96,23 +95,19 @@ extern "C" { typedef void *PVOID; typedef PVOID HANDLE; typedef HANDLE HWND; -#elif defined(GLFW_EXPOSE_NATIVE_COCOA) +#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) #include #if defined(__OBJC__) #import #else - // RAY: Added protection in case OBJC types defined - #if !OBJC_TYPES_DEFINED - typedef void* id; - #endif + #include + typedef void* id; #endif -#elif defined(GLFW_EXPOSE_NATIVE_X11) +#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) #include #include #elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) #include -#elif defined(GLFW_EXPOSE_NATIVE_MIR) - #include #endif #if defined(GLFW_EXPOSE_NATIVE_WGL) @@ -426,50 +421,6 @@ GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); #endif -#if defined(GLFW_EXPOSE_NATIVE_MIR) -/*! @brief Returns the `MirConnection*` used by GLFW. - * - * @return The `MirConnection*` used by GLFW, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI MirConnection* glfwGetMirDisplay(void); - -/*! @brief Returns the Mir output ID of the specified monitor. - * - * @return The Mir output ID of the specified monitor, or zero if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor); - -/*! @brief Returns the `MirWindow*` of the specified window. - * - * @return The `MirWindow*` of the specified window, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI MirWindow* glfwGetMirWindow(GLFWwindow* window); -#endif - #if defined(GLFW_EXPOSE_NATIVE_EGL) /*! @brief Returns the `EGLDisplay` used by GLFW. * diff --git a/src/external/glfw/src/CMakeLists.txt b/src/external/glfw/src/CMakeLists.txt index 76460769..f6b4911f 100644 --- a/src/external/glfw/src/CMakeLists.txt +++ b/src/external/glfw/src/CMakeLists.txt @@ -35,6 +35,10 @@ elseif (_GLFW_WAYLAND) PROTOCOL "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/xdg-shell/xdg-shell.xml" BASENAME xdg-shell) + ecm_add_wayland_client_protocol(glfw_SOURCES + PROTOCOL + "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" + BASENAME xdg-decoration) ecm_add_wayland_client_protocol(glfw_SOURCES PROTOCOL "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/viewporter/viewporter.xml" @@ -51,13 +55,6 @@ elseif (_GLFW_WAYLAND) PROTOCOL "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" BASENAME idle-inhibit-unstable-v1) -elseif (_GLFW_MIR) - set(glfw_HEADERS ${common_HEADERS} mir_platform.h linux_joystick.h - posix_time.h posix_thread.h xkb_unicode.h egl_context.h - osmesa_context.h) - set(glfw_SOURCES ${common_SOURCES} mir_init.c mir_monitor.c mir_window.c - linux_joystick.c posix_time.c posix_thread.c xkb_unicode.c - egl_context.c osmesa_context.c) elseif (_GLFW_OSMESA) set(glfw_HEADERS ${common_HEADERS} null_platform.h null_joystick.h posix_time.h posix_thread.h osmesa_context.h) @@ -104,10 +101,10 @@ set_target_properties(glfw PROPERTIES target_compile_definitions(glfw_objlib PRIVATE _GLFW_USE_CONFIG_H) target_include_directories(glfw_objlib PUBLIC "$" - "$/include>") + "$") target_include_directories(glfw PUBLIC "$" - "$/include>") + "$") target_include_directories(glfw_objlib PRIVATE "${GLFW_SOURCE_DIR}/src" "${GLFW_BINARY_DIR}/src" @@ -128,7 +125,7 @@ target_compile_options(glfw_objlib PRIVATE if (BUILD_SHARED_LIBS) if (WIN32) if (MINGW) - # Remove the lib prefix on the DLL (but not the import library + # Remove the lib prefix on the DLL (but not the import library) set_target_properties(glfw PROPERTIES PREFIX "") # Add a suffix to the import library to avoid naming conflicts @@ -142,7 +139,7 @@ if (BUILD_SHARED_LIBS) target_compile_options(glfw_objlib PRIVATE "-fno-common") set_target_properties(glfw PROPERTIES - INSTALL_NAME_DIR "lib${LIB_SUFFIX}") + INSTALL_NAME_DIR "${CMAKE_INSTALL_LIBDIR}") elseif (UNIX) # Hide symbols not explicitly tagged for export from the shared library target_compile_options(glfw_objlib PRIVATE "-fvisibility=hidden") @@ -162,7 +159,7 @@ if (GLFW_INSTALL) install(TARGETS glfw EXPORT glfwTargets RUNTIME DESTINATION "bin" - ARCHIVE DESTINATION "lib${LIB_SUFFIX}" - LIBRARY DESTINATION "lib${LIB_SUFFIX}") + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") endif() diff --git a/src/external/glfw/src/cocoa_init.m b/src/external/glfw/src/cocoa_init.m index 01a746ba..f3c47957 100644 --- a/src/external/glfw/src/cocoa_init.m +++ b/src/external/glfw/src/cocoa_init.m @@ -27,6 +27,10 @@ #include "internal.h" #include // For MAXPATHLEN +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 + #define NSEventMaskKeyUp NSKeyUpMask + #define NSEventModifierFlagCommand NSCommandKeyMask +#endif // Change to our application bundle's resources directory, if present // @@ -271,17 +275,21 @@ static GLFWbool initializeTIS(void) return updateUnicodeDataNS(); } -@interface GLFWLayoutListener : NSObject +@interface GLFWHelper : NSObject @end -@implementation GLFWLayoutListener +@implementation GLFWHelper - (void)selectedKeyboardInputSourceChanged:(NSObject* )object { updateUnicodeDataNS(); } -@end +- (void)doNothing:(id)object +{ +} + +@end // GLFWHelper ////////////////////////////////////////////////////////////////////////// @@ -291,13 +299,31 @@ static GLFWbool initializeTIS(void) int _glfwPlatformInit(void) { _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; + _glfw.ns.helper = [[GLFWHelper alloc] init]; + + [NSThread detachNewThreadSelector:@selector(doNothing:) + toTarget:_glfw.ns.helper + withObject:nil]; + + [NSApplication sharedApplication]; + + NSEvent* (^block)(NSEvent*) = ^ NSEvent* (NSEvent* event) + { + if ([event modifierFlags] & NSEventModifierFlagCommand) + [[NSApp keyWindow] sendEvent:event]; + + return event; + }; + + _glfw.ns.keyUpMonitor = + [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp + handler:block]; if (_glfw.hints.init.ns.chdir) changeToResourcesDirectory(); - _glfw.ns.listener = [[GLFWLayoutListener alloc] init]; [[NSNotificationCenter defaultCenter] - addObserver:_glfw.ns.listener + addObserver:_glfw.ns.helper selector:@selector(selectedKeyboardInputSourceChanged:) name:NSTextInputContextKeyboardSelectionDidChangeNotification object:nil]; @@ -342,18 +368,21 @@ void _glfwPlatformTerminate(void) _glfw.ns.delegate = nil; } - if (_glfw.ns.listener) + if (_glfw.ns.helper) { [[NSNotificationCenter defaultCenter] - removeObserver:_glfw.ns.listener + removeObserver:_glfw.ns.helper name:NSTextInputContextKeyboardSelectionDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] - removeObserver:_glfw.ns.listener]; - [_glfw.ns.listener release]; - _glfw.ns.listener = nil; + removeObserver:_glfw.ns.helper]; + [_glfw.ns.helper release]; + _glfw.ns.helper = nil; } + if (_glfw.ns.keyUpMonitor) + [NSEvent removeMonitor:_glfw.ns.keyUpMonitor]; + free(_glfw.ns.clipboardString); _glfwTerminateNSGL(); diff --git a/src/external/glfw/src/cocoa_monitor.m b/src/external/glfw/src/cocoa_monitor.m index 986d799e..39fff6f7 100644 --- a/src/external/glfw/src/cocoa_monitor.m +++ b/src/external/glfw/src/cocoa_monitor.m @@ -467,7 +467,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode) CVDisplayLinkRelease(link); } -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { uint32_t i, size = CGDisplayGammaTableCapacity(monitor->ns.displayID); CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue)); @@ -489,6 +489,7 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) } free(values); + return GLFW_TRUE; } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) diff --git a/src/external/glfw/src/cocoa_platform.h b/src/external/glfw/src/cocoa_platform.h index cf6ca9f5..13adaa97 100644 --- a/src/external/glfw/src/cocoa_platform.h +++ b/src/external/glfw/src/cocoa_platform.h @@ -27,12 +27,10 @@ #include #include +#include #if defined(__OBJC__) -#import #import #else -#include -#include typedef void* id; #endif @@ -111,7 +109,9 @@ typedef struct _GLFWlibraryNS TISInputSourceRef inputSource; IOHIDManagerRef hidManager; id unicodeData; - id listener; + id helper; + id keyUpMonitor; + id nibObjects; char keyName[64]; short int keycodes[256]; diff --git a/src/external/glfw/src/cocoa_window.m b/src/external/glfw/src/cocoa_window.m index 7cf4a593..db9935ce 100644 --- a/src/external/glfw/src/cocoa_window.m +++ b/src/external/glfw/src/cocoa_window.m @@ -32,7 +32,6 @@ // Needed for _NSGetProgname #include -// HACK: The 10.12 SDK adds new symbols and immediately deprecates the old ones #if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 #define NSWindowStyleMaskBorderless NSBorderlessWindowMask #define NSWindowStyleMaskClosable NSClosableWindowMask @@ -47,10 +46,9 @@ #define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask #define NSEventMaskAny NSAnyEventMask #define NSEventTypeApplicationDefined NSApplicationDefined - #define NSEventTypeKeyUp NSKeyUp + #define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat #endif - // Returns the style mask corresponding to the window settings // static NSUInteger getStyleMask(_GLFWwindow* window) @@ -237,6 +235,8 @@ static NSUInteger translateKeyToModifierFlag(int key) case GLFW_KEY_LEFT_SUPER: case GLFW_KEY_RIGHT_SUPER: return NSEventModifierFlagCommand; + case GLFW_KEY_CAPS_LOCK: + return NSEventModifierFlagCapsLock; } return 0; @@ -365,7 +365,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; // Delegate for application related notifications //------------------------------------------------------------------------ -@interface GLFWApplicationDelegate : NSObject +@interface GLFWApplicationDelegate : NSObject @end @implementation GLFWApplicationDelegate @@ -438,8 +438,9 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; markedText = [[NSMutableAttributedString alloc] init]; [self updateTrackingAreas]; - [self registerForDraggedTypes:[NSArray arrayWithObjects: - NSFilenamesPboardType, nil]]; + // NOTE: kUTTypeURL corresponds to NSPasteboardTypeURL but is available + // on 10.7 without having been deprecated yet + [self registerForDraggedTypes:@[(__bridge NSString*) kUTTypeURL]]; } return self; @@ -472,6 +473,14 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; return YES; } +- (void)updateLayer +{ + if (window->context.client != GLFW_NO_API) + [window->context.nsgl.object update]; + + _glfwInputWindowDamage(window); +} + - (id)makeBackingLayer { if (window->ns.layer) @@ -485,6 +494,11 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; updateCursorImage(window); } +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + return YES; +} + - (void)mouseDown:(NSEvent *)event { _glfwInputMouseClick(window, @@ -650,7 +664,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; _glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods); - [self interpretKeyEvents:[NSArray arrayWithObject:event]]; + [self interpretKeyEvents:@[event]]; } - (void)flagsChanged:(NSEvent *)event @@ -701,45 +715,33 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (NSDragOperation)draggingEntered:(id )sender { - if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) - == NSDragOperationGeneric) - { - [self setNeedsDisplay:YES]; - return NSDragOperationGeneric; - } - - return NSDragOperationNone; -} - -- (BOOL)prepareForDragOperation:(id )sender -{ - [self setNeedsDisplay:YES]; - return YES; + // HACK: We don't know what to say here because we don't know what the + // application wants to do with the paths + return NSDragOperationGeneric; } - (BOOL)performDragOperation:(id )sender { - NSPasteboard* pasteboard = [sender draggingPasteboard]; - NSArray* files = [pasteboard propertyListForType:NSFilenamesPboardType]; - const NSRect contentRect = [window->ns.view frame]; _glfwInputCursorPos(window, [sender draggingLocation].x, contentRect.size.height - [sender draggingLocation].y); - const NSUInteger count = [files count]; + NSPasteboard* pasteboard = [sender draggingPasteboard]; + NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES}; + NSArray* urls = [pasteboard readObjectsForClasses:@[[NSURL class]] + options:options]; + const NSUInteger count = [urls count]; if (count) { - NSEnumerator* e = [files objectEnumerator]; char** paths = calloc(count, sizeof(char*)); - NSUInteger i; - for (i = 0; i < count; i++) - paths[i] = _glfw_strdup([[e nextObject] UTF8String]); + for (NSUInteger i = 0; i < count; i++) + paths[i] = _glfw_strdup([[urls objectAtIndex:i] fileSystemRepresentation]); _glfwInputDrop(window, (int) count, (const char**) paths); - for (i = 0; i < count; i++) + for (NSUInteger i = 0; i < count; i++) free(paths[i]); free(paths); } @@ -747,11 +749,6 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; return YES; } -- (void)concludeDragOperation:(id )sender -{ - [self setNeedsDisplay:YES]; -} - - (BOOL)hasMarkedText { return [markedText length] > 0; @@ -865,52 +862,6 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; @end -//------------------------------------------------------------------------ -// GLFW application class -//------------------------------------------------------------------------ - -@interface GLFWApplication : NSApplication -{ - NSArray* nibObjects; -} - -@end - -@implementation GLFWApplication - -// From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost -// This works around an AppKit bug, where key up events while holding -// down the command key don't get sent to the key window. -- (void)sendEvent:(NSEvent *)event -{ - if ([event type] == NSEventTypeKeyUp && - ([event modifierFlags] & NSEventModifierFlagCommand)) - { - [[self keyWindow] sendEvent:event]; - } - else - [super sendEvent:event]; -} - - -// No-op thread entry point -// -- (void)doNothing:(id)object -{ -} - -- (void)loadMainMenu -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 - [[NSBundle mainBundle] loadNibNamed:@"MainMenu" - owner:NSApp - topLevelObjects:&nibObjects]; -#else - [[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp]; -#endif -} -@end - // Set up the menu bar (manually) // This is nasty, nasty stuff -- calls to undocumented semi-private APIs that // could go away at any moment, lots of stuff that really should be @@ -1020,32 +971,9 @@ static void createMenuBar(void) // static GLFWbool initializeAppKit(void) { - if (NSApp) + if (_glfw.ns.delegate) return GLFW_TRUE; - // Implicitly create shared NSApplication instance - [GLFWApplication sharedApplication]; - - // Make Cocoa enter multi-threaded mode - [NSThread detachNewThreadSelector:@selector(doNothing:) - toTarget:NSApp - withObject:nil]; - - if (_glfw.hints.init.ns.menubar) - { - // In case we are unbundled, make us a proper UI application - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - - // Menu bar setup must go between sharedApplication above and - // finishLaunching below, in order to properly emulate the behavior - // of NSApplicationMain - - if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) - [NSApp loadMainMenu]; - else - createMenuBar(); - } - // There can only be one application delegate, but we allocate it the // first time a window is created to keep all window code in this file _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init]; @@ -1057,13 +985,34 @@ static GLFWbool initializeAppKit(void) } [NSApp setDelegate:_glfw.ns.delegate]; + + if (_glfw.hints.init.ns.menubar) + { + // In case we are unbundled, make us a proper UI application + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + + // Menu bar setup must go between sharedApplication above and + // finishLaunching below, in order to properly emulate the behavior + // of NSApplicationMain + + if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 + [[NSBundle mainBundle] loadNibNamed:@"MainMenu" + owner:NSApp + topLevelObjects:&_glfw.ns.nibObjects]; +#else + [[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp]; +#endif + } + else + createMenuBar(); + } + [NSApp run]; // Press and Hold prevents some keys from emitting repeated characters - NSDictionary* defaults = - [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], - @"ApplePressAndHoldEnabled", - nil]; + NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO}; [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; return GLFW_TRUE; @@ -1563,9 +1512,6 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) void _glfwPlatformPollEvents(void) { - if (!initializeAppKit()) - return; - for (;;) { NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny @@ -1716,9 +1662,6 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, NSImage* native; NSBitmapImageRep* rep; - if (!initializeAppKit()) - return GLFW_FALSE; - rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:image->width @@ -1728,7 +1671,7 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, hasAlpha:YES isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace - bitmapFormat:NSAlphaNonpremultipliedBitmapFormat + bitmapFormat:NSBitmapFormatAlphaNonpremultiplied bytesPerRow:image->width * 4 bitsPerPixel:32]; @@ -1754,9 +1697,6 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { - if (!initializeAppKit()) - return GLFW_FALSE; - if (shape == GLFW_ARROW_CURSOR) cursor->ns.object = [NSCursor arrowCursor]; else if (shape == GLFW_IBEAM_CURSOR) @@ -1795,26 +1735,24 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) void _glfwPlatformSetClipboardString(const char* string) { - NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil]; - NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - [pasteboard declareTypes:types owner:nil]; + [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; [pasteboard setString:[NSString stringWithUTF8String:string] - forType:NSStringPboardType]; + forType:NSPasteboardTypeString]; } const char* _glfwPlatformGetClipboardString(void) { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; - if (![[pasteboard types] containsObject:NSStringPboardType]) + if (![[pasteboard types] containsObject:NSPasteboardTypeString]) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "Cocoa: Failed to retrieve string from pasteboard"); return NULL; } - NSString* object = [pasteboard stringForType:NSStringPboardType]; + NSString* object = [pasteboard stringForType:NSPasteboardTypeString]; if (!object) { _glfwInputError(GLFW_PLATFORM_ERROR, diff --git a/src/external/glfw/src/egl_context.h b/src/external/glfw/src/egl_context.h index aa339baa..8f3d075c 100644 --- a/src/external/glfw/src/egl_context.h +++ b/src/external/glfw/src/egl_context.h @@ -43,10 +43,6 @@ typedef Window EGLNativeWindowType; #define EGLAPIENTRY typedef struct wl_display* EGLNativeDisplayType; typedef struct wl_egl_window* EGLNativeWindowType; -#elif defined(_GLFW_MIR) - #define EGLAPIENTRY -typedef MirEGLNativeDisplayType EGLNativeDisplayType; -typedef MirEGLNativeWindowType EGLNativeWindowType; #else #error "No supported EGL platform selected" #endif diff --git a/src/external/glfw/src/glfw3.pc.in b/src/external/glfw/src/glfw3.pc.in index f2e4d976..87423e1a 100644 --- a/src/external/glfw/src/glfw3.pc.in +++ b/src/external/glfw/src/glfw3.pc.in @@ -1,12 +1,12 @@ prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} -includedir=${prefix}/include -libdir=${exec_prefix}/lib@LIB_SUFFIX@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ Name: GLFW Description: A multi-platform library for OpenGL, window and input Version: @GLFW_VERSION_FULL@ -URL: http://www.glfw.org/ +URL: https://www.glfw.org/ Requires.private: @GLFW_PKG_DEPS@ Libs: -L${libdir} -l@GLFW_LIB_NAME@ Libs.private: @GLFW_PKG_LIBS@ diff --git a/src/external/glfw/src/glfw_config.h.in b/src/external/glfw/src/glfw_config.h.in index 0b47945c..f418c995 100644 --- a/src/external/glfw/src/glfw_config.h.in +++ b/src/external/glfw/src/glfw_config.h.in @@ -42,8 +42,6 @@ #cmakedefine _GLFW_COCOA // Define this to 1 if building GLFW for Wayland #cmakedefine _GLFW_WAYLAND -// Define this to 1 if building GLFW for Mir -#cmakedefine _GLFW_MIR // Define this to 1 if building GLFW for OSMesa #cmakedefine _GLFW_OSMESA @@ -57,4 +55,6 @@ // Define this to 1 if xkbcommon supports the compose key #cmakedefine HAVE_XKBCOMMON_COMPOSE_H +// Define this to 1 if the libc supports memfd_create() +#cmakedefine HAVE_MEMFD_CREATE diff --git a/src/external/glfw/src/init.c b/src/external/glfw/src/init.c index 9e670d42..4f424c4a 100644 --- a/src/external/glfw/src/init.c +++ b/src/external/glfw/src/init.c @@ -119,6 +119,30 @@ char* _glfw_strdup(const char* source) return result; } +float _glfw_fminf(float a, float b) +{ + if (a != a) + return b; + else if (b != b) + return a; + else if (a < b) + return a; + else + return b; +} + +float _glfw_fmaxf(float a, float b) +{ + if (a != a) + return b; + else if (b != b) + return a; + else if (a > b) + return a; + else + return b; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW event API ////// diff --git a/src/external/glfw/src/input.c b/src/external/glfw/src/input.c index b0bb3de4..460e9f31 100644 --- a/src/external/glfw/src/input.c +++ b/src/external/glfw/src/input.c @@ -1242,7 +1242,7 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state) if (e->type == _GLFW_JOYSTICK_AXIS) { const float value = js->axes[e->index] * e->axisScale + e->axisOffset; - state->axes[i] = fminf(fmaxf(value, -1.f), 1.f); + state->axes[i] = _glfw_fminf(_glfw_fmaxf(value, -1.f), 1.f); } else if (e->type == _GLFW_JOYSTICK_HATBIT) { diff --git a/src/external/glfw/src/internal.h b/src/external/glfw/src/internal.h index 9fc626dd..c7c5bf8f 100644 --- a/src/external/glfw/src/internal.h +++ b/src/external/glfw/src/internal.h @@ -126,7 +126,6 @@ typedef enum VkStructureType VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, - VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF @@ -188,8 +187,6 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void); #include "x11_platform.h" #elif defined(_GLFW_WAYLAND) #include "wl_platform.h" -#elif defined(_GLFW_MIR) - #include "mir_platform.h" #elif defined(_GLFW_OSMESA) #include "null_platform.h" #else @@ -268,6 +265,7 @@ struct _GLFWwndconfig GLFWbool maximized; GLFWbool centerCursor; GLFWbool focusOnShow; + GLFWbool scaleToMonitor; struct { GLFWbool retina; char frameName[256]; @@ -562,8 +560,6 @@ struct _GLFWlibrary GLFWbool KHR_xcb_surface; #elif defined(_GLFW_WAYLAND) GLFWbool KHR_wayland_surface; -#elif defined(_GLFW_MIR) - GLFWbool KHR_mir_surface; #endif } vk; @@ -615,7 +611,7 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale); GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); void _glfwPlatformSetClipboardString(const char* string); @@ -770,4 +766,6 @@ void _glfwTerminateVulkan(void); const char* _glfwGetVulkanResultString(VkResult result); char* _glfw_strdup(const char* source); +float _glfw_fminf(float a, float b); +float _glfw_fmaxf(float a, float b); diff --git a/src/external/glfw/src/linux_joystick.c b/src/external/glfw/src/linux_joystick.c index 2777d0b3..baa3651b 100644 --- a/src/external/glfw/src/linux_joystick.c +++ b/src/external/glfw/src/linux_joystick.c @@ -228,7 +228,7 @@ static GLFWbool openJoystickDevice(const char* path) return GLFW_FALSE; } - strncpy(linjs.path, path, sizeof(linjs.path)); + strncpy(linjs.path, path, sizeof(linjs.path) - 1); memcpy(&js->linjs, &linjs, sizeof(linjs)); pollAbsState(js); diff --git a/src/external/glfw/src/mir_init.c b/src/external/glfw/src/mir_init.c deleted file mode 100644 index 5f9ed373..00000000 --- a/src/external/glfw/src/mir_init.c +++ /dev/null @@ -1,240 +0,0 @@ -//======================================================================== -// GLFW 3.3 Mir - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014-2017 Brandon Schaefer -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#include "internal.h" - -#include -#include -#include - - -// Create key code translation tables -// -static void createKeyTables(void) -{ - int scancode; - - memset(_glfw.mir.keycodes, -1, sizeof(_glfw.mir.keycodes)); - memset(_glfw.mir.scancodes, -1, sizeof(_glfw.mir.scancodes)); - - _glfw.mir.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; - _glfw.mir.keycodes[KEY_1] = GLFW_KEY_1; - _glfw.mir.keycodes[KEY_2] = GLFW_KEY_2; - _glfw.mir.keycodes[KEY_3] = GLFW_KEY_3; - _glfw.mir.keycodes[KEY_4] = GLFW_KEY_4; - _glfw.mir.keycodes[KEY_5] = GLFW_KEY_5; - _glfw.mir.keycodes[KEY_6] = GLFW_KEY_6; - _glfw.mir.keycodes[KEY_7] = GLFW_KEY_7; - _glfw.mir.keycodes[KEY_8] = GLFW_KEY_8; - _glfw.mir.keycodes[KEY_9] = GLFW_KEY_9; - _glfw.mir.keycodes[KEY_0] = GLFW_KEY_0; - _glfw.mir.keycodes[KEY_SPACE] = GLFW_KEY_SPACE; - _glfw.mir.keycodes[KEY_MINUS] = GLFW_KEY_MINUS; - _glfw.mir.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL; - _glfw.mir.keycodes[KEY_Q] = GLFW_KEY_Q; - _glfw.mir.keycodes[KEY_W] = GLFW_KEY_W; - _glfw.mir.keycodes[KEY_E] = GLFW_KEY_E; - _glfw.mir.keycodes[KEY_R] = GLFW_KEY_R; - _glfw.mir.keycodes[KEY_T] = GLFW_KEY_T; - _glfw.mir.keycodes[KEY_Y] = GLFW_KEY_Y; - _glfw.mir.keycodes[KEY_U] = GLFW_KEY_U; - _glfw.mir.keycodes[KEY_I] = GLFW_KEY_I; - _glfw.mir.keycodes[KEY_O] = GLFW_KEY_O; - _glfw.mir.keycodes[KEY_P] = GLFW_KEY_P; - _glfw.mir.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; - _glfw.mir.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; - _glfw.mir.keycodes[KEY_A] = GLFW_KEY_A; - _glfw.mir.keycodes[KEY_S] = GLFW_KEY_S; - _glfw.mir.keycodes[KEY_D] = GLFW_KEY_D; - _glfw.mir.keycodes[KEY_F] = GLFW_KEY_F; - _glfw.mir.keycodes[KEY_G] = GLFW_KEY_G; - _glfw.mir.keycodes[KEY_H] = GLFW_KEY_H; - _glfw.mir.keycodes[KEY_J] = GLFW_KEY_J; - _glfw.mir.keycodes[KEY_K] = GLFW_KEY_K; - _glfw.mir.keycodes[KEY_L] = GLFW_KEY_L; - _glfw.mir.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; - _glfw.mir.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; - _glfw.mir.keycodes[KEY_Z] = GLFW_KEY_Z; - _glfw.mir.keycodes[KEY_X] = GLFW_KEY_X; - _glfw.mir.keycodes[KEY_C] = GLFW_KEY_C; - _glfw.mir.keycodes[KEY_V] = GLFW_KEY_V; - _glfw.mir.keycodes[KEY_B] = GLFW_KEY_B; - _glfw.mir.keycodes[KEY_N] = GLFW_KEY_N; - _glfw.mir.keycodes[KEY_M] = GLFW_KEY_M; - _glfw.mir.keycodes[KEY_COMMA] = GLFW_KEY_COMMA; - _glfw.mir.keycodes[KEY_DOT] = GLFW_KEY_PERIOD; - _glfw.mir.keycodes[KEY_SLASH] = GLFW_KEY_SLASH; - _glfw.mir.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; - _glfw.mir.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE; - _glfw.mir.keycodes[KEY_TAB] = GLFW_KEY_TAB; - _glfw.mir.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; - _glfw.mir.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; - _glfw.mir.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; - _glfw.mir.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; - _glfw.mir.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; - _glfw.mir.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; - _glfw.mir.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; - _glfw.mir.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; - _glfw.mir.keycodes[KEY_MENU] = GLFW_KEY_MENU; - _glfw.mir.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; - _glfw.mir.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; - _glfw.mir.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; - _glfw.mir.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; - _glfw.mir.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE; - _glfw.mir.keycodes[KEY_DELETE] = GLFW_KEY_DELETE; - _glfw.mir.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; - _glfw.mir.keycodes[KEY_ENTER] = GLFW_KEY_ENTER; - _glfw.mir.keycodes[KEY_HOME] = GLFW_KEY_HOME; - _glfw.mir.keycodes[KEY_END] = GLFW_KEY_END; - _glfw.mir.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; - _glfw.mir.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; - _glfw.mir.keycodes[KEY_INSERT] = GLFW_KEY_INSERT; - _glfw.mir.keycodes[KEY_LEFT] = GLFW_KEY_LEFT; - _glfw.mir.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT; - _glfw.mir.keycodes[KEY_DOWN] = GLFW_KEY_DOWN; - _glfw.mir.keycodes[KEY_UP] = GLFW_KEY_UP; - _glfw.mir.keycodes[KEY_F1] = GLFW_KEY_F1; - _glfw.mir.keycodes[KEY_F2] = GLFW_KEY_F2; - _glfw.mir.keycodes[KEY_F3] = GLFW_KEY_F3; - _glfw.mir.keycodes[KEY_F4] = GLFW_KEY_F4; - _glfw.mir.keycodes[KEY_F5] = GLFW_KEY_F5; - _glfw.mir.keycodes[KEY_F6] = GLFW_KEY_F6; - _glfw.mir.keycodes[KEY_F7] = GLFW_KEY_F7; - _glfw.mir.keycodes[KEY_F8] = GLFW_KEY_F8; - _glfw.mir.keycodes[KEY_F9] = GLFW_KEY_F9; - _glfw.mir.keycodes[KEY_F10] = GLFW_KEY_F10; - _glfw.mir.keycodes[KEY_F11] = GLFW_KEY_F11; - _glfw.mir.keycodes[KEY_F12] = GLFW_KEY_F12; - _glfw.mir.keycodes[KEY_F13] = GLFW_KEY_F13; - _glfw.mir.keycodes[KEY_F14] = GLFW_KEY_F14; - _glfw.mir.keycodes[KEY_F15] = GLFW_KEY_F15; - _glfw.mir.keycodes[KEY_F16] = GLFW_KEY_F16; - _glfw.mir.keycodes[KEY_F17] = GLFW_KEY_F17; - _glfw.mir.keycodes[KEY_F18] = GLFW_KEY_F18; - _glfw.mir.keycodes[KEY_F19] = GLFW_KEY_F19; - _glfw.mir.keycodes[KEY_F20] = GLFW_KEY_F20; - _glfw.mir.keycodes[KEY_F21] = GLFW_KEY_F21; - _glfw.mir.keycodes[KEY_F22] = GLFW_KEY_F22; - _glfw.mir.keycodes[KEY_F23] = GLFW_KEY_F23; - _glfw.mir.keycodes[KEY_F24] = GLFW_KEY_F24; - _glfw.mir.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; - _glfw.mir.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; - _glfw.mir.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; - _glfw.mir.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; - _glfw.mir.keycodes[KEY_KP0] = GLFW_KEY_KP_0; - _glfw.mir.keycodes[KEY_KP1] = GLFW_KEY_KP_1; - _glfw.mir.keycodes[KEY_KP2] = GLFW_KEY_KP_2; - _glfw.mir.keycodes[KEY_KP3] = GLFW_KEY_KP_3; - _glfw.mir.keycodes[KEY_KP4] = GLFW_KEY_KP_4; - _glfw.mir.keycodes[KEY_KP5] = GLFW_KEY_KP_5; - _glfw.mir.keycodes[KEY_KP6] = GLFW_KEY_KP_6; - _glfw.mir.keycodes[KEY_KP7] = GLFW_KEY_KP_7; - _glfw.mir.keycodes[KEY_KP8] = GLFW_KEY_KP_8; - _glfw.mir.keycodes[KEY_KP9] = GLFW_KEY_KP_9; - _glfw.mir.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; - _glfw.mir.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; - _glfw.mir.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; - - for (scancode = 0; scancode < 256; scancode++) - { - if (_glfw.mir.keycodes[scancode] > 0) - _glfw.mir.scancodes[_glfw.mir.keycodes[scancode]] = scancode; - } -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformInit(void) -{ - int error; - - _glfw.mir.connection = mir_connect_sync(NULL, __PRETTY_FUNCTION__); - - if (!mir_connection_is_valid(_glfw.mir.connection)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unable to connect to server: %s", - mir_connection_get_error_message(_glfw.mir.connection)); - - return GLFW_FALSE; - } - - _glfw.mir.display = - mir_connection_get_egl_native_display(_glfw.mir.connection); - - createKeyTables(); - - if (!_glfwInitJoysticksLinux()) - return GLFW_FALSE; - - _glfwInitTimerPOSIX(); - - _glfw.mir.eventQueue = calloc(1, sizeof(EventQueue)); - _glfwInitEventQueueMir(_glfw.mir.eventQueue); - - error = pthread_mutex_init(&_glfw.mir.eventMutex, NULL); - if (error) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Failed to create event mutex: %s", - strerror(error)); - return GLFW_FALSE; - } - - _glfwPollMonitorsMir(); - return GLFW_TRUE; -} - -void _glfwPlatformTerminate(void) -{ - _glfwTerminateEGL(); - _glfwTerminateJoysticksLinux(); - - _glfwDeleteEventQueueMir(_glfw.mir.eventQueue); - - pthread_mutex_destroy(&_glfw.mir.eventMutex); - - mir_connection_release(_glfw.mir.connection); -} - -const char* _glfwPlatformGetVersionString(void) -{ - return _GLFW_VERSION_NUMBER " Mir EGL" -#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) - " clock_gettime" -#else - " gettimeofday" -#endif - " evdev" -#if defined(_GLFW_BUILD_DLL) - " shared" -#endif - ; -} - diff --git a/src/external/glfw/src/mir_monitor.c b/src/external/glfw/src/mir_monitor.c deleted file mode 100644 index c340701e..00000000 --- a/src/external/glfw/src/mir_monitor.c +++ /dev/null @@ -1,218 +0,0 @@ -//======================================================================== -// GLFW 3.3 Mir - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014-2017 Brandon Schaefer -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#include "internal.h" - -#include - - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -// Poll for changes in the set of connected monitors -// -void _glfwPollMonitorsMir(void) -{ - int i; - MirDisplayConfig* displayConfig = - mir_connection_create_display_configuration(_glfw.mir.connection); - - int numOutputs = mir_display_config_get_num_outputs(displayConfig); - - for (i = 0; i < numOutputs; i++) - { - const MirOutput* output = mir_display_config_get_output(displayConfig, i); - MirOutputConnectionState state = mir_output_get_connection_state(output); - bool enabled = mir_output_is_enabled(output); - - if (enabled && state == mir_output_connection_state_connected) - { - int widthMM = mir_output_get_physical_width_mm(output); - int heightMM = mir_output_get_physical_height_mm(output); - int x = mir_output_get_position_x(output); - int y = mir_output_get_position_y(output); - int id = mir_output_get_id(output); - size_t currentMode = mir_output_get_current_mode_index(output); - const char* name = mir_output_type_name(mir_output_get_type(output)); - - _GLFWmonitor* monitor = _glfwAllocMonitor(name, - widthMM, - heightMM); - monitor->mir.x = x; - monitor->mir.y = y; - monitor->mir.outputId = id; - monitor->mir.curMode = currentMode; - monitor->modes = _glfwPlatformGetVideoModes(monitor, &monitor->modeCount); - - _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); - } - } - - mir_display_config_release(displayConfig); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) -{ -} - -void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) -{ - if (xpos) - *xpos = monitor->mir.x; - if (ypos) - *ypos = monitor->mir.y; -} - -void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, - float* xscale, float* yscale) -{ - if (xscale) - *xscale = 1.f; - if (yscale) - *yscale = 1.f; -} - -static void FillInRGBBitsFromPixelFormat(GLFWvidmode* mode, const MirPixelFormat pf) -{ - switch (pf) - { - case mir_pixel_format_rgb_565: - mode->redBits = 5; - mode->greenBits = 6; - mode->blueBits = 5; - break; - case mir_pixel_format_rgba_5551: - mode->redBits = 5; - mode->greenBits = 5; - mode->blueBits = 5; - break; - case mir_pixel_format_rgba_4444: - mode->redBits = 4; - mode->greenBits = 4; - mode->blueBits = 4; - break; - case mir_pixel_format_abgr_8888: - case mir_pixel_format_xbgr_8888: - case mir_pixel_format_argb_8888: - case mir_pixel_format_xrgb_8888: - case mir_pixel_format_bgr_888: - case mir_pixel_format_rgb_888: - default: - mode->redBits = 8; - mode->greenBits = 8; - mode->blueBits = 8; - break; - } -} - -GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) -{ - int i; - GLFWvidmode* modes = NULL; - MirDisplayConfig* displayConfig = - mir_connection_create_display_configuration(_glfw.mir.connection); - - int numOutputs = mir_display_config_get_num_outputs(displayConfig); - - for (i = 0; i < numOutputs; i++) - { - const MirOutput* output = mir_display_config_get_output(displayConfig, i); - int id = mir_output_get_id(output); - - if (id != monitor->mir.outputId) - continue; - - MirOutputConnectionState state = mir_output_get_connection_state(output); - bool enabled = mir_output_is_enabled(output); - - // We must have been disconnected - if (!enabled || state != mir_output_connection_state_connected) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Monitor no longer connected"); - return NULL; - } - - int numModes = mir_output_get_num_modes(output); - modes = calloc(numModes, sizeof(GLFWvidmode)); - - for (*found = 0; *found < numModes; (*found)++) - { - const MirOutputMode* mode = mir_output_get_mode(output, *found); - int width = mir_output_mode_get_width(mode); - int height = mir_output_mode_get_height(mode); - double refreshRate = mir_output_mode_get_refresh_rate(mode); - MirPixelFormat currentFormat = mir_output_get_current_pixel_format(output); - - modes[*found].width = width; - modes[*found].height = height; - modes[*found].refreshRate = refreshRate; - - FillInRGBBitsFromPixelFormat(&modes[*found], currentFormat); - } - - break; - } - - mir_display_config_release(displayConfig); - - return modes; -} - -void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) -{ - *mode = monitor->modes[monitor->mir.curMode]; -} - -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI int glfwGetMirMonitor(GLFWmonitor* handle) -{ - _GLFWmonitor* monitor = (_GLFWmonitor*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(0); - return monitor->mir.outputId; -} diff --git a/src/external/glfw/src/mir_platform.h b/src/external/glfw/src/mir_platform.h deleted file mode 100644 index da00a326..00000000 --- a/src/external/glfw/src/mir_platform.h +++ /dev/null @@ -1,133 +0,0 @@ -//======================================================================== -// GLFW 3.3 Mir - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014-2017 Brandon Schaefer -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#include -#include -#include - -#include - -typedef VkFlags VkMirWindowCreateFlagsKHR; - -typedef struct VkMirWindowCreateInfoKHR -{ - VkStructureType sType; - const void* pNext; - VkMirWindowCreateFlagsKHR flags; - MirConnection* connection; - MirWindow* mirWindow; -} VkMirWindowCreateInfoKHR; - -typedef VkResult (APIENTRY *PFN_vkCreateMirWindowKHR)(VkInstance,const VkMirWindowCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); -typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(VkPhysicalDevice,uint32_t,MirConnection*); - -#include "posix_thread.h" -#include "posix_time.h" -#include "linux_joystick.h" -#include "xkb_unicode.h" -#include "egl_context.h" -#include "osmesa_context.h" - -#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) -#define _glfw_dlclose(handle) dlclose(handle) -#define _glfw_dlsym(handle, name) dlsym(handle, name) - -#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->mir.nativeWindow) -#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.mir.display) - -#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowMir mir -#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorMir mir -#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryMir mir -#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorMir mir - -#define _GLFW_PLATFORM_CONTEXT_STATE -#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE - - -// Mir-specific Event Queue -// -typedef struct EventQueue -{ - TAILQ_HEAD(, EventNode) head; -} EventQueue; - -// Mir-specific per-window data -// -typedef struct _GLFWwindowMir -{ - MirWindow* window; - int width; - int height; - MirEGLNativeWindowType nativeWindow; - _GLFWcursor* currentCursor; - -} _GLFWwindowMir; - -// Mir-specific per-monitor data -// -typedef struct _GLFWmonitorMir -{ - int curMode; - int outputId; - int x; - int y; - -} _GLFWmonitorMir; - -// Mir-specific global data -// -typedef struct _GLFWlibraryMir -{ - MirConnection* connection; - MirEGLNativeDisplayType display; - EventQueue* eventQueue; - - short int keycodes[256]; - short int scancodes[GLFW_KEY_LAST + 1]; - - pthread_mutex_t eventMutex; - pthread_cond_t eventCond; - - // The window whose disabled cursor mode is active - _GLFWwindow* disabledCursorWindow; - -} _GLFWlibraryMir; - -// Mir-specific per-cursor data -// TODO: Only system cursors are implemented in Mir atm. Need to wait for support. -// -typedef struct _GLFWcursorMir -{ - MirCursorConfiguration* conf; - MirBufferStream* customCursor; - char const* cursorName; // only needed for system cursors -} _GLFWcursorMir; - - -extern void _glfwPollMonitorsMir(void); -extern void _glfwInitEventQueueMir(EventQueue* queue); -extern void _glfwDeleteEventQueueMir(EventQueue* queue); - diff --git a/src/external/glfw/src/mir_window.c b/src/external/glfw/src/mir_window.c deleted file mode 100644 index a0d17db2..00000000 --- a/src/external/glfw/src/mir_window.c +++ /dev/null @@ -1,975 +0,0 @@ -//======================================================================== -// GLFW 3.3 Mir - www.glfw.org -//------------------------------------------------------------------------ -// Copyright (c) 2014-2017 Brandon Schaefer -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would -// be appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not -// be misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source -// distribution. -// -//======================================================================== - -#include "internal.h" - -#include -#include -#include - - -typedef struct EventNode -{ - TAILQ_ENTRY(EventNode) entries; - const MirEvent* event; - _GLFWwindow* window; -} EventNode; - -static void deleteNode(EventQueue* queue, EventNode* node) -{ - mir_event_unref(node->event); - free(node); -} - -static GLFWbool emptyEventQueue(EventQueue* queue) -{ - return queue->head.tqh_first == NULL; -} - -// TODO The mir_event_ref is not supposed to be used but ... its needed -// in this case. Need to wait until we can read from an FD set up by mir -// for single threaded event handling. -static EventNode* newEventNode(const MirEvent* event, _GLFWwindow* context) -{ - EventNode* newNode = calloc(1, sizeof(EventNode)); - newNode->event = mir_event_ref(event); - newNode->window = context; - - return newNode; -} - -static void enqueueEvent(const MirEvent* event, _GLFWwindow* context) -{ - pthread_mutex_lock(&_glfw.mir.eventMutex); - - EventNode* newNode = newEventNode(event, context); - TAILQ_INSERT_TAIL(&_glfw.mir.eventQueue->head, newNode, entries); - - pthread_cond_signal(&_glfw.mir.eventCond); - - pthread_mutex_unlock(&_glfw.mir.eventMutex); -} - -static EventNode* dequeueEvent(EventQueue* queue) -{ - EventNode* node = NULL; - - pthread_mutex_lock(&_glfw.mir.eventMutex); - - node = queue->head.tqh_first; - - if (node) - TAILQ_REMOVE(&queue->head, node, entries); - - pthread_mutex_unlock(&_glfw.mir.eventMutex); - - return node; -} - -static MirPixelFormat findValidPixelFormat(void) -{ - unsigned int i, validFormats, mirPixelFormats = 32; - MirPixelFormat formats[mir_pixel_formats]; - - mir_connection_get_available_surface_formats(_glfw.mir.connection, formats, - mirPixelFormats, &validFormats); - - for (i = 0; i < validFormats; i++) - { - if (formats[i] == mir_pixel_format_abgr_8888 || - formats[i] == mir_pixel_format_xbgr_8888 || - formats[i] == mir_pixel_format_argb_8888 || - formats[i] == mir_pixel_format_xrgb_8888) - { - return formats[i]; - } - } - - return mir_pixel_format_invalid; -} - -static int mirModToGLFWMod(uint32_t mods) -{ - int publicMods = 0x0; - - if (mods & mir_input_event_modifier_alt) - publicMods |= GLFW_MOD_ALT; - if (mods & mir_input_event_modifier_shift) - publicMods |= GLFW_MOD_SHIFT; - if (mods & mir_input_event_modifier_ctrl) - publicMods |= GLFW_MOD_CONTROL; - if (mods & mir_input_event_modifier_meta) - publicMods |= GLFW_MOD_SUPER; - if (mods & mir_input_event_modifier_caps_lock) - publicMods |= GLFW_MOD_CAPS_LOCK; - if (mods & mir_input_event_modifier_num_lock) - publicMods |= GLFW_MOD_NUM_LOCK; - - return publicMods; -} - -static int toGLFWKeyCode(uint32_t key) -{ - if (key < sizeof(_glfw.mir.keycodes) / sizeof(_glfw.mir.keycodes[0])) - return _glfw.mir.keycodes[key]; - - return GLFW_KEY_UNKNOWN; -} - -static void handleKeyEvent(const MirKeyboardEvent* key_event, _GLFWwindow* window) -{ - const int action = mir_keyboard_event_action (key_event); - const int scan_code = mir_keyboard_event_scan_code(key_event); - const int key_code = mir_keyboard_event_key_code (key_event); - const int modifiers = mir_keyboard_event_modifiers(key_event); - - const int pressed = action == mir_keyboard_action_up ? GLFW_RELEASE : GLFW_PRESS; - const int mods = mirModToGLFWMod(modifiers); - const long text = _glfwKeySym2Unicode(key_code); - const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); - - _glfwInputKey(window, toGLFWKeyCode(scan_code), scan_code, pressed, mods); - - if (text != -1) - _glfwInputChar(window, text, mods, plain); -} - -static void handlePointerButton(_GLFWwindow* window, - int pressed, - const MirPointerEvent* pointer_event) -{ - int mods = mir_pointer_event_modifiers(pointer_event); - const int publicMods = mirModToGLFWMod(mods); - MirPointerButton button = mir_pointer_button_primary; - static uint32_t oldButtonStates = 0; - uint32_t newButtonStates = mir_pointer_event_buttons(pointer_event); - int publicButton = GLFW_MOUSE_BUTTON_LEFT; - - // XOR our old button states our new states to figure out what was added or removed - button = newButtonStates ^ oldButtonStates; - - switch (button) - { - case mir_pointer_button_primary: - publicButton = GLFW_MOUSE_BUTTON_LEFT; - break; - case mir_pointer_button_secondary: - publicButton = GLFW_MOUSE_BUTTON_RIGHT; - break; - case mir_pointer_button_tertiary: - publicButton = GLFW_MOUSE_BUTTON_MIDDLE; - break; - case mir_pointer_button_forward: - // FIXME What is the forward button? - publicButton = GLFW_MOUSE_BUTTON_4; - break; - case mir_pointer_button_back: - // FIXME What is the back button? - publicButton = GLFW_MOUSE_BUTTON_5; - break; - default: - break; - } - - oldButtonStates = newButtonStates; - - _glfwInputMouseClick(window, publicButton, pressed, publicMods); -} - -static void handlePointerMotion(_GLFWwindow* window, - const MirPointerEvent* pointer_event) -{ - const int hscroll = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_hscroll); - const int vscroll = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_vscroll); - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - if (_glfw.mir.disabledCursorWindow != window) - return; - - const int dx = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_relative_x); - const int dy = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_relative_y); - const int current_x = window->virtualCursorPosX; - const int current_y = window->virtualCursorPosY; - - _glfwInputCursorPos(window, dx + current_x, dy + current_y); - } - else - { - const int x = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_x); - const int y = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_y); - - _glfwInputCursorPos(window, x, y); - } - - if (hscroll != 0 || vscroll != 0) - _glfwInputScroll(window, hscroll, vscroll); -} - -static void handlePointerEvent(const MirPointerEvent* pointer_event, - _GLFWwindow* window) -{ - int action = mir_pointer_event_action(pointer_event); - - switch (action) - { - case mir_pointer_action_button_down: - handlePointerButton(window, GLFW_PRESS, pointer_event); - break; - case mir_pointer_action_button_up: - handlePointerButton(window, GLFW_RELEASE, pointer_event); - break; - case mir_pointer_action_motion: - handlePointerMotion(window, pointer_event); - break; - case mir_pointer_action_enter: - case mir_pointer_action_leave: - break; - default: - break; - } -} - -static void handleInput(const MirInputEvent* input_event, _GLFWwindow* window) -{ - int type = mir_input_event_get_type(input_event); - - switch (type) - { - case mir_input_event_type_key: - handleKeyEvent(mir_input_event_get_keyboard_event(input_event), window); - break; - case mir_input_event_type_pointer: - handlePointerEvent(mir_input_event_get_pointer_event(input_event), window); - break; - default: - break; - } -} - -static void handleEvent(const MirEvent* event, _GLFWwindow* window) -{ - int type = mir_event_get_type(event); - - switch (type) - { - case mir_event_type_input: - handleInput(mir_event_get_input_event(event), window); - break; - default: - break; - } -} - -static void addNewEvent(MirWindow* window, const MirEvent* event, void* context) -{ - enqueueEvent(event, context); -} - -static GLFWbool createWindow(_GLFWwindow* window) -{ - MirWindowSpec* spec; - MirBufferUsage buffer_usage = mir_buffer_usage_hardware; - MirPixelFormat pixel_format = findValidPixelFormat(); - - if (pixel_format == mir_pixel_format_invalid) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unable to find a correct pixel format"); - return GLFW_FALSE; - } - - spec = mir_create_normal_window_spec(_glfw.mir.connection, - window->mir.width, - window->mir.height); - - mir_window_spec_set_pixel_format(spec, pixel_format); - mir_window_spec_set_buffer_usage(spec, buffer_usage); - - window->mir.window = mir_create_window_sync(spec); - mir_window_spec_release(spec); - - if (!mir_window_is_valid(window->mir.window)) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unable to create window: %s", - mir_window_get_error_message(window->mir.window)); - - return GLFW_FALSE; - } - - mir_window_set_event_handler(window->mir.window, addNewEvent, window); - - return GLFW_TRUE; -} - -static void setWindowConfinement(_GLFWwindow* window, MirPointerConfinementState state) -{ - MirWindowSpec* spec; - - spec = mir_create_window_spec(_glfw.mir.connection); - mir_window_spec_set_pointer_confinement(spec, state); - - mir_window_apply_spec(window->mir.window, spec); - mir_window_spec_release(spec); -} - -////////////////////////////////////////////////////////////////////////// -////// GLFW internal API ////// -////////////////////////////////////////////////////////////////////////// - -void _glfwInitEventQueueMir(EventQueue* queue) -{ - TAILQ_INIT(&queue->head); -} - -void _glfwDeleteEventQueueMir(EventQueue* queue) -{ - if (queue) - { - EventNode* node, *node_next; - node = queue->head.tqh_first; - - while (node != NULL) - { - node_next = node->entries.tqe_next; - - TAILQ_REMOVE(&queue->head, node, entries); - deleteNode(queue, node); - - node = node_next; - } - - free(queue); - } -} - -////////////////////////////////////////////////////////////////////////// -////// GLFW platform API ////// -////////////////////////////////////////////////////////////////////////// - -int _glfwPlatformCreateWindow(_GLFWwindow* window, - const _GLFWwndconfig* wndconfig, - const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* fbconfig) -{ - if (window->monitor) - { - GLFWvidmode mode; - _glfwPlatformGetVideoMode(window->monitor, &mode); - - mir_window_set_state(window->mir.window, mir_window_state_fullscreen); - - if (wndconfig->width > mode.width || wndconfig->height > mode.height) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Requested window size too large: %ix%i", - wndconfig->width, wndconfig->height); - - return GLFW_FALSE; - } - } - - window->mir.width = wndconfig->width; - window->mir.height = wndconfig->height; - window->mir.currentCursor = NULL; - - if (!createWindow(window)) - return GLFW_FALSE; - - window->mir.nativeWindow = mir_buffer_stream_get_egl_native_window( - mir_window_get_buffer_stream(window->mir.window)); - - if (ctxconfig->client != GLFW_NO_API) - { - if (ctxconfig->source == GLFW_EGL_CONTEXT_API || - ctxconfig->source == GLFW_NATIVE_CONTEXT_API) - { - if (!_glfwInitEGL()) - return GLFW_FALSE; - if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) - { - if (!_glfwInitOSMesa()) - return GLFW_FALSE; - if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) - return GLFW_FALSE; - } - } - - return GLFW_TRUE; -} - -void _glfwPlatformDestroyWindow(_GLFWwindow* window) -{ - if (_glfw.mir.disabledCursorWindow == window) - _glfw.mir.disabledCursorWindow = NULL; - - if (mir_window_is_valid(window->mir.window)) - { - mir_window_release_sync(window->mir.window); - window->mir.window= NULL; - } - - if (window->context.destroy) - window->context.destroy(window); -} - -void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) -{ - MirWindowSpec* spec; - - spec = mir_create_window_spec(_glfw.mir.connection); - mir_window_spec_set_name(spec, title); - mir_window_apply_spec(window->mir.window, spec); - mir_window_spec_release(spec); -} - -void _glfwPlatformSetWindowIcon(_GLFWwindow* window, - int count, const GLFWimage* images) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) -{ - MirWindowSpec* spec; - - spec = mir_create_window_spec(_glfw.mir.connection); - mir_window_spec_set_width (spec, width); - mir_window_spec_set_height(spec, height); - - mir_window_apply_spec(window->mir.window, spec); - mir_window_spec_release(spec); -} - -void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, - int minwidth, int minheight, - int maxwidth, int maxheight) -{ - MirWindowSpec* spec; - - spec = mir_create_window_spec(_glfw.mir.connection); - mir_window_spec_set_max_width (spec, maxwidth); - mir_window_spec_set_max_height(spec, maxheight); - mir_window_spec_set_min_width (spec, minwidth); - mir_window_spec_set_min_height(spec, minheight); - - mir_window_apply_spec(window->mir.window, spec); - mir_window_spec_release(spec); -} - -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, - int* left, int* top, - int* right, int* bottom) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) -{ - if (width) - *width = window->mir.width; - if (height) - *height = window->mir.height; -} - -void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, - float* xscale, float* yscale) -{ - if (xscale) - *xscale = 1.f; - if (yscale) - *yscale = 1.f; -} - -void _glfwPlatformIconifyWindow(_GLFWwindow* window) -{ - MirWindowSpec* spec; - - spec = mir_create_window_spec(_glfw.mir.connection); - mir_window_spec_set_state(spec, mir_window_state_minimized); - - mir_window_apply_spec(window->mir.window, spec); - mir_window_spec_release(spec); -} - -void _glfwPlatformRestoreWindow(_GLFWwindow* window) -{ - MirWindowSpec* spec; - - spec = mir_create_window_spec(_glfw.mir.connection); - mir_window_spec_set_state(spec, mir_window_state_restored); - - mir_window_apply_spec(window->mir.window, spec); - mir_window_spec_release(spec); -} - -void _glfwPlatformMaximizeWindow(_GLFWwindow* window) -{ - MirWindowSpec* spec; - - spec = mir_create_window_spec(_glfw.mir.connection); - mir_window_spec_set_state(spec, mir_window_state_maximized); - - mir_window_apply_spec(window->mir.window, spec); - mir_window_spec_release(spec); -} - -void _glfwPlatformHideWindow(_GLFWwindow* window) -{ - MirWindowSpec* spec; - - spec = mir_create_window_spec(_glfw.mir.connection); - mir_window_spec_set_state(spec, mir_window_state_hidden); - - mir_window_apply_spec(window->mir.window, spec); - mir_window_spec_release(spec); -} - -void _glfwPlatformShowWindow(_GLFWwindow* window) -{ - MirWindowSpec* spec; - - spec = mir_create_window_spec(_glfw.mir.connection); - mir_window_spec_set_state(spec, mir_window_state_restored); - - mir_window_apply_spec(window->mir.window, spec); - mir_window_spec_release(spec); -} - -void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformFocusWindow(_GLFWwindow* window) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, - _GLFWmonitor* monitor, - int xpos, int ypos, - int width, int height, - int refreshRate) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -int _glfwPlatformWindowFocused(_GLFWwindow* window) -{ - return mir_window_get_focus_state(window->mir.window) == mir_window_focus_state_focused; -} - -int _glfwPlatformWindowIconified(_GLFWwindow* window) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); - return GLFW_FALSE; -} - -int _glfwPlatformWindowVisible(_GLFWwindow* window) -{ - return mir_window_get_visibility(window->mir.window) == mir_window_visibility_exposed; -} - -int _glfwPlatformWindowMaximized(_GLFWwindow* window) -{ - return mir_window_get_state(window->mir.window) == mir_window_state_maximized; -} - -int _glfwPlatformWindowHovered(_GLFWwindow* window) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); - return GLFW_FALSE; -} - -int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); - return GLFW_FALSE; -} - -void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) -{ - return 1.f; -} - -void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) -{ -} - -void _glfwPlatformPollEvents(void) -{ - EventNode* node = NULL; - - while ((node = dequeueEvent(_glfw.mir.eventQueue))) - { - handleEvent(node->event, node->window); - deleteNode(_glfw.mir.eventQueue, node); - } -} - -void _glfwPlatformWaitEvents(void) -{ - pthread_mutex_lock(&_glfw.mir.eventMutex); - - while (emptyEventQueue(_glfw.mir.eventQueue)) - pthread_cond_wait(&_glfw.mir.eventCond, &_glfw.mir.eventMutex); - - pthread_mutex_unlock(&_glfw.mir.eventMutex); - - _glfwPlatformPollEvents(); -} - -void _glfwPlatformWaitEventsTimeout(double timeout) -{ - pthread_mutex_lock(&_glfw.mir.eventMutex); - - if (emptyEventQueue(_glfw.mir.eventQueue)) - { - struct timespec time; - clock_gettime(CLOCK_REALTIME, &time); - time.tv_sec += (long) timeout; - time.tv_nsec += (long) ((timeout - (long) timeout) * 1e9); - pthread_cond_timedwait(&_glfw.mir.eventCond, &_glfw.mir.eventMutex, &time); - } - - pthread_mutex_unlock(&_glfw.mir.eventMutex); - - _glfwPlatformPollEvents(); -} - -void _glfwPlatformPostEmptyEvent(void) -{ -} - -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) -{ - if (width) - *width = window->mir.width; - if (height) - *height = window->mir.height; -} - -int _glfwPlatformCreateCursor(_GLFWcursor* cursor, - const GLFWimage* image, - int xhot, int yhot) -{ - MirBufferStream* stream; - - int i_w = image->width; - int i_h = image->height; - - stream = mir_connection_create_buffer_stream_sync(_glfw.mir.connection, - i_w, i_h, - mir_pixel_format_argb_8888, - mir_buffer_usage_software); - - cursor->mir.conf = mir_cursor_configuration_from_buffer_stream(stream, xhot, yhot); - - MirGraphicsRegion region; - mir_buffer_stream_get_graphics_region(stream, ®ion); - - unsigned char* pixels = image->pixels; - char* dest = region.vaddr; - int i; - - for (i = 0; i < i_w * i_h; i++, pixels += 4) - { - unsigned int alpha = pixels[3]; - *dest++ = (char)(pixels[2] * alpha / 255); - *dest++ = (char)(pixels[1] * alpha / 255); - *dest++ = (char)(pixels[0] * alpha / 255); - *dest++ = (char)alpha; - } - - mir_buffer_stream_swap_buffers_sync(stream); - cursor->mir.customCursor = stream; - - return GLFW_TRUE; -} - -static const char* getSystemCursorName(int shape) -{ - switch (shape) - { - case GLFW_ARROW_CURSOR: - return mir_arrow_cursor_name; - case GLFW_IBEAM_CURSOR: - return mir_caret_cursor_name; - case GLFW_CROSSHAIR_CURSOR: - return mir_crosshair_cursor_name; - case GLFW_HAND_CURSOR: - return mir_open_hand_cursor_name; - case GLFW_HRESIZE_CURSOR: - return mir_horizontal_resize_cursor_name; - case GLFW_VRESIZE_CURSOR: - return mir_vertical_resize_cursor_name; - } - - return NULL; -} - -int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) -{ - cursor->mir.conf = NULL; - cursor->mir.customCursor = NULL; - cursor->mir.cursorName = getSystemCursorName(shape); - - return cursor->mir.cursorName != NULL; -} - -void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) -{ - if (cursor->mir.conf) - mir_cursor_configuration_destroy(cursor->mir.conf); - if (cursor->mir.customCursor) - mir_buffer_stream_release_sync(cursor->mir.customCursor); -} - -static void setCursorNameForWindow(MirWindow* window, char const* name) -{ - MirWindowSpec* spec = mir_create_window_spec(_glfw.mir.connection); - mir_window_spec_set_cursor_name(spec, name); - mir_window_apply_spec(window, spec); - mir_window_spec_release(spec); -} - -void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) -{ - if (cursor) - { - window->mir.currentCursor = cursor; - - if (cursor->mir.cursorName) - { - setCursorNameForWindow(window->mir.window, cursor->mir.cursorName); - } - else if (cursor->mir.conf) - { - mir_window_configure_cursor(window->mir.window, cursor->mir.conf); - } - } - else - { - setCursorNameForWindow(window->mir.window, mir_default_cursor_name); - } -} - -void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) -{ - if (mode == GLFW_CURSOR_DISABLED) - { - _glfw.mir.disabledCursorWindow = window; - setWindowConfinement(window, mir_pointer_confined_to_window); - setCursorNameForWindow(window->mir.window, mir_disabled_cursor_name); - } - else - { - // If we were disabled before lets undo that! - if (_glfw.mir.disabledCursorWindow == window) - { - _glfw.mir.disabledCursorWindow = NULL; - setWindowConfinement(window, mir_pointer_unconfined); - } - - if (window->cursorMode == GLFW_CURSOR_NORMAL) - { - _glfwPlatformSetCursor(window, window->mir.currentCursor); - } - else if (window->cursorMode == GLFW_CURSOR_HIDDEN) - { - setCursorNameForWindow(window->mir.window, mir_disabled_cursor_name); - } - } -} - -const char* _glfwPlatformGetScancodeName(int scancode) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); - return NULL; -} - -int _glfwPlatformGetKeyScancode(int key) -{ - return _glfw.mir.scancodes[key]; -} - -void _glfwPlatformSetClipboardString(const char* string) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); -} - -const char* _glfwPlatformGetClipboardString(void) -{ - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Unsupported function %s", __PRETTY_FUNCTION__); - - return NULL; -} - -void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) -{ - if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_mir_surface) - return; - - extensions[0] = "VK_KHR_surface"; - extensions[1] = "VK_KHR_mir_surface"; -} - -int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, - VkPhysicalDevice device, - uint32_t queuefamily) -{ - PFN_vkGetPhysicalDeviceMirPresentationSupportKHR - vkGetPhysicalDeviceMirPresentationSupportKHR = - (PFN_vkGetPhysicalDeviceMirPresentationSupportKHR) - vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceMirPresentationSupportKHR"); - if (!vkGetPhysicalDeviceMirPresentationSupportKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Mir: Vulkan instance missing VK_KHR_mir_surface extension"); - return GLFW_FALSE; - } - - return vkGetPhysicalDeviceMirPresentationSupportKHR(device, - queuefamily, - _glfw.mir.connection); -} - -VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, - _GLFWwindow* window, - const VkAllocationCallbacks* allocator, - VkSurfaceKHR* surface) -{ - VkResult err; - VkMirWindowCreateInfoKHR sci; - PFN_vkCreateMirWindowKHR vkCreateMirWindowKHR; - - vkCreateMirWindowKHR = (PFN_vkCreateMirWindowKHR) - vkGetInstanceProcAddr(instance, "vkCreateMirWindowKHR"); - if (!vkCreateMirWindowKHR) - { - _glfwInputError(GLFW_API_UNAVAILABLE, - "Mir: Vulkan instance missing VK_KHR_mir_surface extension"); - return VK_ERROR_EXTENSION_NOT_PRESENT; - } - - memset(&sci, 0, sizeof(sci)); - sci.sType = VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR; - sci.connection = _glfw.mir.connection; - sci.mirWindow = window->mir.window; - - err = vkCreateMirWindowKHR(instance, &sci, allocator, surface); - if (err) - { - _glfwInputError(GLFW_PLATFORM_ERROR, - "Mir: Failed to create Vulkan surface: %s", - _glfwGetVulkanResultString(err)); - } - - return err; -} - - -////////////////////////////////////////////////////////////////////////// -////// GLFW native API ////// -////////////////////////////////////////////////////////////////////////// - -GLFWAPI MirConnection* glfwGetMirDisplay(void) -{ - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfw.mir.connection; -} - -GLFWAPI MirWindow* glfwGetMirWindow(GLFWwindow* handle) -{ - _GLFWwindow* window = (_GLFWwindow*) handle; - _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return window->mir.window; -} - diff --git a/src/external/glfw/src/monitor.c b/src/external/glfw/src/monitor.c index f7de5500..0ab865e3 100644 --- a/src/external/glfw/src/monitor.c +++ b/src/external/glfw/src/monitor.c @@ -427,12 +427,12 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle) GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) { - int i; - unsigned short values[256]; + unsigned int i; + unsigned short* values; GLFWgammaramp ramp; + const GLFWgammaramp* original; assert(handle != NULL); - assert(gamma == gamma); - assert(gamma >= 0.f); + assert(gamma > 0.f); assert(gamma <= FLT_MAX); _GLFW_REQUIRE_INIT(); @@ -443,18 +443,22 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) return; } - for (i = 0; i < 256; i++) + original = glfwGetGammaRamp(handle); + if (!original) + return; + + values = calloc(original->size, sizeof(unsigned short)); + + for (i = 0; i < original->size; i++) { float value; // Calculate intensity - value = i / 255.f; + value = i / (float) (original->size - 1); // Apply gamma curve value = powf(value, 1.f / gamma) * 65535.f + 0.5f; - // Clamp to value range - if (value > 65535.f) - value = 65535.f; + value = _glfw_fminf(value, 65535.f); values[i] = (unsigned short) value; } @@ -462,9 +466,10 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) ramp.red = values; ramp.green = values; ramp.blue = values; - ramp.size = 256; + ramp.size = original->size; glfwSetGammaRamp(handle, &ramp); + free(values); } GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) @@ -475,7 +480,8 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _glfwFreeGammaArrays(&monitor->currentRamp); - _glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp); + if (!_glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp)) + return NULL; return &monitor->currentRamp; } @@ -501,7 +507,10 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) _GLFW_REQUIRE_INIT(); if (!monitor->originalRamp.size) - _glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp); + { + if (!_glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp)) + return; + } _glfwPlatformSetGammaRamp(monitor, ramp); } diff --git a/src/external/glfw/src/nsgl_context.m b/src/external/glfw/src/nsgl_context.m index 82af9063..ec1012e9 100644 --- a/src/external/glfw/src/nsgl_context.m +++ b/src/external/glfw/src/nsgl_context.m @@ -26,6 +26,10 @@ #include "internal.h" +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101400 + #define NSOpenGLContextParameterSwapInterval NSOpenGLCPSwapInterval + #define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity +#endif static void makeContextCurrentNSGL(_GLFWwindow* window) { @@ -49,7 +53,7 @@ static void swapIntervalNSGL(int interval) GLint sync = interval; [window->context.nsgl.object setValues:&sync - forParameter:NSOpenGLCPSwapInterval]; + forParameter:NSOpenGLContextParameterSwapInterval]; } static int extensionSupportedNSGL(const char* extension) @@ -299,7 +303,8 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, if (fbconfig->transparent) { GLint opaque = 0; - [window->context.nsgl.object setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; + [window->context.nsgl.object setValues:&opaque + forParameter:NSOpenGLContextParameterSurfaceOpacity]; } [window->context.nsgl.object setView:window->ns.view]; diff --git a/src/external/glfw/src/null_monitor.c b/src/external/glfw/src/null_monitor.c index 84b41c7e..45c4a10f 100644 --- a/src/external/glfw/src/null_monitor.c +++ b/src/external/glfw/src/null_monitor.c @@ -58,8 +58,9 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) { } -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { + return GLFW_FALSE; } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) diff --git a/src/external/glfw/src/osmesa_context.c b/src/external/glfw/src/osmesa_context.c index a7de33f2..03651ebf 100644 --- a/src/external/glfw/src/osmesa_context.c +++ b/src/external/glfw/src/osmesa_context.c @@ -240,7 +240,7 @@ GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, if (ctxconfig->forward) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, - "OSMesa: Foward-compatible contexts not supported"); + "OSMesa: Forward-compatible contexts not supported"); return GLFW_FALSE; } diff --git a/src/external/glfw/src/vulkan.c b/src/external/glfw/src/vulkan.c index debc0eff..b8f752fb 100644 --- a/src/external/glfw/src/vulkan.c +++ b/src/external/glfw/src/vulkan.c @@ -136,9 +136,6 @@ GLFWbool _glfwInitVulkan(int mode) #elif defined(_GLFW_WAYLAND) else if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) _glfw.vk.KHR_wayland_surface = GLFW_TRUE; -#elif defined(_GLFW_MIR) - else if (strcmp(ep[i].extensionName, "VK_KHR_mir_surface") == 0) - _glfw.vk.KHR_mir_surface = GLFW_TRUE; #endif } diff --git a/src/external/glfw/src/wgl_context.c b/src/external/glfw/src/wgl_context.c index beccb13e..06ba8b55 100644 --- a/src/external/glfw/src/wgl_context.c +++ b/src/external/glfw/src/wgl_context.c @@ -256,12 +256,20 @@ static void makeContextCurrentWGL(_GLFWwindow* window) static void swapBuffersWGL(_GLFWwindow* window) { - // HACK: Use DwmFlush when desktop composition is enabled - if (_glfwIsCompositionEnabledWin32() && !window->monitor) + if (!window->monitor) { - int count = abs(window->context.wgl.interval); - while (count--) - DwmFlush(); + if (IsWindowsVistaOrGreater()) + { + BOOL enabled; + + // HACK: Use DwmFlush when desktop composition is enabled + if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled) + { + int count = abs(window->context.wgl.interval); + while (count--) + DwmFlush(); + } + } } SwapBuffers(window->context.wgl.dc); @@ -273,10 +281,18 @@ static void swapIntervalWGL(int interval) window->context.wgl.interval = interval; - // HACK: Disable WGL swap interval when desktop composition is enabled to - // avoid interfering with DWM vsync - if (_glfwIsCompositionEnabledWin32() && !window->monitor) - interval = 0; + if (!window->monitor) + { + if (IsWindowsVistaOrGreater()) + { + BOOL enabled; + + // HACK: Disable WGL swap interval when desktop composition is enabled to + // avoid interfering with DWM vsync + if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled) + interval = 0; + } + } if (_glfw.wgl.EXT_swap_control) _glfw.wgl.SwapIntervalEXT(interval); @@ -284,29 +300,17 @@ static void swapIntervalWGL(int interval) static int extensionSupportedWGL(const char* extension) { - const char* extensions; - - if (_glfw.wgl.GetExtensionsStringEXT) - { - extensions = _glfw.wgl.GetExtensionsStringEXT(); - if (extensions) - { - if (_glfwStringInExtensionString(extension, extensions)) - return GLFW_TRUE; - } - } + const char* extensions = NULL; if (_glfw.wgl.GetExtensionsStringARB) - { extensions = _glfw.wgl.GetExtensionsStringARB(wglGetCurrentDC()); - if (extensions) - { - if (_glfwStringInExtensionString(extension, extensions)) - return GLFW_TRUE; - } - } + else if (_glfw.wgl.GetExtensionsStringEXT) + extensions = _glfw.wgl.GetExtensionsStringEXT(); - return GLFW_FALSE; + if (!extensions) + return GLFW_FALSE; + + return _glfwStringInExtensionString(extension, extensions); } static GLFWglproc getProcAddressWGL(const char* procname) diff --git a/src/external/glfw/src/win32_init.c b/src/external/glfw/src/win32_init.c index a913846d..3ee5eb85 100644 --- a/src/external/glfw/src/win32_init.c +++ b/src/external/glfw/src/win32_init.c @@ -62,17 +62,6 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) #endif // _GLFW_BUILD_DLL -// HACK: Define versionhelpers.h functions manually as MinGW lacks the header -BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp) -{ - OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp }; - DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; - ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); - cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); - cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); - return VerifyVersionInfoW(&osvi, mask, cond); -} - // Load necessary libraries (DLLs) // static GLFWbool loadLibraries(void) @@ -100,6 +89,14 @@ static GLFWbool loadLibraries(void) GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware"); _glfw.win32.user32.ChangeWindowMessageFilterEx_ = (PFN_ChangeWindowMessageFilterEx) GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); + _glfw.win32.user32.EnableNonClientDpiScaling_ = (PFN_EnableNonClientDpiScaling) + GetProcAddress(_glfw.win32.user32.instance, "EnableNonClientDpiScaling"); + _glfw.win32.user32.SetProcessDpiAwarenessContext_ = (PFN_SetProcessDpiAwarenessContext) + GetProcAddress(_glfw.win32.user32.instance, "SetProcessDpiAwarenessContext"); + _glfw.win32.user32.GetDpiForWindow_ = (PFN_GetDpiForWindow) + GetProcAddress(_glfw.win32.user32.instance, "GetDpiForWindow"); + _glfw.win32.user32.AdjustWindowRectExForDpi_ = (PFN_AdjustWindowRectExForDpi) + GetProcAddress(_glfw.win32.user32.instance, "AdjustWindowRectExForDpi"); _glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll"); if (_glfw.win32.dinput8.instance) @@ -155,6 +152,13 @@ static GLFWbool loadLibraries(void) GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor"); } + _glfw.win32.ntdll.instance = LoadLibraryA("ntdll.dll"); + if (_glfw.win32.ntdll.instance) + { + _glfw.win32.ntdll.RtlVerifyVersionInfo_ = (PFN_RtlVerifyVersionInfo) + GetProcAddress(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); + } + return GLFW_TRUE; } @@ -179,6 +183,9 @@ static void freeLibraries(void) if (_glfw.win32.shcore.instance) FreeLibrary(_glfw.win32.shcore.instance); + + if (_glfw.win32.ntdll.instance) + FreeLibrary(_glfw.win32.ntdll.instance); } // Create key code translation tables @@ -309,6 +316,7 @@ static void createKeyTables(void) _glfw.win32.keycodes[0x053] = GLFW_KEY_KP_DECIMAL; _glfw.win32.keycodes[0x135] = GLFW_KEY_KP_DIVIDE; _glfw.win32.keycodes[0x11C] = GLFW_KEY_KP_ENTER; + _glfw.win32.keycodes[0x059] = GLFW_KEY_KP_EQUAL; _glfw.win32.keycodes[0x037] = GLFW_KEY_KP_MULTIPLY; _glfw.win32.keycodes[0x04A] = GLFW_KEY_KP_SUBTRACT; @@ -339,8 +347,8 @@ static HWND createHelperWindow(void) return NULL; } - // HACK: The first call to ShowWindow is ignored if the parent process - // passed along a STARTUPINFO, so clear that flag with a no-op call + // HACK: The command to the first ShowWindow call is ignored if the parent + // process passed along a STARTUPINFO, so clear that with a no-op call ShowWindow(window, SW_HIDE); // Register for HID device notifications @@ -502,6 +510,36 @@ void _glfwUpdateKeyNamesWin32(void) } } +// Replacement for IsWindowsVersionOrGreater as MinGW lacks versionhelpers.h +// +BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp }; + DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; + ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the + // latter lies unless the user knew to embedd a non-default manifest + // announcing support for Windows 10 via supportedOS GUID + return RtlVerifyVersionInfo(&osvi, mask, cond) == 0; +} + +// Checks whether we are on at least the specified build of Windows 10 +// +BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), 10, 0, build }; + DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER; + ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); + cond = VerSetConditionMask(cond, VER_BUILDNUMBER, VER_GREATER_EQUAL); + // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the + // latter lies unless the user knew to embedd a non-default manifest + // announcing support for Windows 10 via supportedOS GUID + return RtlVerifyVersionInfo(&osvi, mask, cond) == 0; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -523,7 +561,9 @@ int _glfwPlatformInit(void) createKeyTables(); _glfwUpdateKeyNamesWin32(); - if (IsWindows8Point1OrGreater()) + if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + else if (IsWindows8Point1OrGreater()) SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); else if (IsWindowsVistaOrGreater()) SetProcessDPIAware(); diff --git a/src/external/glfw/src/win32_joystick.c b/src/external/glfw/src/win32_joystick.c index d9d341ff..58123965 100644 --- a/src/external/glfw/src/win32_joystick.c +++ b/src/external/glfw/src/win32_joystick.c @@ -260,6 +260,8 @@ static void closeJoystick(_GLFWjoystick* js) IDirectInputDevice8_Release(js->win32.device); } + free(js->win32.objects); + _glfwFreeJoystick(js); _glfwInputJoystick(js, GLFW_DISCONNECTED); } diff --git a/src/external/glfw/src/win32_monitor.c b/src/external/glfw/src/win32_monitor.c index d1d159b3..07b3614b 100644 --- a/src/external/glfw/src/win32_monitor.c +++ b/src/external/glfw/src/win32_monitor.c @@ -324,9 +324,9 @@ void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* ysc } if (xscale) - *xscale = xdpi / 96.f; + *xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI; if (yscale) - *yscale = ydpi / 96.f; + *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI; } @@ -455,7 +455,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) &mode->blueBits); } -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { HDC dc; WORD values[768]; @@ -469,6 +469,8 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) memcpy(ramp->red, values + 0, 256 * sizeof(unsigned short)); memcpy(ramp->green, values + 256, 256 * sizeof(unsigned short)); memcpy(ramp->blue, values + 512, 256 * sizeof(unsigned short)); + + return GLFW_TRUE; } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) diff --git a/src/external/glfw/src/win32_platform.h b/src/external/glfw/src/win32_platform.h index 59815146..712de7f1 100644 --- a/src/external/glfw/src/win32_platform.h +++ b/src/external/glfw/src/win32_platform.h @@ -98,6 +98,12 @@ #ifndef _WIN32_WINNT_WINBLUE #define _WIN32_WINNT_WINBLUE 0x0602 #endif +#ifndef WM_GETDPISCALEDSIZE + #define WM_GETDPISCALEDSIZE 0x02e4 +#endif +#ifndef USER_DEFAULT_SCREEN_DPI + #define USER_DEFAULT_SCREEN_DPI 96 +#endif #if WINVER < 0x0601 typedef struct @@ -140,20 +146,31 @@ typedef enum } MONITOR_DPI_TYPE; #endif /*DPI_ENUMS_DECLARED*/ +#ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 +#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE) -4) +#endif /*DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2*/ + // HACK: Define versionhelpers.h functions manually as MinGW lacks the header -BOOL IsWindowsVersionOrGreater(WORD major, WORD minor, WORD sp); -#define IsWindowsVistaOrGreater() \ - IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), \ - LOBYTE(_WIN32_WINNT_VISTA), 0) -#define IsWindows7OrGreater() \ - IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), \ - LOBYTE(_WIN32_WINNT_WIN7), 0) -#define IsWindows8OrGreater() \ - IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), \ - LOBYTE(_WIN32_WINNT_WIN8), 0) -#define IsWindows8Point1OrGreater() \ - IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), \ - LOBYTE(_WIN32_WINNT_WINBLUE), 0) +#define IsWindowsXPOrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINXP), \ + LOBYTE(_WIN32_WINNT_WINXP), 0) +#define IsWindowsVistaOrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_VISTA), \ + LOBYTE(_WIN32_WINNT_VISTA), 0) +#define IsWindows7OrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WIN7), \ + LOBYTE(_WIN32_WINNT_WIN7), 0) +#define IsWindows8OrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WIN8), \ + LOBYTE(_WIN32_WINNT_WIN8), 0) +#define IsWindows8Point1OrGreater() \ + _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINBLUE), \ + LOBYTE(_WIN32_WINNT_WINBLUE), 0) + +#define _glfwIsWindows10AnniversaryUpdateOrGreaterWin32() \ + _glfwIsWindows10BuildOrGreaterWin32(14393) +#define _glfwIsWindows10CreatorsUpdateOrGreaterWin32() \ + _glfwIsWindows10BuildOrGreaterWin32(15063) // HACK: Define macros that some xinput.h variants don't #ifndef XINPUT_CAPS_WIRELESS @@ -206,8 +223,16 @@ typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID* // user32.dll function pointer typedefs typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void); typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,CHANGEFILTERSTRUCT*); +typedef BOOL (WINAPI * PFN_EnableNonClientDpiScaling)(HWND); +typedef BOOL (WINAPI * PFN_SetProcessDpiAwarenessContext)(HANDLE); +typedef UINT (WINAPI * PFN_GetDpiForWindow)(HWND); +typedef BOOL (WINAPI * PFN_AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT); #define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_ #define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_ +#define EnableNonClientDpiScaling _glfw.win32.user32.EnableNonClientDpiScaling_ +#define SetProcessDpiAwarenessContext _glfw.win32.user32.SetProcessDpiAwarenessContext_ +#define GetDpiForWindow _glfw.win32.user32.GetDpiForWindow_ +#define AdjustWindowRectExForDpi _glfw.win32.user32.AdjustWindowRectExForDpi_ // dwmapi.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); @@ -223,6 +248,10 @@ typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*, #define SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_ #define GetDpiForMonitor _glfw.win32.shcore.GetDpiForMonitor_ +// ntdll.dll function pointer typedefs +typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLONG); +#define RtlVerifyVersionInfo _glfw.win32.ntdll.RtlVerifyVersionInfo_ + typedef VkFlags VkWin32SurfaceCreateFlagsKHR; typedef struct VkWin32SurfaceCreateInfoKHR @@ -276,6 +305,7 @@ typedef struct _GLFWwindowWin32 GLFWbool maximized; // Whether to enable framebuffer transparency on DWM GLFWbool transparent; + GLFWbool scaleToMonitor; // The last received cursor position, regardless of source int lastCursorPosX, lastCursorPosY; @@ -300,6 +330,7 @@ typedef struct _GLFWlibraryWin32 _GLFWwindow* disabledCursorWindow; RAWINPUT* rawInput; int rawInputSize; + UINT mouseTrailSize; struct { HINSTANCE instance; @@ -322,6 +353,10 @@ typedef struct _GLFWlibraryWin32 HINSTANCE instance; PFN_SetProcessDPIAware SetProcessDPIAware_; PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx_; + PFN_EnableNonClientDpiScaling EnableNonClientDpiScaling_; + PFN_SetProcessDpiAwarenessContext SetProcessDpiAwarenessContext_; + PFN_GetDpiForWindow GetDpiForWindow_; + PFN_AdjustWindowRectExForDpi AdjustWindowRectExForDpi_; } user32; struct { @@ -337,6 +372,11 @@ typedef struct _GLFWlibraryWin32 PFN_GetDpiForMonitor GetDpiForMonitor_; } shcore; + struct { + HINSTANCE instance; + PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_; + } ntdll; + } _GLFWlibraryWin32; // Win32-specific per-monitor data @@ -392,10 +432,11 @@ typedef struct _GLFWmutexWin32 GLFWbool _glfwRegisterWindowClassWin32(void); void _glfwUnregisterWindowClassWin32(void); -GLFWbool _glfwIsCompositionEnabledWin32(void); WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); +BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp); +BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build); void _glfwInputErrorWin32(int error, const char* description); void _glfwUpdateKeyNamesWin32(void); diff --git a/src/external/glfw/src/win32_window.c b/src/external/glfw/src/win32_window.c index d467f2ae..a0abca06 100644 --- a/src/external/glfw/src/win32_window.c +++ b/src/external/glfw/src/win32_window.c @@ -186,14 +186,20 @@ static HICON createIcon(const GLFWimage* image, return handle; } -// Translate client window size to full window size according to styles +// Translate client window size to full window size according to styles and DPI // static void getFullWindowSize(DWORD style, DWORD exStyle, int clientWidth, int clientHeight, - int* fullWidth, int* fullHeight) + int* fullWidth, int* fullHeight, + UINT dpi) { RECT rect = { 0, 0, clientWidth, clientHeight }; - AdjustWindowRectEx(&rect, style, FALSE, exStyle); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); + else + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + *fullWidth = rect.right - rect.left; *fullHeight = rect.bottom - rect.top; } @@ -203,10 +209,14 @@ static void getFullWindowSize(DWORD style, DWORD exStyle, static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) { int xoff, yoff; + UINT dpi = USER_DEFAULT_SCREEN_DPI; const float ratio = (float) window->numer / (float) window->denom; + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + dpi = GetDpiForWindow(window->win32.handle); + getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), - 0, 0, &xoff, &yoff); + 0, 0, &xoff, &yoff, dpi); if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT || edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT) @@ -337,7 +347,16 @@ static void updateWindowStyles(const _GLFWwindow* window) style |= getWindowStyle(window); GetClientRect(window->win32.handle, &rect); - AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window)); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, style, FALSE, + getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window)); + ClientToScreen(window->win32.handle, (POINT*) &rect.left); ClientToScreen(window->win32.handle, (POINT*) &rect.right); SetWindowLongW(window->win32.handle, GWL_STYLE, style); @@ -351,10 +370,12 @@ static void updateWindowStyles(const _GLFWwindow* window) // static void updateFramebufferTransparency(const _GLFWwindow* window) { + BOOL enabled; + if (!IsWindowsVistaOrGreater()) return; - if (_glfwIsCompositionEnabledWin32()) + if (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled) { HRGN region = CreateRectRgn(0, 0, -1, -1); DWM_BLURBEHIND bb = {0}; @@ -396,29 +417,6 @@ static void updateFramebufferTransparency(const _GLFWwindow* window) } } -// Translates a GLFW standard cursor to a resource ID -// -static LPWSTR translateCursorShape(int shape) -{ - switch (shape) - { - case GLFW_ARROW_CURSOR: - return IDC_ARROW; - case GLFW_IBEAM_CURSOR: - return IDC_IBEAM; - case GLFW_CROSSHAIR_CURSOR: - return IDC_CROSS; - case GLFW_HAND_CURSOR: - return IDC_HAND; - case GLFW_HRESIZE_CURSOR: - return IDC_SIZEWE; - case GLFW_VRESIZE_CURSOR: - return IDC_SIZENS; - } - - return NULL; -} - // Retrieves and translates modifier keys // static int getKeyMods(void) @@ -529,7 +527,18 @@ static void fitToMonitor(_GLFWwindow* window) static void acquireMonitor(_GLFWwindow* window) { if (!_glfw.win32.acquiredMonitorCount) + { SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); + + // HACK: When mouse trails are enabled the cursor becomes invisible when + // the OpenGL ICD switches to page flipping + if (IsWindowsXPOrGreater()) + { + SystemParametersInfo(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0); + SystemParametersInfo(SPI_SETMOUSETRAILS, 0, 0, 0); + } + } + if (!window->monitor->window) _glfw.win32.acquiredMonitorCount++; @@ -546,8 +555,14 @@ static void releaseMonitor(_GLFWwindow* window) _glfw.win32.acquiredMonitorCount--; if (!_glfw.win32.acquiredMonitorCount) + { SetThreadExecutionState(ES_CONTINUOUS); + // HACK: Restore mouse trail length saved in acquireMonitor + if (IsWindowsXPOrGreater()) + SystemParametersInfo(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0); + } + _glfwInputMonitorWindow(window->monitor, NULL); _glfwRestoreVideoModeWin32(window->monitor); } @@ -561,9 +576,18 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, if (!window) { // This is the message handling for the hidden helper window + // and for a regular window during its initial creation switch (uMsg) { + case WM_NCCREATE: + { + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + EnableNonClientDpiScaling(hWnd); + + break; + } + case WM_DISPLAYCHANGE: _glfwPollMonitorsWin32(); break; @@ -980,13 +1004,17 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_GETMINMAXINFO: { int xoff, yoff; + UINT dpi = USER_DEFAULT_SCREEN_DPI; MINMAXINFO* mmi = (MINMAXINFO*) lParam; if (window->monitor) break; + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + dpi = GetDpiForWindow(window->win32.handle); + getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), - 0, 0, &xoff, &yoff); + 0, 0, &xoff, &yoff, dpi); if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE) @@ -1032,6 +1060,17 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return TRUE; } + case WM_NCACTIVATE: + case WM_NCPAINT: + { + // Prevent title bar from being drawn after restoring a minimized + // undecorated window + if (!window->decorated) + return TRUE; + + break; + } + case WM_DWMCOMPOSITIONCHANGED: { if (window->win32.transparent) @@ -1039,10 +1078,52 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, return 0; } + case WM_GETDPISCALEDSIZE: + { + if (window->win32.scaleToMonitor) + break; + + // Adjust the window size to keep the client area size constant + if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + { + RECT source = {0}, target = {0}; + SIZE* size = (SIZE*) lParam; + + AdjustWindowRectExForDpi(&source, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + AdjustWindowRectExForDpi(&target, getWindowStyle(window), + FALSE, getWindowExStyle(window), + LOWORD(wParam)); + + size->cx += (target.right - target.left) - + (source.right - source.left); + size->cy += (target.bottom - target.top) - + (source.bottom - source.top); + return TRUE; + } + + break; + } + case WM_DPICHANGED: { - const float xscale = HIWORD(wParam) / 96.f; - const float yscale = LOWORD(wParam) / 96.f; + const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; + const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; + + // Only apply the suggested size if the OS is new enough to have + // sent a WM_GETDPISCALEDSIZE before this + if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) + { + RECT* suggested = (RECT*) lParam; + SetWindowPos(window->win32.handle, HWND_TOP, + suggested->left, + suggested->top, + suggested->right - suggested->left, + suggested->bottom - suggested->top, + SWP_NOACTIVATE | SWP_NOZORDER); + } + _glfwInputWindowContentScale(window, xscale, yscale); break; } @@ -1129,7 +1210,8 @@ static int createNativeWindow(_GLFWwindow* window, getFullWindowSize(style, exStyle, wndconfig->width, wndconfig->height, - &fullWidth, &fullHeight); + &fullWidth, &fullHeight, + USER_DEFAULT_SCREEN_DPI); } wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title); @@ -1168,6 +1250,40 @@ static int createNativeWindow(_GLFWwindow* window, WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); } + window->win32.scaleToMonitor = wndconfig->scaleToMonitor; + + // Adjust window size to account for DPI scaling of the window frame and + // optionally DPI scaling of the client area + // This cannot be done until we know what monitor it was placed on + if (!window->monitor) + { + RECT rect = { 0, 0, wndconfig->width, wndconfig->height }; + + if (wndconfig->scaleToMonitor) + { + float xscale, yscale; + _glfwPlatformGetWindowContentScale(window, &xscale, &yscale); + rect.right = (int) (rect.right * xscale); + rect.bottom = (int) (rect.bottom * yscale); + } + + ClientToScreen(window->win32.handle, (POINT*) &rect.left); + ClientToScreen(window->win32.handle, (POINT*) &rect.right); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, + GetDpiForWindow(window->win32.handle)); + } + else + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + + SetWindowPos(window->win32.handle, NULL, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOZORDER); + } + DragAcceptFiles(window->win32.handle, TRUE); if (fbconfig->transparent) @@ -1227,20 +1343,6 @@ void _glfwUnregisterWindowClassWin32(void) UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL)); } -// Returns whether desktop compositing is enabled -// -GLFWbool _glfwIsCompositionEnabledWin32(void) -{ - if (IsWindowsVistaOrGreater()) - { - BOOL enabled; - if (SUCCEEDED(DwmIsCompositionEnabled(&enabled))) - return enabled; - } - - return FALSE; -} - ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -1378,8 +1480,19 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) { RECT rect = { xpos, ypos, xpos, ypos }; - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE); } @@ -1408,8 +1521,19 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) else { RECT rect = { 0, 0, width, height }; - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + SetWindowPos(window->win32.handle, HWND_TOP, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER); @@ -1464,8 +1588,18 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, _glfwPlatformGetWindowSize(window, &width, &height); SetRect(&rect, 0, 0, width, height); - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } if (left) *left = -rect.left; @@ -1541,8 +1675,19 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, else { RECT rect = { xpos, ypos, xpos + width, ypos + height }; - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); + + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + SetWindowPos(window->win32.handle, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, @@ -1602,8 +1747,18 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, else after = HWND_NOTOPMOST; - AdjustWindowRectEx(&rect, getWindowStyle(window), - FALSE, getWindowExStyle(window)); + if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) + { + AdjustWindowRectExForDpi(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window), + GetDpiForWindow(window->win32.handle)); + } + else + { + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + } + SetWindowPos(window->win32.handle, after, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, @@ -1638,7 +1793,15 @@ int _glfwPlatformWindowHovered(_GLFWwindow* window) int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { - return window->win32.transparent && _glfwIsCompositionEnabledWin32(); + BOOL enabled; + + if (!window->win32.transparent) + return GLFW_FALSE; + + if (!IsWindowsVistaOrGreater()) + return GLFW_FALSE; + + return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled; } void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) @@ -1845,8 +2008,24 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { - cursor->win32.handle = - CopyCursor(LoadCursorW(NULL, translateCursorShape(shape))); + LPCWSTR name = NULL; + + if (shape == GLFW_ARROW_CURSOR) + name = IDC_ARROW; + else if (shape == GLFW_IBEAM_CURSOR) + name = IDC_IBEAM; + else if (shape == GLFW_CROSSHAIR_CURSOR) + name = IDC_CROSS; + else if (shape == GLFW_HAND_CURSOR) + name = IDC_HAND; + else if (shape == GLFW_HRESIZE_CURSOR) + name = IDC_SIZEWE; + else if (shape == GLFW_VRESIZE_CURSOR) + name = IDC_SIZENS; + else + return GLFW_FALSE; + + cursor->win32.handle = CopyCursor(LoadCursorW(NULL, name)); if (!cursor->win32.handle) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, diff --git a/src/external/glfw/src/window.c b/src/external/glfw/src/window.c index 4ed415ca..4e365cb4 100644 --- a/src/external/glfw/src/window.c +++ b/src/external/glfw/src/window.c @@ -369,6 +369,9 @@ GLFWAPI void glfwWindowHint(int hint, int value) case GLFW_COCOA_GRAPHICS_SWITCHING: _glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE; return; + case GLFW_SCALE_TO_MONITOR: + _glfw.hints.window.scaleToMonitor = value ? GLFW_TRUE : GLFW_FALSE; + return; case GLFW_CENTER_CURSOR: _glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE; return; diff --git a/src/external/glfw/src/wl_init.c b/src/external/glfw/src/wl_init.c index c19184d0..c6b209bc 100644 --- a/src/external/glfw/src/wl_init.c +++ b/src/external/glfw/src/wl_init.c @@ -27,6 +27,8 @@ #include "internal.h" #include +#include +#include #include #include #include @@ -42,7 +44,8 @@ static inline int min(int n1, int n2) return n1 < n2 ? n1 : n2; } -static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, int* which) +static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, + int* which) { int focus; _GLFWwindow* window = _glfw.windowListHead; @@ -96,7 +99,7 @@ static void pointerHandleEnter(void* data, } window->wl.decorations.focus = focus; - _glfw.wl.pointerSerial = serial; + _glfw.wl.serial = serial; _glfw.wl.pointerFocus = window; window->wl.hovered = GLFW_TRUE; @@ -117,26 +120,36 @@ static void pointerHandleLeave(void* data, window->wl.hovered = GLFW_FALSE; - _glfw.wl.pointerSerial = serial; + _glfw.wl.serial = serial; _glfw.wl.pointerFocus = NULL; _glfwInputCursorEnter(window, GLFW_FALSE); } -static void setCursor(const char* name) +static void setCursor(_GLFWwindow* window, const char* name) { struct wl_buffer* buffer; struct wl_cursor* cursor; struct wl_cursor_image* image; struct wl_surface* surface = _glfw.wl.cursorSurface; + struct wl_cursor_theme* theme = _glfw.wl.cursorTheme; + int scale = 1; - cursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, - name); + if (window->wl.scale > 1 && _glfw.wl.cursorThemeHiDPI) + { + // We only support up to scale=2 for now, since libwayland-cursor + // requires us to load a different theme for each size. + scale = 2; + theme = _glfw.wl.cursorThemeHiDPI; + } + + cursor = wl_cursor_theme_get_cursor(theme, name); if (!cursor) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Standard cursor not found"); return; } + // TODO: handle animated cursors too. image = cursor->images[0]; if (!image) @@ -145,10 +158,11 @@ static void setCursor(const char* name) buffer = wl_cursor_image_get_buffer(image); if (!buffer) return; - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, surface, - image->hotspot_x, - image->hotspot_y); + image->hotspot_x / scale, + image->hotspot_y / scale); + wl_surface_set_buffer_scale(surface, scale); wl_surface_attach(surface, buffer, 0, 0); wl_surface_damage(surface, 0, 0, image->width, image->height); @@ -211,7 +225,7 @@ static void pointerHandleMotion(void* data, default: assert(0); } - setCursor(cursorName); + setCursor(window, cursorName); } static void pointerHandleButton(void* data, @@ -295,7 +309,7 @@ static void pointerHandleButton(void* data, if (window->wl.decorations.focus != mainWindow) return; - _glfw.wl.pointerSerial = serial; + _glfw.wl.serial = serial; /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev * codes. */ @@ -464,6 +478,7 @@ static void keyboardHandleEnter(void* data, return; } + _glfw.wl.serial = serial; _glfw.wl.keyboardFocus = window; _glfwInputWindowFocus(window, GLFW_TRUE); } @@ -478,6 +493,7 @@ static void keyboardHandleLeave(void* data, if (!window) return; + _glfw.wl.serial = serial; _glfw.wl.keyboardFocus = NULL; _glfwInputWindowFocus(window, GLFW_FALSE); } @@ -561,6 +577,7 @@ static void keyboardHandleKey(void* data, action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE; + _glfw.wl.serial = serial; _glfwInputKey(window, keyCode, key, action, _glfw.wl.xkb.modifiers); @@ -592,6 +609,8 @@ static void keyboardHandleModifiers(void* data, xkb_mod_mask_t mask; unsigned int modifiers = 0; + _glfw.wl.serial = serial; + if (!_glfw.wl.xkb.keymap) return; @@ -686,6 +705,70 @@ static const struct wl_seat_listener seatListener = { seatHandleName, }; +static void dataOfferHandleOffer(void* data, + struct wl_data_offer* dataOffer, + const char* mimeType) +{ +} + +static const struct wl_data_offer_listener dataOfferListener = { + dataOfferHandleOffer, +}; + +static void dataDeviceHandleDataOffer(void* data, + struct wl_data_device* dataDevice, + struct wl_data_offer* id) +{ + if (_glfw.wl.dataOffer) + wl_data_offer_destroy(_glfw.wl.dataOffer); + + _glfw.wl.dataOffer = id; + wl_data_offer_add_listener(_glfw.wl.dataOffer, &dataOfferListener, NULL); +} + +static void dataDeviceHandleEnter(void* data, + struct wl_data_device* dataDevice, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t x, + wl_fixed_t y, + struct wl_data_offer *id) +{ +} + +static void dataDeviceHandleLeave(void* data, + struct wl_data_device* dataDevice) +{ +} + +static void dataDeviceHandleMotion(void* data, + struct wl_data_device* dataDevice, + uint32_t time, + wl_fixed_t x, + wl_fixed_t y) +{ +} + +static void dataDeviceHandleDrop(void* data, + struct wl_data_device* dataDevice) +{ +} + +static void dataDeviceHandleSelection(void* data, + struct wl_data_device* dataDevice, + struct wl_data_offer* id) +{ +} + +static const struct wl_data_device_listener dataDeviceListener = { + dataDeviceHandleDataOffer, + dataDeviceHandleEnter, + dataDeviceHandleLeave, + dataDeviceHandleMotion, + dataDeviceHandleDrop, + dataDeviceHandleSelection, +}; + static void wmBaseHandlePing(void* data, struct xdg_wm_base* wmBase, uint32_t serial) @@ -740,12 +823,28 @@ static void registryHandleGlobal(void* data, wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL); } } + else if (strcmp(interface, "wl_data_device_manager") == 0) + { + if (!_glfw.wl.dataDeviceManager) + { + _glfw.wl.dataDeviceManager = + wl_registry_bind(registry, name, + &wl_data_device_manager_interface, 1); + } + } else if (strcmp(interface, "xdg_wm_base") == 0) { _glfw.wl.wmBase = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL); } + else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) + { + _glfw.wl.decorationManager = + wl_registry_bind(registry, name, + &zxdg_decoration_manager_v1_interface, + 1); + } else if (strcmp(interface, "wp_viewporter") == 0) { _glfw.wl.viewporter = @@ -939,6 +1038,12 @@ static void createKeyTables(void) int _glfwPlatformInit(void) { + const char *cursorTheme; + const char *cursorSizeStr; + char *cursorSizeEnd; + long cursorSizeLong; + int cursorSize; + _glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0"); if (!_glfw.wl.cursor.handle) { @@ -1059,15 +1164,46 @@ int _glfwPlatformInit(void) if (_glfw.wl.pointer && _glfw.wl.shm) { - _glfw.wl.cursorTheme = wl_cursor_theme_load(NULL, 32, _glfw.wl.shm); + cursorTheme = getenv("XCURSOR_THEME"); + cursorSizeStr = getenv("XCURSOR_SIZE"); + cursorSize = 32; + if (cursorSizeStr) + { + errno = 0; + cursorSizeLong = strtol(cursorSizeStr, &cursorSizeEnd, 10); + if (!*cursorSizeEnd && !errno && cursorSizeLong > 0 && cursorSizeLong <= INT_MAX) + cursorSize = (int)cursorSizeLong; + } + _glfw.wl.cursorTheme = + wl_cursor_theme_load(cursorTheme, cursorSize, _glfw.wl.shm); if (!_glfw.wl.cursorTheme) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unable to load default cursor theme"); return GLFW_FALSE; } + // If this happens to be NULL, we just fallback to the scale=1 version. + _glfw.wl.cursorThemeHiDPI = + wl_cursor_theme_load(cursorTheme, 2 * cursorSize, _glfw.wl.shm); _glfw.wl.cursorSurface = wl_compositor_create_surface(_glfw.wl.compositor); + _glfw.wl.cursorTimerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + } + + if (_glfw.wl.seat && _glfw.wl.dataDeviceManager) + { + _glfw.wl.dataDevice = + wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, + _glfw.wl.seat); + wl_data_device_add_listener(_glfw.wl.dataDevice, &dataDeviceListener, NULL); + _glfw.wl.clipboardString = malloc(4096); + if (!_glfw.wl.clipboardString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unable to allocate clipboard memory"); + return GLFW_FALSE; + } + _glfw.wl.clipboardSize = 4096; } return GLFW_TRUE; @@ -1103,6 +1239,8 @@ void _glfwPlatformTerminate(void) if (_glfw.wl.cursorTheme) wl_cursor_theme_destroy(_glfw.wl.cursorTheme); + if (_glfw.wl.cursorThemeHiDPI) + wl_cursor_theme_destroy(_glfw.wl.cursorThemeHiDPI); if (_glfw.wl.cursor.handle) { _glfw_dlclose(_glfw.wl.cursor.handle); @@ -1121,8 +1259,18 @@ void _glfwPlatformTerminate(void) wl_shell_destroy(_glfw.wl.shell); if (_glfw.wl.viewporter) wp_viewporter_destroy(_glfw.wl.viewporter); + if (_glfw.wl.decorationManager) + zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager); if (_glfw.wl.wmBase) xdg_wm_base_destroy(_glfw.wl.wmBase); + if (_glfw.wl.dataSource) + wl_data_source_destroy(_glfw.wl.dataSource); + if (_glfw.wl.dataDevice) + wl_data_device_destroy(_glfw.wl.dataDevice); + if (_glfw.wl.dataOffer) + wl_data_offer_destroy(_glfw.wl.dataOffer); + if (_glfw.wl.dataDeviceManager) + wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager); if (_glfw.wl.pointer) wl_pointer_destroy(_glfw.wl.pointer); if (_glfw.wl.keyboard) @@ -1142,6 +1290,16 @@ void _glfwPlatformTerminate(void) wl_display_flush(_glfw.wl.display); wl_display_disconnect(_glfw.wl.display); } + + if (_glfw.wl.timerfd >= 0) + close(_glfw.wl.timerfd); + if (_glfw.wl.cursorTimerfd >= 0) + close(_glfw.wl.cursorTimerfd); + + if (_glfw.wl.clipboardString) + free(_glfw.wl.clipboardString); + if (_glfw.wl.clipboardSendString) + free(_glfw.wl.clipboardSendString); } const char* _glfwPlatformGetVersionString(void) @@ -1158,4 +1316,3 @@ const char* _glfwPlatformGetVersionString(void) #endif ; } - diff --git a/src/external/glfw/src/wl_monitor.c b/src/external/glfw/src/wl_monitor.c index 5af74084..588f8b0d 100644 --- a/src/external/glfw/src/wl_monitor.c +++ b/src/external/glfw/src/wl_monitor.c @@ -32,16 +32,16 @@ #include -static void geometry(void* data, - struct wl_output* output, - int32_t x, - int32_t y, - int32_t physicalWidth, - int32_t physicalHeight, - int32_t subpixel, - const char* make, - const char* model, - int32_t transform) +static void outputHandleGeometry(void* data, + struct wl_output* output, + int32_t x, + int32_t y, + int32_t physicalWidth, + int32_t physicalHeight, + int32_t subpixel, + const char* make, + const char* model, + int32_t transform) { struct _GLFWmonitor *monitor = data; char name[1024]; @@ -55,12 +55,12 @@ static void geometry(void* data, monitor->name = _glfw_strdup(name); } -static void mode(void* data, - struct wl_output* output, - uint32_t flags, - int32_t width, - int32_t height, - int32_t refresh) +static void outputHandleMode(void* data, + struct wl_output* output, + uint32_t flags, + int32_t width, + int32_t height, + int32_t refresh) { struct _GLFWmonitor *monitor = data; GLFWvidmode mode; @@ -81,16 +81,16 @@ static void mode(void* data, monitor->wl.currentMode = monitor->modeCount - 1; } -static void done(void* data, struct wl_output* output) +static void outputHandleDone(void* data, struct wl_output* output) { struct _GLFWmonitor *monitor = data; _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); } -static void scale(void* data, - struct wl_output* output, - int32_t factor) +static void outputHandleScale(void* data, + struct wl_output* output, + int32_t factor) { struct _GLFWmonitor *monitor = data; @@ -98,10 +98,10 @@ static void scale(void* data, } static const struct wl_output_listener outputListener = { - geometry, - mode, - done, - scale, + outputHandleGeometry, + outputHandleMode, + outputHandleDone, + outputHandleScale, }; @@ -180,18 +180,18 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) *mode = monitor->modes[monitor->wl.currentMode]; } -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { - // TODO _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Gamma ramp getting not supported yet"); + "Wayland: Gamma ramp access it not available"); + return GLFW_FALSE; } -void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, + const GLFWgammaramp* ramp) { - // TODO _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Gamma ramp setting not supported yet"); + "Wayland: Gamma ramp access is not available"); } diff --git a/src/external/glfw/src/wl_platform.h b/src/external/glfw/src/wl_platform.h index ef8419e7..c17ebe88 100644 --- a/src/external/glfw/src/wl_platform.h +++ b/src/external/glfw/src/wl_platform.h @@ -57,6 +57,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR #include "osmesa_context.h" #include "wayland-xdg-shell-client-protocol.h" +#include "wayland-xdg-decoration-client-protocol.h" #include "wayland-viewporter-client-protocol.h" #include "wayland-relative-pointer-unstable-v1-client-protocol.h" #include "wayland-pointer-constraints-unstable-v1-client-protocol.h" @@ -185,6 +186,7 @@ typedef struct _GLFWwindowWayland struct { struct xdg_surface* surface; struct xdg_toplevel* toplevel; + struct zxdg_toplevel_decoration_v1* decoration; } xdg; _GLFWcursor* currentCursor; @@ -210,6 +212,7 @@ typedef struct _GLFWwindowWayland GLFWbool justCreated; struct { + GLFWbool serverSide; struct wl_buffer* buffer; _GLFWdecorationWayland top, left, right, bottom; int focus; @@ -230,7 +233,12 @@ typedef struct _GLFWlibraryWayland struct wl_seat* seat; struct wl_pointer* pointer; struct wl_keyboard* keyboard; + struct wl_data_device_manager* dataDeviceManager; + struct wl_data_device* dataDevice; + struct wl_data_offer* dataOffer; + struct wl_data_source* dataSource; struct xdg_wm_base* wmBase; + struct zxdg_decoration_manager_v1* decorationManager; struct wp_viewporter* viewporter; struct zwp_relative_pointer_manager_v1* relativePointerManager; struct zwp_pointer_constraints_v1* pointerConstraints; @@ -240,13 +248,19 @@ typedef struct _GLFWlibraryWayland int seatVersion; struct wl_cursor_theme* cursorTheme; + struct wl_cursor_theme* cursorThemeHiDPI; struct wl_surface* cursorSurface; - uint32_t pointerSerial; + int cursorTimerfd; + uint32_t serial; int32_t keyboardRepeatRate; int32_t keyboardRepeatDelay; int keyboardLastKey; int keyboardLastScancode; + char* clipboardString; + size_t clipboardSize; + char* clipboardSendString; + size_t clipboardSendSize; int timerfd; short int keycodes[256]; short int scancodes[GLFW_KEY_LAST + 1]; @@ -332,10 +346,12 @@ typedef struct _GLFWmonitorWayland // typedef struct _GLFWcursorWayland { - struct wl_cursor_image* image; + struct wl_cursor* cursor; + struct wl_cursor* cursorHiDPI; struct wl_buffer* buffer; int width, height; int xhot, yhot; + int currentImage; } _GLFWcursorWayland; diff --git a/src/external/glfw/src/wl_window.c b/src/external/glfw/src/wl_window.c index c165ba73..98a64659 100644 --- a/src/external/glfw/src/wl_window.c +++ b/src/external/glfw/src/wl_window.c @@ -39,18 +39,18 @@ #include -static void handlePing(void* data, - struct wl_shell_surface* shellSurface, - uint32_t serial) +static void shellSurfaceHandlePing(void* data, + struct wl_shell_surface* shellSurface, + uint32_t serial) { wl_shell_surface_pong(shellSurface, serial); } -static void handleConfigure(void* data, - struct wl_shell_surface* shellSurface, - uint32_t edges, - int32_t width, - int32_t height) +static void shellSurfaceHandleConfigure(void* data, + struct wl_shell_surface* shellSurface, + uint32_t edges, + int32_t width, + int32_t height) { _GLFWwindow* window = data; float aspectRatio; @@ -94,19 +94,18 @@ static void handleConfigure(void* data, _glfwInputWindowDamage(window); } -static void handlePopupDone(void* data, - struct wl_shell_surface* shellSurface) +static void shellSurfaceHandlePopupDone(void* data, + struct wl_shell_surface* shellSurface) { } static const struct wl_shell_surface_listener shellSurfaceListener = { - handlePing, - handleConfigure, - handlePopupDone + shellSurfaceHandlePing, + shellSurfaceHandleConfigure, + shellSurfaceHandlePopupDone }; -static int -createTmpfileCloexec(char* tmpname) +static int createTmpfileCloexec(char* tmpname) { int fd; @@ -137,8 +136,7 @@ createTmpfileCloexec(char* tmpname) * is set to ENOSPC. If posix_fallocate() is not supported, program may * receive SIGBUS on accessing mmap()'ed file contents instead. */ -static int -createAnonymousFile(off_t size) +static int createAnonymousFile(off_t size) { static const char template[] = "/glfw-shared-XXXXXX"; const char* path; @@ -146,23 +144,37 @@ createAnonymousFile(off_t size) int fd; int ret; - path = getenv("XDG_RUNTIME_DIR"); - if (!path) +#ifdef HAVE_MEMFD_CREATE + fd = memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0) { - errno = ENOENT; - return -1; + // We can add this seal before calling posix_fallocate(), as the file + // is currently zero-sized anyway. + // + // There is also no need to check for the return value, we couldn’t do + // anything with it anyway. + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + } + else +#endif + { + path = getenv("XDG_RUNTIME_DIR"); + if (!path) + { + errno = ENOENT; + return -1; + } + + name = calloc(strlen(path) + sizeof(template), 1); + strcpy(name, path); + strcat(name, template); + + fd = createTmpfileCloexec(name); + free(name); + if (fd < 0) + return -1; } - name = calloc(strlen(path) + sizeof(template), 1); - strcpy(name, path); - strcat(name, template); - - fd = createTmpfileCloexec(name); - - free(name); - - if (fd < 0) - return -1; ret = posix_fallocate(fd, 0, size); if (ret != 0) { @@ -188,7 +200,7 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Creating a buffer file for %d B failed: %m", length); - return GLFW_FALSE; + return NULL; } data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); @@ -197,7 +209,7 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: mmap failed: %m"); close(fd); - return GLFW_FALSE; + return NULL; } pool = wl_shm_create_pool(_glfw.wl.shm, fd, length); @@ -262,11 +274,13 @@ static void createDecorations(_GLFWwindow* window) const GLFWimage image = { 1, 1, data }; GLFWbool opaque = (data[3] == 255); - if (!_glfw.wl.viewporter) + if (!_glfw.wl.viewporter || !window->decorated || window->wl.decorations.serverSide) return; if (!window->wl.decorations.buffer) window->wl.decorations.buffer = createShmBuffer(&image); + if (!window->wl.decorations.buffer) + return; createDecoration(&window->wl.decorations.top, window->wl.surface, window->wl.decorations.buffer, opaque, @@ -307,6 +321,22 @@ static void destroyDecorations(_GLFWwindow* window) destroyDecoration(&window->wl.decorations.bottom); } +static void xdgDecorationHandleConfigure(void* data, + struct zxdg_toplevel_decoration_v1* decoration, + uint32_t mode) +{ + _GLFWwindow* window = data; + + window->wl.decorations.serverSide = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + + if (!window->wl.decorations.serverSide) + createDecorations(window); +} + +static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = { + xdgDecorationHandleConfigure, +}; + // Makes the surface considered as XRGB instead of ARGB. static void setOpaqueRegion(_GLFWwindow* window) { @@ -389,9 +419,9 @@ static void checkScaleChange(_GLFWwindow* window) } } -static void handleEnter(void *data, - struct wl_surface *surface, - struct wl_output *output) +static void surfaceHandleEnter(void *data, + struct wl_surface *surface, + struct wl_output *output) { _GLFWwindow* window = data; _GLFWmonitor* monitor = wl_output_get_user_data(output); @@ -409,9 +439,9 @@ static void handleEnter(void *data, checkScaleChange(window); } -static void handleLeave(void *data, - struct wl_surface *surface, - struct wl_output *output) +static void surfaceHandleLeave(void *data, + struct wl_surface *surface, + struct wl_output *output) { _GLFWwindow* window = data; _GLFWmonitor* monitor = wl_output_get_user_data(output); @@ -431,8 +461,8 @@ static void handleLeave(void *data, } static const struct wl_surface_listener surfaceListener = { - handleEnter, - handleLeave + surfaceHandleEnter, + surfaceHandleLeave }; static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable) @@ -479,13 +509,11 @@ static GLFWbool createSurface(_GLFWwindow* window, if (!window->wl.transparent) setOpaqueRegion(window); - if (window->decorated && !window->monitor) - createDecorations(window); - return GLFW_TRUE; } -static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, int refreshRate) +static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, + int refreshRate) { if (window->wl.xdg.toplevel) { @@ -502,7 +530,8 @@ static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, int refres monitor->wl.output); } setIdleInhibitor(window, GLFW_TRUE); - destroyDecorations(window); + if (!window->wl.decorations.serverSide) + destroyDecorations(window); } static GLFWbool createShellSurface(_GLFWwindow* window) @@ -538,11 +567,13 @@ static GLFWbool createShellSurface(_GLFWwindow* window) { wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); setIdleInhibitor(window, GLFW_FALSE); + createDecorations(window); } else { wl_shell_surface_set_toplevel(window->wl.shellSurface); setIdleInhibitor(window, GLFW_FALSE); + createDecorations(window); } wl_surface_commit(window->wl.surface); @@ -631,6 +662,27 @@ static const struct xdg_surface_listener xdgSurfaceListener = { xdgSurfaceHandleConfigure }; +static void setXdgDecorations(_GLFWwindow* window) +{ + if (_glfw.wl.decorationManager) + { + window->wl.xdg.decoration = + zxdg_decoration_manager_v1_get_toplevel_decoration( + _glfw.wl.decorationManager, window->wl.xdg.toplevel); + zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration, + &xdgDecorationListener, + window); + zxdg_toplevel_decoration_v1_set_mode( + window->wl.xdg.decoration, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + else + { + window->wl.decorations.serverSide = GLFW_FALSE; + createDecorations(window); + } +} + static GLFWbool createXdgSurface(_GLFWwindow* window) { window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase, @@ -678,10 +730,12 @@ static GLFWbool createXdgSurface(_GLFWwindow* window) { xdg_toplevel_set_maximized(window->wl.xdg.toplevel); setIdleInhibitor(window, GLFW_FALSE); + setXdgDecorations(window); } else { setIdleInhibitor(window, GLFW_FALSE); + setXdgDecorations(window); } wl_surface_commit(window->wl.surface); @@ -690,13 +744,75 @@ static GLFWbool createXdgSurface(_GLFWwindow* window) return GLFW_TRUE; } -static void -handleEvents(int timeout) +static void setCursorImage(_GLFWwindow* window, + _GLFWcursorWayland* cursorWayland) +{ + struct itimerspec timer = {}; + struct wl_cursor* wlCursor = cursorWayland->cursor; + struct wl_cursor_image* image; + struct wl_buffer* buffer; + struct wl_surface* surface = _glfw.wl.cursorSurface; + int scale = 1; + + if (!wlCursor) + buffer = cursorWayland->buffer; + else + { + if (window->wl.scale > 1 && cursorWayland->cursorHiDPI) + { + wlCursor = cursorWayland->cursorHiDPI; + scale = 2; + } + + image = wlCursor->images[cursorWayland->currentImage]; + buffer = wl_cursor_image_get_buffer(image); + if (!buffer) + return; + + timer.it_value.tv_sec = image->delay / 1000; + timer.it_value.tv_nsec = (image->delay % 1000) * 1000000; + timerfd_settime(_glfw.wl.cursorTimerfd, 0, &timer, NULL); + + cursorWayland->width = image->width; + cursorWayland->height = image->height; + cursorWayland->xhot = image->hotspot_x; + cursorWayland->yhot = image->hotspot_y; + } + + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, + surface, + cursorWayland->xhot / scale, + cursorWayland->yhot / scale); + wl_surface_set_buffer_scale(surface, scale); + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_damage(surface, 0, 0, + cursorWayland->width, cursorWayland->height); + wl_surface_commit(surface); +} + +static void incrementCursorImage(_GLFWwindow* window) +{ + _GLFWcursor* cursor; + + if (!window || window->wl.decorations.focus != mainWindow) + return; + + cursor = window->wl.currentCursor; + if (cursor && cursor->wl.cursor) + { + cursor->wl.currentImage += 1; + cursor->wl.currentImage %= cursor->wl.cursor->image_count; + setCursorImage(window, &cursor->wl); + } +} + +static void handleEvents(int timeout) { struct wl_display* display = _glfw.wl.display; struct pollfd fds[] = { { wl_display_get_fd(display), POLLIN }, { _glfw.wl.timerfd, POLLIN }, + { _glfw.wl.cursorTimerfd, POLLIN }, }; ssize_t read_ret; uint64_t repeats, i; @@ -719,7 +835,7 @@ handleEvents(int timeout) return; } - if (poll(fds, 2, timeout) > 0) + if (poll(fds, 3, timeout) > 0) { if (fds[0].revents & POLLIN) { @@ -742,6 +858,15 @@ handleEvents(int timeout) _glfw.wl.keyboardLastScancode, GLFW_REPEAT, _glfw.wl.xkb.modifiers); } + + if (fds[2].revents & POLLIN) + { + read_ret = read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)); + if (read_ret != 8) + return; + + incrementCursorImage(_glfw.wl.pointerFocus); + } } else { @@ -860,6 +985,9 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) window->context.destroy(window); destroyDecorations(window); + if (window->wl.xdg.decoration) + zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration); + if (window->wl.decorations.buffer) wl_buffer_destroy(window->wl.decorations.buffer); @@ -956,13 +1084,15 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, } } -void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, + int numer, int denom) { // TODO: find out how to trigger a resize. // The actual limits are checked in the wl_shell_surface::configure handler. } -void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, + int* width, int* height) { _glfwPlatformGetWindowSize(window, width, height); *width *= window->wl.scale; @@ -973,7 +1103,7 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom) { - if (window->decorated && !window->monitor) + if (window->decorated && !window->monitor && !window->wl.decorations.serverSide) { if (top) *top = _GLFW_DECORATION_TOP; @@ -1102,7 +1232,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, else if (window->wl.shellSurface) wl_shell_surface_set_toplevel(window->wl.shellSurface); setIdleInhibitor(window, GLFW_FALSE); - if (window->decorated) + if (!_glfw.wl.decorationManager) createDecorations(window); } _glfwInputWindowMonitor(window, monitor); @@ -1236,6 +1366,9 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int xhot, int yhot) { cursor->wl.buffer = createShmBuffer(image); + if (!cursor->wl.buffer) + return GLFW_FALSE; + cursor->wl.width = image->width; cursor->wl.height = image->height; cursor->wl.xhot = xhot; @@ -1257,28 +1390,37 @@ int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) return GLFW_FALSE; } - cursor->wl.image = standardCursor->images[0]; + cursor->wl.cursor = standardCursor; + cursor->wl.currentImage = 0; + + if (_glfw.wl.cursorThemeHiDPI) + { + standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, + translateCursorShape(shape)); + cursor->wl.cursorHiDPI = standardCursor; + } + return GLFW_TRUE; } void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { // If it's a standard cursor we don't need to do anything here - if (cursor->wl.image) + if (cursor->wl.cursor) return; if (cursor->wl.buffer) wl_buffer_destroy(cursor->wl.buffer); } -static void handleRelativeMotion(void* data, - struct zwp_relative_pointer_v1* pointer, - uint32_t timeHi, - uint32_t timeLo, - wl_fixed_t dx, - wl_fixed_t dy, - wl_fixed_t dxUnaccel, - wl_fixed_t dyUnaccel) +static void relativePointerHandleRelativeMotion(void* data, + struct zwp_relative_pointer_v1* pointer, + uint32_t timeHi, + uint32_t timeLo, + wl_fixed_t dx, + wl_fixed_t dy, + wl_fixed_t dxUnaccel, + wl_fixed_t dyUnaccel) { _GLFWwindow* window = data; @@ -1291,11 +1433,11 @@ static void handleRelativeMotion(void* data, } static const struct zwp_relative_pointer_v1_listener relativePointerListener = { - handleRelativeMotion + relativePointerHandleRelativeMotion }; -static void handleLocked(void* data, - struct zwp_locked_pointer_v1* lockedPointer) +static void lockedPointerHandleLocked(void* data, + struct zwp_locked_pointer_v1* lockedPointer) { } @@ -1315,14 +1457,14 @@ static void unlockPointer(_GLFWwindow* window) static void lockPointer(_GLFWwindow* window); -static void handleUnlocked(void* data, - struct zwp_locked_pointer_v1* lockedPointer) +static void lockedPointerHandleUnlocked(void* data, + struct zwp_locked_pointer_v1* lockedPointer) { } static const struct zwp_locked_pointer_v1_listener lockedPointerListener = { - handleLocked, - handleUnlocked + lockedPointerHandleLocked, + lockedPointerHandleUnlocked }; static void lockPointer(_GLFWwindow* window) @@ -1359,7 +1501,7 @@ static void lockPointer(_GLFWwindow* window) window->wl.pointerLock.relativePointer = relativePointer; window->wl.pointerLock.lockedPointer = lockedPointer; - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0); } @@ -1370,10 +1512,8 @@ static GLFWbool isPointerLocked(_GLFWwindow* window) void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { - struct wl_buffer* buffer; struct wl_cursor* defaultCursor; - struct wl_cursor_image* image; - struct wl_surface* surface = _glfw.wl.cursorSurface; + struct wl_cursor* defaultCursorHiDPI = NULL; if (!_glfw.wl.pointer) return; @@ -1382,7 +1522,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) // If we're not in the correct window just save the cursor // the next time the pointer enters the window the cursor will change - if (window != _glfw.wl.pointerFocus) + if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow) return; // Unlock possible pointer lock if no longer disabled. @@ -1392,7 +1532,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) if (window->cursorMode == GLFW_CURSOR_NORMAL) { if (cursor) - image = cursor->wl.image; + setCursorImage(window, &cursor->wl); else { defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, @@ -1403,33 +1543,19 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) "Wayland: Standard cursor not found"); return; } - image = defaultCursor->images[0]; - } - - if (image) - { - buffer = wl_cursor_image_get_buffer(image); - if (!buffer) - return; - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, - surface, - image->hotspot_x, - image->hotspot_y); - wl_surface_attach(surface, buffer, 0, 0); - wl_surface_damage(surface, 0, 0, - image->width, image->height); - wl_surface_commit(surface); - } - else - { - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, - surface, - cursor->wl.xhot, - cursor->wl.yhot); - wl_surface_attach(surface, cursor->wl.buffer, 0, 0); - wl_surface_damage(surface, 0, 0, - cursor->wl.width, cursor->wl.height); - wl_surface_commit(surface); + if (_glfw.wl.cursorThemeHiDPI) + defaultCursorHiDPI = + wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, + "left_ptr"); + _GLFWcursorWayland cursorWayland = { + defaultCursor, + defaultCursorHiDPI, + NULL, + 0, 0, + 0, 0, + 0 + }; + setCursorImage(window, &cursorWayland); } } else if (window->cursorMode == GLFW_CURSOR_DISABLED) @@ -1439,24 +1565,213 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) } else if (window->cursorMode == GLFW_CURSOR_HIDDEN) { - wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, - NULL, 0, 0); + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0); } } +static void dataSourceHandleTarget(void* data, + struct wl_data_source* dataSource, + const char* mimeType) +{ + if (_glfw.wl.dataSource != dataSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unknown clipboard data source"); + return; + } +} + +static void dataSourceHandleSend(void* data, + struct wl_data_source* dataSource, + const char* mimeType, + int fd) +{ + const char* string = _glfw.wl.clipboardSendString; + size_t len = _glfw.wl.clipboardSendSize; + int ret; + + if (_glfw.wl.dataSource != dataSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unknown clipboard data source"); + return; + } + + if (!string) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Copy requested from an invalid string"); + return; + } + + if (strcmp(mimeType, "text/plain;charset=utf-8") != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Wrong MIME type asked from clipboard"); + close(fd); + return; + } + + while (len > 0) + { + ret = write(fd, string, len); + if (ret == -1 && errno == EINTR) + continue; + if (ret == -1) + { + // TODO: also report errno maybe. + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Error while writing the clipboard"); + close(fd); + return; + } + len -= ret; + } + close(fd); +} + +static void dataSourceHandleCancelled(void* data, + struct wl_data_source* dataSource) +{ + wl_data_source_destroy(dataSource); + + if (_glfw.wl.dataSource != dataSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unknown clipboard data source"); + return; + } + + _glfw.wl.dataSource = NULL; +} + +static const struct wl_data_source_listener dataSourceListener = { + dataSourceHandleTarget, + dataSourceHandleSend, + dataSourceHandleCancelled, +}; + void _glfwPlatformSetClipboardString(const char* string) { - // TODO - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Clipboard setting not implemented yet"); + if (_glfw.wl.dataSource) + { + wl_data_source_destroy(_glfw.wl.dataSource); + _glfw.wl.dataSource = NULL; + } + + if (_glfw.wl.clipboardSendString) + { + free(_glfw.wl.clipboardSendString); + _glfw.wl.clipboardSendString = NULL; + } + + _glfw.wl.clipboardSendString = strdup(string); + if (!_glfw.wl.clipboardSendString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to allocate clipboard string"); + return; + } + _glfw.wl.clipboardSendSize = strlen(string); + _glfw.wl.dataSource = + wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); + if (!_glfw.wl.dataSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to create clipboard source"); + free(_glfw.wl.clipboardSendString); + return; + } + wl_data_source_add_listener(_glfw.wl.dataSource, + &dataSourceListener, + NULL); + wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8"); + wl_data_device_set_selection(_glfw.wl.dataDevice, + _glfw.wl.dataSource, + _glfw.wl.serial); +} + +static GLFWbool growClipboardString(void) +{ + char* clipboard = _glfw.wl.clipboardString; + + clipboard = realloc(clipboard, _glfw.wl.clipboardSize * 2); + if (!clipboard) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to grow clipboard string"); + return GLFW_FALSE; + } + _glfw.wl.clipboardString = clipboard; + _glfw.wl.clipboardSize = _glfw.wl.clipboardSize * 2; + return GLFW_TRUE; } const char* _glfwPlatformGetClipboardString(void) { - // TODO - _glfwInputError(GLFW_PLATFORM_ERROR, - "Wayland: Clipboard getting not implemented yet"); - return NULL; + int fds[2]; + int ret; + size_t len = 0; + + if (!_glfw.wl.dataOffer) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "No clipboard data has been sent yet"); + return NULL; + } + + ret = pipe2(fds, O_CLOEXEC); + if (ret < 0) + { + // TODO: also report errno maybe? + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to create clipboard pipe fds"); + return NULL; + } + + wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]); + close(fds[1]); + + // XXX: this is a huge hack, this function shouldn’t be synchronous! + handleEvents(-1); + + while (1) + { + // Grow the clipboard if we need to paste something bigger, there is no + // shrink operation yet. + if (len + 4096 > _glfw.wl.clipboardSize) + { + if (!growClipboardString()) + { + close(fds[0]); + return NULL; + } + } + + // Then read from the fd to the clipboard, handling all known errors. + ret = read(fds[0], _glfw.wl.clipboardString + len, 4096); + if (ret == 0) + break; + if (ret == -1 && errno == EINTR) + continue; + if (ret == -1) + { + // TODO: also report errno maybe. + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Impossible to read from clipboard fd"); + close(fds[0]); + return NULL; + } + len += ret; + } + close(fds[0]); + if (len + 1 > _glfw.wl.clipboardSize) + { + if (!growClipboardString()) + return NULL; + } + _glfw.wl.clipboardString[len] = '\0'; + return _glfw.wl.clipboardString; } void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) diff --git a/src/external/glfw/src/x11_init.c b/src/external/glfw/src/x11_init.c index c949916d..e3e3ad51 100644 --- a/src/external/glfw/src/x11_init.c +++ b/src/external/glfw/src/x11_init.c @@ -780,8 +780,9 @@ static GLFWbool initExtensions(void) // static void getSystemContentScale(float* xscale, float* yscale) { - // NOTE: Default to the display-wide DPI as we don't currently have a policy - // for which monitor a window is considered to be on + // NOTE: Fall back to the display-wide DPI instead of RandR monitor DPI if + // Xft.dpi retrieval below fails as we don't currently have an exact + // policy for which monitor a window is considered to "be on" float xdpi = DisplayWidth(_glfw.x11.display, _glfw.x11.screen) * 25.4f / DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen); float ydpi = DisplayHeight(_glfw.x11.display, _glfw.x11.screen) * diff --git a/src/external/glfw/src/x11_monitor.c b/src/external/glfw/src/x11_monitor.c index f557fe47..df53041d 100644 --- a/src/external/glfw/src/x11_monitor.c +++ b/src/external/glfw/src/x11_monitor.c @@ -422,7 +422,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) } } -void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) { @@ -438,6 +438,7 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); XRRFreeGamma(gamma); + return GLFW_TRUE; } else if (_glfw.x11.vidmode.available) { @@ -449,6 +450,13 @@ void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) XF86VidModeGetGammaRamp(_glfw.x11.display, _glfw.x11.screen, ramp->size, ramp->red, ramp->green, ramp->blue); + return GLFW_TRUE; + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Gamma ramp access not supported by server"); + return GLFW_FALSE; } } @@ -481,6 +489,11 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) (unsigned short*) ramp->green, (unsigned short*) ramp->blue); } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Gamma ramp access not supported by server"); + } } diff --git a/src/external/glfw/src/x11_window.c b/src/external/glfw/src/x11_window.c index 1c4e9c3c..5e916107 100644 --- a/src/external/glfw/src/x11_window.c +++ b/src/external/glfw/src/x11_window.c @@ -175,29 +175,6 @@ static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer po event->xproperty.atom == notification->xselection.property; } -// Translates a GLFW standard cursor to a font cursor shape -// -static int translateCursorShape(int shape) -{ - switch (shape) - { - case GLFW_ARROW_CURSOR: - return XC_left_ptr; - case GLFW_IBEAM_CURSOR: - return XC_xterm; - case GLFW_CROSSHAIR_CURSOR: - return XC_crosshair; - case GLFW_HAND_CURSOR: - return XC_hand1; - case GLFW_HRESIZE_CURSOR: - return XC_sb_h_double_arrow; - case GLFW_VRESIZE_CURSOR: - return XC_sb_v_double_arrow; - } - - return 0; -} - // Translates an X event modifier state mask // static int translateState(int state) @@ -231,23 +208,6 @@ static int translateKey(int scancode) return _glfw.x11.keycodes[scancode]; } -// Return the GLFW window corresponding to the specified X11 window -// -static _GLFWwindow* findWindowByHandle(Window handle) -{ - _GLFWwindow* window; - - if (XFindContext(_glfw.x11.display, - handle, - _glfw.x11.context, - (XPointer*) &window) != 0) - { - return NULL; - } - - return window; -} - // Sends an EWMH or ICCCM event to the window manager // static void sendEventToWM(_GLFWwindow* window, Atom type, @@ -632,6 +592,15 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, Visual* visual, int depth) { + int width = wndconfig->width; + int height = wndconfig->height; + + if (wndconfig->scaleToMonitor) + { + width *= _glfw.x11.contentScaleX; + height *= _glfw.x11.contentScaleY; + } + // Create a colormap based on the visual used by the current context window->x11.colormap = XCreateColormap(_glfw.x11.display, _glfw.x11.root, @@ -657,7 +626,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, window->x11.handle = XCreateWindow(_glfw.x11.display, _glfw.x11.root, 0, 0, - wndconfig->width, wndconfig->height, + width, height, 0, // Border width depth, // Color depth InputOutput, @@ -760,7 +729,7 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, XFree(hints); } - updateNormalHints(window, wndconfig->width, wndconfig->height); + updateNormalHints(window, width, height); // Set ICCCM WM_CLASS property { @@ -1264,8 +1233,10 @@ static void processEvent(XEvent *event) return; } - window = findWindowByHandle(event->xany.window); - if (window == NULL) + if (XFindContext(_glfw.x11.display, + event->xany.window, + _glfw.x11.context, + (XPointer*) &window) != 0) { // This is an event for a window that has already been destroyed return; @@ -1484,12 +1455,20 @@ static void processEvent(XEvent *event) case EnterNotify: { + // XEnterWindowEvent is XCrossingEvent + const int x = event->xcrossing.x; + const int y = event->xcrossing.y; + // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise // ignore the defined cursor for hidden cursor mode if (window->cursorMode == GLFW_CURSOR_HIDDEN) updateCursorImage(window); _glfwInputCursorEnter(window, GLFW_TRUE); + _glfwInputCursorPos(window, x, y); + + window->x11.lastCursorPosX = x; + window->x11.lastCursorPosY = y; return; } @@ -2479,6 +2458,14 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window) Atom* states; unsigned long i; GLFWbool maximized = GLFW_FALSE; + + if (!_glfw.x11.NET_WM_STATE || + !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || + !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + return maximized; + } + const unsigned long count = _glfwGetWindowPropertyX11(window->x11.handle, _glfw.x11.NET_WM_STATE, @@ -2814,8 +2801,24 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor, int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { - cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, - translateCursorShape(shape)); + int native = 0; + + if (shape == GLFW_ARROW_CURSOR) + native = XC_left_ptr; + else if (shape == GLFW_IBEAM_CURSOR) + native = XC_xterm; + else if (shape == GLFW_CROSSHAIR_CURSOR) + native = XC_crosshair; + else if (shape == GLFW_HAND_CURSOR) + native = XC_hand1; + else if (shape == GLFW_HRESIZE_CURSOR) + native = XC_sb_h_double_arrow; + else if (shape == GLFW_VRESIZE_CURSOR) + native = XC_sb_v_double_arrow; + else + return GLFW_FALSE; + + cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native); if (!cursor->x11.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, From 8bc4050377221e85e0e270b01fc14ba669d90cb7 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sat, 27 Oct 2018 11:11:54 +0200 Subject: [PATCH 47/58] Revert "core: workaround window not being rendered till moved on macOS Mojave" This reverts commit 1fe6d9fc06156257d5210cfa71ecb839fb190722. Because upstream GLFW now contains a fix. --- src/core.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/core.c b/src/core.c index a7ea150b..1c703e5b 100644 --- a/src/core.c +++ b/src/core.c @@ -175,11 +175,9 @@ #include // Required for: glfwGetX11Window() #elif defined(__APPLE__) #include // Required for: usleep() - #include // Required for: objc_msgsend(), sel_registerName() //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition - #define GLFW_EXPOSE_NATIVE_NSGL - #include // Required for: glfwGetCocoaWindow(), glfwGetNSGLContext() + #include // Required for: glfwGetCocoaWindow() #endif #endif @@ -277,10 +275,6 @@ static bool windowReady = false; // Check if window has been init static bool windowMinimized = false; // Check if window has been minimized static const char *windowTitle = NULL; // Window text title... -#if defined(__APPLE__) -static int windowNeedsUpdating = 2; // Times the Cocoa window needs to be updated initially -#endif - static unsigned int displayWidth, displayHeight;// Display width and height (monitor, device-screen, LCD, ...) static int screenWidth, screenHeight; // Screen width and height (used render area) static int renderWidth, renderHeight; // Framebuffer width and height (render area, including black bars if required) @@ -3135,16 +3129,6 @@ static void SwapBuffers(void) { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) glfwSwapBuffers(window); -#if __APPLE__ - // Workaround for missing/erroneous initial rendering on macOS - if (windowNeedsUpdating) - { - // Desugared version of Objective C: [glfwGetNSGLContext(window) update] - ((id (*)(id, SEL))objc_msgSend)(glfwGetNSGLContext(window), sel_registerName("update")); - - windowNeedsUpdating--; - } -#endif #endif #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP) From 010c655f795ca1305f787b134a5cc92d8ef47056 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 8 Jan 2019 10:10:20 +0100 Subject: [PATCH 48/58] Added required define on TCC compiler --- src/rglfw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rglfw.c b/src/rglfw.c index 154eb4ff..853c5200 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -45,6 +45,9 @@ #define _GLFW_USE_MENUBAR // To create and populate the menu bar when the first window is created #define _GLFW_USE_RETINA // To have windows use the full resolution of Retina displays #endif +#if defined(__TINYC__) + #define _WIN32_WINNT_WINXP 0x0501 +#endif // NOTE: _GLFW_MIR experimental platform not supported at this moment From fa250e5d178610234d36b0ce742d0142865da313 Mon Sep 17 00:00:00 2001 From: Jens Pitkanen Date: Tue, 8 Jan 2019 18:31:50 +0200 Subject: [PATCH 49/58] Add project/scripts --- projects/scripts/README.md | 68 +++++++++ projects/scripts/core_basic_window.c | 62 ++++++++ projects/scripts/linux-build.sh | 158 ++++++++++++++++++++ projects/scripts/osx-build.sh | 154 +++++++++++++++++++ projects/scripts/windows-build.bat | 211 +++++++++++++++++++++++++++ 5 files changed, 653 insertions(+) create mode 100644 projects/scripts/README.md create mode 100644 projects/scripts/core_basic_window.c create mode 100755 projects/scripts/linux-build.sh create mode 100755 projects/scripts/osx-build.sh create mode 100644 projects/scripts/windows-build.bat diff --git a/projects/scripts/README.md b/projects/scripts/README.md new file mode 100644 index 00000000..cdfa29bf --- /dev/null +++ b/projects/scripts/README.md @@ -0,0 +1,68 @@ +Here are dependency-less build scripts for raylib projects. + +## Dependencies +The scripts, as mentioned above, do not have dependencies. There's one +exception to this however, and that is Windows, because Windows +doesn't have a built-in C compiler. On Windows, you'll need to install +[Visual Studio][visual-studio] or the [build tools][vs-tools]. If you +didn't install them in the default location, write your changes around +line 101 of [`windows-build.bat`](windows-build.bat). + +## Script customization +First of all, the scripts have a few variables at the very top, which +are supposed to be configured for each project separately: +- `GAME_NAME` variable is used for the executable name. +- `SOURCES` is a list of .c source files, divided by spaces, which are + going to be compiled and linked with raylib to create the final + executable. You can use wildcards, so if you have all your .c files + in a directory called `src`, you can just set `SOURCES` to + `../../src/*.c`. Note: the paths should be either absolute, or + relative to `builds/platform`, hence `../../`. +- `RAYLIB_SRC` should point to the raylib/src directory. In this case, + it's `../../src`, but as with the `SOURCES`, if the path is + relative, it should be relative to `temp/debug`, so it's actually + `../../../../src`. + +## Compilation flags +- `-Os` (`/O1` with MSVC, `-O2` with clang\*) is used for release + builds, to save space. Since it's a good practice to make your games + run on the slowest possible systems, only a few games would benefit + from additional runtime performance on almost all systems. Other + flags: `-flto` (`/GL` and `/LTCG` for MSVC) in release builds, `-O0 + -g` (`/Od /Zi` for MSVC) in debug builds. +- `-Wall -Wextra -Wpedantic` (`/Wall` for MSVC) are used for warnings. + +\* Clang 7.0.1 seems to have problems compiling with `-flto` and `-Os` +enabled at the same time, so `-Os` is replaced with `-O2` for clang. + +## Command line arguments +The build scripts accept some flags, which can be given either one at +a time (`-d -c -r`) or in bunches (`-dcr`). Here's a description of +all of the flags. +- `-h` Describes all the flags, and a few example commands +- `-d` Faster builds that have debug symbols, and enable warnings +- `-u` Run upx\* on the executable after compilation (before -r) +- `-r` Run the executable after compilation +- `-c` Remove the temp/(debug|release) directory, ie. full recompile +- `-q` Suppress this script's informational prints +- `-qq` Suppress all prints, complete silence +- `-v` cl.exe normally prints out a lot of superficial information, as + well as the MSVC build environment activation scripts, but these are + mostly suppressed by default. If you do want to see everything, use + this flag. + +\* This is mostly here to make building simple "shipping" versions + easier, and it's a very small bit in the build scripts. The option + requires that you have upx installed and on your path, of course. + +#### Examples +| What the command does | Command | +|-------------------------------------------------------------|---------------------------| +| Build a release build, on Windows | `windows-build.bat` | +| Build a release build, full recompile, on Linux | `./linux-build.sh -c` | +| Build a debug build and run, on macOS | `./osx-build.sh -d -r` | +| Build in debug, run, don't print at all, on Linux with `sh` | `sh linux-build.sh -drqq` | + + +[visual-studio]: https://visualstudio.microsoft.com/downloads/#visual-studio-community-2017 +[vs-tools]: https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2017 diff --git a/projects/scripts/core_basic_window.c b/projects/scripts/core_basic_window.c new file mode 100644 index 00000000..b30f05de --- /dev/null +++ b/projects/scripts/core_basic_window.c @@ -0,0 +1,62 @@ +/******************************************************************************************* +* +* raylib [core] example - Basic window +* +* Welcome to raylib! +* +* To test examples, just press F6 and execute raylib_compile_execute script +* Note that compiled executable is placed in the same folder as .c file +* +* You can find all basic examples on C:\raylib\raylib\examples folder or +* raylib official webpage: www.raylib.com +* +* Enjoy using raylib. :) +* +* This example has been created using raylib 1.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window"); + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // TODO: Update your variables here + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/projects/scripts/linux-build.sh b/projects/scripts/linux-build.sh new file mode 100755 index 00000000..851ea1a5 --- /dev/null +++ b/projects/scripts/linux-build.sh @@ -0,0 +1,158 @@ +#!/bin/sh +# Change your executable name here +GAME_NAME="game" + +# Set your sources here (relative to the ./builds/linux directory) +# Example with two source folders: +# SOURCES="../../src/*.c ../../src/submodule/*.c" +SOURCES="../../core_basic_window.c" + +# Set your raylib/src location here, relative to the ./temp/x directory +RAYLIB_SRC="../../../../src" + +# About this build script: it does many things, but in essence, it's +# very simple. It has 3 compiler invocations: building raylib (which +# is not done always, see logic by searching "Build raylib"), building +# src/*.c files, and linking together those two. Each invocation is +# wrapped in an if statement to make the -qq flag work, it's pretty +# verbose, sorry. + +# Get arguments +while getopts ":hdurcq" opt; do + case $opt in + h) + echo "Usage: ./linux-build.sh [-hdurcqq]" + echo " -h Show this information" + echo " -d Faster builds that have debug symbols, and enable warnings" + echo " -u Run upx* on the executable after compilation (before -r)" + echo " -r Run the executable after compilation" + echo " -c Remove the temp/(debug|release) directory, ie. full recompile" + echo " -q Suppress this script's informational prints" + echo " -qq Suppress all prints, complete silence (> /dev/null 2>&1)" + echo "" + echo "* This is mostly here to make building simple \"shipping\" versions" + echo " easier, and it's a very small bit in the build scripts. The option" + echo " requires that you have upx installed and on your path, of course." + echo "" + echo "Examples:" + echo " Build a release build: ./linux-build.sh" + echo " Build a release build, full recompile: ./linux-build.sh -c" + echo " Build a debug build and run: ./linux-build.sh -d -r" + echo " Build in debug, run, don't print at all: ./linux-build.sh -drqq" + exit 0 + ;; + d) + BUILD_DEBUG="1" + ;; + u) + UPX_IT="1" + ;; + r) + RUN_AFTER_BUILD="1" + ;; + c) + BUILD_ALL="1" + ;; + q) + if [ -n "$QUIET" ]; then + REALLY_QUIET="1" + else + QUIET="1" + fi + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac +done + +# Set CC if it's not set already +if [ -z "$CC" ]; then + CC=cc +fi + +# Flags +OUTPUT_DIR="builds/linux" +COMPILATION_FLAGS="-std=c99 -Os -flto" +if [ "$CC" = "clang" ]; then + # Clang 7.0.1 fails to compile with -Os, possibly the same bug as this: + # https://www.mail-archive.com/llvm-bugs@lists.llvm.org/msg25771.html + COMPILATION_FLAGS="-std=c99 -O2 -flto" + [ -z "$QUIET" ] && echo "COMPILE-WARNING: \$CC is clang, using -O2 instead of -Os." +fi +FINAL_COMPILE_FLAGS="-s" +WARNING_FLAGS="-Wall -Wextra -Wpedantic" +LINK_FLAGS="-lm -ldl -lpthread -lX11 -lxcb -lGL -lGLX -lXext -lGLdispatch -lXau -lXdmcp" +# Debug changes to flags +if [ -n "$BUILD_DEBUG" ]; then + OUTPUT_DIR="builds-debug/linux" + COMPILATION_FLAGS="-std=c99 -O0 -g" + FINAL_COMPILE_FLAGS="" +fi + +# Display what we're doing +if [ -n "$BUILD_DEBUG" ]; then + [ -z "$QUIET" ] && echo "COMPILE-INFO: Compiling in debug mode. ($COMPILATION_FLAGS $WARNING_FLAGS)" +else + [ -z "$QUIET" ] && echo "COMPILE-INFO: Compiling in release mode. ($COMPILATION_FLAGS $FINAL_COMPILE_FLAGS)" +fi + +# Create the raylib cache directory +ROOT_DIR=$(pwd) +TEMP_DIR="temp/release" +if [ -n "$BUILD_DEBUG" ]; then + TEMP_DIR="temp/debug" +fi +# If there's a -c flag, remove the cache +if [ -d "$TEMP_DIR" ] && [ -n "$BUILD_ALL" ]; then + [ -z "$QUIET" ] && echo "COMPILE-INFO: Found cached raylib, rebuilding." + rm -r "$TEMP_DIR" +fi +# If temp directory doesn't exist, build raylib +if [ ! -d "$TEMP_DIR" ]; then + mkdir -p $TEMP_DIR + cd $TEMP_DIR + RAYLIB_DEFINES="-D_DEFAULT_SOURCE -DPLATFORM_DESKTOP -DGRAPHICS_API_OPENGL_33" + RAYLIB_C_FILES="$RAYLIB_SRC/core.c $RAYLIB_SRC/shapes.c $RAYLIB_SRC/textures.c $RAYLIB_SRC/text.c $RAYLIB_SRC/models.c $RAYLIB_SRC/utils.c $RAYLIB_SRC/audio.c $RAYLIB_SRC/rglfw.c $RAYLIB_SRC/external/mini_al.c" + RAYLIB_INCLUDE_FLAGS="-I$RAYLIB_SRC -I$RAYLIB_SRC/external/glfw/include" + + if [ -n "$REALLY_QUIET" ]; then + $CC -c $RAYLIB_DEFINES $RAYLIB_INCLUDE_FLAGS $COMPILATION_FLAGS $RAYLIB_C_FILES > /dev/null 2>&1 + else + $CC -c $RAYLIB_DEFINES $RAYLIB_INCLUDE_FLAGS $COMPILATION_FLAGS $RAYLIB_C_FILES + fi + [ -z "$QUIET" ] && echo "COMPILE-INFO: Raylib compiled into object files in: $TEMP_DIR/" + cd $ROOT_DIR +fi + +# Build the actual game +mkdir -p $OUTPUT_DIR +cd $OUTPUT_DIR +[ -z "$QUIET" ] && echo "COMPILE-INFO: Compiling game code." +if [ -n "$REALLY_QUIET" ]; then + $CC -c -o main.o -I$RAYLIB_SRC $COMPILATION_FLAGS $WARNING_FLAGS $SOURCES > /dev/null 2>&1 + $CC -o $GAME_NAME $ROOT_DIR/$TEMP_DIR/*.o main.o $LINK_FLAGS > /dev/null 2>&1 +else + $CC -c -o main.o -I$RAYLIB_SRC $COMPILATION_FLAGS $WARNING_FLAGS $SOURCES + $CC -o $GAME_NAME $ROOT_DIR/$TEMP_DIR/*.o main.o $LINK_FLAGS +fi +rm main.o +[ -z "$QUIET" ] && echo "COMPILE-INFO: Game compiled into an executable in: $OUTPUT_DIR/" + +if [ -n "$UPX_IT" ]; then + [ -z "$QUIET" ] && echo "COMPILE-INFO: Packing $GAME_NAME with upx." + upx $GAME_NAME > /dev/null 2>&1 +fi + +if [ -n "$RUN_AFTER_BUILD" ]; then + [ -z "$QUIET" ] && echo "COMPILE-INFO: Running." + if [ -n "$REALLY_QUIET" ]; then + ./$GAME_NAME > /dev/null 2>&1 + else + ./$GAME_NAME + fi +fi +cd $ROOT_DIR + +[ -z "$QUIET" ] && echo "COMPILE-INFO: All done." diff --git a/projects/scripts/osx-build.sh b/projects/scripts/osx-build.sh new file mode 100755 index 00000000..36558f63 --- /dev/null +++ b/projects/scripts/osx-build.sh @@ -0,0 +1,154 @@ +#!/bin/sh +# Change your executable name here +GAME_NAME="game" + +# Set your sources here (relative to the ./builds/osx directory) +# Example with two source folders: +# SOURCES="../../src/*.c ../../src/submodule/*.c" +SOURCES="../../core_basic_window.c" + +# Set your raylib/src location here, relative to the ./temp/x directory +RAYLIB_SRC="../../../../src" + +# About this build script: it does many things, but in essence, it's +# very simple. It has 3 compiler invocations: building raylib (which +# is not done always, see logic by searching "Build raylib"), building +# src/*.c files, and linking together those two. Each invocation is +# wrapped in an if statement to make the -qq flag work, it's pretty +# verbose, sorry. + +# Get arguments +while getopts ":hdurcq" opt; do + case $opt in + h) + echo "Usage: ./osx-build.sh [-hdurcqq]" + echo " -h Show this information" + echo " -d Faster builds that have debug symbols, and enable warnings" + echo " -u Run upx* on the executable after compilation (before -r)" + echo " -r Run the executable after compilation" + echo " -c Remove the temp/(debug|release) directory, ie. full recompile" + echo " -q Suppress this script's informational prints" + echo " -qq Suppress all prints, complete silence (> /dev/null 2>&1)" + echo "" + echo "* This is mostly here to make building simple \"shipping\" versions" + echo " easier, and it's a very small bit in the build scripts. The option" + echo " requires that you have upx installed and on your path, of course." + echo "" + echo "Examples:" + echo " Build a release build: ./osx-build.sh" + echo " Build a release build, full recompile: ./osx-build.sh -c" + echo " Build a debug build and run: ./osx-build.sh -d -r" + echo " Build in debug, run, don't print at all: ./osx-build.sh -drqq" + exit 0 + ;; + d) + BUILD_DEBUG="1" + ;; + u) + UPX_IT="1" + ;; + r) + RUN_AFTER_BUILD="1" + ;; + c) + BUILD_ALL="1" + ;; + q) + if [ -n "$QUIET" ]; then + REALLY_QUIET="1" + else + QUIET="1" + fi + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + esac +done + +# Set CC if it's not set already +if [ -z "$CC" ]; then + CC=cc +fi + +# Flags +OUTPUT_DIR="builds/osx" +COMPILATION_FLAGS="-std=c99 -O2 -flto" +FINAL_COMPILE_FLAGS="-s" +WARNING_FLAGS="-Wall -Wextra -Wpedantic" +LINK_FLAGS="-framework OpenGL -framework OpenAL -framework IOKit -framework CoreVideo -framework Cocoa" +# Debug changes to flags +if [ -n "$BUILD_DEBUG" ]; then + OUTPUT_DIR="builds-debug/osx" + COMPILATION_FLAGS="-std=c99 -O0 -g" + FINAL_COMPILE_FLAGS="" +fi + +# Display what we're doing +if [ -n "$BUILD_DEBUG" ]; then + [ -z "$QUIET" ] && echo "COMPILE-INFO: Compiling in debug mode. ($COMPILATION_FLAGS $WARNING_FLAGS)" +else + [ -z "$QUIET" ] && echo "COMPILE-INFO: Compiling in release mode. ($COMPILATION_FLAGS $FINAL_COMPILE_FLAGS)" +fi + +# Create the raylib cache directory +ROOT_DIR=$(pwd) +TEMP_DIR="temp/release" +if [ -n "$BUILD_DEBUG" ]; then + TEMP_DIR="temp/debug" +fi +# If there's a -c flag, remove the cache +if [ -d "$TEMP_DIR" ] && [ -n "$BUILD_ALL" ]; then + [ -z "$QUIET" ] && echo "COMPILE-INFO: Found cached raylib, rebuilding." + rm -r "$TEMP_DIR" +fi +# If temp directory doesn't exist, build raylib +if [ ! -d "$TEMP_DIR" ]; then + mkdir -p $TEMP_DIR + cd $TEMP_DIR + RAYLIB_DEFINES="-D_DEFAULT_SOURCE -DPLATFORM_DESKTOP -DGRAPHICS_API_OPENGL_33" + RAYLIB_C_FILES="$RAYLIB_SRC/core.c $RAYLIB_SRC/shapes.c $RAYLIB_SRC/textures.c $RAYLIB_SRC/text.c $RAYLIB_SRC/models.c $RAYLIB_SRC/utils.c $RAYLIB_SRC/audio.c $RAYLIB_SRC/external/mini_al.c" + RAYLIB_INCLUDE_FLAGS="-I$RAYLIB_SRC -I$RAYLIB_SRC/external/glfw/include" + + if [ -n "$REALLY_QUIET" ]; then + $CC -c $RAYLIB_DEFINES $RAYLIB_INCLUDE_FLAGS $COMPILATION_FLAGS -x objective-c $RAYLIB_SRC/rglfw.c > /dev/null 2>&1 + $CC -c $RAYLIB_DEFINES $RAYLIB_INCLUDE_FLAGS $COMPILATION_FLAGS $RAYLIB_C_FILES > /dev/null 2>&1 + else + $CC -c $RAYLIB_DEFINES $RAYLIB_INCLUDE_FLAGS $COMPILATION_FLAGS -x objective-c $RAYLIB_SRC/rglfw.c + $CC -c $RAYLIB_DEFINES $RAYLIB_INCLUDE_FLAGS $COMPILATION_FLAGS $RAYLIB_C_FILES + fi + [ -z "$QUIET" ] && echo "COMPILE-INFO: Raylib compiled into object files in: $TEMP_DIR/" + cd $ROOT_DIR +fi + +# Build the actual game +mkdir -p $OUTPUT_DIR +cd $OUTPUT_DIR +[ -z "$QUIET" ] && echo "COMPILE-INFO: Compiling game code." +if [ -n "$REALLY_QUIET" ]; then + $CC -c -o main.o -I$RAYLIB_SRC $COMPILATION_FLAGS $WARNING_FLAGS $SOURCES > /dev/null 2>&1 + $CC -o $GAME_NAME $ROOT_DIR/$TEMP_DIR/*.o main.o $LINK_FLAGS > /dev/null 2>&1 +else + $CC -c -o main.o -I$RAYLIB_SRC $COMPILATION_FLAGS $WARNING_FLAGS $SOURCES + $CC -o $GAME_NAME $ROOT_DIR/$TEMP_DIR/*.o main.o $LINK_FLAGS +fi +rm main.o +[ -z "$QUIET" ] && echo "COMPILE-INFO: Game compiled into an executable in: $OUTPUT_DIR/" + +if [ -n "$UPX_IT" ]; then + [ -z "$QUIET" ] && echo "COMPILE-INFO: Packing $GAME_NAME with upx." + upx $GAME_NAME > /dev/null 2>&1 +fi + +if [ -n "$RUN_AFTER_BUILD" ]; then + [ -z "$QUIET" ] && echo "COMPILE-INFO: Running." + if [ -n "$REALLY_QUIET" ]; then + ./$GAME_NAME > /dev/null 2>&1 + else + ./$GAME_NAME + fi +fi +cd $ROOT_DIR + +[ -z "$QUIET" ] && echo "COMPILE-INFO: All done." diff --git a/projects/scripts/windows-build.bat b/projects/scripts/windows-build.bat new file mode 100644 index 00000000..c4be4c89 --- /dev/null +++ b/projects/scripts/windows-build.bat @@ -0,0 +1,211 @@ +@echo off +REM Change your executable name here +set GAME_NAME=game.exe + +REM Set your sources here (relative to the builds\windows directory) +REM Example with two source folders: +REM set SOURCES=..\..\src\*.c ..\..\src\submodule\*.c +set SOURCES=..\..\core_basic_window.c + +REM Set your raylib/src location here, relative to the ./temp/x directory +set RAYLIB_SRC=..\..\..\..\src + +REM About this build script: it does many things, but in essence, it's +REM very simple. It has 3 compiler invocations: building raylib (which +REM is not done always, see logic by searching "Build raylib"), building +REM src/*.c files, and linking together those two. Each invocation is +REM wrapped in an if statement to make the -qq flag work, it's pretty +REM verbose, sorry. + +REM To skip to the actual building part of the script, search for ":BUILD" + +REM For the ! variable notation +setlocal EnableDelayedExpansion +REM For shifting, which the command line argument parsing needs +setlocal EnableExtensions + + +:ARG_LOOP +set ARG=%1 +if "!ARG!" == "" ( goto PREPARE ) +IF NOT "x!ARG!" == "x!ARG:h=!" ( + goto HELP +) +IF NOT "x!ARG!" == "x!ARG:d=!" ( + set BUILD_DEBUG=1 +) +IF NOT "x!ARG!" == "x!ARG:u=!" ( + set UPX_IT=1 +) +IF NOT "x!ARG!" == "x!ARG:r=!" ( + set RUN_AFTER_BUILD=1 +) +IF NOT "x!ARG!" == "x!ARG:c=!" ( + set BUILD_ALL=1 +) +IF NOT "x!ARG!" == "x!ARG:qq=!" ( + set QUIET=1 + set REALLY_QUIET=1 +) ELSE IF NOT "x!ARG!" == "x!ARG:q=!" ( + IF DEFINED QUIET ( + set REALLY_QUIET=1 + ) ELSE ( + set QUIET=1 + ) +) +IF NOT "x!ARG!" == "x!ARG:v=!" ( + set VERBOSE=1 +) +IF NOT "%1" == "" ( + shift /1 + goto ARG_LOOP +) + + +:HELP +echo Usage: windows-build.bat [-hdurcqqv] +echo -h Show this information +echo -d Faster builds that have debug symbols, and enable warnings +echo -u Run upx* on the executable after compilation (before -r) +echo -r Run the executable after compilation +echo -c Remove the temp\{debug,release} directory, ie. full recompile +echo -q Suppress this script's informational prints +echo -qq Suppress all prints, complete silence +echo -v cl.exe normally prints out a lot of superficial information, as +echo well as the MSVC build environment activation scripts, but these are +echo mostly suppressed by default. If you do want to see everything, use +echo this flag. +echo. +echo * This is mostly here to make building simple "shipping" versions +echo easier, and it's a very small bit in the build scripts. The option +echo requires that you have upx installed and on your path, of course. +echo. +echo Examples: +echo Build a release build: windows-build.bat +echo Build a release build, full recompile: windows-build.bat -c +echo Build a debug build and run: windows-build.bat -d -r +echo Build in debug, run, don't print at all: windows-build.bat -drqq +exit /B + + +:PREPARE +REM Activate the msvc build environment +IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" ( + set VC_INIT="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" +) ELSE IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" ( + set VC_INIT="C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" +) ELSE IF EXIST "C:\Program Files (x86)\Microsoft Visual C++ Build Tools\vcbuildtools.bat" ( + set VC_INIT="C:\Program Files (x86)\Microsoft Visual C++ Build Tools\vcbuildtools.bat" +) ELSE ( + REM Initialize your vc environment here if the defaults don't work + REM set VC_INIT="C:\your\path\here\vcvarsall.bat" + REM And then remove/comment out the following two lines + echo "Couldn't find vcvarsall.bat or vcbuildtools.bat, please set it manually." + exit /B +) +IF DEFINED VERBOSE ( + call !VC_INIT! x86 +) ELSE ( + call !VC_INIT! x86 > NUL 2>&1 +) + + +:BUILD +REM Flags +set OUTPUT_FLAG=/Fe: "!GAME_NAME!" +set COMPILATION_FLAGS=/O1 /GL +set WARNING_FLAGS= +set SUBSYSTEM_FLAGS=/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup +set LINK_FLAGS=/link /LTCG kernel32.lib user32.lib shell32.lib winmm.lib gdi32.lib opengl32.lib +set OUTPUT_DIR=builds\windows +REM Debug changes to flags +IF DEFINED BUILD_DEBUG ( + set OUTPUT_FLAG=/Fe: "!GAME_NAME!" + set COMPILATION_FLAGS=/Od /Zi + set WARNING_FLAGS=/Wall + set SUBSYSTEM_FLAGS= + set LINK_FLAGS=/link kernel32.lib user32.lib shell32.lib winmm.lib gdi32.lib opengl32.lib + set OUTPUT_DIR=builds-debug\windows +) +IF NOT DEFINED VERBOSE ( + set VERBOSITY_FLAG=/nologo +) + +REM Display what we're doing +IF DEFINED BUILD_DEBUG ( + IF NOT DEFINED QUIET echo COMPILE-INFO: Compiling in debug mode, flags: !COMPILATION_FLAGS! !WARNING_FLAGS! +) ELSE ( + IF NOT DEFINED QUIET echo COMPILE-INFO: Compiling in release mode, flags: !COMPILATION_FLAGS! /link /LTCG +) + +REM Create the temp directory for raylib +set "ROOT_DIR=%CD%" +set "TEMP_DIR=temp\release" +IF DEFINED BUILD_DEBUG ( + set "TEMP_DIR=temp\debug" +) + +IF DEFINED BUILD_ALL ( + IF EXIST !TEMP_DIR!\ ( + IF NOT DEFINED QUIET echo COMPILE-INFO: Found cached raylib, rebuilding. + del /Q !TEMP_DIR! + rmdir !TEMP_DIR! + ) +) + +REM Build raylib if it hasn't been cached in TEMP_DIR +IF NOT EXIST !TEMP_DIR!\ ( + mkdir !TEMP_DIR! + cd !TEMP_DIR! + REM Raylib's src folder + set "RAYLIB_DEFINES=/D_DEFAULT_SOURCE /DPLATFORM_DESKTOP /DGRAPHICS_API_OPENGL_33" + set RAYLIB_C_FILES="!RAYLIB_SRC!\core.c" "!RAYLIB_SRC!\shapes.c" "!RAYLIB_SRC!\textures.c" "!RAYLIB_SRC!\text.c" "!RAYLIB_SRC!\models.c" "!RAYLIB_SRC!\utils.c" "!RAYLIB_SRC!\audio.c" "!RAYLIB_SRC!\rglfw.c" "!RAYLIB_SRC!\external\mini_al.c" + set RAYLIB_INCLUDE_FLAGS=/I"!RAYLIB_SRC!" /I"!RAYLIB_SRC!\external\glfw\include" + + IF DEFINED REALLY_QUIET ( + cl.exe /w /c !RAYLIB_DEFINES! !RAYLIB_INCLUDE_FLAGS! !COMPILATION_FLAGS! !RAYLIB_C_FILES! > NUL 2>&1 + ) ELSE ( + cl.exe /w /c !VERBOSITY_FLAG! !RAYLIB_DEFINES! !RAYLIB_INCLUDE_FLAGS! !COMPILATION_FLAGS! !RAYLIB_C_FILES! + ) + IF NOT DEFINED QUIET echo COMPILE-INFO: Raylib compiled into object files in: !TEMP_DIR!\ + + REM Out of the temp directory + cd !ROOT_DIR! +) + +REM Move to the build directory +IF NOT EXIST !OUTPUT_DIR! mkdir !OUTPUT_DIR! +cd !OUTPUT_DIR! + +REM Build the actual game +IF NOT DEFINED QUIET echo COMPILE-INFO: Compiling game code. +IF DEFINED REALLY_QUIET ( + cl.exe !VERBOSITY_FLAG! /Fo"main.obj" !COMPILATION_FLAGS! !WARNING_FLAGS! /c /I"!RAYLIB_SRC!" !SOURCES! > NUL 2>&1 + cl.exe !VERBOSITY_FLAG! !OUTPUT_FLAG! "!ROOT_DIR!\!TEMP_DIR!\*.obj" main.obj !LINK_FLAGS! !SUBSYSTEM_FLAGS! > NUL 2>&1 +) ELSE ( + cl.exe !VERBOSITY_FLAG! /Fo"main.obj" !COMPILATION_FLAGS! !WARNING_FLAGS! /c /I"!RAYLIB_SRC!" !SOURCES! + cl.exe !VERBOSITY_FLAG! !OUTPUT_FLAG! "!ROOT_DIR!\!TEMP_DIR!\*.obj" main.obj !LINK_FLAGS! !SUBSYSTEM_FLAGS! +) +del main.obj +IF NOT DEFINED QUIET echo COMPILE-INFO: Game compiled into an executable in: !OUTPUT_DIR!\ + +REM Run upx +IF DEFINED UPX_IT ( + IF NOT DEFINED QUIET echo COMPILE-INFO: Packing !GAME_NAME! with upx. + upx !GAME_NAME! > NUL 2>&1 +) + +REM Finally, run the produced executable +IF DEFINED RUN_AFTER_BUILD ( + IF NOT DEFINED QUIET echo COMPILE-INFO: Running. + IF DEFINED REALLY_QUIET ( + !GAME_NAME! > NUL 2>&1 + ) ELSE ( + !GAME_NAME! + ) +) + +REM Back to development directory +cd !ROOT_DIR! + +IF NOT DEFINED QUIET echo COMPILE-INFO: All done. From df54e73b21bbdbf27bbf8c7ee9841a8a7af11375 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 8 Jan 2019 21:05:23 +0100 Subject: [PATCH 50/58] Update shell.html --- templates/web_shell/shell.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/web_shell/shell.html b/templates/web_shell/shell.html index b5d1feed..f158c432 100644 --- a/templates/web_shell/shell.html +++ b/templates/web_shell/shell.html @@ -14,9 +14,9 @@ - - - + + + @@ -25,11 +25,11 @@ - - + + - +