Compare commits

..

No commits in common. "master" and "voip_toxav_video" have entirely different histories.

198 changed files with 2695 additions and 4905 deletions

View File

@ -25,7 +25,7 @@ jobs:
run: sudo apt update && sudo apt -y install libsodium-dev cmake libvpx-dev libopus-dev run: sudo apt update && sudo apt -y install libsodium-dev cmake libvpx-dev libopus-dev
- name: Configure CMake - name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DTOMATO_BREAKPAD=ON -DTOMATO_TOX_AV=ON -DCMAKE_EXE_LINKER_FLAGS=-gz run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DTOMATO_TOX_AV=ON
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4 -t tomato run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4 -t tomato
@ -64,8 +64,6 @@ jobs:
strategy: strategy:
matrix: matrix:
platform: platform:
- vcpkg_toolkit: arm-neon-android
ndk_abi: armeabi-v7a
- vcpkg_toolkit: arm64-android - vcpkg_toolkit: arm64-android
ndk_abi: arm64-v8a ndk_abi: arm64-v8a
- vcpkg_toolkit: x64-android - vcpkg_toolkit: x64-android
@ -103,7 +101,7 @@ jobs:
- name: Configure CMake - name: Configure CMake
env: env:
ANDROID_NDK_HOME: ${{steps.setup_ndk.outputs.ndk-path}} ANDROID_NDK_HOME: ${{steps.setup_ndk.outputs.ndk-path}}
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=/usr/local/share/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{matrix.platform.vcpkg_toolkit}} -DANDROID=1 -DANDROID_PLATFORM=23 -DANDROID_ABI=${{matrix.platform.ndk_abi}} -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=${{steps.setup_ndk.outputs.ndk-path}}/build/cmake/android.toolchain.cmake -DSDLIMAGE_JPG_SHARED=OFF -DSDLIMAGE_PNG_SHARED=OFF -DTOMATO_MAIN_SO=ON -DTOMATO_TOX_AV=ON -DCMAKE_EXE_LINKER_FLAGS=-gz run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=/usr/local/share/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=${{matrix.platform.vcpkg_toolkit}} -DANDROID=1 -DANDROID_PLATFORM=23 -DANDROID_ABI=${{matrix.platform.ndk_abi}} -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=${{steps.setup_ndk.outputs.ndk-path}}/build/cmake/android.toolchain.cmake -DSDLIMAGE_JPG_SHARED=OFF -DSDLIMAGE_PNG_SHARED=OFF -DTOMATO_MAIN_SO=ON -DTOMATO_TOX_AV=ON
- name: Build (tomato) - name: Build (tomato)
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4 -t tomato run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4 -t tomato
@ -166,7 +164,7 @@ jobs:
#- uses: ilammy/setup-nasm@v1 #- uses: ilammy/setup-nasm@v1
- name: Configure CMake - name: Configure CMake
run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DSDLIMAGE_VENDORED=ON -DSDLIMAGE_DEPS_SHARED=ON -DSDLIMAGE_JXL=OFF -DSDLIMAGE_AVIF=OFF -DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe -DTOMATO_BREAKPAD=ON -DTOMATO_TOX_AV=ON run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DSDLIMAGE_VENDORED=ON -DSDLIMAGE_DEPS_SHARED=ON -DSDLIMAGE_JXL=OFF -DSDLIMAGE_AVIF=OFF -DPKG_CONFIG_EXECUTABLE=C:/vcpkg/installed/x64-windows/tools/pkgconf/pkgconf.exe -DTOMATO_TOX_AV=ON
- name: Build - name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -t tomato run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -t tomato

View File

@ -38,8 +38,6 @@ jobs:
strategy: strategy:
matrix: matrix:
platform: platform:
- vcpkg_toolkit: arm-neon-android
ndk_abi: armeabi-v7a
- vcpkg_toolkit: arm64-android - vcpkg_toolkit: arm64-android
ndk_abi: arm64-v8a ndk_abi: arm64-v8a
- vcpkg_toolkit: x64-android - vcpkg_toolkit: x64-android
@ -61,9 +59,9 @@ jobs:
distribution: 'temurin' distribution: 'temurin'
java-version: '17' java-version: '17'
#- name: update vcpkg - name: update vcpkg
# run: | run: |
# git clone https://github.com/microsoft/vcpkg.git git clone https://github.com/microsoft/vcpkg.git
- name: Install Dependencies (host) - name: Install Dependencies (host)
run: sudo apt update && sudo apt -y install cmake pkg-config nasm run: sudo apt update && sudo apt -y install cmake pkg-config nasm
@ -71,8 +69,7 @@ jobs:
- name: Install Dependencies (target) - name: Install Dependencies (target)
env: env:
ANDROID_NDK_HOME: ${{steps.setup_ndk.outputs.ndk-path}} ANDROID_NDK_HOME: ${{steps.setup_ndk.outputs.ndk-path}}
#run: vcpkg install --triplet ${{matrix.platform.vcpkg_toolkit}} --overlay-ports=vcpkg/ports libsodium opus libvpx libpng libjpeg-turbo run: vcpkg install --triplet ${{matrix.platform.vcpkg_toolkit}} --overlay-ports=vcpkg/ports libsodium opus libvpx libpng libjpeg-turbo
run: vcpkg install --triplet ${{matrix.platform.vcpkg_toolkit}} libsodium opus libvpx libpng libjpeg-turbo
# vcpkg scripts root /usr/local/share/vcpkg/scripts # vcpkg scripts root /usr/local/share/vcpkg/scripts
- name: Configure CMake - name: Configure CMake

View File

@ -20,9 +20,9 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
option(TOMATO_MAIN_SO "Build tomato as a shared object (for eg android apps)" ANDROID) option(TOMATO_MAIN_SO "Build tomato as a shared object (for eg android apps)" ANDROID)
option(TOMATO_ASAN "Build tomato with asan (gcc/clang/msvc)" OFF) option(TOMATO_ASAN "Build tomato with asan (gcc/clang/msvc)" OFF)
option(TOMATO_BREAKPAD "Build tomato with breakpad crash dumping" OFF)
option(TOMATO_TOX_AV "Build tomato with ToxAV" OFF) option(TOMATO_TOX_AV "Build tomato with ToxAV" OFF)
message("II TOMATO_TOX_AV: ${TOMATO_TOX_AV}")
if (TOMATO_ASAN) if (TOMATO_ASAN)
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
@ -43,17 +43,6 @@ if (TOMATO_ASAN)
endif() endif()
endif() endif()
message("II TOMATO_BREAKPAD: ${TOMATO_BREAKPAD}")
if (TOMATO_BREAKPAD)
if (LINUX) # TODO: test if android
# HACK: workaround an ugly cmake bug,
# where subdirs can now propergate enable_language upwards
enable_language(ASM)
endif()
endif()
message("II TOMATO_TOX_AV: ${TOMATO_TOX_AV}")
# uggly, but it needs to be defined for all of tomato. # uggly, but it needs to be defined for all of tomato.
# but this also means that we can not compile tomato in the same cmake as plugins # but this also means that we can not compile tomato in the same cmake as plugins
add_compile_definitions(ENTT_API_EXPORT) add_compile_definitions(ENTT_API_EXPORT)

View File

@ -33,16 +33,17 @@
android:required="false" /> android:required="false" />
<!-- Audio recording support --> <!-- Audio recording support -->
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- if you want to capture audio, uncomment this. -->
<uses-feature <!-- <uses-feature
android:name="android.hardware.microphone" android:name="android.hardware.microphone"
android:required="false" /> android:required="false" /> -->
<!-- Camera support --> <!-- Camera support -->
<!-- if you want to record video, uncomment this. -->
<!--
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-feature <uses-feature android:name="android.hardware.camera" />
android:name="android.hardware.camera" -->
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@ -57,6 +58,9 @@
<!-- Allow access to the vibrator --> <!-- Allow access to the vibrator -->
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<!-- if you want to capture audio, uncomment this. -->
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
<!-- Create a Java class extending SDLActivity and place it in a <!-- Create a Java class extending SDLActivity and place it in a
directory under app/src/main/java matching the package, e.g. app/src/main/java/com/gamemaker/game/MyGame.java directory under app/src/main/java matching the package, e.g. app/src/main/java/com/gamemaker/game/MyGame.java

View File

@ -24,7 +24,3 @@ add_subdirectory(./libwebp)
add_subdirectory(./qoi) add_subdirectory(./qoi)
add_subdirectory(./sdl_image) add_subdirectory(./sdl_image)
if (TOMATO_BREAKPAD)
add_subdirectory(./breakpad)
endif()

View File

@ -1,139 +0,0 @@
cmake_minimum_required(VERSION 3.16...3.24 FATAL_ERROR)
include(FetchContent)
if (NOT TARGET breakpad_client)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR ${CMAKE_SYSTEM_NAME} STREQUAL "Android")
if (NOT TARGET lss)
FetchContent_Declare(lss
GIT_REPOSITORY https://chromium.googlesource.com/linux-syscall-support/
GIT_TAG 9719c1e1e676814c456b55f5f070eabad6709d31
FIND_PACKAGE_ARGS # for the future
)
FetchContent_GetProperties(lss)
if(NOT lss_POPULATED)
FetchContent_Populate(lss)
# HACK: breakpad expects this at a specific path
configure_file(
${lss_SOURCE_DIR}/linux_syscall_support.h
${CMAKE_CURRENT_BINARY_DIR}/third_party/lss/linux_syscall_support.h
@ONLY
)
add_library(lss INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/third_party/lss/linux_syscall_support.h)
target_include_directories(lss INTERFACE ${CMAKE_CURRENT_BINARY_DIR})
endif()
endif()
endif()
FetchContent_Declare(breakpad
GIT_REPOSITORY https://chromium.googlesource.com/breakpad/breakpad
GIT_TAG v2023.06.01
FIND_PACKAGE_ARGS # for the future
)
FetchContent_GetProperties(breakpad)
if(NOT breakpad_POPULATED)
FetchContent_Populate(breakpad)
add_library(breakpad_common STATIC
${breakpad_SOURCE_DIR}/src/common/convert_UTF.h
${breakpad_SOURCE_DIR}/src/common/convert_UTF.cc
${breakpad_SOURCE_DIR}/src/common/md5.h
${breakpad_SOURCE_DIR}/src/common/md5.cc
${breakpad_SOURCE_DIR}/src/common/string_conversion.h
${breakpad_SOURCE_DIR}/src/common/string_conversion.cc
)
target_include_directories(breakpad_common PUBLIC "${breakpad_SOURCE_DIR}/src")
if (WIN32)
target_sources(breakpad_common PUBLIC
${breakpad_SOURCE_DIR}/src/common/windows/guid_string.h
${breakpad_SOURCE_DIR}/src/common/windows/guid_string.cc
)
add_library(breakpad_client STATIC)
target_sources(breakpad_client
PUBLIC
${breakpad_SOURCE_DIR}/src/client/windows/handler/exception_handler.h
${breakpad_SOURCE_DIR}/src/client/windows/common/ipc_protocol.h
${breakpad_SOURCE_DIR}/src/client/windows/crash_generation/crash_generation_client.h
${breakpad_SOURCE_DIR}/src/client/windows/crash_generation/minidump_generator.h
PRIVATE
${breakpad_SOURCE_DIR}/src/client/windows/handler/exception_handler.cc
${breakpad_SOURCE_DIR}/src/client/windows/crash_generation/crash_generation_client.cc
${breakpad_SOURCE_DIR}/src/client/windows/crash_generation/minidump_generator.cc
)
target_compile_definitions(breakpad_client PRIVATE UNICODE)
#elseif() # TODO: mac, ios and any other platform
else() # assume linux
enable_language(ASM) # mostly to document, needs to be set in parent
target_sources(breakpad_common PUBLIC
${breakpad_SOURCE_DIR}/src/common/linux/elf_core_dump.cc
${breakpad_SOURCE_DIR}/src/common/linux/elfutils.h
${breakpad_SOURCE_DIR}/src/common/linux/elfutils.cc
${breakpad_SOURCE_DIR}/src/common/linux/file_id.h
${breakpad_SOURCE_DIR}/src/common/linux/file_id.cc
${breakpad_SOURCE_DIR}/src/common/linux/guid_creator.h
${breakpad_SOURCE_DIR}/src/common/linux/guid_creator.cc
${breakpad_SOURCE_DIR}/src/common/linux/linux_libc_support.cc
${breakpad_SOURCE_DIR}/src/common/linux/memory_mapped_file.cc
${breakpad_SOURCE_DIR}/src/common/linux/safe_readlink.cc
${breakpad_SOURCE_DIR}/src/common/linux/breakpad_getcontext.h
${breakpad_SOURCE_DIR}/src/common/linux/breakpad_getcontext.S
)
#set_property(SOURCE ${breakpad_SOURCE_DIR}/src/common/linux/breakpad_getcontext.S APPEND PROPERTY COMPILE_OPTIONS "-x" "assembler-with-cpp")
add_library(breakpad_client STATIC)
target_sources(breakpad_client
PUBLIC
${breakpad_SOURCE_DIR}/src/client/linux/handler/exception_handler.h
${breakpad_SOURCE_DIR}/src/client/linux/handler/minidump_descriptor.h
${breakpad_SOURCE_DIR}/src/client/linux/crash_generation/crash_generation_client.h
${breakpad_SOURCE_DIR}/src/client/linux/log/log.h
${breakpad_SOURCE_DIR}/src/client/linux/microdump_writer/microdump_writer.h
${breakpad_SOURCE_DIR}/src/client/linux/minidump_writer/minidump_writer.h
${breakpad_SOURCE_DIR}/src/client/linux/minidump_writer/pe_file.h
${breakpad_SOURCE_DIR}/src/client/linux/minidump_writer/pe_structs.h
${breakpad_SOURCE_DIR}/src/client/linux/minidump_writer/proc_cpuinfo_reader.h
PRIVATE
${breakpad_SOURCE_DIR}/src/client/linux/handler/exception_handler.cc
${breakpad_SOURCE_DIR}/src/client/linux/handler/minidump_descriptor.cc
${breakpad_SOURCE_DIR}/src/client/linux/crash_generation/crash_generation_client.cc
${breakpad_SOURCE_DIR}/src/client/linux/crash_generation/crash_generation_server.cc
${breakpad_SOURCE_DIR}/src/client/linux/log/log.cc
${breakpad_SOURCE_DIR}/src/client/linux/microdump_writer/microdump_writer.cc
${breakpad_SOURCE_DIR}/src/client/linux/dump_writer_common/thread_info.cc
${breakpad_SOURCE_DIR}/src/client/linux/dump_writer_common/ucontext_reader.cc
${breakpad_SOURCE_DIR}/src/client/linux/minidump_writer/minidump_writer.cc
${breakpad_SOURCE_DIR}/src/client/linux/minidump_writer/linux_core_dumper.cc
${breakpad_SOURCE_DIR}/src/client/linux/minidump_writer/linux_dumper.cc
${breakpad_SOURCE_DIR}/src/client/linux/minidump_writer/linux_ptrace_dumper.cc
${breakpad_SOURCE_DIR}/src/client/linux/minidump_writer/pe_file.cc
)
endif()
if (TARGET lss)
target_link_libraries(breakpad_common PUBLIC lss)
target_link_libraries(breakpad_client PUBLIC lss)
endif()
if (TARGET breakpad_client)
if (NOT WIN32)
target_sources(breakpad_client PUBLIC
${breakpad_SOURCE_DIR}/src/client/minidump_file_writer-inl.h
${breakpad_SOURCE_DIR}/src/client/minidump_file_writer.h
${breakpad_SOURCE_DIR}/src/client/minidump_file_writer.cc
)
endif()
target_link_libraries(breakpad_client PUBLIC breakpad_common)
target_include_directories(breakpad_client PUBLIC "${breakpad_SOURCE_DIR}/src")
target_compile_features(breakpad_client PUBLIC cxx_std_11)
endif()
endif()
#FetchContent_MakeAvailable(breakpad)
endif()

View File

@ -8,7 +8,6 @@ on:
# "scheduled" workflow, while maintaining ability to perform local CI builds. # "scheduled" workflow, while maintaining ability to perform local CI builds.
workflows: workflows:
- scheduled - scheduled
- manual
branches: branches:
- master - master
- docking - docking
@ -124,11 +123,6 @@ jobs:
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj /p:Platform=Win32 /p:Configuration=Release' run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj /p:Platform=Win32 /p:Configuration=Release'
if: github.event_name == 'workflow_run' if: github.event_name == 'workflow_run'
- name: Build Win32 example_sdl2_sdlrenderer2
shell: cmd
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj /p:Platform=Win32 /p:Configuration=Release'
if: github.event_name == 'workflow_run'
- name: Build Win32 example_sdl2_vulkan - name: Build Win32 example_sdl2_vulkan
shell: cmd shell: cmd
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj /p:Platform=Win32 /p:Configuration=Release' run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj /p:Platform=Win32 /p:Configuration=Release'
@ -174,11 +168,6 @@ jobs:
shell: cmd shell: cmd
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release' run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release'
- name: Build x64 example_sdl2_sdlrenderer2
shell: cmd
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj /p:Platform=x64 /p:Configuration=Release'
if: github.event_name == 'workflow_run'
- name: Build x64 example_sdl2_vulkan - name: Build x64 example_sdl2_vulkan
shell: cmd shell: cmd
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release' run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release'
@ -218,7 +207,7 @@ jobs:
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx12/example_win32_directx12.vcxproj /p:Platform=x64 /p:Configuration=Release' run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx12/example_win32_directx12.vcxproj /p:Platform=x64 /p:Configuration=Release'
Linux: Linux:
runs-on: ubuntu-24.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -324,18 +313,6 @@ jobs:
EOF EOF
g++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp g++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp
- name: Build example_null (with C++20)
run: |
cat > example_single_file.cpp <<'EOF'
#define IMGUI_DISABLE_OBSOLETE_KEYIO
#define IMGUI_IMPLEMENTATION
#include "misc/single_file/imgui_single_file.h"
#include "examples/example_null/main.cpp"
EOF
g++ -I. -std=c++20 -Wall -Wformat -o example_single_file example_single_file.cpp
- name: Build example_null (with IMGUI_DISABLE_DEMO_WINDOWS and IMGUI_DISABLE_DEBUG_TOOLS) - name: Build example_null (with IMGUI_DISABLE_DEMO_WINDOWS and IMGUI_DISABLE_DEBUG_TOOLS)
run: | run: |
cat > example_single_file.cpp <<'EOF' cat > example_single_file.cpp <<'EOF'
@ -393,18 +370,6 @@ jobs:
EOF EOF
g++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp g++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp
- name: Build example_null (C++26, Clang)
run: |
cat > example_single_file.cpp <<'EOF'
#define IMGUI_IMPLEMENTATION
#define IMGUI_DISABLE_DEMO_WINDOWS
#include "misc/single_file/imgui_single_file.h"
#include "examples/example_null/main.cpp"
EOF
clang++ -I. -std=c++26 -Wall -Wformat -fno-exceptions -fno-threadsafe-statics -lc -lm -o example_single_file example_single_file.cpp
- name: Build example_null (without c++ runtime, Clang) - name: Build example_null (without c++ runtime, Clang)
run: | run: |
cat > example_single_file.cpp <<'EOF' cat > example_single_file.cpp <<'EOF'
@ -457,17 +422,6 @@ jobs:
EOF EOF
clang++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp clang++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp
- name: Build example_null (single file build, c++20)
run: |
cat > example_single_file.cpp <<'EOF'
#define IMGUI_IMPLEMENTATION
#include "misc/single_file/imgui_single_file.h"
#include "examples/example_null/main.cpp"
EOF
clang++ -I. -std=c++20 -Wall -Wformat -o example_single_file example_single_file.cpp
- name: Build example_null (without c++ runtime) - name: Build example_null (without c++ runtime)
run: | run: |
cat > example_single_file.cpp <<'EOF' cat > example_single_file.cpp <<'EOF'
@ -516,7 +470,7 @@ jobs:
xcodebuild -project examples/example_apple_metal/example_apple_metal.xcodeproj -target example_apple_metal_ios CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO xcodebuild -project examples/example_apple_metal/example_apple_metal.xcodeproj -target example_apple_metal_ios CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
Emscripten: Emscripten:
runs-on: ubuntu-24.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -527,7 +481,6 @@ jobs:
emsdk-master/emsdk update emsdk-master/emsdk update
emsdk-master/emsdk install latest emsdk-master/emsdk install latest
emsdk-master/emsdk activate latest emsdk-master/emsdk activate latest
sudo apt-get install build-essential
- name: Build example_sdl2_opengl3 with Emscripten - name: Build example_sdl2_opengl3 with Emscripten
run: | run: |
@ -536,28 +489,15 @@ jobs:
popd popd
make -C examples/example_sdl2_opengl3 -f Makefile.emscripten make -C examples/example_sdl2_opengl3 -f Makefile.emscripten
# This build compiles example_glfw_wgpu using Makefile.emscripten and Emscripten GLFW built-in implementation (-sUSE_GLFW=3) - name: Build example_glfw_wgpu
# This ensures 2 things: the make build works, and the GLFW built-in implementation is tested
- name: Build example_glfw_wgpu with Emscripten/Makefile
run: | run: |
pushd emsdk-master pushd emsdk-master
source ./emsdk_env.sh source ./emsdk_env.sh
popd popd
make -C examples/example_glfw_wgpu -f Makefile.emscripten make -C examples/example_glfw_wgpu -f Makefile.emscripten
# This build compiles example_glfw_wgpu using CMakeLists.txt and Emscripten GLFW contrib port (--use-port=contrib.glfw3)
# This ensures 2 things: the CMake build works, and the GLFW contrib port is tested
- name: Build example_glfw_wgpu with Emscripten/CMake
run: |
pushd emsdk-master
source ./emsdk_env.sh
popd
emcc -v
emcmake cmake -B build -DCMAKE_BUILD_TYPE=Release examples/example_glfw_wgpu
cmake --build build
Android: Android:
runs-on: ubuntu-24.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View File

@ -1,12 +0,0 @@
#
# This is a dummy workflow used to trigger full builds manually.
#
name: manual
on: workflow_dispatch
jobs:
manual:
runs-on: ubuntu-latest
steps:
- run: exit 0

View File

@ -42,5 +42,5 @@ jobs:
fi fi
cd examples/example_null cd examples/example_null
pvs-studio-analyzer trace -- make WITH_EXTRA_WARNINGS=1 pvs-studio-analyzer trace -- make WITH_EXTRA_WARNINGS=1
pvs-studio-analyzer analyze --disableLicenseExpirationCheck -e ../../imstb_rectpack.h -e ../../imstb_textedit.h -e ../../imstb_truetype.h -l ../../pvs-studio.lic -o pvs-studio.log pvs-studio-analyzer analyze -e ../../imstb_rectpack.h -e ../../imstb_textedit.h -e ../../imstb_truetype.h -l ../../pvs-studio.lic -o pvs-studio.log
plog-converter -a 'GA:1,2;OP:1' -d V1071 -t errorfile -w pvs-studio.log plog-converter -a 'GA:1,2;OP:1' -d V1071 -t errorfile -w pvs-studio.log

View File

@ -20,9 +20,6 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn
// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn
// 2022-11-30: Renderer: Restoring using al_draw_indexed_prim() when Allegro version is >= 5.2.5. // 2022-11-30: Renderer: Restoring using al_draw_indexed_prim() when Allegro version is >= 5.2.5.
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported). // 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
@ -294,7 +291,7 @@ void ImGui_ImplAllegro5_InvalidateDeviceObjects()
} }
#if ALLEGRO_HAS_CLIPBOARD #if ALLEGRO_HAS_CLIPBOARD
static const char* ImGui_ImplAllegro5_GetClipboardText(ImGuiContext*) static const char* ImGui_ImplAllegro5_GetClipboardText(void*)
{ {
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
if (bd->ClipboardTextData) if (bd->ClipboardTextData)
@ -303,15 +300,14 @@ static const char* ImGui_ImplAllegro5_GetClipboardText(ImGuiContext*)
return bd->ClipboardTextData; return bd->ClipboardTextData;
} }
static void ImGui_ImplAllegro5_SetClipboardText(ImGuiContext*, const char* text) static void ImGui_ImplAllegro5_SetClipboardText(void*, const char* text)
{ {
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData(); ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
al_set_clipboard_text(bd->Display, text); al_set_clipboard_text(bd->Display, text);
} }
#endif #endif
// Not static to allow third-party code to use that if they want to (but undocumented) static ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code)
ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code)
{ {
switch (key_code) switch (key_code)
{ {
@ -451,9 +447,9 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display)
bd->VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro)); bd->VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro));
#if ALLEGRO_HAS_CLIPBOARD #if ALLEGRO_HAS_CLIPBOARD
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); io.SetClipboardTextFn = ImGui_ImplAllegro5_SetClipboardText;
platform_io.Platform_SetClipboardTextFn = ImGui_ImplAllegro5_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplAllegro5_GetClipboardText;
platform_io.Platform_GetClipboardTextFn = ImGui_ImplAllegro5_GetClipboardText; io.ClipboardUserData = nullptr;
#endif #endif
return true; return true;

View File

@ -18,18 +18,8 @@
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). // - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp // - Introduction, links and more at the top of imgui.cpp
// About Emscripten support:
// - Emscripten provides its own GLFW (3.2.1) implementation (syntax: "-sUSE_GLFW=3"), but Joystick is broken and several features are not supported (multiple windows, clipboard, timer, etc.)
// - A third-party Emscripten GLFW (3.4.0) implementation (syntax: "--use-port=contrib.glfw3") fixes the Joystick issue and implements all relevant features for the browser.
// See https://github.com/pongasoft/emscripten-glfw/blob/master/docs/Comparison.md for details.
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn
// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn
// - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn
// 2024-07-31: Added ImGui_ImplGlfw_Sleep() helper function for usage by our examples app, since GLFW doesn't provide one.
// 2024-07-08: *BREAKING* Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWWindow* parameter. // 2024-07-08: *BREAKING* Renamed ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback to ImGui_ImplGlfw_InstallEmscriptenCallbacks(), added GLFWWindow* parameter.
// 2024-07-08: Emscripten: Added support for GLFW3 contrib port (GLFW 3.4.0 features + bug fixes): to enable, replace -sUSE_GLFW=3 with --use-port=contrib.glfw3 (requires emscripten 3.1.59+) (https://github.com/pongasoft/emscripten-glfw) // 2024-07-08: Emscripten: Added support for GLFW3 contrib port (GLFW 3.4.0 features + bug fixes): to enable, replace -sUSE_GLFW=3 with --use-port=contrib.glfw3 (requires emscripten 3.1.59+) (https://github.com/pongasoft/emscripten-glfw)
// 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. // 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions.
@ -109,9 +99,6 @@
#endif #endif
#include <GLFW/glfw3native.h> // for glfwGetCocoaWindow() #include <GLFW/glfw3native.h> // for glfwGetCocoaWindow()
#endif #endif
#ifndef _WIN32
#include <unistd.h> // for usleep()
#endif
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
#include <emscripten.h> #include <emscripten.h>
@ -185,12 +172,19 @@ static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData()
} }
// Functions // Functions
static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data)
// Not static to allow third-party code to use that if they want to (but undocumented)
ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode)
{ {
IM_UNUSED(scancode); return glfwGetClipboardString((GLFWwindow*)user_data);
switch (keycode) }
static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text)
{
glfwSetClipboardString((GLFWwindow*)user_data, text);
}
static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key)
{
switch (key)
{ {
case GLFW_KEY_TAB: return ImGuiKey_Tab; case GLFW_KEY_TAB: return ImGuiKey_Tab;
case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow; case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow;
@ -358,7 +352,6 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo
io.AddMouseWheelEvent((float)xoffset, (float)yoffset); io.AddMouseWheelEvent((float)xoffset, (float)yoffset);
} }
// FIXME: should this be baked into ImGui_ImplGlfw_KeyToImGuiKey()? then what about the values passed to io.SetKeyEventNativeData()?
static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode)
{ {
#if GLFW_HAS_GETKEYNAME && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) #if GLFW_HAS_GETKEYNAME && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3)
@ -406,7 +399,7 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, i
keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode);
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode, scancode); ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode);
io.AddKeyEvent(imgui_key, (action == GLFW_PRESS)); io.AddKeyEvent(imgui_key, (action == GLFW_PRESS));
io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code) io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code)
} }
@ -569,11 +562,7 @@ void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows)
} }
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817 EM_JS(void, ImGui_ImplGlfw_EmscriptenOpenURL, (char const* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); });
void ImGui_ImplGlfw_EmscriptenOpenURL(const char* url) { if (url) emscripten::glfw3::OpenURL(url); }
#else
EM_JS(void, ImGui_ImplGlfw_EmscriptenOpenURL, (const char* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); });
#endif
#endif #endif
static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api)
@ -593,11 +582,11 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
bd->Window = window; bd->Window = window;
bd->Time = 0.0; bd->Time = 0.0;
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(NULL, text); }; io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) { return glfwGetClipboardString(NULL); }; io.ClipboardUserData = bd->Window;
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; }; io.PlatformOpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplGlfw_EmscriptenOpenURL(url); return true; };
#endif #endif
// Create mouse cursors // Create mouse cursors
@ -648,23 +637,6 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw
::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc); ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc);
#endif #endif
// Emscripten: the same application can run on various platforms, so we detect the Apple platform at runtime
// to override io.ConfigMacOSXBehaviors from its default (which is always false in Emscripten).
#ifdef __EMSCRIPTEN__
#if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817
if (emscripten::glfw3::IsRuntimePlatformApple())
{
ImGui::GetIO().ConfigMacOSXBehaviors = true;
// Due to how the browser (poorly) handles the Meta Key, this line essentially disables repeats when used.
// This means that Meta + V only registers a single key-press, even if the keys are held.
// This is a compromise for dealing with this issue in ImGui since ImGui implements key repeat itself.
// See https://github.com/pongasoft/emscripten-glfw/blob/v3.4.0.20240817/docs/Usage.md#the-problem-of-the-super-key
emscripten::glfw3::SetSuperPlusKeyTimeouts(10, 10);
}
#endif
#endif
bd->ClientApi = client_api; bd->ClientApi = client_api;
return true; return true;
} }
@ -853,16 +825,6 @@ void ImGui_ImplGlfw_NewFrame()
ImGui_ImplGlfw_UpdateGamepads(); ImGui_ImplGlfw_UpdateGamepads();
} }
// GLFW doesn't provide a portable sleep function
void ImGui_ImplGlfw_Sleep(int milliseconds)
{
#ifdef _WIN32
::Sleep(milliseconds);
#else
usleep(milliseconds * 1000);
#endif
}
#ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3
static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data) static EM_BOOL ImGui_ImplGlfw_OnCanvasSizeChange(int event_type, const EmscriptenUiEvent* event, void* user_data)
{ {

View File

@ -57,7 +57,4 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key,
IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c);
IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event); IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event);
// GLFW helpers
IMGUI_IMPL_API void ImGui_ImplGlfw_Sleep(int milliseconds);
#endif // #ifndef IMGUI_DISABLE #endif // #ifndef IMGUI_DISABLE

View File

@ -29,10 +29,6 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn
// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn
// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn
// 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library. // 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library.
// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F20 function keys. Stopped mapping F13 into PrintScreen. // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F20 function keys. Stopped mapping F13 into PrintScreen.
// 2023-04-09: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_Pen. // 2023-04-09: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_Pen.
@ -259,9 +255,7 @@ static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view);
@end @end
// Functions // Functions
static ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code)
// Not static to allow third-party code to use that if they want to (but undocumented)
ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code)
{ {
switch (key_code) switch (key_code)
{ {
@ -399,7 +393,6 @@ IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(void* _Nullable view) {
bool ImGui_ImplOSX_Init(NSView* view) bool ImGui_ImplOSX_Init(NSView* view)
{ {
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
IMGUI_CHECKVERSION(); IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
@ -430,14 +423,14 @@ bool ImGui_ImplOSX_Init(NSView* view)
// Note that imgui.cpp also include default OSX clipboard handlers which can be enabled // Note that imgui.cpp also include default OSX clipboard handlers which can be enabled
// by adding '#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS' in imconfig.h and adding '-framework ApplicationServices' to your linker command-line. // by adding '#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS' in imconfig.h and adding '-framework ApplicationServices' to your linker command-line.
// Since we are already in ObjC land here, it is easy for us to add a clipboard handler using the NSPasteboard api. // Since we are already in ObjC land here, it is easy for us to add a clipboard handler using the NSPasteboard api.
platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* str) -> void io.SetClipboardTextFn = [](void*, const char* str) -> void
{ {
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
[pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil]; [pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil];
[pasteboard setString:[NSString stringWithUTF8String:str] forType:NSPasteboardTypeString]; [pasteboard setString:[NSString stringWithUTF8String:str] forType:NSPasteboardTypeString];
}; };
platform_io.Platform_GetClipboardTextFn = [](ImGuiContext*) -> const char* io.GetClipboardTextFn = [](void*) -> const char*
{ {
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
NSString* available = [pasteboard availableTypeFromArray: [NSArray arrayWithObject:NSPasteboardTypeString]]; NSString* available = [pasteboard availableTypeFromArray: [NSArray arrayWithObject:NSPasteboardTypeString]];
@ -472,7 +465,7 @@ bool ImGui_ImplOSX_Init(NSView* view)
[view addSubview:bd->KeyEventResponder]; [view addSubview:bd->KeyEventResponder];
ImGui_ImplOSX_AddTrackingArea(view); ImGui_ImplOSX_AddTrackingArea(view);
platform_io.Platform_SetImeDataFn = [](ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) -> void io.PlatformSetImeDataFn = [](ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) -> void
{ {
ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData();
if (data->WantVisible) if (data->WantVisible)

View File

@ -21,14 +21,6 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-09-09: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190)
// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn
// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn
// - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn
// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn
// 2024-08-19: Storing SDL's Uint32 WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*.
// 2024-08-19: ImGui_ImplSDL2_ProcessEvent() now ignores events intended for other SDL windows. (#7853)
// 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions. // 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions.
// 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library. // 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library.
// 2024-02-14: Inputs: Handle gamepad disconnection. Added ImGui_ImplSDL2_SetGamepadMode(). // 2024-02-14: Inputs: Handle gamepad disconnection. Added ImGui_ImplSDL2_SetGamepadMode().
@ -113,15 +105,11 @@
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0 #define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0
#endif #endif
#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6)
#if SDL_HAS_VULKAN
extern "C" { extern DECLSPEC void SDLCALL SDL_Vulkan_GetDrawableSize(SDL_Window* window, int* w, int* h); }
#endif
// SDL Data // SDL Data
struct ImGui_ImplSDL2_Data struct ImGui_ImplSDL2_Data
{ {
SDL_Window* Window; SDL_Window* Window;
Uint32 WindowID;
SDL_Renderer* Renderer; SDL_Renderer* Renderer;
Uint64 Time; Uint64 Time;
char* ClipboardTextData; char* ClipboardTextData;
@ -152,7 +140,7 @@ static ImGui_ImplSDL2_Data* ImGui_ImplSDL2_GetBackendData()
} }
// Functions // Functions
static const char* ImGui_ImplSDL2_GetClipboardText(ImGuiContext*) static const char* ImGui_ImplSDL2_GetClipboardText(void*)
{ {
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
if (bd->ClipboardTextData) if (bd->ClipboardTextData)
@ -161,7 +149,7 @@ static const char* ImGui_ImplSDL2_GetClipboardText(ImGuiContext*)
return bd->ClipboardTextData; return bd->ClipboardTextData;
} }
static void ImGui_ImplSDL2_SetClipboardText(ImGuiContext*, const char* text) static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text)
{ {
SDL_SetClipboardText(text); SDL_SetClipboardText(text);
} }
@ -180,8 +168,7 @@ static void ImGui_ImplSDL2_PlatformSetImeData(ImGuiContext*, ImGuiViewport*, ImG
} }
} }
// Not static to allow third-party code to use that if they want to (but undocumented) static ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
{ {
IM_UNUSED(scancode); IM_UNUSED(scancode);
switch (keycode) switch (keycode)
@ -319,12 +306,6 @@ static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & KMOD_GUI) != 0); io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & KMOD_GUI) != 0);
} }
static ImGuiViewport* ImGui_ImplSDL2_GetViewportForWindowID(Uint32 window_id)
{
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : NULL;
}
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
@ -340,8 +321,6 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
{ {
case SDL_MOUSEMOTION: case SDL_MOUSEMOTION:
{ {
if (ImGui_ImplSDL2_GetViewportForWindowID(event->motion.windowID) == NULL)
return false;
ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
io.AddMousePosEvent(mouse_pos.x, mouse_pos.y); io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
@ -349,8 +328,6 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
} }
case SDL_MOUSEWHEEL: case SDL_MOUSEWHEEL:
{ {
if (ImGui_ImplSDL2_GetViewportForWindowID(event->wheel.windowID) == NULL)
return false;
//IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
#if SDL_VERSION_ATLEAST(2,0,18) // If this fails to compile on Emscripten: update to latest Emscripten! #if SDL_VERSION_ATLEAST(2,0,18) // If this fails to compile on Emscripten: update to latest Emscripten!
float wheel_x = -event->wheel.preciseX; float wheel_x = -event->wheel.preciseX;
@ -369,8 +346,6 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONUP:
{ {
if (ImGui_ImplSDL2_GetViewportForWindowID(event->button.windowID) == NULL)
return false;
int mouse_button = -1; int mouse_button = -1;
if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; } if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }
@ -386,16 +361,12 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
} }
case SDL_TEXTINPUT: case SDL_TEXTINPUT:
{ {
if (ImGui_ImplSDL2_GetViewportForWindowID(event->text.windowID) == NULL)
return false;
io.AddInputCharactersUTF8(event->text.text); io.AddInputCharactersUTF8(event->text.text);
return true; return true;
} }
case SDL_KEYDOWN: case SDL_KEYDOWN:
case SDL_KEYUP: case SDL_KEYUP:
{ {
if (ImGui_ImplSDL2_GetViewportForWindowID(event->key.windowID) == NULL)
return false;
ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod); ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod);
ImGuiKey key = ImGui_ImplSDL2_KeyEventToImGuiKey(event->key.keysym.sym, event->key.keysym.scancode); ImGuiKey key = ImGui_ImplSDL2_KeyEventToImGuiKey(event->key.keysym.sym, event->key.keysym.scancode);
io.AddKeyEvent(key, (event->type == SDL_KEYDOWN)); io.AddKeyEvent(key, (event->type == SDL_KEYDOWN));
@ -404,8 +375,6 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
} }
case SDL_WINDOWEVENT: case SDL_WINDOWEVENT:
{ {
if (ImGui_ImplSDL2_GetViewportForWindowID(event->window.windowID) == NULL)
return false;
// - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right. // - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right.
// - However we won't get a correct LEAVE event for a captured window. // - However we won't get a correct LEAVE event for a captured window.
// - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late, // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late,
@ -464,17 +433,15 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
bd->Window = window; bd->Window = window;
bd->WindowID = SDL_GetWindowID(window);
bd->Renderer = renderer; bd->Renderer = renderer;
bd->MouseCanUseGlobalState = mouse_can_use_global_state; bd->MouseCanUseGlobalState = mouse_can_use_global_state;
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText; io.ClipboardUserData = nullptr;
platform_io.Platform_ClipboardUserData = nullptr; io.PlatformSetImeDataFn = ImGui_ImplSDL2_PlatformSetImeData;
platform_io.Platform_SetImeDataFn = ImGui_ImplSDL2_PlatformSetImeData;
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); return true; }; io.PlatformOpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); return true; };
#endif #endif
// Gamepad handling // Gamepad handling
@ -495,7 +462,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void
// Set platform dependent data in viewport // Set platform dependent data in viewport
// Our mouse update function expect PlatformHandle to be filled for the main viewport // Our mouse update function expect PlatformHandle to be filled for the main viewport
ImGuiViewport* main_viewport = ImGui::GetMainViewport(); ImGuiViewport* main_viewport = ImGui::GetMainViewport();
main_viewport->PlatformHandle = (void*)(intptr_t)bd->WindowID; main_viewport->PlatformHandle = (void*)window;
main_viewport->PlatformHandleRaw = nullptr; main_viewport->PlatformHandleRaw = nullptr;
SDL_SysWMinfo info; SDL_SysWMinfo info;
SDL_VERSION(&info.version); SDL_VERSION(&info.version);
@ -765,10 +732,6 @@ void ImGui_ImplSDL2_NewFrame()
w = h = 0; w = h = 0;
if (bd->Renderer != nullptr) if (bd->Renderer != nullptr)
SDL_GetRendererOutputSize(bd->Renderer, &display_w, &display_h); SDL_GetRendererOutputSize(bd->Renderer, &display_w, &display_h);
#if SDL_HAS_VULKAN
else if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_VULKAN)
SDL_Vulkan_GetDrawableSize(bd->Window, &display_w, &display_h);
#endif
else else
SDL_GL_GetDrawableSize(bd->Window, &display_w, &display_h); SDL_GL_GetDrawableSize(bd->Window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h); io.DisplaySize = ImVec2((float)w, (float)h);

View File

@ -2,7 +2,7 @@
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) // (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) // (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN**)
// Implemented features: // Implemented features:
// [X] Platform: Clipboard support. // [X] Platform: Clipboard support.
@ -21,13 +21,6 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807)
// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn
// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn
// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn
// 2024-08-19: Storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*.
// 2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853)
// 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807) // 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807)
// 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801) // 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801)
// 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794) // 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794)
@ -87,7 +80,6 @@
struct ImGui_ImplSDL3_Data struct ImGui_ImplSDL3_Data
{ {
SDL_Window* Window; SDL_Window* Window;
SDL_WindowID WindowID;
SDL_Renderer* Renderer; SDL_Renderer* Renderer;
Uint64 Time; Uint64 Time;
char* ClipboardTextData; char* ClipboardTextData;
@ -121,7 +113,7 @@ static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData()
} }
// Functions // Functions
static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*) static const char* ImGui_ImplSDL3_GetClipboardText(void*)
{ {
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
if (bd->ClipboardTextData) if (bd->ClipboardTextData)
@ -131,7 +123,7 @@ static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*)
return bd->ClipboardTextData; return bd->ClipboardTextData;
} }
static void ImGui_ImplSDL3_SetClipboardText(ImGuiContext*, const char* text) static void ImGui_ImplSDL3_SetClipboardText(void*, const char* text)
{ {
SDL_SetClipboardText(text); SDL_SetClipboardText(text);
} }
@ -139,8 +131,7 @@ static void ImGui_ImplSDL3_SetClipboardText(ImGuiContext*, const char* text)
static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data)
{ {
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle; SDL_Window* window = (SDL_Window*)viewport->PlatformHandle;
SDL_Window* window = SDL_GetWindowFromID(window_id);
if ((data->WantVisible == false || bd->ImeWindow != window) && bd->ImeWindow != NULL) if ((data->WantVisible == false || bd->ImeWindow != window) && bd->ImeWindow != NULL)
{ {
SDL_StopTextInput(bd->ImeWindow); SDL_StopTextInput(bd->ImeWindow);
@ -159,8 +150,7 @@ static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* view
} }
} }
// Not static to allow third-party code to use that if they want to (but undocumented) static ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
{ {
// Keypad doesn't have individual key values in SDL3 // Keypad doesn't have individual key values in SDL3
switch (scancode) switch (scancode)
@ -302,13 +292,6 @@ static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0); io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0);
} }
static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id)
{
ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : NULL;
}
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
@ -324,8 +307,6 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
{ {
case SDL_EVENT_MOUSE_MOTION: case SDL_EVENT_MOUSE_MOTION:
{ {
if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == NULL)
return false;
ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
io.AddMousePosEvent(mouse_pos.x, mouse_pos.y); io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
@ -333,8 +314,6 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
} }
case SDL_EVENT_MOUSE_WHEEL: case SDL_EVENT_MOUSE_WHEEL:
{ {
if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == NULL)
return false;
//IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY); //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
float wheel_x = -event->wheel.x; float wheel_x = -event->wheel.x;
float wheel_y = event->wheel.y; float wheel_y = event->wheel.y;
@ -348,8 +327,6 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP: case SDL_EVENT_MOUSE_BUTTON_UP:
{ {
if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == NULL)
return false;
int mouse_button = -1; int mouse_button = -1;
if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; } if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }
@ -365,16 +342,12 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
} }
case SDL_EVENT_TEXT_INPUT: case SDL_EVENT_TEXT_INPUT:
{ {
if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == NULL)
return false;
io.AddInputCharactersUTF8(event->text.text); io.AddInputCharactersUTF8(event->text.text);
return true; return true;
} }
case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP: case SDL_EVENT_KEY_UP:
{ {
if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == NULL)
return false;
//IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode, event->key.mod); //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%d: key=%d, scancode=%d, mod=%X\n", (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP", event->key.key, event->key.scancode, event->key.mod);
ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod); ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod);
ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode); ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode);
@ -384,8 +357,6 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
} }
case SDL_EVENT_WINDOW_MOUSE_ENTER: case SDL_EVENT_WINDOW_MOUSE_ENTER:
{ {
if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL)
return false;
bd->MouseWindowID = event->window.windowID; bd->MouseWindowID = event->window.windowID;
bd->MousePendingLeaveFrame = 0; bd->MousePendingLeaveFrame = 0;
return true; return true;
@ -396,19 +367,15 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
// FIXME: Unconfirmed whether this is still needed with SDL3. // FIXME: Unconfirmed whether this is still needed with SDL3.
case SDL_EVENT_WINDOW_MOUSE_LEAVE: case SDL_EVENT_WINDOW_MOUSE_LEAVE:
{ {
if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL)
return false;
bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1; bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1;
return true; return true;
} }
case SDL_EVENT_WINDOW_FOCUS_GAINED: case SDL_EVENT_WINDOW_FOCUS_GAINED:
case SDL_EVENT_WINDOW_FOCUS_LOST: io.AddFocusEvent(true);
{ return true;
if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == NULL) case SDL_EVENT_WINDOW_FOCUS_LOST:
return false; io.AddFocusEvent(false);
io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED);
return true; return true;
}
case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_ADDED:
case SDL_EVENT_GAMEPAD_REMOVED: case SDL_EVENT_GAMEPAD_REMOVED:
{ {
@ -421,7 +388,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window) static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window)
{ {
viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(window); viewport->PlatformHandle = window;
viewport->PlatformHandleRaw = nullptr; viewport->PlatformHandleRaw = nullptr;
#if defined(_WIN32) && !defined(__WINRT__) #if defined(_WIN32) && !defined(__WINRT__)
viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
@ -456,14 +423,13 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
bd->Window = window; bd->Window = window;
bd->WindowID = SDL_GetWindowID(window);
bd->Renderer = renderer; bd->Renderer = renderer;
bd->MouseCanUseGlobalState = mouse_can_use_global_state; bd->MouseCanUseGlobalState = mouse_can_use_global_state;
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); io.SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText;
platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText;
platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText; io.ClipboardUserData = nullptr;
platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData; io.PlatformSetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData;
// Gamepad handling // Gamepad handling
bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst; bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst;
@ -564,7 +530,7 @@ static void ImGui_ImplSDL3_UpdateMouseData()
// We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below) // We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below)
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside
SDL_CaptureMouse(bd->MouseButtonsDown != 0); SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE);
SDL_Window* focused_window = SDL_GetKeyboardFocus(); SDL_Window* focused_window = SDL_GetKeyboardFocus();
const bool is_app_focused = (bd->Window == focused_window); const bool is_app_focused = (bd->Window == focused_window);
#else #else
@ -674,7 +640,7 @@ static void ImGui_ImplSDL3_UpdateGamepads()
{ {
ImGui_ImplSDL3_CloseGamepads(); ImGui_ImplSDL3_CloseGamepads();
int sdl_gamepads_count = 0; int sdl_gamepads_count = 0;
SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count); const SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count);
for (int n = 0; n < sdl_gamepads_count; n++) for (int n = 0; n < sdl_gamepads_count; n++)
if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n])) if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n]))
{ {
@ -683,7 +649,6 @@ static void ImGui_ImplSDL3_UpdateGamepads()
break; break;
} }
bd->WantUpdateGamepadsList = false; bd->WantUpdateGamepadsList = false;
SDL_free(sdl_gamepads);
} }
// FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.

View File

@ -2,7 +2,7 @@
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) // (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) // (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN**)
// Implemented features: // Implemented features:
// [X] Platform: Clipboard support. // [X] Platform: Clipboard support.

View File

@ -1,8 +1,6 @@
// dear imgui: Renderer Backend for SDL_Renderer for SDL3 // dear imgui: Renderer Backend for SDL_Renderer for SDL3
// (Requires: SDL 3.0.0+) // (Requires: SDL 3.0.0+)
// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**)
// Note how SDL_Renderer is an _optional_ component of SDL3. // Note how SDL_Renderer is an _optional_ component of SDL3.
// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. // For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX.
// If your application will want to render any non trivial amount of graphics other than UI, // If your application will want to render any non trivial amount of graphics other than UI,
@ -158,8 +156,8 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer*
SDL_Rect ClipRect; SDL_Rect ClipRect;
}; };
BackupSDLRendererState old = {}; BackupSDLRendererState old = {};
old.ViewportEnabled = SDL_RenderViewportSet(renderer); old.ViewportEnabled = SDL_RenderViewportSet(renderer) == SDL_TRUE;
old.ClipEnabled = SDL_RenderClipEnabled(renderer); old.ClipEnabled = SDL_RenderClipEnabled(renderer) == SDL_TRUE;
SDL_GetRenderViewport(renderer, &old.Viewport); SDL_GetRenderViewport(renderer, &old.Viewport);
SDL_GetRenderClipRect(renderer, &old.ClipRect); SDL_GetRenderClipRect(renderer, &old.ClipRect);

View File

@ -1,8 +1,6 @@
// dear imgui: Renderer Backend for SDL_Renderer for SDL3 // dear imgui: Renderer Backend for SDL_Renderer for SDL3
// (Requires: SDL 3.0.0+) // (Requires: SDL 3.0.0+)
// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**)
// Note how SDL_Renderer is an _optional_ component of SDL3. // Note how SDL_Renderer is an _optional_ component of SDL3.
// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. // For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX.
// If your application will want to render any non trivial amount of graphics other than UI, // If your application will want to render any non trivial amount of graphics other than UI,

View File

@ -16,7 +16,6 @@
// CHANGELOG // CHANGELOG
// (minor and older changes stripped away, please see git history for details) // (minor and older changes stripped away, please see git history for details)
// 2024-09-16: Added support for optional IMGUI_IMPL_WEBGPU_BACKEND_DAWN / IMGUI_IMPL_WEBGPU_BACKEND_WGPU define to handle ever-changing native implementations. (#7977)
// 2024-01-22: Added configurable PipelineMultisampleState struct. (#7240) // 2024-01-22: Added configurable PipelineMultisampleState struct. (#7240)
// 2024-01-22: (Breaking) ImGui_ImplWGPU_Init() now takes a ImGui_ImplWGPU_InitInfo structure instead of variety of parameters, allowing for easier further changes. // 2024-01-22: (Breaking) ImGui_ImplWGPU_Init() now takes a ImGui_ImplWGPU_InitInfo structure instead of variety of parameters, allowing for easier further changes.
// 2024-01-22: Fixed pipeline layout leak. (#7245) // 2024-01-22: Fixed pipeline layout leak. (#7245)
@ -36,18 +35,6 @@
// 2021-02-18: Change blending equation to preserve alpha in output buffer. // 2021-02-18: Change blending equation to preserve alpha in output buffer.
// 2021-01-28: Initial version. // 2021-01-28: Initial version.
// When targeting native platforms (i.e. NOT emscripten), one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN
// or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided. See imgui_impl_wgpu.h for more details.
#ifndef __EMSCRIPTEN__
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) == defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#error exactly one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be defined!
#endif
#else
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#error neither IMGUI_IMPL_WEBGPU_BACKEND_DAWN nor IMGUI_IMPL_WEBGPU_BACKEND_WGPU may be defined if targeting emscripten!
#endif
#endif
#include "imgui.h" #include "imgui.h"
#ifndef IMGUI_DISABLE #ifndef IMGUI_DISABLE
#include "imgui_impl_wgpu.h" #include "imgui_impl_wgpu.h"
@ -257,15 +244,9 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c
{ {
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
WGPUShaderSourceWGSL wgsl_desc = {};
wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
wgsl_desc.code = { wgsl_source, WGPU_STRLEN };
#else
WGPUShaderModuleWGSLDescriptor wgsl_desc = {};
wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor;
wgsl_desc.code = wgsl_source; wgsl_desc.code = wgsl_source;
#endif
WGPUShaderModuleDescriptor desc = {}; WGPUShaderModuleDescriptor desc = {};
desc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&wgsl_desc); desc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&wgsl_desc);
@ -679,11 +660,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
// Create depth-stencil State // Create depth-stencil State
WGPUDepthStencilState depth_stencil_state = {}; WGPUDepthStencilState depth_stencil_state = {};
depth_stencil_state.format = bd->depthStencilFormat; depth_stencil_state.format = bd->depthStencilFormat;
#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN
depth_stencil_state.depthWriteEnabled = WGPUOptionalBool_False;
#else
depth_stencil_state.depthWriteEnabled = false; depth_stencil_state.depthWriteEnabled = false;
#endif
depth_stencil_state.depthCompare = WGPUCompareFunction_Always; depth_stencil_state.depthCompare = WGPUCompareFunction_Always;
depth_stencil_state.stencilFront.compare = WGPUCompareFunction_Always; depth_stencil_state.stencilFront.compare = WGPUCompareFunction_Always;
depth_stencil_state.stencilFront.failOp = WGPUStencilOperation_Keep; depth_stencil_state.stencilFront.failOp = WGPUStencilOperation_Keep;
@ -753,15 +730,7 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info)
// Setup backend capabilities flags // Setup backend capabilities flags
ImGui_ImplWGPU_Data* bd = IM_NEW(ImGui_ImplWGPU_Data)(); ImGui_ImplWGPU_Data* bd = IM_NEW(ImGui_ImplWGPU_Data)();
io.BackendRendererUserData = (void*)bd; io.BackendRendererUserData = (void*)bd;
#if defined(__EMSCRIPTEN__)
io.BackendRendererName = "imgui_impl_webgpu_emscripten";
#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
io.BackendRendererName = "imgui_impl_webgpu_dawn";
#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
io.BackendRendererName = "imgui_impl_webgpu_wgpu";
#else
io.BackendRendererName = "imgui_impl_webgpu"; io.BackendRendererName = "imgui_impl_webgpu";
#endif
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
bd->initInfo = *init_info; bd->initInfo = *init_info;

View File

@ -2,13 +2,6 @@
// This needs to be used along with a Platform Binding (e.g. GLFW) // This needs to be used along with a Platform Binding (e.g. GLFW)
// (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.) // (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.)
// Important note to dawn and/or wgpu users: when targeting native platforms (i.e. NOT emscripten),
// one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided.
// Add #define to your imconfig.h file, or as a compilation flag in your build system.
// This requirement will be removed once WebGPU stabilizes and backends converge on a unified interface.
//#define IMGUI_IMPL_WEBGPU_BACKEND_DAWN
//#define IMGUI_IMPL_WEBGPU_BACKEND_WGPU
// Implemented features: // Implemented features:
// [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.

View File

@ -107,7 +107,7 @@ struct ImGui_ImplWin32_Data
{ {
HWND hWnd; HWND hWnd;
HWND MouseHwnd; HWND MouseHwnd;
int MouseTrackedArea; // 0: not tracked, 1: client area, 2: non-client area int MouseTrackedArea; // 0: not tracked, 1: client are, 2: non-client area
int MouseButtonsDown; int MouseButtonsDown;
INT64 Time; INT64 Time;
INT64 TicksPerSecond; INT64 TicksPerSecond;
@ -419,14 +419,12 @@ void ImGui_ImplWin32_NewFrame()
ImGui_ImplWin32_UpdateGamepads(); ImGui_ImplWin32_UpdateGamepads();
} }
// Map VK_xxx to ImGuiKey_xxx. // There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED, we assign it an arbitrary value to make code more readable (VK_ codes go up to 255)
// Not static to allow third-party code to use that if they want to (but undocumented) #define IM_VK_KEYPAD_ENTER (VK_RETURN + 256)
ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam)
{
// There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED.
if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED))
return ImGuiKey_KeypadEnter;
// Map VK_xxx to ImGuiKey_xxx.
static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam)
{
switch (wParam) switch (wParam)
{ {
case VK_TAB: return ImGuiKey_Tab; case VK_TAB: return ImGuiKey_Tab;
@ -475,6 +473,7 @@ ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam)
case VK_MULTIPLY: return ImGuiKey_KeypadMultiply; case VK_MULTIPLY: return ImGuiKey_KeypadMultiply;
case VK_SUBTRACT: return ImGuiKey_KeypadSubtract; case VK_SUBTRACT: return ImGuiKey_KeypadSubtract;
case VK_ADD: return ImGuiKey_KeypadAdd; case VK_ADD: return ImGuiKey_KeypadAdd;
case IM_VK_KEYPAD_ENTER: return ImGuiKey_KeypadEnter;
case VK_LSHIFT: return ImGuiKey_LeftShift; case VK_LSHIFT: return ImGuiKey_LeftShift;
case VK_LCONTROL: return ImGuiKey_LeftCtrl; case VK_LCONTROL: return ImGuiKey_LeftCtrl;
case VK_LMENU: return ImGuiKey_LeftAlt; case VK_LMENU: return ImGuiKey_LeftAlt;
@ -630,16 +629,6 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
} }
return 0; return 0;
} }
case WM_DESTROY:
if (bd->MouseHwnd == hwnd && bd->MouseTrackedArea != 0)
{
TRACKMOUSEEVENT tme_cancel = { sizeof(tme_cancel), TME_CANCEL, hwnd, 0 };
::TrackMouseEvent(&tme_cancel);
bd->MouseHwnd = nullptr;
bd->MouseTrackedArea = 0;
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
}
return 0;
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
@ -693,9 +682,12 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA
// Submit modifiers // Submit modifiers
ImGui_ImplWin32_UpdateKeyModifiers(); ImGui_ImplWin32_UpdateKeyModifiers();
// Obtain virtual key code and convert to ImGuiKey // Obtain virtual key code
const ImGuiKey key = ImGui_ImplWin32_KeyEventToImGuiKey(wParam, lParam); // (keypad enter doesn't have its own... VK_RETURN with KF_EXTENDED flag means keypad enter, see IM_VK_KEYPAD_ENTER definition for details, it is mapped to ImGuiKey_KeyPadEnter.)
const int vk = (int)wParam; int vk = (int)wParam;
if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED))
vk = IM_VK_KEYPAD_ENTER;
const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk);
const int scancode = (int)LOBYTE(HIWORD(lParam)); const int scancode = (int)LOBYTE(HIWORD(lParam));
// Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event. // Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event.

View File

@ -135,7 +135,7 @@ Generally:
It is unlikely you will add value to your project by creating your own backend. It is unlikely you will add value to your project by creating your own backend.
Also: Also:
The [multi-viewports feature](https://github.com/ocornut/imgui/wiki/Multi-Viewports) of the 'docking' branch allows The [multi-viewports feature](https://github.com/ocornut/imgui/issues/1542) of the 'docking' branch allows
Dear ImGui windows to be seamlessly detached from the main application window. This is achieved using an Dear ImGui windows to be seamlessly detached from the main application window. This is achieved using an
extra layer to the Platform and Renderer backends, which allows Dear ImGui to communicate platform-specific extra layer to the Platform and Renderer backends, which allows Dear ImGui to communicate platform-specific
requests such as: "create an additional OS window", "create a render context", "get the OS position of this requests such as: "create an additional OS window", "create a render context", "get the OS position of this

View File

@ -35,211 +35,6 @@ HOW TO UPDATE?
and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users.
- Please report any issue! - Please report any issue!
-----------------------------------------------------------------------
VERSION 1.91.3 (Released 2024-10-04)
-----------------------------------------------------------------------
Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.3
Breaking changes:
- Drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is still a special
value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)
- Drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange.
It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed).
Although unlikely, it you wish to only clamp on text input but want v_min==v_max==0.0f
to mean unclamped drags, you can use _ClampOnInput instead of _AlwaysClamp. (#7968, #3361, #76)
Other changes:
- Error Handling: Enabled/improved error recovery systems. (#1651, #5654)
- Error recovery is provided as a way to facilitate:
- Recovery after a programming error. Native code or scripting language (the later
tends to facilitate iterating on code while running).
- Recovery after running an exception handler or any error processing which may skip code
after an error has been detected.
- Error recovery is not perfect nor guaranteed! It is a feature to ease development.
You not are not supposed to rely on it in the course of a normal application run.
- Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT().
- By design, we do not allow error recovery to be 100% silent. One of the options needs to be enabled!
- Possible usage: facilitate recovery from errors triggered from a scripting language or
after specific exceptions handlers. Surface errors to programmers in less aggressive ways.
- Always ensure that on programmers seats you have at minimum Asserts or Tooltips enabled
when making direct imgui API calls! Otherwise it would severely hinder your ability to
catch and correct mistakes!
- Added io.ConfigErrorRecovery to enable error recovery support.
- Added io.ConfigErrorRecoveryEnableAssert to assert on recoverable errors.
- Added io.ConfigErrorRecoveryEnableDebugLog to output to debug log on recoverable errors.
- Added io.ConfigErrorRecoveryEnableTooltip to enable displaying an error tooltip on recoverable errors.
The tooltip include a way to enable asserts if they were disabled.
- All options are enabled by default.
- Read https://github.com/ocornut/imgui/wiki/Error-Handling for a bit more details.
- Windows: BeginChild(): made it possible to call SetNextWindowSize() on a child window
using ImGuiChildFlags_ResizeX,ImGuiChildFlags_ResizeY in order to override its current
size. (#1710, #8020)
- Scrollbar: Shift+Click scroll to clicked location (pre-1.90.8 default). (#8002, #7328)
- Scrollbar: added io.ConfigScrollbarScrollByPage setting (default to true). (#8002, #7328)
Set io.ConfigScrollbarScrollByPage=false to enforce always scrolling to clicked location.
- Drags: split ImGuiSliderFlags_AlwaysClamp into two distinct flags: (#7968, #3361, #76)
- ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput + ImGuiSliderFlags_ClampZeroRange.
- Previously _AlwaysClamp only did the equivalent of _ClampOnInput.
- Added ImGuiSliderFlags_ClampOnInput which is now a subset of AlwaysClamp.
(note that it was the old name of AlwaysClamp, but we are reintroducing that name).
- Added ImGuiSliderFlags_ClampZeroRange to enforce clamping even when v_min==v_max==0.0f
in drag functions. Sliders are not affected.
- Tooltips, Drag and Drop: Fixed an issue where the fallback drag and drop payload tooltip
appeared during drag and drop release.
- Tooltips, Drag and Drop: Stabilized name of drag and drop tooltip window so that
transitioning from an item tooltip to a drag tooltip doesn't leak window auto-sizing
info from one to the other. (#8036)
- Tooltips: Tooltips triggered from touch inputs are positioned above the item. (#8036)
- Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal.
- Backends: WebGPU: Fixed DAWN api change using WGPUStringView in WGPUShaderSourceWGSL.
(#8009, #8010) [@blitz-research]
-----------------------------------------------------------------------
VERSION 1.91.2 (Released 2024-09-19)
-----------------------------------------------------------------------
Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.2
Breaking changes:
- Internals: using multiple overlayed ButtonBehavior() with same ID will now have the
io.ConfigDebugHighlightIdConflicts=true feature emit a warning. (#8030)
It was one of the rare case where using same ID is legal. Workarounds:
- use single ButtonBehavior() call with multiple _MouseButton flags
- or surround the calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()
Other changes:
- Added io.ConfigDebugHighlightIdConflicts debug feature! (#7961, #7669)
THIS DETECTS THE MOST COMMON USER ERROR BY FIRST-TIME DEAR IMGUI PROGRAMMERS!
- The tool detects when multiple items are sharing the same identifier, due to not
using PushID/PopID in loops, or not using ID stack facilities such as "##" suffixes.
Very frequently it happens when using empty "" labels.
- When hovering an item with a conflicting ID, all visible items with the same ID will
be highlighted and an explanatory tooltip is made visible.
- The feature may be disabled and is exposed in Demo->Tools menu.
- I've been wanting to add this tool for a long time, but was stalled by finding a way to
not make it spammy + make it practically zero cost. After @pthom made various proposals to
solve the same problem (thanks for pushing me!), I decided it was time to finish it.
- Added ImGuiItemFlags_AllowDuplicateId to use with PushItemFlag()/PopItemFlag() if for some
reason you intend to have duplicate identifiers.
- (#74, #96, #480, #501, #647, #654, #719, #843, #894, #1057, #1173, #1390, #1414, #1556, #1768,
#2041, #2116, #2330, #2475, #2562, #2667, #2807, #2885, #3102, #3375, #3526, #3964, #4008,
#4070, #4158, #4172, #4199, #4375, #4395, #4471, #4548, #4612, #4631, #4657, #4796, #5210,
#5303, #5360, #5393, #5533, #5692, #5707, #5729, #5773, #5787, #5884, #6046, #6093, #6186,
#6223, #6364, #6387, #6567, #6692, #6724, #6939, #6984, #7246, #7270, #7375, #7421, #7434,
#7472, #7581, #7724, #7926, #7937 and probably more..)
- Nav: pressing any keyboard key while holding Alt disable toggling nav layer on Alt release. (#4439)
- MultiSelect+Tables: fixed an issue where box-select would skip items while drag-scrolling
in a table with outer borders. (#7970, #7821).
- Inputs: SetNextItemShortcut() with ImGuiInputFlags_Tooltip doesn't show tooltip when item is active.
- InputText: internal refactoring to simplify and optimize the code. The ImWchar buffer has been
removed. Simplifications allowed to implement new optimizations for handling very large text buffers
(e.g. in our testing, handling of a 1 MB text buffer is now 3 times faster in VS2022 Debug build).
This is the first step toward more refactoring. (#7925) [@alektron, @ocornut]
- InputText: added CJK double-width punctuation to list of separators considered for CTRL+Arrow.
- Tables: fixed auto-width columns when using synced-instances of same table. The previous fix
done in v1.90.5 was incomplete. (#7218)
- Tables: fixed assertion related to inconsistent outer clipping when sizes are not rounded. (#7957) [@eclbtownsend]
- Tables: fixed assertion with tables with borders when clipped by parent. (#6765, #3752, #7428)
- Windows: fixed an issue where double-click to collapse could be triggered even while another
item is active, if the item didn't use the left mouse button. (#7841)
- Misc: Made it accepted to call SetMouseCursor() with any out-of-bound value, as a way to allow
hacking in custom cursors if desirable.
- Fonts: fixed ellipsis "..." rendering width miscalculation bug introduced in 1.91.0. (#7976) [@DDeimos]
- TextLinkOpenURL(): modified tooltip to display a verb "Open 'xxxx'". (#7885, #7660)
- Backends: SDL2: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190) [@scribam]
- Backends: GLFW+Emscripten: use OSX behaviors automatically when using contrib glfw port. (#7965, #7915)
[@ypujante]
- Backends: WebGPU: Added support for optional IMGUI_IMPL_WEBGPU_BACKEND_DAWN / IMGUI_IMPL_WEBGPU_BACKEND_WGPU
defines to handle ever-changing native implementations. (#7977, #7969, #6602, #6188, #7523) [@acgaudette]
-----------------------------------------------------------------------
VERSION 1.91.1 (Released 2024-09-04)
-----------------------------------------------------------------------
Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.1
Breaking changes:
- BeginChild(): renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. [@cfillion]
Kept inline redirection flag (will obsolete).
- IO: moved clipboard functions from ImGuiIO to ImGuiPlatformIO:
- io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn
- io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn
- in function signatures, changed 'void* user_data' to 'ImGuiContext* ctx' for consistency
with other functions. Pull your user data from platform_io.ClipboardUserData if used.
- as this is will affect all users of custom engines/backends, we are providing proper
legacy redirection (will obsolete).
- IO: moved other functions from ImGuiIO to ImGuiPlatformIO:
- io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn (#7660)
- io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn
- io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278)
- access those via GetPlatformIO() instead of GetIO().
(Because PlatformOpenInShellFn and PlatformSetImeDataFn were introduced very recently and
often automatically set by core library and backends, we are exceptionally not maintaining
a legacy redirection symbol for those two.)
- Commented the old ImageButton() signature obsoleted in 1.89 (~August 2022). (#5533, #4471, #2464, #1390)
- old ImageButton() used ImTextureId as item id (created issue with e.g. multiple buttons in same scope, transient texture id values, opaque computation of ID)
- new ImageButton() requires an explicit 'const char* str_id'
- old ImageButton() had frame_padding' override argument.
- new ImageButton() always use style.FramePadding, which you can modify using PushStyleVar()/PopStyleVar().
Other changes:
- IO: Added GetPlatformIO() and ImGuiPlatformIO, pulled from 'docking' branch, which
is a centralized spot to connect os/platform/renderer related functions.
Clipboard, IME and OpenInShell hooks are moved here. (#7660)
- IO, InputText: fixed an issue where typing text in an InputText() would defer character
processing by one frame, because of the trickling input queue. Reworked interleaved
keys<>char trickling to take account for keys known to input characters. (#7889, #4921, #4858)
- Windows: adjust default ClipRect to better match rendering of thick borders (which are in
theory not supported). Compensate for the fact that borders are centered around the windows
edge rather than inner. (#7887, #7888 + #3312, #7540, #3756, #6170, #6365)
- Made BeginItemTooltip() and IsItemHovered() with delay flag infer an implicit ID (for
ID-less items such as Text element) in a way that works when item resizes. (#7945, #1485)
- MultiSelect+TreeNode+Drag and Drop: fixed an issue where carrying a drag and drop payload
over an already open tree node using multi-select would incorrectly select it. (#7850)
- MultiSelect+TreeNode: default open behavior is _OpenOnDoubleClick + _OpenOnArrow when
used in a multi-select context without any ImGuiTreeNode_OpenOnXXX flags set. (#7850)
- Tables: fixes/revert a 1.90 change were outer border would be moved bottom and right
by an extra pixel + rework the change so that contents doesn't overlap the bottom and
right border in a scrolling table. (#6765, #3752, #7428)
- Tables: fixed an issue resizing columns or querying hovered column/row when using multiple
synched instances that are layed out at different X positions. (#7933)
- Tabs: avoid queuing a refocus when tab is already focused, which would have the
side-effect of e.g. closing popup on a mouse release. (#7914)
- InputText: allow callback to update buffer while in read-only mode. (imgui_club/#46)
- InputText: fixed an issue programmatically refocusing a multi-line input which was just active. (#4761, #7870)
- TextLink(), TextLinkOpenURL(): change mouse cursor to Hand shape when hovered. (#7885, #7660)
- Tooltips, Drag and Drop: made it possible to override BeginTooltip() position while inside
a drag and drop source or target: a SetNextWindowPos() call won't be overridden. (#6973)
- PlotHistogram, PlotLines: register item ID and use button behavior in a more idiomatic manner,
fixes preventing e.g. GetItemID() and other ID-based helper to work. (#7935, #3072)
- Style: added PushStyleVarX(), PushStyleVarY() helpers to conveniently modify only
one component of a ImVec2 var.
- Fonts: made it possible to use PushFont()/PopFont() calls across Begin() calls. (#3224, #3875, #6398, #7903)
- Backends:
- Backends: GLFW: added ImGui_ImplGlfw_Sleep() helper function because GLFW does not
provide a way to do a portable sleep. (#7844)
- Backends: GLFW+Emscripten: Use OpenURL() from GLFW3 contrib port when available and using
the contrib port instead of Emscripten own GLFW3 implementation. (#7647, #7915, #7660) [@ypujante]
- Backends: SDL2, SDL3: ignore events of other SDL windows. (#7853) [@madebr, @ocornut]
- Backends: SDL2, SDL3: storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*.
- Backends: SDL3: Update for API changes: SDL_GetGamepads() memory ownership logic was reverted back
by SDL3 on July 27. (#7918, #7898, #7807) [@cheyao, @MattGuerrette]
- Backends: GLFW: passing null window to glfwGetClipboardString()/glfwSetClipboardString()
since GLFW own tests are doing that and it seems unnecessary.
- Backends: SDL2, SDL3, GLFW, OSX, Allegro: update to set function handlers in ImGuiPlatformIO
instead of ImGuiIO.
- Examples:
- Examples: GLFW (all), SDL2 (all), SDL3 (all), Win32+OpenGL3: rework examples main loop
to handle minimization without burning CPU or GPU by running unthrottled code. (#7844)
- Examples: SDL3: Update for API changes: SDL_Init() returns 0 on failure.
----------------------------------------------------------------------- -----------------------------------------------------------------------
VERSION 1.91.0 (Released 2024-07-30) VERSION 1.91.0 (Released 2024-07-30)
----------------------------------------------------------------------- -----------------------------------------------------------------------
@ -282,7 +77,6 @@ Other changes:
- Added TextLink(), TextLinkOpenURL() hyperlink widgets. (#7660) - Added TextLink(), TextLinkOpenURL() hyperlink widgets. (#7660)
- IO: added io.PlatformOpenInShellFn handler to open a link/folder/file in OS shell. (#7660) - IO: added io.PlatformOpenInShellFn handler to open a link/folder/file in OS shell. (#7660)
(*EDIT* From next version 1.91.1 we moved this to platform_io.Platform_OpenInShellFn *EDIT**)
Added IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS to disable default Windows/Linux/Mac implementations. Added IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS to disable default Windows/Linux/Mac implementations.
- IO: added io.ConfigNavSwapGamepadButtons to swap Activate/Cancel (A<>B) buttons, to match the - IO: added io.ConfigNavSwapGamepadButtons to swap Activate/Cancel (A<>B) buttons, to match the
typical "Nintendo/Japanese consoles" button layout when using Gamepad navigation. (#787, #5723) typical "Nintendo/Japanese consoles" button layout when using Gamepad navigation. (#787, #5723)
@ -392,7 +186,7 @@ Other changes:
- Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename. - Backends: SDL2,SDL3,OSX: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() rename.
- Backends: GLFW,SDL2: Added io.PlatformOpenInShellFn handler for web/Emscripten versions. (#7660) - Backends: GLFW,SDL2: Added io.PlatformOpenInShellFn handler for web/Emscripten versions. (#7660)
[@ypujante, @ocornut] [@ypujante, @ocornut]
- Backends: GLFW+Emscripten: Added support for GLFW3 contrib port which fixes many of the things - Backends; GLFW+Emscripten: Added support for GLFW3 contrib port which fixes many of the things
not supported by the embedded GLFW: gamepad support, mouse cursor shapes, copy to clipboard, not supported by the embedded GLFW: gamepad support, mouse cursor shapes, copy to clipboard,
workaround for Super/Meta key, different ways of resizing, multi-window (glfw/canvas) support. workaround for Super/Meta key, different ways of resizing, multi-window (glfw/canvas) support.
(#7647) [@ypujante] (#7647) [@ypujante]
@ -987,7 +781,6 @@ Breaking changes:
Before: BeginChild("Name", size, false) Before: BeginChild("Name", size, false)
After: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None) After: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None)
Existing code will still work as 'ImGuiChildFlags_Border == true', but you are encouraged to update call sites. Existing code will still work as 'ImGuiChildFlags_Border == true', but you are encouraged to update call sites.
**AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'**
- BeginChild(): Added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for - BeginChild(): Added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for
the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense
for use with BeginChild() anyhow, passing it to Begin() had no effect. Now that we accept for use with BeginChild() anyhow, passing it to Begin() had no effect. Now that we accept
@ -1037,7 +830,6 @@ Other changes:
child windows from the bottom/right border (toward layout direction). Resized child windows child windows from the bottom/right border (toward layout direction). Resized child windows
settings are saved and persistent in .ini file. (#1710) settings are saved and persistent in .ini file. (#1710)
- BeginChild(): Added ImGuiChildFlags_Border as a replacement for 'bool border = true' parameter. - BeginChild(): Added ImGuiChildFlags_Border as a replacement for 'bool border = true' parameter.
**AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'**
- BeginChild(): Added ImGuiChildFlags_AutoResizeX and ImGuiChildFlags_AutoResizeY to auto-resize - BeginChild(): Added ImGuiChildFlags_AutoResizeX and ImGuiChildFlags_AutoResizeY to auto-resize
on one axis, while generally providing a size on the other axis. (#1666, #1395, #1496, #1710) on one axis, while generally providing a size on the other axis. (#1666, #1395, #1496, #1710)
e.g. BeginChild("name", {-FLT_MIN, 0.0f}, ImGuiChildFlags_AutoResizeY); e.g. BeginChild("name", {-FLT_MIN, 0.0f}, ImGuiChildFlags_AutoResizeY);

View File

@ -77,9 +77,9 @@ or view this file with any Markdown viewer.
### Q: Which version should I get? ### Q: Which version should I get?
I occasionally tag [Releases](https://github.com/ocornut/imgui/releases) but it is generally safe and recommended to sync to master/latest. The library is fairly stable and regressions tend to be fixed fast when reported. I occasionally tag [Releases](https://github.com/ocornut/imgui/releases) but it is generally safe and recommended to sync to master/latest. The library is fairly stable and regressions tend to be fixed fast when reported.
You may use the ['docking'](https://github.com/ocornut/imgui/tree/docking) branch which includes: You may use the [docking](https://github.com/ocornut/imgui/tree/docking) branch which includes:
- [Docking features](https://github.com/ocornut/imgui/wiki/Docking) - [Docking features](https://github.com/ocornut/imgui/issues/2109)
- [Multi-viewport features](https://github.com/ocornut/imgui/wiki/Multi-Viewports) - [Multi-viewport features](https://github.com/ocornut/imgui/issues/1542)
Many projects are using this branch and it is kept in sync with master regularly. Many projects are using this branch and it is kept in sync with master regularly.
@ -204,11 +204,10 @@ ctx->RSSetScissorRects(1, &r);
### Q: How can I have multiple widgets with the same label? ### Q: How can I have multiple widgets with the same label?
### Q: How can I have multiple windows with the same label? ### Q: How can I have multiple windows with the same label?
**USING THE SAME LABEL+ID IS THE MOST COMMON USER MISTAKE!** **USING THE SAME LABEL+ID IS THE MOST COMMON USER MISTAKE:**
<br>**USING AN EMPTY LABEL IS THE SAME AS USING THE SAME LABEL AS YOUR PARENT WIDGET!**
<table> <table>
<tr> <tr>
<td><img src="https://github.com/user-attachments/assets/776a8315-1164-4178-9a8c-df52e0ff28aa"></td> <td><img src="https://github.com/ocornut/imgui/assets/8225057/76eb9467-74d1-4e95-9f56-be81c6dd029d"></td>
<td> <td>
<pre lang="cpp"> <pre lang="cpp">
ImGui::Begin("Incorrect!"); ImGui::Begin("Incorrect!");
@ -640,7 +639,7 @@ The applications in examples/ are doing that.
Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode). Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode).
You may also use `MultiByteToWideChar()` or `ToUnicode()` to retrieve Unicode codepoints from MultiByte characters or keyboard state. You may also use `MultiByteToWideChar()` or `ToUnicode()` to retrieve Unicode codepoints from MultiByte characters or keyboard state.
Windows: if your language is relying on an Input Method Editor (IME), you can write your HWND to ImGui::GetMainViewport()->PlatformHandleRaw Windows: if your language is relying on an Input Method Editor (IME), you can write your HWND to ImGui::GetMainViewport()->PlatformHandleRaw
for the default implementation of GetPlatformIO().Platform_SetImeDataFn() to set your Microsoft IME position correctly. for the default implementation of io.PlatformSetImeDataFn() to set your Microsoft IME position correctly.
##### [Return to Index](#index) ##### [Return to Index](#index)
@ -655,7 +654,7 @@ You may take a look at:
- [Quotes](https://github.com/ocornut/imgui/wiki/Quotes) - [Quotes](https://github.com/ocornut/imgui/wiki/Quotes)
- [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) - [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui)
- [Funding & Sponsors](https://github.com/ocornut/imgui/wiki/Funding) - [Funding & Sponsors](https://github.com/ocornut/imgui/wiki/Funding)
- [Gallery](https://github.com/ocornut/imgui/issues?q=label%3Agallery) - [Gallery](https://github.com/ocornut/imgui/issues/7503)
##### [Return to Index](#index) ##### [Return to Index](#index)
@ -701,7 +700,7 @@ There is an auto-generated [c-api for Dear ImGui (cimgui)](https://github.com/ci
- Individuals: you can support continued maintenance and development via PayPal donations. See [README](https://github.com/ocornut/imgui/blob/master/docs/README.md). - Individuals: you can support continued maintenance and development via PayPal donations. See [README](https://github.com/ocornut/imgui/blob/master/docs/README.md).
- If you are experienced with Dear ImGui and C++, look at [GitHub Issues](https://github.com/ocornut/imgui/issues), [GitHub Discussions](https://github.com/ocornut/imgui/discussions), the [Wiki](https://github.com/ocornut/imgui/wiki), read [docs/TODO.txt](https://github.com/ocornut/imgui/blob/master/docs/TODO.txt), and see how you want to help and can help! - If you are experienced with Dear ImGui and C++, look at [GitHub Issues](https://github.com/ocornut/imgui/issues), [GitHub Discussions](https://github.com/ocornut/imgui/discussions), the [Wiki](https://github.com/ocornut/imgui/wiki), read [docs/TODO.txt](https://github.com/ocornut/imgui/blob/master/docs/TODO.txt), and see how you want to help and can help!
- Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere, etc. - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere, etc.
You may post screenshots or links in the [gallery threads](https://github.com/ocornut/imgui/issues?q=label%3Agallery). Visuals are ideal as they inspire other programmers. Disclosing your use of Dear ImGui helps the library grow credibility, and helps other teams and programmers with taking decisions. You may post screenshots or links in the [gallery threads](https://github.com/ocornut/imgui/issues/7503). Visuals are ideal as they inspire other programmers. Disclosing your use of Dear ImGui helps the library grow credibility, and helps other teams and programmers with taking decisions.
- If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues or sometimes incomplete PR. - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues or sometimes incomplete PR.
##### [Return to Index](#index) ##### [Return to Index](#index)

View File

@ -50,9 +50,7 @@ All loaded fonts glyphs are rendered into a single texture atlas ahead of time.
### (4) Font atlas texture fails to upload to GPU. ### (4) Font atlas texture fails to upload to GPU.
This is often of byproduct of point 3. If you have large number of glyphs or multiple fonts, the texture may become too big for your graphics API. **The typical result of failing to upload a texture is if every glyph or everything appears as empty white rectangles.** Mind the fact that some graphics drivers have texture size limitation. If you are building a PC application, mind the fact that your users may use hardware with lower limitations than yours. This is often of byproduct of point 3. If you have large number of glyphs or multiple fonts, the texture may become too big for your graphics API. **The typical result of failing to upload a texture is if every glyph or everything appears as empty black or white rectangle.** Mind the fact that some graphics drivers have texture size limitation. If you are building a PC application, mind the fact that your users may use hardware with lower limitations than yours.
![empty squares](https://github.com/user-attachments/assets/68b50fb5-8b9d-4c38-baec-6ac384f06d26)
Some solutions: Some solutions:
- You may reduce oversampling, e.g. `font_config.OversampleH = 1`, this will half your texture size for a quality loss. - You may reduce oversampling, e.g. `font_config.OversampleH = 1`, this will half your texture size for a quality loss.
@ -62,8 +60,6 @@ Some solutions:
- Set `io.Fonts.Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;` to disable rounding the texture height to the next power of two. - Set `io.Fonts.Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;` to disable rounding the texture height to the next power of two.
- Set `io.Fonts.TexDesiredWidth` to specify a texture width to reduce maximum texture height (see comment in `ImFontAtlas::Build()` function). - Set `io.Fonts.TexDesiredWidth` to specify a texture width to reduce maximum texture height (see comment in `ImFontAtlas::Build()` function).
Future versions of Dear ImGui should solve this problem.
##### [Return to Index](#index) ##### [Return to Index](#index)
--------------------------------------- ---------------------------------------

View File

@ -22,7 +22,7 @@ Businesses: support continued development and maintenance via invoiced sponsorin
Dear ImGui is a **bloat-free graphical user interface library for C++**. It outputs optimized vertex buffers that you can render anytime in your 3D-pipeline-enabled application. It is fast, portable, renderer agnostic, and self-contained (no external dependencies). Dear ImGui is a **bloat-free graphical user interface library for C++**. It outputs optimized vertex buffers that you can render anytime in your 3D-pipeline-enabled application. It is fast, portable, renderer agnostic, and self-contained (no external dependencies).
Dear ImGui is designed to **enable fast iterations** and to **empower programmers** to create **content creation tools and visualization / debug tools** (as opposed to UI for the average end-user). It favors simplicity and productivity toward this goal and lacks certain features commonly found in more high-level libraries. Among other things, full internationalization (right-to-left text, bidirectional text, text shaping etc.) and accessibility features are not supported. Dear ImGui is designed to **enable fast iterations** and to **empower programmers** to create **content creation tools and visualization / debug tools** (as opposed to UI for the average end-user). It favors simplicity and productivity toward this goal and lacks certain features commonly found in more high-level libraries.
Dear ImGui is particularly suited to integration in game engines (for tooling), real-time 3D applications, fullscreen applications, embedded applications, or any applications on console platforms where operating system features are non-standard. Dear ImGui is particularly suited to integration in game engines (for tooling), real-time 3D applications, fullscreen applications, embedded applications, or any applications on console platforms where operating system features are non-standard.
@ -141,7 +141,7 @@ Also see [Wiki](https://github.com/ocornut/imgui/wiki) for more links and ideas.
Examples projects using Dear ImGui: [Tracy](https://github.com/wolfpld/tracy) (profiler), [ImHex](https://github.com/WerWolv/ImHex) (hex editor/data analysis), [RemedyBG](https://remedybg.itch.io/remedybg) (debugger) and [hundreds of others](https://github.com/ocornut/imgui/wiki/Software-using-Dear-ImGui). Examples projects using Dear ImGui: [Tracy](https://github.com/wolfpld/tracy) (profiler), [ImHex](https://github.com/WerWolv/ImHex) (hex editor/data analysis), [RemedyBG](https://remedybg.itch.io/remedybg) (debugger) and [hundreds of others](https://github.com/ocornut/imgui/wiki/Software-using-Dear-ImGui).
For more user-submitted screenshots of projects using Dear ImGui, check out the [Gallery Threads](https://github.com/ocornut/imgui/issues?q=label%3Agallery)! For more user-submitted screenshots of projects using Dear ImGui, check out the [Gallery Threads](https://github.com/ocornut/imgui/issues/7503)!
For a list of third-party widgets and extensions, check out the [Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page. For a list of third-party widgets and extensions, check out the [Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page.
@ -170,11 +170,11 @@ Private support is available for paying business customers (E-mail: _contact @ d
**Which version should I get?** **Which version should I get?**
We occasionally tag [Releases](https://github.com/ocornut/imgui/releases) (with nice releases notes) but it is generally safe and recommended to sync to latest `master` or `docking` branch. The library is fairly stable and regressions tend to be fixed fast when reported. Advanced users may want to use the `docking` branch with [Multi-Viewport](https://github.com/ocornut/imgui/wiki/Multi-Viewports) and [Docking](https://github.com/ocornut/imgui/wiki/Docking) features. This branch is kept in sync with master regularly. We occasionally tag [Releases](https://github.com/ocornut/imgui/releases) (with nice releases notes) but it is generally safe and recommended to sync to latest `master` or `docking` branch. The library is fairly stable and regressions tend to be fixed fast when reported. Advanced users may want to use the `docking` branch with [Multi-Viewport](https://github.com/ocornut/imgui/issues/1542) and [Docking](https://github.com/ocornut/imgui/issues/2109) features. This branch is kept in sync with master regularly.
**Who uses Dear ImGui?** **Who uses Dear ImGui?**
See the [Quotes](https://github.com/ocornut/imgui/wiki/Quotes), [Funding & Sponsors](https://github.com/ocornut/imgui/wiki/Funding), and [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) Wiki pages for an idea of who is using Dear ImGui. Please add your game/software if you can! Also, see the [Gallery Threads](https://github.com/ocornut/imgui/issues?q=label%3Agallery)! See the [Quotes](https://github.com/ocornut/imgui/wiki/Quotes), [Funding & Sponsors](https://github.com/ocornut/imgui/wiki/Funding), and [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) Wiki pages for an idea of who is using Dear ImGui. Please add your game/software if you can! Also, see the [Gallery Threads](https://github.com/ocornut/imgui/issues/7503)!
How to help How to help
----------- -----------

View File

@ -65,6 +65,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- selectable: generic BeginSelectable()/EndSelectable() mechanism. (work out alongside range-select branch) - selectable: generic BeginSelectable()/EndSelectable() mechanism. (work out alongside range-select branch)
- selectable: a way to visualize partial/mixed selection (e.g. parent tree node has children with mixed selection) - selectable: a way to visualize partial/mixed selection (e.g. parent tree node has children with mixed selection)
- input text: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now and super fragile. (WIP branch)
- input text: preserve scrolling when unfocused? - input text: preserve scrolling when unfocused?
- input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541) - input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541)
- input text: expose CursorPos in char filter event (#816) - input text: expose CursorPos in char filter event (#816)
@ -189,11 +190,11 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- tree node/opt: could avoid formatting when clipped (flag assuming we don't care about width/height, assume single line height? format only %s/%c to be able to count height?) - tree node/opt: could avoid formatting when clipped (flag assuming we don't care about width/height, assume single line height? format only %s/%c to be able to count height?)
- settings: write more decent code to allow saving/loading new fields: columns, selected tree nodes? - settings: write more decent code to allow saving/loading new fields: columns, selected tree nodes?
- settings: facilitate extension lazily calling AddSettingsHandler() while running and still getting their data call the ReadXXX handlers immediately.
- settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file (#437) - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file (#437)
- settings/persistence: helpers to make TreeNodeBehavior persist (even during dev!) - may need to store some semantic and/or data type in ImGuiStoragePair - settings/persistence: helpers to make TreeNodeBehavior persist (even during dev!) - may need to store some semantic and/or data type in ImGuiStoragePair
- style: better default styles. (#707) - style: better default styles. (#707)
- style: PushStyleVar: allow direct access to individual float X/Y elements.
- style: add a highlighted text color (for headers, etc.) - style: add a highlighted text color (for headers, etc.)
- style: border types: out-screen, in-screen, etc. (#447) - style: border types: out-screen, in-screen, etc. (#447)
- style: add window shadow (fading away from the window. Paint-style calculation of vertices alpha after drawlist would be easier) - style: add window shadow (fading away from the window. Paint-style calculation of vertices alpha after drawlist would be easier)

View File

@ -91,11 +91,6 @@ int main(int, char**)
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
glfwPollEvents(); glfwPollEvents();
if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0)
{
ImGui_ImplGlfw_Sleep(10);
continue;
}
// Start the Dear ImGui frame // Start the Dear ImGui frame
ImGui_ImplOpenGL2_NewFrame(); ImGui_ImplOpenGL2_NewFrame();

View File

@ -127,11 +127,6 @@ int main(int, char**)
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
glfwPollEvents(); glfwPollEvents();
if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0)
{
ImGui_ImplGlfw_Sleep(10);
continue;
}
// Start the Dear ImGui frame // Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame();

View File

@ -494,11 +494,6 @@ int main(int, char**)
g_MainWindowData.FrameIndex = 0; g_MainWindowData.FrameIndex = 0;
g_SwapChainRebuild = false; g_SwapChainRebuild = false;
} }
if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0)
{
ImGui_ImplGlfw_Sleep(10);
continue;
}
// Start the Dear ImGui frame // Start the Dear ImGui frame
ImGui_ImplVulkan_NewFrame(); ImGui_ImplVulkan_NewFrame();

View File

@ -79,11 +79,6 @@ add_executable(example_glfw_wgpu
${IMGUI_DIR}/imgui_tables.cpp ${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp ${IMGUI_DIR}/imgui_widgets.cpp
) )
IF(NOT EMSCRIPTEN)
target_compile_definitions(example_glfw_wgpu PUBLIC
"IMGUI_IMPL_WEBGPU_BACKEND_DAWN"
)
endif()
target_include_directories(example_glfw_wgpu PUBLIC target_include_directories(example_glfw_wgpu PUBLIC
${IMGUI_DIR} ${IMGUI_DIR}
${IMGUI_DIR}/backends ${IMGUI_DIR}/backends
@ -96,6 +91,7 @@ if(EMSCRIPTEN)
if("${IMGUI_EMSCRIPTEN_GLFW3}" STREQUAL "--use-port=contrib.glfw3") if("${IMGUI_EMSCRIPTEN_GLFW3}" STREQUAL "--use-port=contrib.glfw3")
target_compile_options(example_glfw_wgpu PUBLIC target_compile_options(example_glfw_wgpu PUBLIC
"${IMGUI_EMSCRIPTEN_GLFW3}" "${IMGUI_EMSCRIPTEN_GLFW3}"
"-DEMSCRIPTEN_USE_PORT_CONTRIB_GLFW3" # unnecessary beyond emscripten 3.1.59
) )
endif() endif()
message(STATUS "Using ${IMGUI_EMSCRIPTEN_GLFW3} GLFW implementation") message(STATUS "Using ${IMGUI_EMSCRIPTEN_GLFW3} GLFW implementation")

View File

@ -151,11 +151,6 @@ int main(int, char**)
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
glfwPollEvents(); glfwPollEvents();
if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0)
{
ImGui_ImplGlfw_Sleep(10);
continue;
}
// React to changes in screen size // React to changes in screen size
int width, height; int width, height;

View File

@ -1,6 +1,6 @@
# #
# Cross Platform Makefile # Cross Platform Makefile
# Compatible with MSYS2/MINGW, Ubuntu 14.04.1+ and Mac OS X # Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X
# #
# Important: This is a "null backend" application, with no visible output or interaction! # Important: This is a "null backend" application, with no visible output or interaction!
# This is used for testing purpose and continuous integration, and has little use for end-user. # This is used for testing purpose and continuous integration, and has little use for end-user.

View File

@ -126,11 +126,6 @@ int main(int, char**)
CreateRenderTarget(); CreateRenderTarget();
} }
} }
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
{
SDL_Delay(10);
continue;
}
// Start the Dear ImGui frame // Start the Dear ImGui frame
ImGui_ImplDX11_NewFrame(); ImGui_ImplDX11_NewFrame();

View File

@ -105,11 +105,6 @@ int main(int, char**)
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
done = true; done = true;
} }
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
{
SDL_Delay(10);
continue;
}
// Start the Dear ImGui frame // Start the Dear ImGui frame
ImGui_ImplOpenGL2_NewFrame(); ImGui_ImplOpenGL2_NewFrame();

View File

@ -140,11 +140,6 @@ int main(int, char**)
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
done = true; done = true;
} }
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
{
SDL_Delay(10);
continue;
}
// Start the Dear ImGui frame // Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame();

View File

@ -47,7 +47,7 @@ int main(int, char**)
if (renderer == nullptr) if (renderer == nullptr)
{ {
SDL_Log("Error creating SDL_Renderer!"); SDL_Log("Error creating SDL_Renderer!");
return -1; return 0;
} }
//SDL_RendererInfo info; //SDL_RendererInfo info;
//SDL_GetRendererInfo(renderer, &info); //SDL_GetRendererInfo(renderer, &info);
@ -107,11 +107,6 @@ int main(int, char**)
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
done = true; done = true;
} }
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
{
SDL_Delay(10);
continue;
}
// Start the Dear ImGui frame // Start the Dear ImGui frame
ImGui_ImplSDLRenderer2_NewFrame(); ImGui_ImplSDLRenderer2_NewFrame();

View File

@ -492,11 +492,6 @@ int main(int, char**)
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
done = true; done = true;
} }
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
{
SDL_Delay(10);
continue;
}
// Resize swap chain? // Resize swap chain?
int fb_width, fb_height; int fb_width, fb_height;

View File

@ -9,11 +9,11 @@ Use the provided project file (.vcxproj). Add to solution (imgui_examples.sln) i
Use build_win32.bat or directly: Use build_win32.bat or directly:
``` ```
set SDL3_DIR=path_to_your_sdl3_folder set SDL2_DIR=path_to_your_sdl3_folder
cl /Zi /MD /utf-8 /I.. /I..\.. /I%SDL3_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_opengl3.cpp ..\..\imgui*.cpp /FeDebug/example_sdl3_opengl3.exe /FoDebug/ /link /libpath:%SDL3_DIR%\lib\x86 SDL3.lib opengl32.lib /subsystem:console cl /Zi /MD /utf-8 /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_opengl3.cpp ..\..\imgui*.cpp /FeDebug/example_sdl3_opengl3.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x86 SDL3.lib opengl32.lib /subsystem:console
# ^^ include paths ^^ source files ^^ output exe ^^ output dir ^^ libraries # ^^ include paths ^^ source files ^^ output exe ^^ output dir ^^ libraries
# or for 64-bit: # or for 64-bit:
cl /Zi /MD /utf-8 /I.. /I..\.. /I%SDL3_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_opengl3.cpp ..\..\imgui*.cpp /FeDebug/example_sdl3_opengl3.exe /FoDebug/ /link /libpath:%SDL3_DIR%\lib\x64 SDL3.lib SDL2mainopengl32.lib /subsystem:console cl /Zi /MD /utf-8 /I.. /I..\.. /I%SDL2_DIR%\include main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_opengl3.cpp ..\..\imgui*.cpp /FeDebug/example_sdl3_opengl3.exe /FoDebug/ /link /libpath:%SDL2_DIR%\lib\x64 SDL3.lib SDL2mainopengl32.lib /subsystem:console
``` ```
## Linux and similar Unixes ## Linux and similar Unixes

View File

@ -27,7 +27,7 @@
int main(int, char**) int main(int, char**)
{ {
// Setup SDL // Setup SDL
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMEPAD) != 0)
{ {
printf("Error: SDL_Init(): %s\n", SDL_GetError()); printf("Error: SDL_Init(): %s\n", SDL_GetError());
return -1; return -1;
@ -136,11 +136,6 @@ int main(int, char**)
if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window))
done = true; done = true;
} }
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
{
SDL_Delay(10);
continue;
}
// Start the Dear ImGui frame // Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame();

View File

@ -25,7 +25,7 @@
int main(int, char**) int main(int, char**)
{ {
// Setup SDL // Setup SDL
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMEPAD) != 0)
{ {
printf("Error: SDL_Init(): %s\n", SDL_GetError()); printf("Error: SDL_Init(): %s\n", SDL_GetError());
return -1; return -1;
@ -111,11 +111,6 @@ int main(int, char**)
if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window))
done = true; done = true;
} }
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
{
SDL_Delay(10);
continue;
}
// Start the Dear ImGui frame // Start the Dear ImGui frame
ImGui_ImplSDLRenderer3_NewFrame(); ImGui_ImplSDLRenderer3_NewFrame();
@ -167,9 +162,6 @@ int main(int, char**)
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer); ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer);
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);
} }
#ifdef __EMSCRIPTEN__
EMSCRIPTEN_MAINLOOP_END;
#endif
// Cleanup // Cleanup
ImGui_ImplSDLRenderer3_Shutdown(); ImGui_ImplSDLRenderer3_Shutdown();

View File

@ -1,8 +0,0 @@
@REM Build for MINGW64 or 32 from MSYS2.
@set OUT_DIR=Debug
@set OUT_EXE=example_win32_opengl3
@set INCLUDES=-I../.. -I../../backends
@set SOURCES=main.cpp ../../backends/imgui_impl_opengl3.cpp ../../backends/imgui_impl_win32.cpp ../../imgui*.cpp
@set LIBS=-lopengl32 -lgdi32 -ldwmapi
mkdir %OUT_DIR%
g++ -DUNICODE %INCLUDES% %SOURCES% -o %OUT_DIR%/%OUT_EXE%.exe --static -mwindows %LIBS% %LIBS%

View File

@ -108,11 +108,6 @@ int main(int, char**)
} }
if (done) if (done)
break; break;
if (::IsIconic(hwnd))
{
::Sleep(10);
continue;
}
// Start the Dear ImGui frame // Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplOpenGL3_NewFrame();

View File

@ -17,21 +17,20 @@
// - So the next logical step was to refactor all examples to follow that layout of using a "main loop" function. // - So the next logical step was to refactor all examples to follow that layout of using a "main loop" function.
// This worked, but it made us lose all the nice things we had... // This worked, but it made us lose all the nice things we had...
// Since only about 4 examples really need to run with Emscripten, here's our solution: // Since only about 3 examples really need to run with Emscripten, here's our solution:
// - Use some weird macros and capturing lambda to turn a loop in main() into a function. // - Use some weird macros and capturing lambda to turn a loop in main() into a function.
// - Hide all that crap in this file so it doesn't make our examples unusually ugly. // - Hide all that crap in this file so it doesn't make our examples unusually ugly.
// As a stance and principle of Dear ImGui development we don't use C++ headers and we don't // As a stance and principle of Dear ImGui development we don't use C++ headers and we don't
// want to suggest to the newcomer that we would ever use C++ headers as this would affect // want to suggest to the newcomer that we would ever use C++ headers as this would affect
// the initial judgment of many of our target audience. // the initial judgment of many of our target audience.
// - Technique is based on this idea: https://github.com/ocornut/imgui/pull/2492/ // - Technique is based on this idea: https://github.com/ocornut/imgui/pull/2492/
// - The do { } while (0) is to allow our code calling continue in the main loop.
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
#include <emscripten.h> #include <emscripten.h>
#include <functional> #include <functional>
static std::function<void()> MainLoopForEmscriptenP; static std::function<void()> MainLoopForEmscriptenP;
static void MainLoopForEmscripten() { MainLoopForEmscriptenP(); } static void MainLoopForEmscripten() { MainLoopForEmscriptenP(); }
#define EMSCRIPTEN_MAINLOOP_BEGIN MainLoopForEmscriptenP = [&]() { do #define EMSCRIPTEN_MAINLOOP_BEGIN MainLoopForEmscriptenP = [&]()
#define EMSCRIPTEN_MAINLOOP_END while (0); }; emscripten_set_main_loop(MainLoopForEmscripten, 0, true) #define EMSCRIPTEN_MAINLOOP_END ; emscripten_set_main_loop(MainLoopForEmscripten, 0, true)
#else #else
#define EMSCRIPTEN_MAINLOOP_BEGIN #define EMSCRIPTEN_MAINLOOP_BEGIN
#define EMSCRIPTEN_MAINLOOP_END #define EMSCRIPTEN_MAINLOOP_END

View File

@ -43,7 +43,7 @@
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME). //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")). //#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default io.PlatformOpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,16 @@
// dear imgui, v1.91.3 // dear imgui, v1.91.0
// (headers) // (headers)
// Help: // Help:
// - See links below. // - See links below.
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
// - Read top of imgui.cpp for more details, links and comments. // - Read top of imgui.cpp for more details, links and comments.
// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4.
// Resources: // Resources:
// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md) // - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)
// - Homepage ................... https://github.com/ocornut/imgui // - Homepage ................... https://github.com/ocornut/imgui
// - Releases & changelog ....... https://github.com/ocornut/imgui/releases // - Releases & changelog ....... https://github.com/ocornut/imgui/releases
// - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!) // - Gallery .................... https://github.com/ocornut/imgui/issues/7503 (please post your screenshots/video there!)
// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
// - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code) // - Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)
// - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more) // - Third-party Extensions https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)
@ -28,8 +27,8 @@
// Library Version // Library Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
#define IMGUI_VERSION "1.91.3" #define IMGUI_VERSION "1.91.0"
#define IMGUI_VERSION_NUM 19130 #define IMGUI_VERSION_NUM 19100
#define IMGUI_HAS_TABLE #define IMGUI_HAS_TABLE
/* /*
@ -49,7 +48,7 @@ Index of this file:
// [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData)
// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont)
// [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) // [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport)
// [SECTION] ImGuiPlatformIO + other Platform Dependent Interfaces (ImGuiPlatformImeData) // [SECTION] Platform Dependent Interfaces (ImGuiPlatformImeData)
// [SECTION] Obsolete functions and types // [SECTION] Obsolete functions and types
*/ */
@ -172,14 +171,13 @@ struct ImFontGlyph; // A single font glyph (code point + coordin
struct ImFontGlyphRangesBuilder; // Helper to build glyph ranges from text/string data struct ImFontGlyphRangesBuilder; // Helper to build glyph ranges from text/string data
struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*OBSOLETE* please avoid using) struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*OBSOLETE* please avoid using)
struct ImGuiContext; // Dear ImGui context (opaque structure, unless including imgui_internal.h) struct ImGuiContext; // Dear ImGui context (opaque structure, unless including imgui_internal.h)
struct ImGuiIO; // Main configuration and I/O between your application and ImGui (also see: ImGuiPlatformIO) struct ImGuiIO; // Main configuration and I/O between your application and ImGui
struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use)
struct ImGuiKeyData; // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions. struct ImGuiKeyData; // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions.
struct ImGuiListClipper; // Helper to manually clip large list of items struct ImGuiListClipper; // Helper to manually clip large list of items
struct ImGuiMultiSelectIO; // Structure to interact with a BeginMultiSelect()/EndMultiSelect() block struct ImGuiMultiSelectIO; // Structure to interact with a BeginMultiSelect()/EndMultiSelect() block
struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame
struct ImGuiPayload; // User data payload for drag and drop operations struct ImGuiPayload; // User data payload for drag and drop operations
struct ImGuiPlatformIO; // Interface between platform/renderer backends and ImGui (e.g. Clipboard, IME hooks). Extends ImGuiIO. In docking branch, this gets extended to support multi-viewports.
struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function. struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function.
struct ImGuiSelectionBasicStorage; // Optional helper to store multi-selection state + apply multi-selection requests. struct ImGuiSelectionBasicStorage; // Optional helper to store multi-selection state + apply multi-selection requests.
struct ImGuiSelectionExternalStorage;//Optional helper to apply multi-selection requests to existing randomly accessible storage. struct ImGuiSelectionExternalStorage;//Optional helper to apply multi-selection requests to existing randomly accessible storage.
@ -282,8 +280,8 @@ typedef void* (*ImGuiMemAllocFunc)(size_t sz, void* user_data);
typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions() typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // Function signature for ImGui::SetAllocatorFunctions()
// ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type] // ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type]
// - This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type. // This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type.
// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4. // Add '#define IMGUI_DEFINE_MATH_OPERATORS' in your imconfig.h file to benefit from courtesy maths operators for those types.
IM_MSVC_RUNTIME_CHECKS_OFF IM_MSVC_RUNTIME_CHECKS_OFF
struct ImVec2 struct ImVec2
{ {
@ -326,8 +324,7 @@ namespace ImGui
IMGUI_API void SetCurrentContext(ImGuiContext* ctx); IMGUI_API void SetCurrentContext(ImGuiContext* ctx);
// Main // Main
IMGUI_API ImGuiIO& GetIO(); // access the ImGuiIO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags) IMGUI_API ImGuiIO& GetIO(); // access the IO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags)
IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // access the ImGuiPlatformIO structure (mostly hooks/functions to connect to platform/renderer and OS Clipboard, IME etc.)
IMGUI_API ImGuiStyle& GetStyle(); // access the Style structure (colors, sizes). Always use PushStyleColor(), PushStyleVar() to modify style mid-frame! IMGUI_API ImGuiStyle& GetStyle(); // access the Style structure (colors, sizes). Always use PushStyleColor(), PushStyleVar() to modify style mid-frame!
IMGUI_API void NewFrame(); // start a new Dear ImGui frame, you can submit any command from this point until Render()/EndFrame(). IMGUI_API void NewFrame(); // start a new Dear ImGui frame, you can submit any command from this point until Render()/EndFrame().
IMGUI_API void EndFrame(); // ends the Dear ImGui frame. automatically called by Render(). If you don't need to render data (skipping rendering) you may call EndFrame() without Render()... but you'll have wasted CPU already! If you don't need to render, better to not create any windows and not call NewFrame() at all! IMGUI_API void EndFrame(); // ends the Dear ImGui frame. automatically called by Render(). If you don't need to render data (skipping rendering) you may call EndFrame() without Render()... but you'll have wasted CPU already! If you don't need to render, better to not create any windows and not call NewFrame() at all!
@ -369,10 +366,10 @@ namespace ImGui
// Child Windows // Child Windows
// - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child. // - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child.
// - Before 1.90 (November 2023), the "ImGuiChildFlags child_flags = 0" parameter was "bool border = false". // - Before 1.90 (November 2023), the "ImGuiChildFlags child_flags = 0" parameter was "bool border = false".
// This API is backward compatible with old code, as we guarantee that ImGuiChildFlags_Borders == true. // This API is backward compatible with old code, as we guarantee that ImGuiChildFlags_Border == true.
// Consider updating your old code: // Consider updating your old code:
// BeginChild("Name", size, false) -> Begin("Name", size, 0); or Begin("Name", size, ImGuiChildFlags_None); // BeginChild("Name", size, false) -> Begin("Name", size, 0); or Begin("Name", size, ImGuiChildFlags_None);
// BeginChild("Name", size, true) -> Begin("Name", size, ImGuiChildFlags_Borders); // BeginChild("Name", size, true) -> Begin("Name", size, ImGuiChildFlags_Border);
// - Manual sizing (each axis can use a different setting e.g. ImVec2(0.0f, 400.0f)): // - Manual sizing (each axis can use a different setting e.g. ImVec2(0.0f, 400.0f)):
// == 0.0f: use remaining parent window size for this axis. // == 0.0f: use remaining parent window size for this axis.
// > 0.0f: use specified size for this axis. // > 0.0f: use specified size for this axis.
@ -440,10 +437,8 @@ namespace ImGui
IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col); // modify a style color. always use this if you modify the style after NewFrame(). IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col); // modify a style color. always use this if you modify the style after NewFrame().
IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col);
IMGUI_API void PopStyleColor(int count = 1); IMGUI_API void PopStyleColor(int count = 1);
IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame()! IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); // modify a style float variable. always use this if you modify the style after NewFrame().
IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. " IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); // modify a style ImVec2 variable. always use this if you modify the style after NewFrame().
IMGUI_API void PushStyleVarX(ImGuiStyleVar idx, float val_x); // modify X component of a style ImVec2 variable. "
IMGUI_API void PushStyleVarY(ImGuiStyleVar idx, float val_y); // modify Y component of a style ImVec2 variable. "
IMGUI_API void PopStyleVar(int count = 1); IMGUI_API void PopStyleVar(int count = 1);
IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); // modify specified shared item flag, e.g. PushItemFlag(ImGuiItemFlags_NoTabStop, true) IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); // modify specified shared item flag, e.g. PushItemFlag(ImGuiItemFlags_NoTabStop, true)
IMGUI_API void PopItemFlag(); IMGUI_API void PopItemFlag();
@ -460,7 +455,7 @@ namespace ImGui
// - Use the ShowStyleEditor() function to interactively see/edit the colors. // - Use the ShowStyleEditor() function to interactively see/edit the colors.
IMGUI_API ImFont* GetFont(); // get current font IMGUI_API ImFont* GetFont(); // get current font
IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied
IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a white pixel, useful to draw custom shapes via the ImDrawList API IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API
IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier, packed as a 32-bit value suitable for ImDrawList IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier, packed as a 32-bit value suitable for ImDrawList
IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList
IMGUI_API ImU32 GetColorU32(ImU32 col, float alpha_mul = 1.0f); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList IMGUI_API ImU32 GetColorU32(ImU32 col, float alpha_mul = 1.0f); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList
@ -843,7 +838,7 @@ namespace ImGui
// Legacy Columns API (prefer using Tables!) // Legacy Columns API (prefer using Tables!)
// - You can also use SameLine(pos_x) to mimic simplified columns. // - You can also use SameLine(pos_x) to mimic simplified columns.
IMGUI_API void Columns(int count = 1, const char* id = NULL, bool borders = true); IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true);
IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished
IMGUI_API int GetColumnIndex(); // get current column index IMGUI_API int GetColumnIndex(); // get current column index
IMGUI_API float GetColumnWidth(int column_index = -1); // get column width (in pixels). pass -1 to use current column IMGUI_API float GetColumnWidth(int column_index = -1); // get column width (in pixels). pass -1 to use current column
@ -888,7 +883,7 @@ namespace ImGui
// - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors)
// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
// - Tooltips windows by exception are opted out of disabling. // - Tooltips windows by exception are opted out of disabling.
// - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls) // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.
IMGUI_API void BeginDisabled(bool disabled = true); IMGUI_API void BeginDisabled(bool disabled = true);
IMGUI_API void EndDisabled(); IMGUI_API void EndDisabled();
@ -1098,7 +1093,7 @@ enum ImGuiWindowFlags_
}; };
// Flags for ImGui::BeginChild() // Flags for ImGui::BeginChild()
// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'. // (Legacy: bit 0 must always correspond to ImGuiChildFlags_Border to be backward compatible with old API using 'bool border = false'.
// About using AutoResizeX/AutoResizeY flags: // About using AutoResizeX/AutoResizeY flags:
// - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints"). // - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints").
// - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing. // - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing.
@ -1109,7 +1104,7 @@ enum ImGuiWindowFlags_
enum ImGuiChildFlags_ enum ImGuiChildFlags_
{ {
ImGuiChildFlags_None = 0, ImGuiChildFlags_None = 0,
ImGuiChildFlags_Borders = 1 << 0, // Show an outer border and enable WindowPadding. (IMPORTANT: this is always == 1 == true for legacy reason) ImGuiChildFlags_Border = 1 << 0, // Show an outer border and enable WindowPadding. (IMPORTANT: this is always == 1 == true for legacy reason)
ImGuiChildFlags_AlwaysUseWindowPadding = 1 << 1, // Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense) ImGuiChildFlags_AlwaysUseWindowPadding = 1 << 1, // Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense)
ImGuiChildFlags_ResizeX = 1 << 2, // Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags_NoSavedSettings passed to window flags) ImGuiChildFlags_ResizeX = 1 << 2, // Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags_NoSavedSettings passed to window flags)
ImGuiChildFlags_ResizeY = 1 << 3, // Allow resize from bottom border (layout direction). " ImGuiChildFlags_ResizeY = 1 << 3, // Allow resize from bottom border (layout direction). "
@ -1118,11 +1113,6 @@ enum ImGuiChildFlags_
ImGuiChildFlags_AlwaysAutoResize = 1 << 6, // Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED. ImGuiChildFlags_AlwaysAutoResize = 1 << 6, // Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED.
ImGuiChildFlags_FrameStyle = 1 << 7, // Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding. ImGuiChildFlags_FrameStyle = 1 << 7, // Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.
ImGuiChildFlags_NavFlattened = 1 << 8, // [BETA] Share focus scope, allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. ImGuiChildFlags_NavFlattened = 1 << 8, // [BETA] Share focus scope, allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows.
// Obsolete names
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGuiChildFlags_Border = ImGuiChildFlags_Borders, // Renamed in 1.91.1 (August 2024) for consistency.
#endif
}; };
// Flags for ImGui::PushItemFlag() // Flags for ImGui::PushItemFlag()
@ -1135,7 +1125,6 @@ enum ImGuiItemFlags_
ImGuiItemFlags_NoNavDefaultFocus = 1 << 2, // false // Disable item being a candidate for default focus (e.g. used by title bar items). ImGuiItemFlags_NoNavDefaultFocus = 1 << 2, // false // Disable item being a candidate for default focus (e.g. used by title bar items).
ImGuiItemFlags_ButtonRepeat = 1 << 3, // false // Any button-like behavior will have repeat mode enabled (based on io.KeyRepeatDelay and io.KeyRepeatRate values). Note that you can also call IsItemActive() after any button to tell if it is being held. ImGuiItemFlags_ButtonRepeat = 1 << 3, // false // Any button-like behavior will have repeat mode enabled (based on io.KeyRepeatDelay and io.KeyRepeatRate values). Note that you can also call IsItemActive() after any button to tell if it is being held.
ImGuiItemFlags_AutoClosePopups = 1 << 4, // true // MenuItem()/Selectable() automatically close their parent popup window. ImGuiItemFlags_AutoClosePopups = 1 << 4, // true // MenuItem()/Selectable() automatically close their parent popup window.
ImGuiItemFlags_AllowDuplicateId = 1 << 5, // false // Allow submitting an item with the same identifier as an item already submitted this frame without triggering a warning tooltip if io.ConfigDebugHighlightIdConflicts is set.
}; };
// Flags for ImGui::InputText() // Flags for ImGui::InputText()
@ -1188,8 +1177,8 @@ enum ImGuiTreeNodeFlags_
ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack
ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes) ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes)
ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, // Default node to be open ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, // Default node to be open
ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, // Open on double-click instead of simple click (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined. ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, // Need double-click to open node
ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Open when clicking on the arrow part (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined. ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Only open when clicking on the arrow part. If ImGuiTreeNodeFlags_OpenOnDoubleClick is also set, single-click arrow or double-click all box to open.
ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes). ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes).
ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag! ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag!
ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding() before the node. ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding() before the node.
@ -1788,18 +1777,19 @@ enum ImGuiColorEditFlags_
// Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. // Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
// We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. // We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.
// (Those are per-item flags. There is shared behavior flag too: ImGuiIO: io.ConfigDragClickToInputText) // (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigDragClickToInputText)
enum ImGuiSliderFlags_ enum ImGuiSliderFlags_
{ {
ImGuiSliderFlags_None = 0, ImGuiSliderFlags_None = 0,
ImGuiSliderFlags_Logarithmic = 1 << 5, // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits. ImGuiSliderFlags_AlwaysClamp = 1 << 4, // Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.
ImGuiSliderFlags_NoRoundToFormat = 1 << 6, // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits). ImGuiSliderFlags_Logarithmic = 1 << 5, // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits.
ImGuiSliderFlags_NoInput = 1 << 7, // Disable CTRL+Click or Enter key allowing to input text directly into the widget. ImGuiSliderFlags_NoRoundToFormat = 1 << 6, // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits).
ImGuiSliderFlags_WrapAround = 1 << 8, // Enable wrapping around from max to min and from min to max. Only supported by DragXXX() functions for now. ImGuiSliderFlags_NoInput = 1 << 7, // Disable CTRL+Click or Enter key allowing to input text directly into the widget.
ImGuiSliderFlags_ClampOnInput = 1 << 9, // Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds. ImGuiSliderFlags_WrapAround = 1 << 8, // Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions for now.
ImGuiSliderFlags_ClampZeroRange = 1 << 10, // Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it. ImGuiSliderFlags_InvalidMask_ = 0x7000000F, // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed.
ImGuiSliderFlags_AlwaysClamp = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange,
ImGuiSliderFlags_InvalidMask_ = 0x7000000F, // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed. // Obsolete names
//ImGuiSliderFlags_ClampOnInput = ImGuiSliderFlags_AlwaysClamp, // [renamed in 1.79]
}; };
// Identify a mouse button. // Identify a mouse button.
@ -1948,7 +1938,7 @@ enum ImGuiTableColumnFlags_
ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table).
ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction. ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction.
ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction. ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction.
ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will submit an empty label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers. You may append into this cell by calling TableSetColumnIndex() right after the TableHeadersRow() call. ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit horizontal label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers.
ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width. ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width.
ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default). ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default).
ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column. ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column.
@ -2203,8 +2193,6 @@ struct ImGuiStyle
// - initialization: backends and user code writes to ImGuiIO. // - initialization: backends and user code writes to ImGuiIO.
// - main loop: backends writes to ImGuiIO, user code and imgui code reads from ImGuiIO. // - main loop: backends writes to ImGuiIO, user code and imgui code reads from ImGuiIO.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Also see ImGui::GetPlatformIO() and ImGuiPlatformIO struct for OS/platform related functions: clipboard, IME etc.
//-----------------------------------------------------------------------------
// [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions. // [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions.
// If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and *NOT* io.KeysData[key]->DownDuration. // If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and *NOT* io.KeysData[key]->DownDuration.
@ -2231,7 +2219,6 @@ struct ImGuiIO
const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified). const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified).
void* UserData; // = NULL // Store your own data. void* UserData; // = NULL // Store your own data.
// Font system
ImFontAtlas*Fonts; // <auto> // Font atlas: load, rasterize and pack one or more fonts into a single texture. ImFontAtlas*Fonts; // <auto> // Font atlas: load, rasterize and pack one or more fonts into a single texture.
float FontGlobalScale; // = 1.0f // Global scale all fonts float FontGlobalScale; // = 1.0f // Global scale all fonts
bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel.
@ -2239,7 +2226,6 @@ struct ImGuiIO
ImVec2 DisplayFramebufferScale; // = (1, 1) // For retina display or other situations where window coordinates are different from framebuffer coordinates. This generally ends up in ImDrawData::FramebufferScale. ImVec2 DisplayFramebufferScale; // = (1, 1) // For retina display or other situations where window coordinates are different from framebuffer coordinates. This generally ends up in ImDrawData::FramebufferScale.
// Miscellaneous options // Miscellaneous options
// (you can visualize and interact with all options in 'Demo->Configuration')
bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations. bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.
bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // Swap Cmd<>Ctrl keys + OS X style text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl. bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // Swap Cmd<>Ctrl keys + OS X style text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl.
bool ConfigNavSwapGamepadButtons; // = false // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout. bool ConfigNavSwapGamepadButtons; // = false // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
@ -2249,7 +2235,6 @@ struct ImGuiIO
bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard. bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard.
bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)
bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar. bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.
bool ConfigScrollbarScrollByPage; // = true // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location.
float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable. float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
// Inputs Behaviors // Inputs Behaviors
@ -2264,37 +2249,12 @@ struct ImGuiIO
// Debug options // Debug options
//------------------------------------------------------------------ //------------------------------------------------------------------
// Options to configure Error Handling and how we handle recoverable errors [EXPERIMENTAL]
// - Error recovery is provided as a way to facilitate:
// - Recovery after a programming error (native code or scripting language - the later tends to facilitate iterating on code while running).
// - Recovery after running an exception handler or any error processing which may skip code after an error has been detected.
// - Error recovery is not perfect nor guaranteed! It is a feature to ease development.
// You not are not supposed to rely on it in the course of a normal application run.
// - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT().
// - By design, we do NOT allow error recovery to be 100% silent. One of the three options needs to be checked!
// - Always ensure that on programmers seats you have at minimum Asserts or Tooltips enabled when making direct imgui API calls!
// Otherwise it would severely hinder your ability to catch and correct mistakes!
// Read https://github.com/ocornut/imgui/wiki/Error-Handling for details.
// - Programmer seats: keep asserts (default), or disable asserts and keep error tooltips (new and nice!)
// - Non-programmer seats: maybe disable asserts, but make sure errors are resurfaced (tooltips, visible log entries, use callback etc.)
// - Recovery after error/exception: record stack sizes with ErrorRecoveryStoreState(), disable assert, set log callback (to e.g. trigger high-level breakpoint), recover with ErrorRecoveryTryToRecoverState(), restore settings.
bool ConfigErrorRecovery; // = true // Enable error recovery support. Some errors won't be detected and lead to direct crashes if recovery is disabled.
bool ConfigErrorRecoveryEnableAssert; // = true // Enable asserts on recoverable error. By default call IM_ASSERT() when returning from a failing IM_ASSERT_USER_ERROR()
bool ConfigErrorRecoveryEnableDebugLog; // = true // Enable debug log output on recoverable errors.
bool ConfigErrorRecoveryEnableTooltip; // = true // Enable tooltip on recoverable errors. The tooltip include a way to enable asserts if they were disabled.
// Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro. // Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro.
// - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability. // - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability.
// - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application. // - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.
// e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version). // e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version).
bool ConfigDebugIsDebuggerPresent; // = false // Enable various tools calling IM_DEBUG_BREAK(). bool ConfigDebugIsDebuggerPresent; // = false // Enable various tools calling IM_DEBUG_BREAK().
// Tools to detect code submitting items with conflicting/duplicate IDs
// - Code should use PushID()/PopID() in loops, or append "##xx" to same-label identifiers.
// - Empty label e.g. Button("") == same ID as parent widget/node. Use Button("##xx") instead!
// - See FAQ https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-about-the-id-stack-system
bool ConfigDebugHighlightIdConflicts;// = true // Highlight and show an error message when multiple items have conflicting identifiers.
// Tools to test correct Begin/End and BeginChild/EndChild behaviors. // Tools to test correct Begin/End and BeginChild/EndChild behaviors.
// - Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX() // - Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX()
// - This is inconsistent with other BeginXXX functions and create confusion for many users. // - This is inconsistent with other BeginXXX functions and create confusion for many users.
@ -2311,7 +2271,7 @@ struct ImGuiIO
bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower) bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)
//------------------------------------------------------------------ //------------------------------------------------------------------
// Platform Identifiers // Platform Functions
// (the imgui_impl_xxxx backend files are setting those up for you) // (the imgui_impl_xxxx backend files are setting those up for you)
//------------------------------------------------------------------ //------------------------------------------------------------------
@ -2322,6 +2282,25 @@ struct ImGuiIO
void* BackendRendererUserData; // = NULL // User data for renderer backend void* BackendRendererUserData; // = NULL // User data for renderer backend
void* BackendLanguageUserData; // = NULL // User data for non C++ programming language backend void* BackendLanguageUserData; // = NULL // User data for non C++ programming language backend
// Optional: Access OS clipboard
// (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures)
const char* (*GetClipboardTextFn)(void* user_data);
void (*SetClipboardTextFn)(void* user_data, const char* text);
void* ClipboardUserData;
// Optional: Open link/folder/file in OS Shell
// (default to use ShellExecuteA() on Windows, system() on Linux/Mac)
bool (*PlatformOpenInShellFn)(ImGuiContext* ctx, const char* path);
void* PlatformOpenInShellUserData;
// Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows)
// (default to use native imm32 api on Windows)
void (*PlatformSetImeDataFn)(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
//void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); // [Renamed to io.PlatformSetImeDataFn in 1.91.0]
// Optional: Platform locale
ImWchar PlatformLocaleDecimalPoint; // '.' // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point
//------------------------------------------------------------------ //------------------------------------------------------------------
// Input - Call before calling NewFrame() // Input - Call before calling NewFrame()
//------------------------------------------------------------------ //------------------------------------------------------------------
@ -2423,14 +2402,6 @@ struct ImGuiIO
//void* ImeWindowHandle; // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. //void* ImeWindowHandle; // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning.
#endif #endif
// Legacy: before 1.91.1, clipboard functions were stored in ImGuiIO instead of ImGuiPlatformIO.
// As this is will affect all users of custom engines/backends, we are providing proper legacy redirection (will obsolete).
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
const char* (*GetClipboardTextFn)(void* user_data);
void (*SetClipboardTextFn)(void* user_data, const char* text);
void* ClipboardUserData;
#endif
IMGUI_API ImGuiIO(); IMGUI_API ImGuiIO();
}; };
@ -2693,7 +2664,7 @@ struct ImGuiListClipper
// Helpers: ImVec2/ImVec4 operators // Helpers: ImVec2/ImVec4 operators
// - It is important that we are keeping those disabled by default so they don't leak in user space. // - It is important that we are keeping those disabled by default so they don't leak in user space.
// - This is in order to allow user enabling implicit cast operators between ImVec2/ImVec4 and their own types (using IM_VEC2_CLASS_EXTRA in imconfig.h) // - This is in order to allow user enabling implicit cast operators between ImVec2/ImVec4 and their own types (using IM_VEC2_CLASS_EXTRA in imconfig.h)
// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4. // - You can use '#define IMGUI_DEFINE_MATH_OPERATORS' to import our operators, provided as a courtesy.
#ifdef IMGUI_DEFINE_MATH_OPERATORS #ifdef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED #define IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED
IM_MSVC_RUNTIME_CHECKS_OFF IM_MSVC_RUNTIME_CHECKS_OFF
@ -2888,7 +2859,7 @@ struct ImGuiSelectionBasicStorage
ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set<ImGuiID>. Prefer not accessing directly: iterate with GetNextSelectedItem(). ImGuiStorage _Storage; // [Internal] Selection set. Think of this as similar to e.g. std::set<ImGuiID>. Prefer not accessing directly: iterate with GetNextSelectedItem().
// Methods // Methods
IMGUI_API ImGuiSelectionBasicStorage(); ImGuiSelectionBasicStorage();
IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. It uses 'items_count' passed to BeginMultiSelect() IMGUI_API void ApplyRequests(ImGuiMultiSelectIO* ms_io); // Apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. It uses 'items_count' passed to BeginMultiSelect()
IMGUI_API bool Contains(ImGuiID id) const; // Query if an item id is in selection. IMGUI_API bool Contains(ImGuiID id) const; // Query if an item id is in selection.
IMGUI_API void Clear(); // Clear selection IMGUI_API void Clear(); // Clear selection
@ -3169,8 +3140,8 @@ struct ImDrawList
//inline void AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024) //inline void AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024)
//inline void AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0) { AddEllipseFilled(center, ImVec2(radius_x, radius_y), col, rot, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024) //inline void AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0) { AddEllipseFilled(center, ImVec2(radius_x, radius_y), col, rot, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024)
//inline void PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments = 0) { PathEllipticalArcTo(center, ImVec2(radius_x, radius_y), rot, a_min, a_max, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024) //inline void PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments = 0) { PathEllipticalArcTo(center, ImVec2(radius_x, radius_y), rot, a_min, a_max, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024)
//inline void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); } // OBSOLETED in 1.80 (Jan 2021) //inline void AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); } // OBSOLETED in 1.80 (Jan 2021)
//inline void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } // OBSOLETED in 1.80 (Jan 2021) //inline void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } // OBSOLETED in 1.80 (Jan 2021)
// [Internal helpers] // [Internal helpers]
IMGUI_API void _ResetForNewFrame(); IMGUI_API void _ResetForNewFrame();
@ -3180,7 +3151,6 @@ struct ImDrawList
IMGUI_API void _OnChangedClipRect(); IMGUI_API void _OnChangedClipRect();
IMGUI_API void _OnChangedTextureID(); IMGUI_API void _OnChangedTextureID();
IMGUI_API void _OnChangedVtxOffset(); IMGUI_API void _OnChangedVtxOffset();
IMGUI_API void _SetTextureID(ImTextureID texture_id);
IMGUI_API int _CalcCircleAutoSegmentCount(float radius) const; IMGUI_API int _CalcCircleAutoSegmentCount(float radius) const;
IMGUI_API void _PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step); IMGUI_API void _PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step);
IMGUI_API void _PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments); IMGUI_API void _PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments);
@ -3415,7 +3385,7 @@ struct ImFontAtlas
struct ImFont struct ImFont
{ {
// Members: Hot ~20/24 bytes (for CalcTextSize) // Members: Hot ~20/24 bytes (for CalcTextSize)
ImVector<float> IndexAdvanceX; // 12-16 // out // // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this info, and are often bottleneck in large UI). ImVector<float> IndexAdvanceX; // 12-16 // out // // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this this info, and are often bottleneck in large UI).
float FallbackAdvanceX; // 4 // out // = FallbackGlyph->AdvanceX float FallbackAdvanceX; // 4 // out // = FallbackGlyph->AdvanceX
float FontSize; // 4 // in // // Height of characters/line, set during loading (don't change after loading) float FontSize; // 4 // in // // Height of characters/line, set during loading (don't change after loading)
@ -3475,7 +3445,7 @@ enum ImGuiViewportFlags_
ImGuiViewportFlags_None = 0, ImGuiViewportFlags_None = 0,
ImGuiViewportFlags_IsPlatformWindow = 1 << 0, // Represent a Platform Window ImGuiViewportFlags_IsPlatformWindow = 1 << 0, // Represent a Platform Window
ImGuiViewportFlags_IsPlatformMonitor = 1 << 1, // Represent a Platform Monitor (unused yet) ImGuiViewportFlags_IsPlatformMonitor = 1 << 1, // Represent a Platform Monitor (unused yet)
ImGuiViewportFlags_OwnedByApp = 1 << 2, // Platform Window: Is created/managed by the application (rather than a dear imgui backend) ImGuiViewportFlags_OwnedByApp = 1 << 2, // Platform Window: is created/managed by the application (rather than a dear imgui backend)
}; };
// - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows.
@ -3509,38 +3479,7 @@ struct ImGuiViewport
// [SECTION] Platform Dependent Interfaces // [SECTION] Platform Dependent Interfaces
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Access via ImGui::GetPlatformIO() // (Optional) Support for IME (Input Method Editor) via the io.PlatformSetImeDataFn() function.
struct ImGuiPlatformIO
{
IMGUI_API ImGuiPlatformIO();
//------------------------------------------------------------------
// Input - Interface with OS/backends
//------------------------------------------------------------------
// Optional: Access OS clipboard
// (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures)
const char* (*Platform_GetClipboardTextFn)(ImGuiContext* ctx);
void (*Platform_SetClipboardTextFn)(ImGuiContext* ctx, const char* text);
void* Platform_ClipboardUserData;
// Optional: Open link/folder/file in OS Shell
// (default to use ShellExecuteA() on Windows, system() on Linux/Mac)
bool (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path);
void* Platform_OpenInShellUserData;
// Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows)
// (default to use native imm32 api on Windows)
void (*Platform_SetImeDataFn)(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
void* Platform_ImeUserData;
//void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); // [Renamed to platform_io.PlatformSetImeDataFn in 1.91.1]
// Optional: Platform locale
// [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point
ImWchar Platform_LocaleDecimalPoint; // '.'
};
// (Optional) Support for IME (Input Method Editor) via the platform_io.Platform_SetImeDataFn() function.
struct ImGuiPlatformImeData struct ImGuiPlatformImeData
{ {
bool WantVisible; // A widget wants the IME to be visible bool WantVisible; // A widget wants the IME to be visible
@ -3570,8 +3509,8 @@ namespace ImGui
// OBSOLETED in 1.90.0 (from September 2023) // OBSOLETED in 1.90.0 (from September 2023)
static inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); } static inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); }
static inline void EndChildFrame() { EndChild(); } static inline void EndChildFrame() { EndChild(); }
//static inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders //static inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border
//static inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders //static inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border
static inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } static inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); }
IMGUI_API bool Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1); IMGUI_API bool Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1);
IMGUI_API bool ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1); IMGUI_API bool ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1);
@ -3580,13 +3519,13 @@ namespace ImGui
// OBSOLETED in 1.89.4 (from March 2023) // OBSOLETED in 1.89.4 (from March 2023)
static inline void PushAllowKeyboardFocus(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); } static inline void PushAllowKeyboardFocus(bool tab_stop) { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); }
static inline void PopAllowKeyboardFocus() { PopItemFlag(); } static inline void PopAllowKeyboardFocus() { PopItemFlag(); }
// OBSOLETED in 1.89 (from August 2022)
IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Use new ImageButton() signature (explicit item id, regular FramePadding)
// OBSOLETED in 1.87 (from February 2022 but more formally obsoleted April 2024) // OBSOLETED in 1.87 (from February 2022 but more formally obsoleted April 2024)
IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value! IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value!
//static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; } //static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; }
// Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE)
//-- OBSOLETED in 1.89 (from August 2022)
//IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // --> Use new ImageButton() signature (explicit item id, regular FramePadding). Refer to code in 1.91 if you want to grab a copy of this version.
//-- OBSOLETED in 1.88 (from May 2022) //-- OBSOLETED in 1.88 (from May 2022)
//static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. //static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value.
//static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. //static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value.

View File

@ -1,4 +1,4 @@
// dear imgui, v1.91.3 // dear imgui, v1.91.0
// (demo code) // (demo code)
// Help: // Help:
@ -116,9 +116,6 @@ Index of this file:
#if !defined(_MSC_VER) || _MSC_VER >= 1800 #if !defined(_MSC_VER) || _MSC_VER >= 1800
#include <inttypes.h> // PRId64/PRIu64, not avail in some MinGW headers. #include <inttypes.h> // PRId64/PRIu64, not avail in some MinGW headers.
#endif #endif
#ifdef __EMSCRIPTEN__
#include <emscripten/version.h> // __EMSCRIPTEN_major__ etc.
#endif
// Visual Studio warnings // Visual Studio warnings
#ifdef _MSC_VER #ifdef _MSC_VER
@ -313,24 +310,23 @@ static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, Exampl
static ExampleTreeNode* ExampleTree_CreateDemoTree() static ExampleTreeNode* ExampleTree_CreateDemoTree()
{ {
static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" }; static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" };
const size_t NAME_MAX_LEN = sizeof(ExampleTreeNode::Name); char name_buf[32];
char name_buf[NAME_MAX_LEN];
int uid = 0; int uid = 0;
ExampleTreeNode* node_L0 = ExampleTree_CreateNode("<ROOT>", ++uid, NULL); ExampleTreeNode* node_L0 = ExampleTree_CreateNode("<ROOT>", ++uid, NULL);
const int root_items_multiplier = 2; const int root_items_multiplier = 2;
for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * root_items_multiplier; idx_L0++) for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * root_items_multiplier; idx_L0++)
{ {
snprintf(name_buf, IM_ARRAYSIZE(name_buf), "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier); snprintf(name_buf, 32, "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier);
ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0); ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0);
const int number_of_childs = (int)strlen(node_L1->Name); const int number_of_childs = (int)strlen(node_L1->Name);
for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++) for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++)
{ {
snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Child %d", idx_L1); snprintf(name_buf, 32, "Child %d", idx_L1);
ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1); ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1);
node_L2->HasData = true; node_L2->HasData = true;
if (idx_L1 == 0) if (idx_L1 == 0)
{ {
snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Sub-child %d", 0); snprintf(name_buf, 32, "Sub-child %d", 0);
ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2); ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2);
node_L3->HasData = true; node_L3->HasData = true;
} }
@ -539,37 +535,15 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges); ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges);
ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback."); ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback.");
ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly); ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly);
ImGui::Checkbox("io.ConfigScrollbarScrollByPage", &io.ConfigScrollbarScrollByPage);
ImGui::SameLine(); HelpMarker("Enable scrolling page by page when clicking outside the scrollbar grab.\nWhen disabled, always scroll to clicked location.\nWhen enabled, Shift+Click scrolls to clicked location.");
ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors); ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors);
ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors."); ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
ImGui::Text("Also see Style->Rendering for rendering options."); ImGui::Text("Also see Style->Rendering for rendering options.");
// Also read: https://github.com/ocornut/imgui/wiki/Error-Handling
ImGui::SeparatorText("Error Handling");
ImGui::Checkbox("io.ConfigErrorRecovery", &io.ConfigErrorRecovery);
ImGui::SameLine(); HelpMarker(
"Options to configure how we handle recoverable errors.\n"
"- Error recovery is not perfect nor guaranteed! It is a feature to ease development.\n"
"- You not are not supposed to rely on it in the course of a normal application run.\n"
"- Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.\n"
"- Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API call!"
"Otherwise it would severely hinder your ability to catch and correct mistakes!");
ImGui::Checkbox("io.ConfigErrorRecoveryEnableAssert", &io.ConfigErrorRecoveryEnableAssert);
ImGui::Checkbox("io.ConfigErrorRecoveryEnableDebugLog", &io.ConfigErrorRecoveryEnableDebugLog);
ImGui::Checkbox("io.ConfigErrorRecoveryEnableTooltip", &io.ConfigErrorRecoveryEnableTooltip);
if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip)
io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true;
// Also read: https://github.com/ocornut/imgui/wiki/Debug-Tools
ImGui::SeparatorText("Debug"); ImGui::SeparatorText("Debug");
ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent); ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent);
ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application."); ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.");
ImGui::Checkbox("io.ConfigDebugHighlightIdConflicts", &io.ConfigDebugHighlightIdConflicts);
ImGui::SameLine(); HelpMarker("Highlight and show an error message when multiple items have conflicting identifiers.");
ImGui::BeginDisabled(); ImGui::BeginDisabled();
ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce); ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce); // .
ImGui::EndDisabled(); ImGui::EndDisabled();
ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover."); ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover.");
ImGui::Checkbox("io.ConfigDebugBeginReturnValueLoop", &io.ConfigDebugBeginReturnValueLoop); ImGui::Checkbox("io.ConfigDebugBeginReturnValueLoop", &io.ConfigDebugBeginReturnValueLoop);
@ -597,7 +571,6 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos); ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos);
ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset); ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset);
ImGui::EndDisabled(); ImGui::EndDisabled();
ImGui::TreePop(); ImGui::TreePop();
ImGui::Spacing(); ImGui::Spacing();
} }
@ -707,7 +680,6 @@ static void ShowDemoWindowMenuBar(ImGuiDemoWindowData* demo_data)
if (ImGui::BeginMenu("Tools")) if (ImGui::BeginMenu("Tools"))
{ {
IMGUI_DEMO_MARKER("Menu/Tools"); IMGUI_DEMO_MARKER("Menu/Tools");
ImGuiIO& io = ImGui::GetIO();
#ifndef IMGUI_DISABLE_DEBUG_TOOLS #ifndef IMGUI_DISABLE_DEBUG_TOOLS
const bool has_debug_tools = true; const bool has_debug_tools = true;
#else #else
@ -716,16 +688,14 @@ static void ShowDemoWindowMenuBar(ImGuiDemoWindowData* demo_data)
ImGui::MenuItem("Metrics/Debugger", NULL, &demo_data->ShowMetrics, has_debug_tools); ImGui::MenuItem("Metrics/Debugger", NULL, &demo_data->ShowMetrics, has_debug_tools);
ImGui::MenuItem("Debug Log", NULL, &demo_data->ShowDebugLog, has_debug_tools); ImGui::MenuItem("Debug Log", NULL, &demo_data->ShowDebugLog, has_debug_tools);
ImGui::MenuItem("ID Stack Tool", NULL, &demo_data->ShowIDStackTool, has_debug_tools); ImGui::MenuItem("ID Stack Tool", NULL, &demo_data->ShowIDStackTool, has_debug_tools);
bool is_debugger_present = io.ConfigDebugIsDebuggerPresent; ImGui::MenuItem("Style Editor", NULL, &demo_data->ShowStyleEditor);
bool is_debugger_present = ImGui::GetIO().ConfigDebugIsDebuggerPresent;
if (ImGui::MenuItem("Item Picker", NULL, false, has_debug_tools && is_debugger_present)) if (ImGui::MenuItem("Item Picker", NULL, false, has_debug_tools && is_debugger_present))
ImGui::DebugStartItemPicker(); ImGui::DebugStartItemPicker();
if (!is_debugger_present) if (!is_debugger_present)
ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable the menu option to avoid casual users crashing the application.\n\nYou can however always access the Item Picker in Metrics->Tools."); ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable the menu option to avoid casual users crashing the application.\n\nYou can however always access the Item Picker in Metrics->Tools.");
ImGui::MenuItem("Style Editor", NULL, &demo_data->ShowStyleEditor); ImGui::Separator();
ImGui::MenuItem("About Dear ImGui", NULL, &demo_data->ShowAbout); ImGui::MenuItem("About Dear ImGui", NULL, &demo_data->ShowAbout);
ImGui::SeparatorText("Debug Options");
ImGui::MenuItem("Highlight ID Conflicts", NULL, &io.ConfigDebugHighlightIdConflicts, has_debug_tools);
ImGui::EndMenu(); ImGui::EndMenu();
} }
ImGui::EndMenuBar(); ImGui::EndMenuBar();
@ -2026,12 +1996,8 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
ImGui::SameLine(); ImGui::SameLine();
ImGui::SliderInt("Sample count", &display_count, 1, 400); ImGui::SliderInt("Sample count", &display_count, 1, 400);
float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw; float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw;
ImGui::PlotLines("Lines##2", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));
ImGui::PlotHistogram("Histogram##2", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80)); ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));
ImGui::Separator();
ImGui::Text("Need better plotting and graphing? Consider using ImPlot:");
ImGui::TextLinkOpenURL("https://github.com/epezent/implot");
ImGui::Separator(); ImGui::Separator();
ImGui::TreePop(); ImGui::TreePop();
@ -2265,10 +2231,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
// Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same! // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same!
static ImGuiSliderFlags flags = ImGuiSliderFlags_None; static ImGuiSliderFlags flags = ImGuiSliderFlags_None;
ImGui::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", &flags, ImGuiSliderFlags_AlwaysClamp); ImGui::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", &flags, ImGuiSliderFlags_AlwaysClamp);
ImGui::CheckboxFlags("ImGuiSliderFlags_ClampOnInput", &flags, ImGuiSliderFlags_ClampOnInput); ImGui::SameLine(); HelpMarker("Always clamp value to min/max bounds (if any) when input manually with CTRL+Click.");
ImGui::SameLine(); HelpMarker("Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.");
ImGui::CheckboxFlags("ImGuiSliderFlags_ClampZeroRange", &flags, ImGuiSliderFlags_ClampZeroRange);
ImGui::SameLine(); HelpMarker("Clamp even if min==max==0.0f. Otherwise DragXXX functions don't clamp.");
ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", &flags, ImGuiSliderFlags_Logarithmic); ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", &flags, ImGuiSliderFlags_Logarithmic);
ImGui::SameLine(); HelpMarker("Enable logarithmic editing (more precision for small values)."); ImGui::SameLine(); HelpMarker("Enable logarithmic editing (more precision for small values).");
ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", &flags, ImGuiSliderFlags_NoRoundToFormat); ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", &flags, ImGuiSliderFlags_NoRoundToFormat);
@ -2286,8 +2249,6 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
ImGui::DragFloat("DragFloat (0 -> +inf)", &drag_f, 0.005f, 0.0f, FLT_MAX, "%.3f", flags); ImGui::DragFloat("DragFloat (0 -> +inf)", &drag_f, 0.005f, 0.0f, FLT_MAX, "%.3f", flags);
ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags); ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags);
ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags); ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags);
//ImGui::DragFloat("DragFloat (0 -> 0)", &drag_f, 0.005f, 0.0f, 0.0f, "%.3f", flags); // To test ClampZeroRange
//ImGui::DragFloat("DragFloat (100 -> 100)", &drag_f, 0.005f, 100.0f, 100.0f, "%.3f", flags);
ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags); ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags);
// Sliders // Sliders
@ -2627,11 +2588,6 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)"); IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)");
if (ImGui::TreeNode("Drag to reorder items (simple)")) if (ImGui::TreeNode("Drag to reorder items (simple)"))
{ {
// FIXME: there is temporary (usually single-frame) ID Conflict during reordering as a same item may be submitting twice.
// This code was always slightly faulty but in a way which was not easily noticeable.
// Until we fix this, enable ImGuiItemFlags_AllowDuplicateId to disable detecting the issue.
ImGui::PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true);
// Simple reordering // Simple reordering
HelpMarker( HelpMarker(
"We don't use the drag and drop api at all here! " "We don't use the drag and drop api at all here! "
@ -2653,8 +2609,6 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
} }
} }
} }
ImGui::PopItemFlag();
ImGui::TreePop(); ImGui::TreePop();
} }
@ -2807,7 +2761,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
static bool embed_all_inside_a_child_window = false; static bool embed_all_inside_a_child_window = false;
ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window); ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window);
if (embed_all_inside_a_child_window) if (embed_all_inside_a_child_window)
ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), ImGuiChildFlags_Borders); ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), ImGuiChildFlags_Border);
// Testing IsWindowFocused() function with its various flags. // Testing IsWindowFocused() function with its various flags.
ImGui::BulletText( ImGui::BulletText(
@ -2855,7 +2809,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data)
ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow),
ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary)); ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary));
ImGui::BeginChild("child", ImVec2(0, 50), ImGuiChildFlags_Borders); ImGui::BeginChild("child", ImVec2(0, 50), ImGuiChildFlags_Border);
ImGui::Text("This is another child window for testing the _ChildWindows flag."); ImGui::Text("This is another child window for testing the _ChildWindows flag.");
ImGui::EndChild(); ImGui::EndChild();
if (embed_all_inside_a_child_window) if (embed_all_inside_a_child_window)
@ -3420,7 +3374,7 @@ static void ShowDemoWindowMultiSelect(ImGuiDemoWindowData* demo_data)
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear); ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear);
ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width. ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width.
if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY))
{ {
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_ARRAYSIZE(items)); ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_ARRAYSIZE(items));
ImGuiSelectionExternalStorage storage_wrapper; ImGuiSelectionExternalStorage storage_wrapper;
@ -3722,7 +3676,7 @@ static void ShowDemoWindowMultiSelect(ImGuiDemoWindowData* demo_data)
{ {
ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize()); ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize());
if (widget_type == WidgetType_TreeNode) if (widget_type == WidgetType_TreeNode)
ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f));
ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size); ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
selection.ApplyRequests(ms_io); selection.ApplyRequests(ms_io);
@ -3738,7 +3692,7 @@ static void ShowDemoWindowMultiSelect(ImGuiDemoWindowData* demo_data)
ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX); ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f);
//ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacingY, 0.0f); //ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f));
} }
ImGuiListClipper clipper; ImGuiListClipper clipper;
@ -3926,7 +3880,7 @@ static void ShowDemoWindowLayout()
if (!disable_menu) if (!disable_menu)
window_flags |= ImGuiWindowFlags_MenuBar; window_flags |= ImGuiWindowFlags_MenuBar;
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
ImGui::BeginChild("ChildR", ImVec2(0, 260), ImGuiChildFlags_Borders, window_flags); ImGui::BeginChild("ChildR", ImVec2(0, 260), ImGuiChildFlags_Border, window_flags);
if (!disable_menu && ImGui::BeginMenuBar()) if (!disable_menu && ImGui::BeginMenuBar())
{ {
if (ImGui::BeginMenu("Menu")) if (ImGui::BeginMenu("Menu"))
@ -3955,11 +3909,8 @@ static void ShowDemoWindowLayout()
ImGui::SeparatorText("Manual-resize"); ImGui::SeparatorText("Manual-resize");
{ {
HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents."); HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents.");
//if (ImGui::Button("Set Height to 200"))
// ImGui::SetNextWindowSize(ImVec2(-FLT_MIN, 200.0f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg)); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY))
for (int n = 0; n < 10; n++) for (int n = 0; n < 10; n++)
ImGui::Text("Line %04d", n); ImGui::Text("Line %04d", n);
ImGui::PopStyleColor(); ImGui::PopStyleColor();
@ -3977,7 +3928,7 @@ static void ShowDemoWindowLayout()
ImGui::DragInt("Max Height (in Lines)", &max_height_in_lines, 0.2f); ImGui::DragInt("Max Height (in Lines)", &max_height_in_lines, 0.2f);
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines)); ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines));
if (ImGui::BeginChild("ConstrainedChild", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY)) if (ImGui::BeginChild("ConstrainedChild", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY))
for (int n = 0; n < draw_lines; n++) for (int n = 0; n < draw_lines; n++)
ImGui::Text("Line %04d", n); ImGui::Text("Line %04d", n);
ImGui::EndChild(); ImGui::EndChild();
@ -3995,11 +3946,11 @@ static void ShowDemoWindowLayout()
{ {
static int offset_x = 0; static int offset_x = 0;
static bool override_bg_color = true; static bool override_bg_color = true;
static ImGuiChildFlags child_flags = ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY; static ImGuiChildFlags child_flags = ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY;
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000); ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000);
ImGui::Checkbox("Override ChildBg color", &override_bg_color); ImGui::Checkbox("Override ChildBg color", &override_bg_color);
ImGui::CheckboxFlags("ImGuiChildFlags_Borders", &child_flags, ImGuiChildFlags_Borders); ImGui::CheckboxFlags("ImGuiChildFlags_Border", &child_flags, ImGuiChildFlags_Border);
ImGui::CheckboxFlags("ImGuiChildFlags_AlwaysUseWindowPadding", &child_flags, ImGuiChildFlags_AlwaysUseWindowPadding); ImGui::CheckboxFlags("ImGuiChildFlags_AlwaysUseWindowPadding", &child_flags, ImGuiChildFlags_AlwaysUseWindowPadding);
ImGui::CheckboxFlags("ImGuiChildFlags_ResizeX", &child_flags, ImGuiChildFlags_ResizeX); ImGui::CheckboxFlags("ImGuiChildFlags_ResizeX", &child_flags, ImGuiChildFlags_ResizeX);
ImGui::CheckboxFlags("ImGuiChildFlags_ResizeY", &child_flags, ImGuiChildFlags_ResizeY); ImGui::CheckboxFlags("ImGuiChildFlags_ResizeY", &child_flags, ImGuiChildFlags_ResizeY);
@ -4266,7 +4217,7 @@ static void ShowDemoWindowLayout()
// down by FramePadding.y ahead of time) // down by FramePadding.y ahead of time)
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
ImGui::Text("OK Blahblah"); ImGui::SameLine(); ImGui::Text("OK Blahblah"); ImGui::SameLine();
ImGui::Button("Some framed item##2"); ImGui::SameLine(); ImGui::Button("Some framed item"); ImGui::SameLine();
HelpMarker("We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y"); HelpMarker("We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y");
// SmallButton() uses the same vertical padding as Text // SmallButton() uses the same vertical padding as Text
@ -4409,7 +4360,7 @@ static void ShowDemoWindowLayout()
const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0; const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0;
const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i); const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Borders, child_flags); const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Border, child_flags);
if (ImGui::BeginMenuBar()) if (ImGui::BeginMenuBar())
{ {
ImGui::TextUnformatted("abc"); ImGui::TextUnformatted("abc");
@ -4456,7 +4407,7 @@ static void ShowDemoWindowLayout()
float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f; float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f;
ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0); ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0);
ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i); ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), ImGuiChildFlags_Borders, child_flags); bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), ImGuiChildFlags_Border, child_flags);
if (scroll_to_off) if (scroll_to_off)
ImGui::SetScrollX(scroll_to_off_px); ImGui::SetScrollX(scroll_to_off_px);
if (scroll_to_pos) if (scroll_to_pos)
@ -4498,7 +4449,7 @@ static void ShowDemoWindowLayout()
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f));
ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30); ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30);
ImGui::BeginChild("scrolling", scrolling_child_size, ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar); ImGui::BeginChild("scrolling", scrolling_child_size, ImGuiChildFlags_Border, ImGuiWindowFlags_HorizontalScrollbar);
for (int line = 0; line < lines; line++) for (int line = 0; line < lines; line++)
{ {
// Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine() // Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine()
@ -4644,7 +4595,7 @@ static void ShowDemoWindowLayout()
} }
if (show_child) if (show_child)
{ {
ImGui::BeginChild("child", ImVec2(0, 0), ImGuiChildFlags_Borders); ImGui::BeginChild("child", ImVec2(0, 0), ImGuiChildFlags_Border);
ImGui::EndChild(); ImGui::EndChild();
} }
ImGui::End(); ImGui::End();
@ -5130,8 +5081,8 @@ const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL;
static void PushStyleCompact() static void PushStyleCompact()
{ {
ImGuiStyle& style = ImGui::GetStyle(); ImGuiStyle& style = ImGui::GetStyle();
ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, (float)(int)(style.FramePadding.y * 0.60f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(style.FramePadding.x, (float)(int)(style.FramePadding.y * 0.60f)));
ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, (float)(int)(style.ItemSpacing.y * 0.60f)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x, (float)(int)(style.ItemSpacing.y * 0.60f)));
} }
static void PopStyleCompact() static void PopStyleCompact()
@ -6165,7 +6116,7 @@ static void ShowDemoWindowTables()
for (int row = 0; row < 8; row++) for (int row = 0; row < 8; row++)
{ {
if ((row % 3) == 2) if ((row % 3) == 2)
ImGui::PushStyleVarY(ImGuiStyleVar_CellPadding, 20.0f); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(style.CellPadding.x, 20.0f));
ImGui::TableNextRow(ImGuiTableRowFlags_None); ImGui::TableNextRow(ImGuiTableRowFlags_None);
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("CellPadding.y = %.2f", style.CellPadding.y); ImGui::Text("CellPadding.y = %.2f", style.CellPadding.y);
@ -6444,11 +6395,7 @@ static void ShowDemoWindowTables()
// FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox. // FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox.
static bool column_selected[3] = {}; static bool column_selected[3] = {};
// Instead of calling TableHeadersRow() we'll submit custom headers ourselves. // Instead of calling TableHeadersRow() we'll submit custom headers ourselves
// (A different approach is also possible:
// - Specify ImGuiTableColumnFlags_NoHeaderLabel in some TableSetupColumn() call.
// - Call TableHeadersRow() normally. This will submit TableHeader() with no name.
// - Then call TableSetColumnIndex() to position yourself in the column and submit your stuff e.g. Checkbox().)
ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
for (int column = 0; column < COLUMNS_COUNT; column++) for (int column = 0; column < COLUMNS_COUNT; column++)
{ {
@ -6463,7 +6410,6 @@ static void ShowDemoWindowTables()
ImGui::PopID(); ImGui::PopID();
} }
// Submit table contents
for (int row = 0; row < 5; row++) for (int row = 0; row < 5; row++)
{ {
ImGui::TableNextRow(); ImGui::TableNextRow();
@ -6498,7 +6444,6 @@ static void ShowDemoWindowTables()
ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX); ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX);
ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY); ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY);
ImGui::CheckboxFlags("_Resizable", &table_flags, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("_Resizable", &table_flags, ImGuiTableFlags_Resizable);
ImGui::CheckboxFlags("_Sortable", &table_flags, ImGuiTableFlags_Sortable);
ImGui::CheckboxFlags("_NoBordersInBody", &table_flags, ImGuiTableFlags_NoBordersInBody); ImGui::CheckboxFlags("_NoBordersInBody", &table_flags, ImGuiTableFlags_NoBordersInBody);
ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags, ImGuiTableFlags_HighlightHoveredColumn); ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags, ImGuiTableFlags_HighlightHoveredColumn);
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
@ -7189,14 +7134,12 @@ static void ShowDemoWindowColumns()
{ {
if (h_borders && ImGui::GetColumnIndex() == 0) if (h_borders && ImGui::GetColumnIndex() == 0)
ImGui::Separator(); ImGui::Separator();
ImGui::PushID(i);
ImGui::Text("%c%c%c", 'a' + i, 'a' + i, 'a' + i); ImGui::Text("%c%c%c", 'a' + i, 'a' + i, 'a' + i);
ImGui::Text("Width %.2f", ImGui::GetColumnWidth()); ImGui::Text("Width %.2f", ImGui::GetColumnWidth());
ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x); ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x);
ImGui::Text("Offset %.2f", ImGui::GetColumnOffset()); ImGui::Text("Offset %.2f", ImGui::GetColumnOffset());
ImGui::Text("Long text that is likely to clip"); ImGui::Text("Long text that is likely to clip");
ImGui::Button("Button", ImVec2(-FLT_MIN, 0.0f)); ImGui::Button("Button", ImVec2(-FLT_MIN, 0.0f));
ImGui::PopID();
ImGui::NextColumn(); ImGui::NextColumn();
} }
ImGui::Columns(1); ImGui::Columns(1);
@ -7519,8 +7462,7 @@ static void ShowDemoWindowInputs()
IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT); IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT);
ImGuiMouseCursor current = ImGui::GetMouseCursor(); ImGuiMouseCursor current = ImGui::GetMouseCursor();
const char* cursor_name = (current >= ImGuiMouseCursor_Arrow) && (current < ImGuiMouseCursor_COUNT) ? mouse_cursors_names[current] : "N/A"; ImGui::Text("Current mouse cursor = %d: %s", current, mouse_cursors_names[current]);
ImGui::Text("Current mouse cursor = %d: %s", current, cursor_name);
ImGui::BeginDisabled(true); ImGui::BeginDisabled(true);
ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors); ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);
ImGui::EndDisabled(); ImGui::EndDisabled();
@ -7745,7 +7687,6 @@ void ImGui::ShowAboutWindow(bool* p_open)
#endif #endif
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
ImGui::Text("define: __EMSCRIPTEN__"); ImGui::Text("define: __EMSCRIPTEN__");
ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__);
#endif #endif
ImGui::Separator(); ImGui::Separator();
ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL"); ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL");
@ -8000,7 +7941,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
"Right-click to open edit options menu."); "Right-click to open edit options menu.");
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX)); ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX));
ImGui::BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); ImGui::BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Border | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
ImGui::PushItemWidth(ImGui::GetFontSize() * -12); ImGui::PushItemWidth(ImGui::GetFontSize() * -12);
for (int i = 0; i < ImGuiCol_COUNT; i++) for (int i = 0; i < ImGuiCol_COUNT; i++)
{ {
@ -8234,7 +8175,7 @@ static void ShowExampleMenuFile()
{ {
static bool enabled = true; static bool enabled = true;
ImGui::MenuItem("Enabled", "", &enabled); ImGui::MenuItem("Enabled", "", &enabled);
ImGui::BeginChild("child", ImVec2(0, 60), ImGuiChildFlags_Borders); ImGui::BeginChild("child", ImVec2(0, 60), ImGuiChildFlags_Border);
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
ImGui::Text("Scrolling Text %d", i); ImGui::Text("Scrolling Text %d", i);
ImGui::EndChild(); ImGui::EndChild();
@ -8831,7 +8772,7 @@ static void ShowExampleAppLayout(bool* p_open)
// Left // Left
static int selected = 0; static int selected = 0;
{ {
ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX); ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX);
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
{ {
// FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav // FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav
@ -8892,7 +8833,7 @@ struct ExampleAppPropertyEditor
{ {
// Left side: draw tree // Left side: draw tree
// - Currently using a table to benefit from RowBg feature // - Currently using a table to benefit from RowBg feature
if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened)) if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Border | ImGuiChildFlags_NavFlattened))
{ {
ImGui::SetNextItemWidth(-FLT_MIN); ImGui::SetNextItemWidth(-FLT_MIN);
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip);
@ -9506,7 +9447,7 @@ static void ShowExampleAppCustomRendering(bool* p_open)
// To use a child window instead we could use, e.g: // To use a child window instead we could use, e.g:
// ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding // ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding
// ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color // ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color
// ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove); // ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_NoMove);
// ImGui::PopStyleColor(); // ImGui::PopStyleColor();
// ImGui::PopStyleVar(); // ImGui::PopStyleVar();
// [...] // [...]
@ -10158,7 +10099,7 @@ struct ExampleAssetsBrowser
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.x + LayoutItemSpacing))); ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.x + LayoutItemSpacing)));
if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove)) if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Border, ImGuiWindowFlags_NoMove))
{ {
ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImDrawList* draw_list = ImGui::GetWindowDrawList();

View File

@ -1,4 +1,4 @@
// dear imgui, v1.91.3 // dear imgui, v1.91.0
// (drawing and font code) // (drawing and font code)
/* /*
@ -526,6 +526,7 @@ void ImDrawList::_OnChangedClipRect()
CmdBuffer.pop_back(); CmdBuffer.pop_back();
return; return;
} }
curr_cmd->ClipRect = _CmdHeader.ClipRect; curr_cmd->ClipRect = _CmdHeader.ClipRect;
} }
@ -548,6 +549,7 @@ void ImDrawList::_OnChangedTextureID()
CmdBuffer.pop_back(); CmdBuffer.pop_back();
return; return;
} }
curr_cmd->TextureId = _CmdHeader.TextureId; curr_cmd->TextureId = _CmdHeader.TextureId;
} }
@ -623,15 +625,6 @@ void ImDrawList::PopTextureID()
_OnChangedTextureID(); _OnChangedTextureID();
} }
// This is used by ImGui::PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTextureID()/PopTextureID().
void ImDrawList::_SetTextureID(ImTextureID texture_id)
{
if (_CmdHeader.TextureId == texture_id)
return;
_CmdHeader.TextureId = texture_id;
_OnChangedTextureID();
}
// Reserve space for a number of vertices and indices. // Reserve space for a number of vertices and indices.
// You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or // You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or
// submit the intermediate results. PrimUnreserve() can be used to release unused allocations. // submit the intermediate results. PrimUnreserve() can be used to release unused allocations.
@ -2499,14 +2492,13 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
{ {
IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!");
IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0); IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0);
IM_ASSERT(font_cfg->SizePixels > 0.0f && "Is ImFontConfig struct correctly initialized?"); IM_ASSERT(font_cfg->SizePixels > 0.0f);
IM_ASSERT(font_cfg->OversampleH > 0 && font_cfg->OversampleV > 0 && "Is ImFontConfig struct correctly initialized?");
// Create new font // Create new font
if (!font_cfg->MergeMode) if (!font_cfg->MergeMode)
Fonts.push_back(IM_NEW(ImFont)); Fonts.push_back(IM_NEW(ImFont));
else else
IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font. IM_ASSERT(!Fonts.empty() && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.
ConfigData.push_back(*font_cfg); ConfigData.push_back(*font_cfg);
ImFontConfig& new_font_cfg = ConfigData.back(); ImFontConfig& new_font_cfg = ConfigData.back();
@ -2823,9 +2815,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
{ {
// Check for valid range. This may also help detect *some* dangling pointers, because a common // Check for valid range. This may also help detect *some* dangling pointers, because a common
// user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent, // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent.
// or to forget to zero-terminate the glyph range array. IM_ASSERT(src_range[0] <= src_range[1]);
IM_ASSERT(src_range[0] <= src_range[1] && "Invalid range: is your glyph range array persistent? it is zero-terminated?");
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]); src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
} }
dst_tmp.SrcCount++; dst_tmp.SrcCount++;

View File

@ -1,4 +1,4 @@
// dear imgui, v1.91.3 // dear imgui, v1.91.0
// (internal structures/api) // (internal structures/api)
// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
@ -28,7 +28,6 @@ Index of this file:
// [SECTION] Viewport support // [SECTION] Viewport support
// [SECTION] Settings support // [SECTION] Settings support
// [SECTION] Localization support // [SECTION] Localization support
// [SECTION] Error handling, State recovery support
// [SECTION] Metrics, Debug tools // [SECTION] Metrics, Debug tools
// [SECTION] Generic context hooks // [SECTION] Generic context hooks
// [SECTION] ImGuiContext (main imgui context) // [SECTION] ImGuiContext (main imgui context)
@ -95,7 +94,6 @@ Index of this file:
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead #pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
#endif #endif
// In 1.89.4, we moved the implementation of "courtesy maths operators" from imgui_internal.h in imgui.h // In 1.89.4, we moved the implementation of "courtesy maths operators" from imgui_internal.h in imgui.h
@ -132,7 +130,6 @@ struct ImGuiContext; // Main Dear ImGui context
struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine
struct ImGuiDataVarInfo; // Variable information (e.g. to access style variables from an enum) struct ImGuiDataVarInfo; // Variable information (e.g. to access style variables from an enum)
struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum
struct ImGuiErrorRecoveryState; // Storage of stack sizes for error handling and recovery
struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup()
struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box
struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a deactivating InputText() while another is stealing active id struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a deactivating InputText() while another is stealing active id
@ -149,6 +146,7 @@ struct ImGuiOldColumnData; // Storage data for a single column for lega
struct ImGuiOldColumns; // Storage data for a columns set for legacy Columns() api struct ImGuiOldColumns; // Storage data for a columns set for legacy Columns() api
struct ImGuiPopupData; // Storage for current popup stack struct ImGuiPopupData; // Storage for current popup stack
struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file
struct ImGuiStackSizes; // Storage of stack sizes for debugging/asserting
struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it
struct ImGuiTabBar; // Storage for a tab bar struct ImGuiTabBar; // Storage for a tab bar
struct ImGuiTabItem; // Storage for a tab item (within a tab bar) struct ImGuiTabItem; // Storage for a tab item (within a tab bar)
@ -174,7 +172,7 @@ typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // E
// Flags // Flags
typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later)
typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags
typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow() typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow();
typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags
typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns()
typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight()
@ -188,6 +186,8 @@ typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // F
typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest() typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest()
typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy() typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy()
typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...);
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Context pointer // [SECTION] Context pointer
// See implementation of this variable in imgui.cpp for comments and details. // See implementation of this variable in imgui.cpp for comments and details.
@ -197,6 +197,24 @@ typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // F
extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#endif #endif
//-------------------------------------------------------------------------
// [SECTION] STB libraries includes
//-------------------------------------------------------------------------
namespace ImStb
{
#undef IMSTB_TEXTEDIT_STRING
#undef IMSTB_TEXTEDIT_CHARTYPE
#define IMSTB_TEXTEDIT_STRING ImGuiInputTextState
#define IMSTB_TEXTEDIT_CHARTYPE ImWchar
#define IMSTB_TEXTEDIT_GETWIDTH_NEWLINE (-1.0f)
#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
#include "imstb_textedit.h"
} // namespace ImStb
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Macros // [SECTION] Macros
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -217,7 +235,6 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#else #else
#define IMGUI_DEBUG_LOG(...) ((void)0) #define IMGUI_DEBUG_LOG(...) ((void)0)
#endif #endif
#define IMGUI_DEBUG_LOG_ERROR(...) do { ImGuiContext& g2 = *GImGui; if (g2.DebugLogFlags & ImGuiDebugLogFlags_EventError) IMGUI_DEBUG_LOG(__VA_ARGS__); else g2.DebugLogSkippedErrors++; } while (0)
#define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
#define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
@ -239,6 +256,12 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer
#define IM_ASSERT_PARANOID(_EXPR) #define IM_ASSERT_PARANOID(_EXPR)
#endif #endif
// Error handling
// Down the line in some frameworks/languages we would like to have a way to redirect those to the programmer and recover from more faults.
#ifndef IM_ASSERT_USER_ERROR
#define IM_ASSERT_USER_ERROR(_EXP,_MSG) IM_ASSERT((_EXP) && _MSG) // Recoverable User Error
#endif
// Misc Macros // Misc Macros
#define IM_PI 3.14159265358979323846f #define IM_PI 3.14159265358979323846f
#ifdef _WIN32 #ifdef _WIN32
@ -360,7 +383,7 @@ IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end
IMGUI_API void ImStrTrimBlanks(char* str); // Remove leading and trailing blanks from a buffer. IMGUI_API void ImStrTrimBlanks(char* str); // Remove leading and trailing blanks from a buffer.
IMGUI_API const char* ImStrSkipBlank(const char* str); // Find first non-blank character. IMGUI_API const char* ImStrSkipBlank(const char* str); // Find first non-blank character.
IMGUI_API int ImStrlenW(const ImWchar* str); // Computer string length (ImWchar string) IMGUI_API int ImStrlenW(const ImWchar* str); // Computer string length (ImWchar string)
IMGUI_API const char* ImStrbol(const char* buf_mid_line, const char* buf_begin); // Find beginning-of-line IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line (ImWchar string)
IM_MSVC_RUNTIME_CHECKS_OFF IM_MSVC_RUNTIME_CHECKS_OFF
static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; } static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }
static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
@ -828,7 +851,7 @@ enum ImGuiDataTypePrivate_
enum ImGuiItemFlagsPrivate_ enum ImGuiItemFlagsPrivate_
{ {
// Controlled by user // Controlled by user
ImGuiItemFlags_Disabled = 1 << 10, // false // Disable interactions (DOES NOT affect visuals. DO NOT mix direct use of this with BeginDisabled(). See BeginDisabled()/EndDisabled() for full disable feature, and github #211). ImGuiItemFlags_Disabled = 1 << 10, // false // Disable interactions (DOES NOT affect visuals, see BeginDisabled()/EndDisabled() for full disable feature, and github #211).
ImGuiItemFlags_ReadOnly = 1 << 11, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. ImGuiItemFlags_ReadOnly = 1 << 11, // false // [ALPHA] Allow hovering interactions but underlying value is not changed.
ImGuiItemFlags_MixedValue = 1 << 12, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) ImGuiItemFlags_MixedValue = 1 << 12, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13, // false // Disable hoverable check in ItemHoverable() ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13, // false // Disable hoverable check in ItemHoverable()
@ -947,7 +970,6 @@ enum ImGuiTreeNodeFlagsPrivate_
{ {
ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader() ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader()
ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, but reversed trees (#6517) ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, but reversed trees (#6517)
ImGuiTreeNodeFlags_OpenOnMask_ = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow,
}; };
enum ImGuiSeparatorFlags_ enum ImGuiSeparatorFlags_
@ -1086,31 +1108,20 @@ struct IMGUI_API ImGuiInputTextDeactivatedState
ImGuiInputTextDeactivatedState() { memset(this, 0, sizeof(*this)); } ImGuiInputTextDeactivatedState() { memset(this, 0, sizeof(*this)); }
void ClearFreeMemory() { ID = 0; TextA.clear(); } void ClearFreeMemory() { ID = 0; TextA.clear(); }
}; };
// Forward declare imstb_textedit.h structure + make its main configuration define accessible
#undef IMSTB_TEXTEDIT_STRING
#undef IMSTB_TEXTEDIT_CHARTYPE
#define IMSTB_TEXTEDIT_STRING ImGuiInputTextState
#define IMSTB_TEXTEDIT_CHARTYPE char
#define IMSTB_TEXTEDIT_GETWIDTH_NEWLINE (-1.0f)
#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99
#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999
namespace ImStb { struct STB_TexteditState; }
typedef ImStb::STB_TexteditState ImStbTexteditState;
// Internal state of the currently focused/edited text input box // Internal state of the currently focused/edited text input box
// For a given item ID, access with ImGui::GetInputTextState() // For a given item ID, access with ImGui::GetInputTextState()
struct IMGUI_API ImGuiInputTextState struct IMGUI_API ImGuiInputTextState
{ {
ImGuiContext* Ctx; // parent UI context (needs to be set explicitly by parent). ImGuiContext* Ctx; // parent UI context (needs to be set explicitly by parent).
ImStbTexteditState* Stb; // State for stb_textedit.h
ImGuiID ID; // widget id owning the text state ImGuiID ID; // widget id owning the text state
int CurLenA; // UTF-8 length of the string in TextA (in bytes) int CurLenW, CurLenA; // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 length is valid even if TextA is not.
ImVector<char> TextA; // main UTF8 buffer. ImVector<ImWchar> TextW; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer.
ImVector<char> TextA; // temporary UTF8 buffer for callbacks and other operations. this is not updated in every code-path! size=capacity.
ImVector<char> InitialTextA; // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered) ImVector<char> InitialTextA; // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
ImVector<char> CallbackTextBackup; // temporary storage for callback to support automatic reconcile of undo-stack bool TextAIsValid; // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument)
int BufCapacityA; // end-user buffer capacity int BufCapacityA; // end-user buffer capacity
ImVec2 Scroll; // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y) float ScrollX; // horizontal scrolling/offset
ImStb::STB_TexteditState Stb; // state for stb_textedit.h
float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately float CursorAnim; // timer for cursor blink, reset on every user action so the cursor reappears immediately
bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!)
bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection
@ -1120,31 +1131,32 @@ struct IMGUI_API ImGuiInputTextState
int ReloadSelectionStart; // POSITIONS ARE IN IMWCHAR units *NOT* UTF-8 this is why this is not exposed yet. int ReloadSelectionStart; // POSITIONS ARE IN IMWCHAR units *NOT* UTF-8 this is why this is not exposed yet.
int ReloadSelectionEnd; int ReloadSelectionEnd;
ImGuiInputTextState(); ImGuiInputTextState() { memset(this, 0, sizeof(*this)); }
~ImGuiInputTextState(); void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); }
void ClearText() { CurLenA = 0; TextA[0] = 0; CursorClamp(); } void ClearFreeMemory() { TextW.clear(); TextA.clear(); InitialTextA.clear(); }
void ClearFreeMemory() { TextA.clear(); InitialTextA.clear(); } int GetUndoAvailCount() const { return Stb.undostate.undo_point; }
int GetRedoAvailCount() const { return IMSTB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; }
void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation
void OnCharPressed(unsigned int c);
// Cursor & Selection // Cursor & Selection
void CursorAnimReset(); void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking
void CursorClamp(); void CursorClamp() { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); }
bool HasSelection() const; bool HasSelection() const { return Stb.select_start != Stb.select_end; }
void ClearSelection(); void ClearSelection() { Stb.select_start = Stb.select_end = Stb.cursor; }
int GetCursorPos() const; int GetCursorPos() const { return Stb.cursor; }
int GetSelectionStart() const; int GetSelectionStart() const { return Stb.select_start; }
int GetSelectionEnd() const; int GetSelectionEnd() const { return Stb.select_end; }
void SelectAll(); void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; }
// Reload user buf (WIP #2890) // Reload user buf (WIP #2890)
// If you modify underlying user-passed const char* while active you need to call this (InputText V2 may lift this) // If you modify underlying user-passed const char* while active you need to call this (InputText V2 may lift this)
// strcpy(my_buf, "hello"); // strcpy(my_buf, "hello");
// if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item // if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item
// state->ReloadUserBufAndSelectAll(); // state->ReloadUserBufAndSelectAll();
void ReloadUserBufAndSelectAll(); void ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
void ReloadUserBufAndKeepSelection(); void ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb.select_start; ReloadSelectionEnd = Stb.select_end; }
void ReloadUserBufAndMoveToEnd(); void ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }
}; };
enum ImGuiWindowRefreshFlags_ enum ImGuiWindowRefreshFlags_
@ -1253,12 +1265,9 @@ struct ImGuiTreeNodeStackData
ImRect NavRect; // Used for nav landing ImRect NavRect; // Used for nav landing
}; };
// sizeof() = 20 struct IMGUI_API ImGuiStackSizes
struct IMGUI_API ImGuiErrorRecoveryState
{ {
short SizeOfWindowStack;
short SizeOfIDStack; short SizeOfIDStack;
short SizeOfTreeStack;
short SizeOfColorStack; short SizeOfColorStack;
short SizeOfStyleVarStack; short SizeOfStyleVarStack;
short SizeOfFontStack; short SizeOfFontStack;
@ -1268,16 +1277,18 @@ struct IMGUI_API ImGuiErrorRecoveryState
short SizeOfBeginPopupStack; short SizeOfBeginPopupStack;
short SizeOfDisabledStack; short SizeOfDisabledStack;
ImGuiErrorRecoveryState() { memset(this, 0, sizeof(*this)); } ImGuiStackSizes() { memset(this, 0, sizeof(*this)); }
void SetToContextState(ImGuiContext* ctx);
void CompareWithContextState(ImGuiContext* ctx);
}; };
// Data saved for each window pushed into the stack // Data saved for each window pushed into the stack
struct ImGuiWindowStackData struct ImGuiWindowStackData
{ {
ImGuiWindow* Window; ImGuiWindow* Window;
ImGuiLastItemData ParentLastItemDataBackup; ImGuiLastItemData ParentLastItemDataBackup;
ImGuiErrorRecoveryState StackSizesInBegin; // Store size of various stacks for asserting ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting
bool DisabledOverrideReenable; // Non-child window override disabled flag bool DisabledOverrideReenable; // Non-child window override disabled flag
}; };
struct ImGuiShrinkWidthItem struct ImGuiShrinkWidthItem
@ -1794,28 +1805,23 @@ struct ImGuiViewportP : public ImGuiViewport
ImDrawList* BgFgDrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. ImDrawList* BgFgDrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays.
ImDrawData DrawDataP; ImDrawData DrawDataP;
ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData
ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!)
// Per-viewport work area ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height).
// - Insets are >= 0.0f values, distance from viewport corners to work area. ImVec2 BuildWorkOffsetMin; // Work Area: Offset being built during current frame. Generally >= 0.0f.
// - BeginMainMenuBar() and DockspaceOverViewport() tend to use work area to avoid stepping over existing contents. ImVec2 BuildWorkOffsetMax; // Work Area: Offset being built during current frame. Generally <= 0.0f.
// - Generally 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land.
ImVec2 WorkInsetMin; // Work Area inset locked for the frame. GetWorkRect() always fits within GetMainRect().
ImVec2 WorkInsetMax; // "
ImVec2 BuildWorkInsetMin; // Work Area inset accumulator for current frame, to become next frame's WorkInset
ImVec2 BuildWorkInsetMax; // "
ImGuiViewportP() { BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; } ImGuiViewportP() { BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; }
~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); } ~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); }
// Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect)
ImVec2 CalcWorkRectPos(const ImVec2& inset_min) const { return ImVec2(Pos.x + inset_min.x, Pos.y + inset_min.y); } ImVec2 CalcWorkRectPos(const ImVec2& off_min) const { return ImVec2(Pos.x + off_min.x, Pos.y + off_min.y); }
ImVec2 CalcWorkRectSize(const ImVec2& inset_min, const ImVec2& inset_max) const { return ImVec2(ImMax(0.0f, Size.x - inset_min.x - inset_max.x), ImMax(0.0f, Size.y - inset_min.y - inset_max.y)); } ImVec2 CalcWorkRectSize(const ImVec2& off_min, const ImVec2& off_max) const { return ImVec2(ImMax(0.0f, Size.x - off_min.x + off_max.x), ImMax(0.0f, Size.y - off_min.y + off_max.y)); }
void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkInsetMin); WorkSize = CalcWorkRectSize(WorkInsetMin, WorkInsetMax); } // Update public fields void UpdateWorkRect() { WorkPos = CalcWorkRectPos(WorkOffsetMin); WorkSize = CalcWorkRectSize(WorkOffsetMin, WorkOffsetMax); } // Update public fields
// Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry) // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry)
ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); } ImRect GetWorkRect() const { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); }
ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkInsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkInsetMin, BuildWorkInsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); } ImRect GetBuildWorkRect() const { ImVec2 pos = CalcWorkRectPos(BuildWorkOffsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkOffsetMin, BuildWorkOffsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); }
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -1869,7 +1875,6 @@ enum ImGuiLocKey : int
ImGuiLocKey_WindowingMainMenuBar, ImGuiLocKey_WindowingMainMenuBar,
ImGuiLocKey_WindowingPopup, ImGuiLocKey_WindowingPopup,
ImGuiLocKey_WindowingUntitled, ImGuiLocKey_WindowingUntitled,
ImGuiLocKey_OpenLink_s,
ImGuiLocKey_CopyLink, ImGuiLocKey_CopyLink,
ImGuiLocKey_COUNT ImGuiLocKey_COUNT
}; };
@ -1880,21 +1885,6 @@ struct ImGuiLocEntry
const char* Text; const char* Text;
}; };
//-----------------------------------------------------------------------------
// [SECTION] Error handling, State recovery support
//-----------------------------------------------------------------------------
// Macros used by Recoverable Error handling
// - Only dispatch error if _EXPR: evaluate as assert (similar to an assert macro).
// - The message will always be a string literal, in order to increase likelihood of being display by an assert handler.
// - See 'Demo->Configuration->Error Handling' and ImGuiIO definitions for details on error handling.
// - Read https://github.com/ocornut/imgui/wiki/Error-Handling for details on error handling.
#ifndef IM_ASSERT_USER_ERROR
#define IM_ASSERT_USER_ERROR(_EXPR,_MSG) do { if (!(_EXPR) && ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } while (0) // Recoverable User Error
#endif
// The error callback is currently not public, as it is expected that only advanced users will rely on it.
typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Metrics, Debug Tools // [SECTION] Metrics, Debug Tools
@ -1904,19 +1894,16 @@ enum ImGuiDebugLogFlags_
{ {
// Event types // Event types
ImGuiDebugLogFlags_None = 0, ImGuiDebugLogFlags_None = 0,
ImGuiDebugLogFlags_EventError = 1 << 0, // Error submitted by IM_ASSERT_USER_ERROR() ImGuiDebugLogFlags_EventActiveId = 1 << 0,
ImGuiDebugLogFlags_EventActiveId = 1 << 1, ImGuiDebugLogFlags_EventFocus = 1 << 1,
ImGuiDebugLogFlags_EventFocus = 1 << 2, ImGuiDebugLogFlags_EventPopup = 1 << 2,
ImGuiDebugLogFlags_EventPopup = 1 << 3, ImGuiDebugLogFlags_EventNav = 1 << 3,
ImGuiDebugLogFlags_EventNav = 1 << 4, ImGuiDebugLogFlags_EventClipper = 1 << 4,
ImGuiDebugLogFlags_EventClipper = 1 << 5, ImGuiDebugLogFlags_EventSelection = 1 << 5,
ImGuiDebugLogFlags_EventSelection = 1 << 6, ImGuiDebugLogFlags_EventIO = 1 << 6,
ImGuiDebugLogFlags_EventIO = 1 << 7, ImGuiDebugLogFlags_EventInputRouting = 1 << 7,
ImGuiDebugLogFlags_EventInputRouting = 1 << 8,
ImGuiDebugLogFlags_EventDocking = 1 << 9, // Unused in this branch
ImGuiDebugLogFlags_EventViewport = 1 << 10, // Unused in this branch
ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport, ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting,
ImGuiDebugLogFlags_OutputToTTY = 1 << 20, // Also send output to TTY ImGuiDebugLogFlags_OutputToTTY = 1 << 20, // Also send output to TTY
ImGuiDebugLogFlags_OutputToTestEngine = 1 << 21, // Also send output to Test Engine ImGuiDebugLogFlags_OutputToTestEngine = 1 << 21, // Also send output to Test Engine
}; };
@ -2006,7 +1993,6 @@ struct ImGuiContext
bool Initialized; bool Initialized;
bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it. bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it.
ImGuiIO IO; ImGuiIO IO;
ImGuiPlatformIO PlatformIO;
ImGuiStyle Style; ImGuiStyle Style;
ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back()
float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window.
@ -2055,11 +2041,9 @@ struct ImGuiContext
ImVec2 WheelingAxisAvg; ImVec2 WheelingAxisAvg;
// Item/widgets state and tracking information // Item/widgets state and tracking information
ImGuiID DebugDrawIdConflicts; // Set when we detect multiple items with the same identifier
ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
ImGuiID HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredId; // Hovered widget, filled during the frame
ImGuiID HoveredIdPreviousFrame; ImGuiID HoveredIdPreviousFrame;
int HoveredIdPreviousFrameItemCount; // Count numbers of items using the same ID as last frame's hovered id
float HoveredIdTimer; // Measure contiguous hovering time float HoveredIdTimer; // Measure contiguous hovering time
float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active
bool HoveredIdAllowOverlap; bool HoveredIdAllowOverlap;
@ -2271,8 +2255,8 @@ struct ImGuiContext
ImGuiComboPreviewData ComboPreviewData; ImGuiComboPreviewData ComboPreviewData;
ImRect WindowResizeBorderExpectedRect; // Expected border rect, switch to relative edit if moving ImRect WindowResizeBorderExpectedRect; // Expected border rect, switch to relative edit if moving
bool WindowResizeRelativeMode; bool WindowResizeRelativeMode;
short ScrollbarSeekMode; // 0: scroll to clicked location, -1/+1: prev/next page. short ScrollbarSeekMode; // 0: relative, -1/+1: prev/next page.
float ScrollbarClickDeltaToGrabCenter; // When scrolling to mouse location: distance between mouse and center of grab box, normalized in parent space. float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage?
float SliderGrabClickOffset; float SliderGrabClickOffset;
float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. float SliderCurrentAccum; // Accumulated slider delta when using navigation controls.
bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it? bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it?
@ -2283,14 +2267,13 @@ struct ImGuiContext
short DisabledStackSize; short DisabledStackSize;
short LockMarkEdited; short LockMarkEdited;
short TooltipOverrideCount; short TooltipOverrideCount;
ImGuiWindow* TooltipPreviousWindow; // Window of last tooltip submitted during the frame
ImVector<char> ClipboardHandlerData; // If no custom clipboard handler is defined ImVector<char> ClipboardHandlerData; // If no custom clipboard handler is defined
ImVector<ImGuiID> MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once ImVector<ImGuiID> MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once
ImGuiTypingSelectState TypingSelectState; // State for GetTypingSelectRequest() ImGuiTypingSelectState TypingSelectState; // State for GetTypingSelectRequest()
// Platform support // Platform support
ImGuiPlatformImeData PlatformImeData; // Data updated by current frame ImGuiPlatformImeData PlatformImeData; // Data updated by current frame
ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data. When changed we call the platform_io.Platform_SetImeDataFn() handler. ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data. When changed we call the io.PlatformSetImeDataFn() handler.
// Settings // Settings
bool SettingsLoaded; bool SettingsLoaded;
@ -2318,22 +2301,11 @@ struct ImGuiContext
int LogDepthToExpand; int LogDepthToExpand;
int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call.
// Error Handling
ImGuiErrorCallback ErrorCallback; // = NULL. May be exposed in public API eventually.
void* ErrorCallbackUserData; // = NULL
ImVec2 ErrorTooltipLockedPos;
bool ErrorFirst;
int ErrorCountCurrentFrame; // [Internal] Number of errors submitted this frame.
ImGuiErrorRecoveryState StackSizesInNewFrame; // [Internal]
ImGuiErrorRecoveryState*StackSizesInBeginForCurrentWindow; // [Internal]
// Debug Tools // Debug Tools
// (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.) // (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.)
int DebugDrawIdConflictsCount; // Locked count (preserved when holding CTRL)
ImGuiDebugLogFlags DebugLogFlags; ImGuiDebugLogFlags DebugLogFlags;
ImGuiTextBuffer DebugLogBuf; ImGuiTextBuffer DebugLogBuf;
ImGuiTextIndex DebugLogIndex; ImGuiTextIndex DebugLogIndex;
int DebugLogSkippedErrors;
ImGuiDebugLogFlags DebugLogAutoDisableFlags; ImGuiDebugLogFlags DebugLogAutoDisableFlags;
ImU8 DebugLogAutoDisableFrames; ImU8 DebugLogAutoDisableFrames;
ImU8 DebugLocateFrames; // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above. ImU8 DebugLocateFrames; // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above.
@ -2360,7 +2332,208 @@ struct ImGuiContext
ImVector<char> TempBuffer; // Temporary text buffer ImVector<char> TempBuffer; // Temporary text buffer
char TempKeychordName[64]; char TempKeychordName[64];
ImGuiContext(ImFontAtlas* shared_font_atlas); ImGuiContext(ImFontAtlas* shared_font_atlas)
{
IO.Ctx = this;
InputTextState.Ctx = this;
Initialized = false;
FontAtlasOwnedByContext = shared_font_atlas ? false : true;
Font = NULL;
FontSize = FontBaseSize = FontScale = CurrentDpiScale = 0.0f;
IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
Time = 0.0f;
FrameCount = 0;
FrameCountEnded = FrameCountRendered = -1;
WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;
GcCompactAll = false;
TestEngineHookItems = false;
TestEngine = NULL;
memset(ContextName, 0, sizeof(ContextName));
InputEventsNextMouseSource = ImGuiMouseSource_Mouse;
InputEventsNextEventId = 1;
WindowsActiveCount = 0;
CurrentWindow = NULL;
HoveredWindow = NULL;
HoveredWindowUnderMovingWindow = NULL;
HoveredWindowBeforeClear = NULL;
MovingWindow = NULL;
WheelingWindow = NULL;
WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;
WheelingWindowReleaseTimer = 0.0f;
DebugHookIdInfo = 0;
HoveredId = HoveredIdPreviousFrame = 0;
HoveredIdAllowOverlap = false;
HoveredIdIsDisabled = false;
HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
ItemUnclipByLog = false;
ActiveId = 0;
ActiveIdIsAlive = 0;
ActiveIdTimer = 0.0f;
ActiveIdIsJustActivated = false;
ActiveIdAllowOverlap = false;
ActiveIdNoClearOnFocusLoss = false;
ActiveIdHasBeenPressedBefore = false;
ActiveIdHasBeenEditedBefore = false;
ActiveIdHasBeenEditedThisFrame = false;
ActiveIdFromShortcut = false;
ActiveIdClickOffset = ImVec2(-1, -1);
ActiveIdWindow = NULL;
ActiveIdSource = ImGuiInputSource_None;
ActiveIdMouseButton = -1;
ActiveIdPreviousFrame = 0;
ActiveIdPreviousFrameIsAlive = false;
ActiveIdPreviousFrameHasBeenEditedBefore = false;
ActiveIdPreviousFrameWindow = NULL;
LastActiveId = 0;
LastActiveIdTimer = 0.0f;
LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;
ActiveIdUsingNavDirMask = 0x00;
ActiveIdUsingAllKeyboardKeys = false;
CurrentFocusScopeId = 0;
CurrentItemFlags = ImGuiItemFlags_None;
DebugShowGroupRects = false;
NavWindow = NULL;
NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
NavLayer = ImGuiNavLayer_Main;
NavNextActivateId = 0;
NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
NavHighlightActivatedId = 0;
NavHighlightActivatedTimer = 0.0f;
NavInputSource = ImGuiInputSource_Keyboard;
NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
NavIdIsAlive = false;
NavMousePosDirty = false;
NavDisableHighlight = true;
NavDisableMouseHover = false;
NavAnyRequest = false;
NavInitRequest = false;
NavInitRequestFromMove = false;
NavMoveSubmitted = false;
NavMoveScoringItems = false;
NavMoveForwardToNextFrame = false;
NavMoveFlags = ImGuiNavMoveFlags_None;
NavMoveScrollFlags = ImGuiScrollFlags_None;
NavMoveKeyMods = ImGuiMod_None;
NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
NavScoringDebugCount = 0;
NavTabbingDir = 0;
NavTabbingCounter = 0;
NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0;
NavJustMovedToKeyMods = ImGuiMod_None;
NavJustMovedToIsTabbing = false;
NavJustMovedToHasSelectionData = false;
// All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac...
// FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this..
ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab);
ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab);
NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
NavWindowingToggleLayer = false;
NavWindowingToggleKey = ImGuiKey_None;
DimBgRatio = 0.0f;
DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;
DragDropSourceFlags = ImGuiDragDropFlags_None;
DragDropSourceFrameCount = -1;
DragDropMouseButton = -1;
DragDropTargetId = 0;
DragDropAcceptFlags = ImGuiDragDropFlags_None;
DragDropAcceptIdCurrRectSurface = 0.0f;
DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;
DragDropAcceptFrameCount = -1;
DragDropHoldJustPressedId = 0;
memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));
ClipperTempDataStacked = 0;
CurrentTable = NULL;
TablesTempDataStacked = 0;
CurrentTabBar = NULL;
CurrentMultiSelect = NULL;
MultiSelectTempDataStacked = 0;
HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
MouseCursor = ImGuiMouseCursor_Arrow;
MouseStationaryTimer = 0.0f;
TempInputId = 0;
memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue));
BeginMenuDepth = BeginComboDepth = 0;
ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
ColorEditCurrentID = ColorEditSavedID = 0;
ColorEditSavedHue = ColorEditSavedSat = 0.0f;
ColorEditSavedColor = 0;
WindowResizeRelativeMode = false;
ScrollbarSeekMode = 0;
ScrollbarClickDeltaToGrabCenter = 0.0f;
SliderGrabClickOffset = 0.0f;
SliderCurrentAccum = 0.0f;
SliderCurrentAccumDirty = false;
DragCurrentAccumDirty = false;
DragCurrentAccum = 0.0f;
DragSpeedDefaultRatio = 1.0f / 100.0f;
DisabledAlphaBackup = 0.0f;
DisabledStackSize = 0;
LockMarkEdited = 0;
TooltipOverrideCount = 0;
PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
SettingsLoaded = false;
SettingsDirtyTimer = 0.0f;
HookIdNext = 0;
memset(LocalizationTable, 0, sizeof(LocalizationTable));
LogEnabled = false;
LogType = ImGuiLogType_None;
LogNextPrefix = LogNextSuffix = NULL;
LogFile = NULL;
LogLinePosY = FLT_MAX;
LogLineFirstItem = false;
LogDepthRef = 0;
LogDepthToExpand = LogDepthToExpandDefault = 2;
DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY;
DebugLocateId = 0;
DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
DebugLogAutoDisableFrames = 0;
DebugLocateFrames = 0;
DebugBeginReturnValueCullDepth = -1;
DebugItemPickerActive = false;
DebugItemPickerMouseButton = ImGuiMouseButton_Left;
DebugItemPickerBreakId = 0;
DebugFlashStyleColorTime = 0.0f;
DebugFlashStyleColorIdx = ImGuiCol_COUNT;
// Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
DebugBreakInWindow = 0;
DebugBreakInTable = 0;
DebugBreakInLocateId = false;
DebugBreakKeyChord = ImGuiKey_Pause;
DebugBreakInShortcutRouting = ImGuiKey_None;
memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
FramerateSecPerFrameAccum = 0.0f;
WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;
memset(TempKeychordName, 0, sizeof(TempKeychordName));
}
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -2437,7 +2610,7 @@ struct IMGUI_API ImGuiWindow
ImVec2 WindowPadding; // Window padding at the time of Begin(). ImVec2 WindowPadding; // Window padding at the time of Begin().
float WindowRounding; // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc. float WindowRounding; // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc.
float WindowBorderSize; // Window border size at the time of Begin(). float WindowBorderSize; // Window border size at the time of Begin().
float TitleBarHeight, MenuBarHeight; // Note that those used to be function before 2024/05/28. If you have old code calling TitleBarHeight() you can change it to TitleBarHeight. float TitleBarHeight, MenuBarHeight;
float DecoOuterSizeX1, DecoOuterSizeY1; // Left/Up offsets. Sum of non-scrolling outer decorations (X1 generally == 0.0f. Y1 generally = TitleBarHeight + MenuBarHeight). Locked during Begin(). float DecoOuterSizeX1, DecoOuterSizeY1; // Left/Up offsets. Sum of non-scrolling outer decorations (X1 generally == 0.0f. Y1 generally = TitleBarHeight + MenuBarHeight). Locked during Begin().
float DecoOuterSizeX2, DecoOuterSizeY2; // Right/Down offsets (X2 generally == ScrollbarSize.x, Y2 == ScrollbarSizes.y). float DecoOuterSizeX2, DecoOuterSizeY2; // Right/Down offsets (X2 generally == ScrollbarSize.x, Y2 == ScrollbarSizes.y).
float DecoInnerSizeX1, DecoInnerSizeY1; // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes). float DecoInnerSizeX1, DecoInnerSizeY1; // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes).
@ -2534,7 +2707,6 @@ public:
ImGuiID GetID(const char* str, const char* str_end = NULL); ImGuiID GetID(const char* str, const char* str_end = NULL);
ImGuiID GetID(const void* ptr); ImGuiID GetID(const void* ptr);
ImGuiID GetID(int n); ImGuiID GetID(int n);
ImGuiID GetIDFromPos(const ImVec2& p_abs);
ImGuiID GetIDFromRectangle(const ImRect& r_abs); ImGuiID GetIDFromRectangle(const ImRect& r_abs);
// We don't use g.FontSize because the window may be != g.CurrentWindow. // We don't use g.FontSize because the window may be != g.CurrentWindow.
@ -2583,10 +2755,9 @@ struct ImGuiTabItem
ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; } ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; }
}; };
// Storage for a tab bar (sizeof() 160 bytes) // Storage for a tab bar (sizeof() 152 bytes)
struct IMGUI_API ImGuiTabBar struct IMGUI_API ImGuiTabBar
{ {
ImGuiWindow* Window;
ImVector<ImGuiTabItem> Tabs; ImVector<ImGuiTabItem> Tabs;
ImGuiTabBarFlags Flags; ImGuiTabBarFlags Flags;
ImGuiID ID; // Zero for tab-bars used by docking ImGuiID ID; // Zero for tab-bars used by docking
@ -2647,7 +2818,6 @@ struct ImGuiTableColumn
float MaxX; float MaxX;
float WidthRequest; // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout() float WidthRequest; // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout()
float WidthAuto; // Automatic width float WidthAuto; // Automatic width
float WidthMax; // Maximum width (FIXME: overwritten by each instance)
float StretchWeight; // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially. float StretchWeight; // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially.
float InitStretchWeightOrWidth; // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_). float InitStretchWeightOrWidth; // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_).
ImRect ClipRect; // Clipping rectangle for the column ImRect ClipRect; // Clipping rectangle for the column
@ -2946,8 +3116,8 @@ namespace ImGui
inline void SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; } inline void SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; }
inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); }
inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); }
inline ImVec2 WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); }
inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); } inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); }
inline ImVec2 WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); }
// Windows: Display Order and Focus Order // Windows: Display Order and Focus Order
IMGUI_API void FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0); IMGUI_API void FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0);
@ -3127,7 +3297,7 @@ namespace ImGui
inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; } inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; }
inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; } inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; }
inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; } inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; }
inline bool IsLRModKey(ImGuiKey key) { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; } inline bool IsModKey(ImGuiKey key) { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; }
ImGuiKeyChord FixupKeyChord(ImGuiKeyChord key_chord); ImGuiKeyChord FixupKeyChord(ImGuiKeyChord key_chord);
inline ImGuiKey ConvertSingleModFlagToKey(ImGuiKey key) inline ImGuiKey ConvertSingleModFlagToKey(ImGuiKey key)
{ {
@ -3291,7 +3461,7 @@ namespace ImGui
IMGUI_API ImRect TableGetCellBgRect(const ImGuiTable* table, int column_n); IMGUI_API ImRect TableGetCellBgRect(const ImGuiTable* table, int column_n);
IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n); IMGUI_API const char* TableGetColumnName(const ImGuiTable* table, int column_n);
IMGUI_API ImGuiID TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no = 0); IMGUI_API ImGuiID TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no = 0);
IMGUI_API float TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n); IMGUI_API float TableGetMaxColumnWidth(const ImGuiTable* table, int column_n);
IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n);
IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table);
IMGUI_API void TableRemove(ImGuiTable* table); IMGUI_API void TableRemove(ImGuiTable* table);
@ -3319,7 +3489,6 @@ namespace ImGui
IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id);
IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
IMGUI_API void TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); IMGUI_API void TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
IMGUI_API void TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name);
IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset); IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset);
IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImVec2 mouse_pos); IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImVec2 mouse_pos);
IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar);
@ -3337,7 +3506,7 @@ namespace ImGui
IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);
IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f);
IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);
IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_None); // Navigation highlight IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_None); // Navigation highlight
@ -3402,7 +3571,6 @@ namespace ImGui
IMGUI_API bool DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty = NULL); IMGUI_API bool DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty = NULL);
IMGUI_API int DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2); IMGUI_API int DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2);
IMGUI_API bool DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max); IMGUI_API bool DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max);
IMGUI_API bool DataTypeIsZero(ImGuiDataType data_type, const void* p_data);
// InputText // InputText
IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
@ -3431,18 +3599,11 @@ namespace ImGui
IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window); IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window);
IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window); IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window);
// Error handling, State Recovery
IMGUI_API bool ErrorLog(const char* msg);
IMGUI_API void ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out);
IMGUI_API void ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in);
IMGUI_API void ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in);
IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
IMGUI_API void ErrorCheckEndFrameFinalizeErrorTooltip();
IMGUI_API bool BeginErrorTooltip();
IMGUI_API void EndErrorTooltip();
// Debug Tools // Debug Tools
IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free
IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
IMGUI_API void DebugDrawCursorPos(ImU32 col = IM_COL32(255, 0, 0, 255)); IMGUI_API void DebugDrawCursorPos(ImU32 col = IM_COL32(255, 0, 0, 255));
IMGUI_API void DebugDrawLineExtents(ImU32 col = IM_COL32(255, 0, 0, 255)); IMGUI_API void DebugDrawLineExtents(ImU32 col = IM_COL32(255, 0, 0, 255));
IMGUI_API void DebugDrawItemRect(ImU32 col = IM_COL32(255, 0, 0, 255)); IMGUI_API void DebugDrawItemRect(ImU32 col = IM_COL32(255, 0, 0, 255));
@ -3530,7 +3691,7 @@ extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiI
// In IMGUI_VERSION_NUM >= 18934: changed IMGUI_TEST_ENGINE_ITEM_ADD(bb,id) to IMGUI_TEST_ENGINE_ITEM_ADD(id,bb,item_data); // In IMGUI_VERSION_NUM >= 18934: changed IMGUI_TEST_ENGINE_ITEM_ADD(bb,id) to IMGUI_TEST_ENGINE_ITEM_ADD(id,bb,item_data);
#define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _ID, _BB, _ITEM_DATA) // Register item bounding box #define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _ID, _BB, _ITEM_DATA) // Register item bounding box
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional)
#define IMGUI_TEST_ENGINE_LOG(_FMT,...) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log #define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log
#else #else
#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) ((void)0) #define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) ((void)0)
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g) #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g)

View File

@ -1,4 +1,4 @@
// dear imgui, v1.91.3 // dear imgui, v1.91.0
// (tables and columns code) // (tables and columns code)
/* /*
@ -328,7 +328,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
// - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[]. // - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0; const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;
const ImVec2 avail_size = GetContentRegionAvail(); const ImVec2 avail_size = GetContentRegionAvail();
const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f)); const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);
const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);
const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows! const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows!
if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size) if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)
@ -460,27 +460,16 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;
inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
// Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752) // Make left and top borders not overlap our contents by offsetting HostClipRect (#6765)
// (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the // (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the
// limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap // limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap
// problem only affect scrolling tables in this case we can get away with doing it without extra cost). // problem only affect scrolling tables in this case we can get away with doing it without extra cost).
if (inner_window != outer_window) if (inner_window != outer_window)
{ {
// FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize,
// it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with
// different x/y values to BeginChild().
if (flags & ImGuiTableFlags_BordersOuterV) if (flags & ImGuiTableFlags_BordersOuterV)
{
table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x); table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x);
if (inner_window->DecoOuterSizeX2 == 0.0f)
table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x);
}
if (flags & ImGuiTableFlags_BordersOuterH) if (flags & ImGuiTableFlags_BordersOuterH)
{
table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y); table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y);
if (inner_window->DecoOuterSizeY2 == 0.0f)
table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y);
}
} }
// Padding and Spacing // Padding and Spacing
@ -508,7 +497,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect; table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect;
table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width
table->InnerClipRect.ClipWithFull(table->HostClipRect); table->InnerClipRect.ClipWithFull(table->HostClipRect);
table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.Max.y; table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : inner_window->ClipRect.Max.y;
table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow
table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow() table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow()
@ -866,7 +855,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
// Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping) // Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping)
// Combine width from regular rows + width from headers unless requested not to. // Combine width from regular rows + width from headers unless requested not to.
if (!column->IsPreserveWidthAuto && table->InstanceCurrent == 0) if (!column->IsPreserveWidthAuto)
column->WidthAuto = TableGetColumnWidthAuto(table, column); column->WidthAuto = TableGetColumnWidthAuto(table, column);
// Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto) // Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto)
@ -1070,12 +1059,16 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
continue; continue;
} }
// Detect hovered column
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
// Lock start position // Lock start position
column->MinX = offset_x; column->MinX = offset_x;
// Lock width based on start position and minimum/maximum width for this position // Lock width based on start position and minimum/maximum width for this position
column->WidthMax = TableCalcMaxColumnWidth(table, column_n); float max_width = TableGetMaxColumnWidth(table, column_n);
column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax); column->WidthGiven = ImMin(column->WidthGiven, max_width);
column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth)); column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth));
column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;
@ -1124,13 +1117,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
column->Flags |= ImGuiTableColumnFlags_IsVisible; column->Flags |= ImGuiTableColumnFlags_IsVisible;
if (column->SortOrder != -1) if (column->SortOrder != -1)
column->Flags |= ImGuiTableColumnFlags_IsSorted; column->Flags |= ImGuiTableColumnFlags_IsSorted;
if (table->HoveredColumnBody == column_n)
// Detect hovered column
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
{
column->Flags |= ImGuiTableColumnFlags_IsHovered; column->Flags |= ImGuiTableColumnFlags_IsHovered;
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
}
// Alignment // Alignment
// FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in // FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in
@ -1261,7 +1249,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
if (table->Flags & ImGuiTableFlags_NoClip) if (table->Flags & ImGuiTableFlags_NoClip)
table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP); table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
else else
inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect? inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false);
} }
// Process hit-testing on resizing borders. Actual size change will be applied in EndTable() // Process hit-testing on resizing borders. Actual size change will be applied in EndTable()
@ -1484,9 +1472,7 @@ void ImGui::EndTable()
{ {
short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask; short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;
inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently. inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently.
g.CurrentTable = NULL; // To avoid error recovery recursing
EndChild(); EndChild();
g.CurrentTable = table;
inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask; inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
} }
else else
@ -2013,7 +1999,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
{ {
for (int column_n = 0; column_n < table->ColumnsCount; column_n++) for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main; table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;
const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y); const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y; table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
if (unfreeze_rows_actual) if (unfreeze_rows_actual)
@ -2022,8 +2008,8 @@ void ImGui::TableEndRow(ImGuiTable* table)
table->IsUnfrozenRows = true; table->IsUnfrozenRows = true;
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, table->InnerClipRect.Max.y); table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = table->InnerClipRect.Max.y; table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen; table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y); IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
@ -2209,8 +2195,8 @@ void ImGui::TableEndCell(ImGuiTable* table)
// Note that actual columns widths are computed in TableUpdateLayout(). // Note that actual columns widths are computed in TableUpdateLayout().
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis. // Maximum column content width given current layout. Use column->MinX so this value on a per-column basis.
float ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n) float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n)
{ {
const ImGuiTableColumn* column = &table->Columns[column_n]; const ImGuiTableColumn* column = &table->Columns[column_n];
float max_width = FLT_MAX; float max_width = FLT_MAX;
@ -2272,7 +2258,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width)
// Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded) // Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
IM_ASSERT(table->MinColumnWidth > 0.0f); IM_ASSERT(table->MinColumnWidth > 0.0f);
const float min_width = table->MinColumnWidth; const float min_width = table->MinColumnWidth;
const float max_width = ImMax(min_width, column_0->WidthMax); // Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933) const float max_width = ImMax(min_width, TableGetMaxColumnWidth(table, column_n));
column_0_width = ImClamp(column_0_width, min_width, max_width); column_0_width = ImClamp(column_0_width, min_width, max_width);
if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width) if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
return; return;
@ -2757,7 +2743,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
const ImU32 outer_col = table->BorderColorStrong; const ImU32 outer_col = table->BorderColorStrong;
if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)
{ {
inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size); inner_drawlist->AddRect(outer_border.Min, outer_border.Max + ImVec2(1, 1), outer_col, 0.0f, 0, border_size);
} }
else if (table->Flags & ImGuiTableFlags_BordersOuterV) else if (table->Flags & ImGuiTableFlags_BordersOuterV)
{ {
@ -3021,8 +3007,7 @@ float ImGui::TableGetHeaderAngledMaxLabelWidth()
// The intent is that advanced users willing to create customized headers would not need to use this helper // The intent is that advanced users willing to create customized headers would not need to use this helper
// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets. // and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this. // See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
// This code is intentionally written to not make much use of internal functions, to give you better direction // This code is constructed to not make much use of internal functions, as it is intended to be a template to copy.
// if you need to write your own.
// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public. // FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
void ImGui::TableHeadersRow() void ImGui::TableHeadersRow()
{ {
@ -3030,8 +3015,7 @@ void ImGui::TableHeadersRow()
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!"); IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
// Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make // Layout if not already done (this is automatically done by TableNextRow, we do it here solely to facilitate stepping in debugger as it is frequent to step in TableUpdateLayout)
// it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this.
if (!table->IsLayoutLocked) if (!table->IsLayoutLocked)
TableUpdateLayout(table); TableUpdateLayout(table);
@ -3048,7 +3032,8 @@ void ImGui::TableHeadersRow()
if (!TableSetColumnIndex(column_n)) if (!TableSetColumnIndex(column_n))
continue; continue;
// Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation) // Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)
// In your own code you may omit the PushID/PopID all-together, provided you know they won't collide.
const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n); const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n);
PushID(column_n); PushID(column_n);
TableHeader(name); TableHeader(name);
@ -4411,7 +4396,7 @@ void ImGui::EndColumns()
{ {
ButtonBehavior(column_hit_rect, column_id, &hovered, &held); ButtonBehavior(column_hit_rect, column_id, &hovered, &held);
if (hovered || held) if (hovered || held)
SetMouseCursor(ImGuiMouseCursor_ResizeEW); g.MouseCursor = ImGuiMouseCursor_ResizeEW;
if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize)) if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize))
dragging_column = n; dragging_column = n;
} }
@ -4443,12 +4428,12 @@ void ImGui::EndColumns()
NavUpdateCurrentWindowIsScrollPushableX(); NavUpdateCurrentWindowIsScrollPushableX();
} }
void ImGui::Columns(int columns_count, const char* id, bool borders) void ImGui::Columns(int columns_count, const char* id, bool border)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindow();
IM_ASSERT(columns_count >= 1); IM_ASSERT(columns_count >= 1);
ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder); ImGuiOldColumnFlags flags = (border ? 0 : ImGuiOldColumnFlags_NoBorder);
//flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior //flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior
ImGuiOldColumns* columns = window->DC.CurrentColumns; ImGuiOldColumns* columns = window->DC.CurrentColumns;
if (columns != NULL && columns->Count == columns_count && columns->Flags == flags) if (columns != NULL && columns->Count == columns_count && columns->Flags == flags)

File diff suppressed because it is too large Load Diff

View File

@ -3,8 +3,6 @@
// Those changes would need to be pushed into nothings/stb: // Those changes would need to be pushed into nothings/stb:
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783) // - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
// - Added name to struct or it may be forward declared in our code.
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
// Grep for [DEAR IMGUI] to find the changes. // Grep for [DEAR IMGUI] to find the changes.
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_* // - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
@ -211,7 +209,6 @@
// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) // int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) // int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key) // void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
// void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)
// //
// Each of these functions potentially updates the string and updates the // Each of these functions potentially updates the string and updates the
// state. // state.
@ -246,12 +243,7 @@
// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit // various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is // set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to // clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
// anything other type you want before including. // anything other type you wante before including.
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
// transformed into text and stb_textedit_text() is automatically called.
//
// text: [DEAR IMGUI] added 2024-09
// call this to text inputs sent to the textfield.
// //
// //
// When rendering, you can read the cursor position and selection state from // When rendering, you can read the cursor position and selection state from
@ -326,7 +318,7 @@ typedef struct
int undo_char_point, redo_char_point; int undo_char_point, redo_char_point;
} StbUndoState; } StbUndoState;
typedef struct STB_TexteditState typedef struct
{ {
///////////////////// /////////////////////
// //
@ -446,13 +438,13 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
if (x < r.x1) { if (x < r.x1) {
// search characters in row for one that straddles 'x' // search characters in row for one that straddles 'x'
prev_x = r.x0; prev_x = r.x0;
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) { for (k=0; k < r.num_chars; ++k) {
float w = STB_TEXTEDIT_GETWIDTH(str, i, k); float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
if (x < prev_x+w) { if (x < prev_x+w) {
if (x < prev_x+w/2) if (x < prev_x+w/2)
return k+i; return k+i;
else else
return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k); return k+i+1;
} }
prev_x += w; prev_x += w;
} }
@ -571,7 +563,7 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
// now scan to find xpos // now scan to find xpos
find->x = r.x0; find->x = r.x0;
for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first) for (i=0; first+i < n; ++i)
find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
} }
@ -648,17 +640,6 @@ static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditSt
} }
} }
// [DEAR IMGUI]
// Functions must be implemented for UTF8 support
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx) (idx - 1)
#endif
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx) (idx + 1)
#endif
#ifdef STB_TEXTEDIT_IS_SPACE #ifdef STB_TEXTEDIT_IS_SPACE
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx ) static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
{ {
@ -739,44 +720,36 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
#define STB_TEXTEDIT_KEYTYPE int #define STB_TEXTEDIT_KEYTYPE int
#endif #endif
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
{
// can't add newline in single-line mode
if (text[0] == '\n' && state->single_line)
return;
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
state->cursor += text_len;
state->has_preferred_x = 0;
}
}
else {
stb_textedit_delete_selection(str, state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
stb_text_makeundo_insert(state, state->cursor, text_len);
state->cursor += text_len;
state->has_preferred_x = 0;
}
}
}
// API key: process a keyboard input // API key: process a keyboard input
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
{ {
retry: retry:
switch (key) { switch (key) {
default: { default: {
#ifdef STB_TEXTEDIT_KEYTOTEXT
int c = STB_TEXTEDIT_KEYTOTEXT(key); int c = STB_TEXTEDIT_KEYTOTEXT(key);
if (c > 0) { if (c > 0) {
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c; IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE) c;
stb_textedit_text(str, state, &ch, 1);
// can't add newline in single-line mode
if (c == '\n' && state->single_line)
break;
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
++state->cursor;
state->has_preferred_x = 0;
}
} else {
stb_textedit_delete_selection(str,state); // implicitly clamps
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
stb_text_makeundo_insert(state, state->cursor, 1);
++state->cursor;
state->has_preferred_x = 0;
}
}
} }
#endif
break; break;
} }
@ -802,7 +775,7 @@ retry:
stb_textedit_move_to_first(state); stb_textedit_move_to_first(state);
else else
if (state->cursor > 0) if (state->cursor > 0)
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor); --state->cursor;
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
@ -811,7 +784,7 @@ retry:
if (STB_TEXT_HAS_SELECTION(state)) if (STB_TEXT_HAS_SELECTION(state))
stb_textedit_move_to_last(str, state); stb_textedit_move_to_last(str, state);
else else
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); ++state->cursor;
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
@ -821,7 +794,7 @@ retry:
stb_textedit_prep_selection_at_cursor(state); stb_textedit_prep_selection_at_cursor(state);
// move selection left // move selection left
if (state->select_end > 0) if (state->select_end > 0)
state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end); --state->select_end;
state->cursor = state->select_end; state->cursor = state->select_end;
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
@ -871,7 +844,7 @@ retry:
case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
stb_textedit_prep_selection_at_cursor(state); stb_textedit_prep_selection_at_cursor(state);
// move selection right // move selection right
state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end); ++state->select_end;
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
state->cursor = state->select_end; state->cursor = state->select_end;
state->has_preferred_x = 0; state->has_preferred_x = 0;
@ -927,7 +900,7 @@ retry:
x += dx; x += dx;
if (x > goal_x) if (x > goal_x)
break; break;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); ++state->cursor;
} }
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
@ -989,7 +962,7 @@ retry:
x += dx; x += dx;
if (x > goal_x) if (x > goal_x)
break; break;
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor); ++state->cursor;
} }
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
@ -1017,7 +990,7 @@ retry:
else { else {
int n = STB_TEXTEDIT_STRINGLEN(str); int n = STB_TEXTEDIT_STRINGLEN(str);
if (state->cursor < n) if (state->cursor < n)
stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor); stb_textedit_delete(str, state, state->cursor, 1);
} }
state->has_preferred_x = 0; state->has_preferred_x = 0;
break; break;
@ -1029,9 +1002,8 @@ retry:
else { else {
stb_textedit_clamp(str, state); stb_textedit_clamp(str, state);
if (state->cursor > 0) { if (state->cursor > 0) {
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor); stb_textedit_delete(str, state, state->cursor-1, 1);
stb_textedit_delete(str, state, prev, state->cursor - prev); --state->cursor;
state->cursor = prev;
} }
} }
state->has_preferred_x = 0; state->has_preferred_x = 0;

View File

@ -480,9 +480,8 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2) for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
{ {
// Check for valid range. This may also help detect *some* dangling pointers, because a common // Check for valid range. This may also help detect *some* dangling pointers, because a common
// user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent, // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent.
// or to forget to zero-terminate the glyph range array. IM_ASSERT(src_range[0] <= src_range[1]);
IM_ASSERT(src_range[0] <= src_range[1] && "Invalid range: is your glyph range array persistent? it is zero-terminated?");
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]); src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
} }
dst_tmp.SrcCount++; dst_tmp.SrcCount++;

View File

@ -23,11 +23,7 @@ if (NOT TARGET SDL3::SDL3)
#GIT_TAG e949f12f63cdfcef4bdf456936ee676e0a3f9de6 # tip 18-07-2024 - broke bmp surface (before too) #GIT_TAG e949f12f63cdfcef4bdf456936ee676e0a3f9de6 # tip 18-07-2024 - broke bmp surface (before too)
#GIT_TAG 67b973b5fad633b3be76d4daf4fd9fece292c25f # tip 29-07-2024 #GIT_TAG 67b973b5fad633b3be76d4daf4fd9fece292c25f # tip 29-07-2024
#GIT_TAG 6e885d96193a4b0096fe7fed6d4e6c3e5f247283 # tip 09-09-2024 #GIT_TAG 6e885d96193a4b0096fe7fed6d4e6c3e5f247283 # tip 09-09-2024
#GIT_TAG 9dd8859240703d886941733ad32c1dc6f50d64f0 # tip 19-09-2024 GIT_TAG 9dd8859240703d886941733ad32c1dc6f50d64f0 # tip 19-09-2024
#GIT_TAG afdf325fb4090e93a124519d1a3bc1fbe0ba9025 # bad
#GIT_TAG e292d1f5ace469f718d7b6b4dec8c28e37dcaa0e # tip 05-10-2024 (3.1.3)
#GIT_TAG 2654d5d48b8f764148a7c246fea85b32b1133578 # tip 18-10-2024
GIT_TAG f8468d580d903e106640800034a4721aca24264c # tip 15-11-2024
FIND_PACKAGE_ARGS # for the future FIND_PACKAGE_ARGS # for the future
) )

View File

@ -22,8 +22,7 @@ if (NOT TARGET SDL3_image::SDL3_image)
#GIT_TAG 8abc07df88cc035997e797967ac2f479b0e50981 # tip 18-07-2024 #GIT_TAG 8abc07df88cc035997e797967ac2f479b0e50981 # tip 18-07-2024
#GIT_TAG 2a27018eda394a4e005cd8ba6bb3bfd0298809c7 # tip 29-07-2024 #GIT_TAG 2a27018eda394a4e005cd8ba6bb3bfd0298809c7 # tip 29-07-2024
#GIT_TAG c1b272450d306db3845086cc231acae736b92f4f # tip 09-09-2024 #GIT_TAG c1b272450d306db3845086cc231acae736b92f4f # tip 09-09-2024
#GIT_TAG b56e6c4d1a1c03b3904a8ad21f1fa73c651ffbfc # tip 17-09-2024 GIT_TAG b56e6c4d1a1c03b3904a8ad21f1fa73c651ffbfc # tip 17-09-2024
GIT_TAG 6f4584340b9b78542d11bf0232a1a0862de1f0a9 # tip 25-09-2024
FIND_PACKAGE_ARGS # for the future FIND_PACKAGE_ARGS # for the future
) )
FetchContent_MakeAvailable(SDL3_image) FetchContent_MakeAvailable(SDL3_image)

@ -1 +1 @@
Subproject commit 3e6c857c8ad509a94e5a309a5061c5729fbcc439 Subproject commit 9728f71c9833baa65995e19e993d3450da750c20

@ -1 +1 @@
Subproject commit c5ee5ac3f9f0f9239d8b05be9c2d392f439dfa30 Subproject commit e574c4f7798ab8529879fb9f58795ac3c346daf6

@ -1 +1 @@
Subproject commit ed640ba08cf8452e202ed567cad48ad396b8e1db Subproject commit 2801fc21fbd6f69479f6638ab1725d00238698f8

@ -1 +1 @@
Subproject commit cd196562aff9b39e11a415bfb7a33f2066bf86c4 Subproject commit 1a3d9dd1870b1f45e252ff636adfd0c1f0ccf521

@ -1 +1 @@
Subproject commit 727c341899a82c911a27a5cac6d09bb23ce06b1d Subproject commit 7cd6a2b0de13820a75b9028ae90595cd48a2231c

@ -1 +1 @@
Subproject commit a38c3a33055e91c9d9ceed507c0e3c2f63dc7fa4 Subproject commit 17d2baf7365c3499172dc5afd71171cb3a138d99

View File

@ -1,6 +1,5 @@
--- ---
bazel-opt_task: bazel-opt_task:
timeout_in: 5m
container: container:
image: toxchat/toktok-stack:latest-release image: toxchat/toktok-stack:latest-release
cpu: 2 cpu: 2
@ -8,9 +7,9 @@ bazel-opt_task:
configure_script: configure_script:
- git submodule update --init --recursive - git submodule update --init --recursive
- /src/workspace/tools/inject-repo c-toxcore - /src/workspace/tools/inject-repo c-toxcore
- sed -i -e 's/build --config=remote/#&/' /src/workspace/.bazelrc.local
test_all_script: test_all_script:
- cd /src/workspace && bazel test -k - cd /src/workspace && bazel test -k
--remote_cache=http://$CIRRUS_HTTP_CACHE_HOST
--build_tag_filters=-haskell --build_tag_filters=-haskell
--test_tag_filters=-haskell --test_tag_filters=-haskell
-- --
@ -18,7 +17,6 @@ bazel-opt_task:
-//c-toxcore/auto_tests:tcp_relay_test # Cirrus doesn't allow external network connections. -//c-toxcore/auto_tests:tcp_relay_test # Cirrus doesn't allow external network connections.
bazel-dbg_task: bazel-dbg_task:
timeout_in: 5m
container: container:
image: toxchat/toktok-stack:latest-debug image: toxchat/toktok-stack:latest-debug
cpu: 2 cpu: 2
@ -28,15 +26,14 @@ bazel-dbg_task:
- /src/workspace/tools/inject-repo c-toxcore - /src/workspace/tools/inject-repo c-toxcore
test_all_script: test_all_script:
- cd /src/workspace && bazel test -k - cd /src/workspace && bazel test -k
--remote_cache=http://$CIRRUS_HTTP_CACHE_HOST
--build_tag_filters=-haskell --build_tag_filters=-haskell
--test_tag_filters=-haskell --test_tag_filters=-haskell
--remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST
-- --
//c-toxcore/... //c-toxcore/...
-//c-toxcore/auto_tests:tcp_relay_test # Cirrus doesn't allow external network connections. -//c-toxcore/auto_tests:tcp_relay_test # Cirrus doesn't allow external network connections.
cimple_task: cimple_task:
timeout_in: 5m
container: container:
image: toxchat/toktok-stack:latest-release image: toxchat/toktok-stack:latest-release
cpu: 2 cpu: 2
@ -44,18 +41,17 @@ cimple_task:
configure_script: configure_script:
- git submodule update --init --recursive - git submodule update --init --recursive
- /src/workspace/tools/inject-repo c-toxcore - /src/workspace/tools/inject-repo c-toxcore
- sed -i -e 's/build --config=remote/#&/' /src/workspace/.bazelrc.local
test_all_script: test_all_script:
- cd /src/workspace && bazel test -k - cd /src/workspace && bazel test -k
--remote_cache=http://$CIRRUS_HTTP_CACHE_HOST
--build_tag_filters=haskell --build_tag_filters=haskell
--test_tag_filters=haskell --test_tag_filters=haskell
-- --
//c-toxcore/... //c-toxcore/...
freebsd_task: freebsd_task:
timeout_in: 5m
freebsd_instance: freebsd_instance:
image_family: freebsd-14-1 image_family: freebsd-14-0
configure_script: configure_script:
- PAGER=cat ASSUME_ALWAYS_YES=YES pkg install - PAGER=cat ASSUME_ALWAYS_YES=YES pkg install
cmake cmake

View File

@ -36,6 +36,9 @@ add_flag -Wno-padded
# This warns on things like _XOPEN_SOURCE, which we currently need (we # This warns on things like _XOPEN_SOURCE, which we currently need (we
# probably won't need these in the future). # probably won't need these in the future).
add_flag -Wno-reserved-id-macro add_flag -Wno-reserved-id-macro
# TODO(iphydf): Clean these up. They are likely not bugs, but still
# potential issues and probably confusing.
add_flag -Wno-sign-compare
# We don't want to have default cases, we want to explicitly define all cases # We don't want to have default cases, we want to explicitly define all cases
add_flag -Wno-switch-default add_flag -Wno-switch-default
# __attribute__((nonnull)) causes this warning on defensive null checks. # __attribute__((nonnull)) causes this warning on defensive null checks.

View File

@ -47,7 +47,8 @@ add_flag -Wunused-value
# struct Foo foo = {0}; is a common idiom. # struct Foo foo = {0}; is a common idiom.
add_flag -Wno-missing-field-initializers add_flag -Wno-missing-field-initializers
# Checked by clang, but gcc is warning when it's not necessary. # TODO(iphydf): Clean these up. They are likely not bugs, but still
# potential issues and probably confusing.
add_flag -Wno-sign-compare add_flag -Wno-sign-compare
# File transfer code has this. # File transfer code has this.
add_flag -Wno-type-limits add_flag -Wno-type-limits

View File

@ -1,12 +0,0 @@
name: release
on:
push:
branches: [master]
pull_request_target:
branches: [master]
types: [opened, reopened, synchronize]
jobs:
release:
uses: TokTok/ci-tools/.github/workflows/release-drafter.yml@master

View File

@ -1,3 +1,3 @@
[submodule "third_party/cmp"] [submodule "third_party/cmp"]
path = third_party/cmp path = third_party/cmp
url = https://github.com/TokTok/cmp url = https://github.com/camgunz/cmp

View File

@ -161,7 +161,7 @@ option(DHT_BOOTSTRAP "Enable building of DHT_bootstrap" ON)
option(BOOTSTRAP_DAEMON "Enable building of tox-bootstrapd" ON) option(BOOTSTRAP_DAEMON "Enable building of tox-bootstrapd" ON)
if(BOOTSTRAP_DAEMON AND WIN32) if(BOOTSTRAP_DAEMON AND WIN32)
message(WARNING "Building tox-bootstrapd for Windows is not supported, disabling") message(WARNING "Building tox-bootstrapd for Windows is not supported, disabling")
set(BOOTSTRAP_DAEMON OFF CACHE BOOL "" FORCE) set(BOOTSTRAP_DAEMON OFF)
endif() endif()
option(BUILD_FUZZ_TESTS "Build fuzzing harnesses" OFF) option(BUILD_FUZZ_TESTS "Build fuzzing harnesses" OFF)
@ -177,7 +177,6 @@ include(Dependencies)
if(MUST_BUILD_TOXAV) if(MUST_BUILD_TOXAV)
set(NO_TOXAV_ERROR_TYPE SEND_ERROR) set(NO_TOXAV_ERROR_TYPE SEND_ERROR)
set(BUILD_TOXAV ON CACHE BOOL "" FORCE)
else() else()
set(NO_TOXAV_ERROR_TYPE WARNING) set(NO_TOXAV_ERROR_TYPE WARNING)
endif() endif()
@ -185,11 +184,11 @@ endif()
if(BUILD_TOXAV) if(BUILD_TOXAV)
if(NOT OPUS_FOUND) if(NOT OPUS_FOUND)
message(${NO_TOXAV_ERROR_TYPE} "Option BUILD_TOXAV is enabled but required library OPUS was not found.") message(${NO_TOXAV_ERROR_TYPE} "Option BUILD_TOXAV is enabled but required library OPUS was not found.")
set(BUILD_TOXAV OFF CACHE BOOL "" FORCE) set(BUILD_TOXAV OFF)
endif() endif()
if(NOT VPX_FOUND) if(NOT VPX_FOUND)
message(${NO_TOXAV_ERROR_TYPE} "Option BUILD_TOXAV is enabled but required library VPX was not found.") message(${NO_TOXAV_ERROR_TYPE} "Option BUILD_TOXAV is enabled but required library VPX was not found.")
set(BUILD_TOXAV OFF CACHE BOOL "" FORCE) set(BUILD_TOXAV OFF)
endif() endif()
endif() endif()
@ -392,7 +391,6 @@ if(BUILD_TOXAV)
toxav/rtp.h toxav/rtp.h
toxav/toxav.c toxav/toxav.c
toxav/toxav.h toxav/toxav.h
toxav/toxav_hacks.h
toxav/toxav_old.c toxav/toxav_old.c
toxav/video.c toxav/video.c
toxav/video.h) toxav/video.h)
@ -599,7 +597,7 @@ if(BOOTSTRAP_DAEMON)
add_subdirectory(other/bootstrap_daemon) add_subdirectory(other/bootstrap_daemon)
else() else()
message(WARNING "Option BOOTSTRAP_DAEMON is enabled but required library LIBCONFIG was not found.") message(WARNING "Option BOOTSTRAP_DAEMON is enabled but required library LIBCONFIG was not found.")
set(BOOTSTRAP_DAEMON OFF CACHE BOOL "" FORCE) set(BOOTSTRAP_DAEMON OFF)
endif() endif()
endif() endif()

View File

@ -420,7 +420,7 @@ static void test_groupav(AutoTox *autotoxes)
tox_events_callback_conference_connected(autotoxes[i].dispatch, handle_conference_connected); tox_events_callback_conference_connected(autotoxes[i].dispatch, handle_conference_connected);
} }
ck_assert_msg(toxav_add_av_groupchat(autotoxes[0].tox, audio_callback, &autotoxes[0]) != -1, ck_assert_msg(toxav_add_av_groupchat(autotoxes[0].tox, audio_callback, &autotoxes[0]) != UINT32_MAX,
"failed to create group"); "failed to create group");
printf("tox #%u: inviting its first friend\n", autotoxes[0].index); printf("tox #%u: inviting its first friend\n", autotoxes[0].index);
ck_assert_msg(tox_conference_invite(autotoxes[0].tox, 0, 0, nullptr) != 0, "failed to invite friend"); ck_assert_msg(tox_conference_invite(autotoxes[0].tox, 0, 0, nullptr) != 0, "failed to invite friend");

View File

@ -28,7 +28,7 @@ static void handle_conference_invite(
ck_assert_msg(!state->joined, "invitation callback generated for already joined conference"); ck_assert_msg(!state->joined, "invitation callback generated for already joined conference");
if (friend_number != UINT32_MAX) { if (friend_number != -1) {
Tox_Err_Conference_Join err; Tox_Err_Conference_Join err;
state->conference = tox_conference_join(autotox->tox, friend_number, cookie, length, &err); state->conference = tox_conference_join(autotox->tox, friend_number, cookie, length, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK, ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK,

View File

@ -21,7 +21,7 @@ static void handle_conference_invite(
const uint8_t *cookie = tox_event_conference_invite_get_cookie(event); const uint8_t *cookie = tox_event_conference_invite_get_cookie(event);
const size_t length = tox_event_conference_invite_get_cookie_length(event); const size_t length = tox_event_conference_invite_get_cookie_length(event);
if (friend_number != UINT32_MAX) { if (friend_number != -1) {
Tox_Err_Conference_Join err; Tox_Err_Conference_Join err;
state->conference = tox_conference_join(autotox->tox, friend_number, cookie, length, &err); state->conference = tox_conference_join(autotox->tox, friend_number, cookie, length, &err);
ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK, ck_assert_msg(err == TOX_ERR_CONFERENCE_JOIN_OK,

View File

@ -168,33 +168,29 @@ static void test_av_three_calls(void)
Time_Data time_data; Time_Data time_data;
pthread_mutex_init(&time_data.lock, nullptr); pthread_mutex_init(&time_data.lock, nullptr);
{ {
Tox_Options *opts = tox_options_new(nullptr);
ck_assert(opts != nullptr);
tox_options_set_experimental_thread_safety(opts, true);
Tox_Err_New error; Tox_Err_New error;
bootstrap = tox_new_log(opts, &error, &index[0]); bootstrap = tox_new_log(nullptr, &error, &index[0]);
ck_assert(error == TOX_ERR_NEW_OK); ck_assert(error == TOX_ERR_NEW_OK);
time_data.clock = current_time_monotonic(bootstrap->mono_time); time_data.clock = current_time_monotonic(bootstrap->mono_time);
set_current_time_callback(bootstrap, &time_data); set_current_time_callback(bootstrap, &time_data);
alice = tox_new_log(opts, &error, &index[1]); alice = tox_new_log(nullptr, &error, &index[1]);
ck_assert(error == TOX_ERR_NEW_OK); ck_assert(error == TOX_ERR_NEW_OK);
set_current_time_callback(alice, &time_data); set_current_time_callback(alice, &time_data);
bobs[0] = tox_new_log(opts, &error, &index[2]); bobs[0] = tox_new_log(nullptr, &error, &index[2]);
ck_assert(error == TOX_ERR_NEW_OK); ck_assert(error == TOX_ERR_NEW_OK);
set_current_time_callback(bobs[0], &time_data); set_current_time_callback(bobs[0], &time_data);
bobs[1] = tox_new_log(opts, &error, &index[3]); bobs[1] = tox_new_log(nullptr, &error, &index[3]);
ck_assert(error == TOX_ERR_NEW_OK); ck_assert(error == TOX_ERR_NEW_OK);
set_current_time_callback(bobs[1], &time_data); set_current_time_callback(bobs[1], &time_data);
bobs[2] = tox_new_log(opts, &error, &index[4]); bobs[2] = tox_new_log(nullptr, &error, &index[4]);
ck_assert(error == TOX_ERR_NEW_OK); ck_assert(error == TOX_ERR_NEW_OK);
set_current_time_callback(bobs[2], &time_data); set_current_time_callback(bobs[2], &time_data);
tox_options_free(opts);
} }
printf("Created 5 instances of Tox\n"); printf("Created 5 instances of Tox\n");

View File

@ -27,6 +27,7 @@ run() {
-Wno-missing-noreturn \ -Wno-missing-noreturn \
-Wno-old-style-cast \ -Wno-old-style-cast \
-Wno-padded \ -Wno-padded \
-Wno-sign-compare \
-Wno-switch-default \ -Wno-switch-default \
-Wno-tautological-pointer-compare \ -Wno-tautological-pointer-compare \
-Wno-unreachable-code-return \ -Wno-unreachable-code-return \

View File

@ -20,6 +20,8 @@ CPPCHECK+=("--suppress=knownConditionTrueFalse")
CPPCHECK+=("--suppress=missingIncludeSystem") CPPCHECK+=("--suppress=missingIncludeSystem")
# TODO(iphydf): Maybe fix? # TODO(iphydf): Maybe fix?
CPPCHECK+=("--suppress=signConversion") CPPCHECK+=("--suppress=signConversion")
# TODO(iphydf): Fixed in the toxav refactor PR.
CPPCHECK+=("--suppress=redundantAssignment")
# We use this for VLAs. # We use this for VLAs.
CPPCHECK_CXX+=("--suppress=allocaCalled") CPPCHECK_CXX+=("--suppress=allocaCalled")

View File

@ -5,11 +5,11 @@ FROM alpine:3.19.0 AS build
RUN ["apk", "--no-cache", "add",\ RUN ["apk", "--no-cache", "add",\
"clang",\ "clang",\
"cmake",\ "cmake",\
"linux-headers",\
"libconfig-dev",\ "libconfig-dev",\
"libconfig-static",\ "libconfig-static",\
"libsodium-dev",\ "libsodium-dev",\
"libsodium-static",\ "libsodium-static",\
"linux-headers",\
"musl-dev",\ "musl-dev",\
"ninja",\ "ninja",\
"python3"] "python3"]

View File

@ -138,20 +138,9 @@ static void parse_tcp_relay_ports_config(config_t *cfg, uint16_t **tcp_relay_por
} }
} }
// A wrapper function that actually takes a bool argument
static int tox_config_lookup_bool(const config_t *config, const char *path, bool *bool_value)
{
int int_value = 0;
if (config_lookup_bool(config, path, &int_value) == CONFIG_FALSE) {
return CONFIG_FALSE;
}
*bool_value = int_value != 0;
return CONFIG_TRUE;
}
bool get_general_config(const char *cfg_file_path, char **pid_file_path, char **keys_file_path, int *port, bool get_general_config(const char *cfg_file_path, char **pid_file_path, char **keys_file_path, int *port,
bool *enable_ipv6, bool *enable_ipv4_fallback, bool *enable_lan_discovery, bool *enable_tcp_relay, int *enable_ipv6, int *enable_ipv4_fallback, int *enable_lan_discovery, int *enable_tcp_relay,
uint16_t **tcp_relay_ports, int *tcp_relay_port_count, bool *enable_motd, char **motd) uint16_t **tcp_relay_ports, int *tcp_relay_port_count, int *enable_motd, char **motd)
{ {
config_t cfg; config_t cfg;
@ -218,14 +207,14 @@ bool get_general_config(const char *cfg_file_path, char **pid_file_path, char **
memcpy(*keys_file_path, tmp_keys_file, keys_file_path_len); memcpy(*keys_file_path, tmp_keys_file, keys_file_path_len);
// Get IPv6 option // Get IPv6 option
if (tox_config_lookup_bool(&cfg, NAME_ENABLE_IPV6, enable_ipv6) == CONFIG_FALSE) { if (config_lookup_bool(&cfg, NAME_ENABLE_IPV6, enable_ipv6) == CONFIG_FALSE) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_IPV6); log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_IPV6);
log_write(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_IPV6, DEFAULT_ENABLE_IPV6 ? "true" : "false"); log_write(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_IPV6, DEFAULT_ENABLE_IPV6 ? "true" : "false");
*enable_ipv6 = DEFAULT_ENABLE_IPV6; *enable_ipv6 = DEFAULT_ENABLE_IPV6;
} }
// Get IPv4 fallback option // Get IPv4 fallback option
if (tox_config_lookup_bool(&cfg, NAME_ENABLE_IPV4_FALLBACK, enable_ipv4_fallback) == CONFIG_FALSE) { if (config_lookup_bool(&cfg, NAME_ENABLE_IPV4_FALLBACK, enable_ipv4_fallback) == CONFIG_FALSE) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_IPV4_FALLBACK); log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_IPV4_FALLBACK);
log_write(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_IPV4_FALLBACK, log_write(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_IPV4_FALLBACK,
DEFAULT_ENABLE_IPV4_FALLBACK ? "true" : "false"); DEFAULT_ENABLE_IPV4_FALLBACK ? "true" : "false");
@ -233,7 +222,7 @@ bool get_general_config(const char *cfg_file_path, char **pid_file_path, char **
} }
// Get LAN discovery option // Get LAN discovery option
if (tox_config_lookup_bool(&cfg, NAME_ENABLE_LAN_DISCOVERY, enable_lan_discovery) == CONFIG_FALSE) { if (config_lookup_bool(&cfg, NAME_ENABLE_LAN_DISCOVERY, enable_lan_discovery) == CONFIG_FALSE) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_LAN_DISCOVERY); log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_LAN_DISCOVERY);
log_write(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_LAN_DISCOVERY, log_write(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_LAN_DISCOVERY,
DEFAULT_ENABLE_LAN_DISCOVERY ? "true" : "false"); DEFAULT_ENABLE_LAN_DISCOVERY ? "true" : "false");
@ -241,28 +230,28 @@ bool get_general_config(const char *cfg_file_path, char **pid_file_path, char **
} }
// Get TCP relay option // Get TCP relay option
if (tox_config_lookup_bool(&cfg, NAME_ENABLE_TCP_RELAY, enable_tcp_relay) == CONFIG_FALSE) { if (config_lookup_bool(&cfg, NAME_ENABLE_TCP_RELAY, enable_tcp_relay) == CONFIG_FALSE) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_TCP_RELAY); log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_TCP_RELAY);
log_write(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_TCP_RELAY, log_write(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_TCP_RELAY,
DEFAULT_ENABLE_TCP_RELAY ? "true" : "false"); DEFAULT_ENABLE_TCP_RELAY ? "true" : "false");
*enable_tcp_relay = DEFAULT_ENABLE_TCP_RELAY; *enable_tcp_relay = DEFAULT_ENABLE_TCP_RELAY;
} }
if (*enable_tcp_relay) { if (*enable_tcp_relay != 0) {
parse_tcp_relay_ports_config(&cfg, tcp_relay_ports, tcp_relay_port_count); parse_tcp_relay_ports_config(&cfg, tcp_relay_ports, tcp_relay_port_count);
} else { } else {
*tcp_relay_port_count = 0; *tcp_relay_port_count = 0;
} }
// Get MOTD option // Get MOTD option
if (tox_config_lookup_bool(&cfg, NAME_ENABLE_MOTD, enable_motd) == CONFIG_FALSE) { if (config_lookup_bool(&cfg, NAME_ENABLE_MOTD, enable_motd) == CONFIG_FALSE) {
log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_MOTD); log_write(LOG_LEVEL_WARNING, "No '%s' setting in configuration file.\n", NAME_ENABLE_MOTD);
log_write(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_MOTD, log_write(LOG_LEVEL_WARNING, "Using default '%s': %s\n", NAME_ENABLE_MOTD,
DEFAULT_ENABLE_MOTD ? "true" : "false"); DEFAULT_ENABLE_MOTD ? "true" : "false");
*enable_motd = DEFAULT_ENABLE_MOTD; *enable_motd = DEFAULT_ENABLE_MOTD;
} }
if (*enable_motd) { if (*enable_motd != 0) {
// Get MOTD // Get MOTD
const char *tmp_motd; const char *tmp_motd;
@ -284,14 +273,14 @@ bool get_general_config(const char *cfg_file_path, char **pid_file_path, char **
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_PID_FILE_PATH, *pid_file_path); log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_PID_FILE_PATH, *pid_file_path);
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_KEYS_FILE_PATH, *keys_file_path); log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_KEYS_FILE_PATH, *keys_file_path);
log_write(LOG_LEVEL_INFO, "'%s': %d\n", NAME_PORT, *port); log_write(LOG_LEVEL_INFO, "'%s': %d\n", NAME_PORT, *port);
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_IPV6, *enable_ipv6 ? "true" : "false"); log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_IPV6, *enable_ipv6 != 0 ? "true" : "false");
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_IPV4_FALLBACK, *enable_ipv4_fallback ? "true" : "false"); log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_IPV4_FALLBACK, *enable_ipv4_fallback != 0 ? "true" : "false");
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_LAN_DISCOVERY, *enable_lan_discovery ? "true" : "false"); log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_LAN_DISCOVERY, *enable_lan_discovery != 0 ? "true" : "false");
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_TCP_RELAY, *enable_tcp_relay ? "true" : "false"); log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_TCP_RELAY, *enable_tcp_relay != 0 ? "true" : "false");
// Show info about tcp ports only if tcp relay is enabled // Show info about tcp ports only if tcp relay is enabled
if (*enable_tcp_relay) { if (*enable_tcp_relay != 0) {
if (*tcp_relay_port_count == 0) { if (*tcp_relay_port_count == 0) {
log_write(LOG_LEVEL_ERROR, "No TCP ports could be read.\n"); log_write(LOG_LEVEL_ERROR, "No TCP ports could be read.\n");
} else { } else {
@ -303,9 +292,9 @@ bool get_general_config(const char *cfg_file_path, char **pid_file_path, char **
} }
} }
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_MOTD, *enable_motd ? "true" : "false"); log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_ENABLE_MOTD, *enable_motd != 0 ? "true" : "false");
if (*enable_motd) { if (*enable_motd != 0) {
log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_MOTD, *motd); log_write(LOG_LEVEL_INFO, "'%s': %s\n", NAME_MOTD, *motd);
} }

View File

@ -17,14 +17,14 @@
* *
* Important: You are responsible for freeing `pid_file_path` and `keys_file_path` * Important: You are responsible for freeing `pid_file_path` and `keys_file_path`
* also, iff `tcp_relay_ports_count` > 0, then you are responsible for freeing `tcp_relay_ports` * also, iff `tcp_relay_ports_count` > 0, then you are responsible for freeing `tcp_relay_ports`
* and also `motd` iff `enable_motd` is true. * and also `motd` iff `enable_motd` is set.
* *
* @return true on success, * @return true on success,
* false on failure, doesn't modify any data pointed by arguments. * false on failure, doesn't modify any data pointed by arguments.
*/ */
bool get_general_config(const char *cfg_file_path, char **pid_file_path, char **keys_file_path, int *port, bool get_general_config(const char *cfg_file_path, char **pid_file_path, char **keys_file_path, int *port,
bool *enable_ipv6, bool *enable_ipv4_fallback, bool *enable_lan_discovery, bool *enable_tcp_relay, int *enable_ipv6, int *enable_ipv4_fallback, int *enable_lan_discovery, int *enable_tcp_relay,
uint16_t **tcp_relay_ports, int *tcp_relay_port_count, bool *enable_motd, char **motd); uint16_t **tcp_relay_ports, int *tcp_relay_port_count, int *enable_motd, char **motd);
/** /**
* Bootstraps off nodes listed in the config file. * Bootstraps off nodes listed in the config file.

View File

@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2024 The TokTok team. * Copyright © 2016-2023 The TokTok team.
* Copyright © 2014-2016 Tox project. * Copyright © 2014-2016 Tox project.
*/ */
@ -10,19 +10,17 @@
#ifndef C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_CONFIG_DEFAULTS_H #ifndef C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_CONFIG_DEFAULTS_H
#define C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_CONFIG_DEFAULTS_H #define C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_CONFIG_DEFAULTS_H
#include <stdbool.h>
#include "global.h" #include "global.h"
#define DEFAULT_PID_FILE_PATH "tox-bootstrapd.pid" #define DEFAULT_PID_FILE_PATH "tox-bootstrapd.pid"
#define DEFAULT_KEYS_FILE_PATH "tox-bootstrapd.keys" #define DEFAULT_KEYS_FILE_PATH "tox-bootstrapd.keys"
#define DEFAULT_PORT 33445 #define DEFAULT_PORT 33445
#define DEFAULT_ENABLE_IPV6 true #define DEFAULT_ENABLE_IPV6 1 // 1 - true, 0 - false
#define DEFAULT_ENABLE_IPV4_FALLBACK true #define DEFAULT_ENABLE_IPV4_FALLBACK 1 // 1 - true, 0 - false
#define DEFAULT_ENABLE_LAN_DISCOVERY true #define DEFAULT_ENABLE_LAN_DISCOVERY 1 // 1 - true, 0 - false
#define DEFAULT_ENABLE_TCP_RELAY true #define DEFAULT_ENABLE_TCP_RELAY 1 // 1 - true, 0 - false
#define DEFAULT_TCP_RELAY_PORTS 443, 3389, 33445 // comma-separated list of ports #define DEFAULT_TCP_RELAY_PORTS 443, 3389, 33445 // comma-separated list of ports
#define DEFAULT_ENABLE_MOTD true #define DEFAULT_ENABLE_MOTD 1 // 1 - true, 0 - false
#define DEFAULT_MOTD DAEMON_NAME #define DEFAULT_MOTD DAEMON_NAME
#endif // C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_CONFIG_DEFAULTS_H #endif // C_TOXCORE_OTHER_BOOTSTRAP_DAEMON_SRC_CONFIG_DEFAULTS_H

View File

@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-3.0-or-later /* SPDX-License-Identifier: GPL-3.0-or-later
* Copyright © 2016-2024 The TokTok team. * Copyright © 2016-2018 The TokTok team.
* Copyright © 2014-2016 Tox project. * Copyright © 2014-2016 Tox project.
*/ */
@ -240,13 +240,13 @@ int main(int argc, char *argv[])
char *pid_file_path = nullptr; char *pid_file_path = nullptr;
char *keys_file_path = nullptr; char *keys_file_path = nullptr;
int start_port = 0; int start_port = 0;
bool enable_ipv6 = false; int enable_ipv6 = 0;
bool enable_ipv4_fallback = false; int enable_ipv4_fallback = 0;
bool enable_lan_discovery = false; int enable_lan_discovery = 0;
bool enable_tcp_relay = false; int enable_tcp_relay = 0;
uint16_t *tcp_relay_ports = nullptr; uint16_t *tcp_relay_ports = nullptr;
int tcp_relay_port_count = 0; int tcp_relay_port_count = 0;
bool enable_motd = false; int enable_motd = 0;
char *motd = nullptr; char *motd = nullptr;
if (get_general_config(cfg_file_path, &pid_file_path, &keys_file_path, &start_port, &enable_ipv6, &enable_ipv4_fallback, if (get_general_config(cfg_file_path, &pid_file_path, &keys_file_path, &start_port, &enable_ipv6, &enable_ipv4_fallback,
@ -281,7 +281,7 @@ int main(int argc, char *argv[])
free(pid_file_path); free(pid_file_path);
IP ip; IP ip;
ip_init(&ip, enable_ipv6); ip_init(&ip, enable_ipv6 != 0);
Logger *logger = logger_new(); Logger *logger = logger_new();
@ -296,10 +296,10 @@ int main(int argc, char *argv[])
Networking_Core *net = new_networking_ex(logger, mem, ns, &ip, start_port, end_port, nullptr); Networking_Core *net = new_networking_ex(logger, mem, ns, &ip, start_port, end_port, nullptr);
if (net == nullptr) { if (net == nullptr) {
if (enable_ipv6 && enable_ipv4_fallback) { if (enable_ipv6 != 0 && enable_ipv4_fallback != 0) {
log_write(LOG_LEVEL_WARNING, "Couldn't initialize IPv6 networking. Falling back to using IPv4.\n"); log_write(LOG_LEVEL_WARNING, "Couldn't initialize IPv6 networking. Falling back to using IPv4.\n");
enable_ipv6 = false; enable_ipv6 = 0;
ip_init(&ip, enable_ipv6); ip_init(&ip, enable_ipv6 != 0);
net = new_networking_ex(logger, mem, ns, &ip, start_port, end_port, nullptr); net = new_networking_ex(logger, mem, ns, &ip, start_port, end_port, nullptr);
if (net == nullptr) { if (net == nullptr) {
@ -334,7 +334,7 @@ int main(int argc, char *argv[])
mono_time_update(mono_time); mono_time_update(mono_time);
DHT *const dht = new_dht(logger, mem, rng, ns, mono_time, net, true, enable_lan_discovery); DHT *const dht = new_dht(logger, mem, rng, ns, mono_time, net, true, enable_lan_discovery != 0);
if (dht == nullptr) { if (dht == nullptr) {
log_write(LOG_LEVEL_ERROR, "Couldn't initialize Tox DHT instance. Exiting.\n"); log_write(LOG_LEVEL_ERROR, "Couldn't initialize Tox DHT instance. Exiting.\n");
@ -429,7 +429,7 @@ int main(int argc, char *argv[])
gca_onion_init(group_announce, onion_a); gca_onion_init(group_announce, onion_a);
if (enable_motd) { if (enable_motd != 0) {
if (bootstrap_set_callbacks(dht_get_net(dht), DAEMON_VERSION_NUMBER, (uint8_t *)motd, strlen(motd) + 1) == 0) { if (bootstrap_set_callbacks(dht_get_net(dht), DAEMON_VERSION_NUMBER, (uint8_t *)motd, strlen(motd) + 1) == 0) {
log_write(LOG_LEVEL_INFO, "Set MOTD successfully.\n"); log_write(LOG_LEVEL_INFO, "Set MOTD successfully.\n");
free(motd); free(motd);
@ -472,7 +472,7 @@ int main(int argc, char *argv[])
TCP_Server *tcp_server = nullptr; TCP_Server *tcp_server = nullptr;
if (enable_tcp_relay) { if (enable_tcp_relay != 0) {
if (tcp_relay_port_count == 0) { if (tcp_relay_port_count == 0) {
log_write(LOG_LEVEL_ERROR, "No TCP relay ports read. Exiting.\n"); log_write(LOG_LEVEL_ERROR, "No TCP relay ports read. Exiting.\n");
kill_onion_announce(onion_a); kill_onion_announce(onion_a);
@ -488,7 +488,7 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
tcp_server = new_tcp_server(logger, mem, rng, ns, enable_ipv6, tcp_server = new_tcp_server(logger, mem, rng, ns, enable_ipv6 != 0,
tcp_relay_port_count, tcp_relay_ports, tcp_relay_port_count, tcp_relay_ports,
dht_get_self_secret_key(dht), onion, forwarding); dht_get_self_secret_key(dht), onion, forwarding);
@ -535,7 +535,7 @@ int main(int argc, char *argv[])
} }
} }
if (bootstrap_from_config(cfg_file_path, dht, enable_ipv6)) { if (bootstrap_from_config(cfg_file_path, dht, enable_ipv6 != 0)) {
log_write(LOG_LEVEL_INFO, "List of bootstrap nodes read successfully.\n"); log_write(LOG_LEVEL_INFO, "List of bootstrap nodes read successfully.\n");
} else { } else {
log_write(LOG_LEVEL_ERROR, "Couldn't read list of bootstrap nodes in %s. Exiting.\n", cfg_file_path); log_write(LOG_LEVEL_ERROR, "Couldn't read list of bootstrap nodes in %s. Exiting.\n", cfg_file_path);
@ -561,7 +561,7 @@ int main(int argc, char *argv[])
Broadcast_Info *broadcast = nullptr; Broadcast_Info *broadcast = nullptr;
if (enable_lan_discovery) { if (enable_lan_discovery != 0) {
broadcast = lan_discovery_init(ns); broadcast = lan_discovery_init(ns);
log_write(LOG_LEVEL_INFO, "Initialized LAN discovery successfully.\n"); log_write(LOG_LEVEL_INFO, "Initialized LAN discovery successfully.\n");
} }
@ -589,12 +589,12 @@ int main(int argc, char *argv[])
do_dht(dht); do_dht(dht);
if (enable_lan_discovery && mono_time_is_timeout(mono_time, last_lan_discovery, LAN_DISCOVERY_INTERVAL)) { if (enable_lan_discovery != 0 && mono_time_is_timeout(mono_time, last_lan_discovery, LAN_DISCOVERY_INTERVAL)) {
lan_discovery_send(dht_get_net(dht), broadcast, dht_get_self_public_key(dht), net_htons_port); lan_discovery_send(dht_get_net(dht), broadcast, dht_get_self_public_key(dht), net_htons_port);
last_lan_discovery = mono_time_get(mono_time); last_lan_discovery = mono_time_get(mono_time);
} }
if (enable_tcp_relay) { if (enable_tcp_relay != 0) {
do_tcp_server(tcp_server, mono_time); do_tcp_server(tcp_server, mono_time);
} }
@ -618,7 +618,7 @@ int main(int argc, char *argv[])
break; break;
default: default:
log_write(LOG_LEVEL_INFO, "Received (%ld) signal. Exiting.\n", (long)caught_signal); log_write(LOG_LEVEL_INFO, "Received (%d) signal. Exiting.\n", caught_signal);
} }
lan_discovery_kill(broadcast); lan_discovery_kill(broadcast);

View File

@ -4,4 +4,4 @@ go 1.17
require github.com/gorilla/websocket v1.5.1 require github.com/gorilla/websocket v1.5.1
require golang.org/x/net v0.23.0 // indirect require golang.org/x/net v0.17.0 // indirect

View File

@ -4,8 +4,6 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -13,10 +11,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -28,22 +24,17 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

View File

@ -3,10 +3,10 @@ FROM alpine:3.14.0
RUN ["apk", "add", "--no-cache", \ RUN ["apk", "add", "--no-cache", \
"bash", \ "bash", \
"gcc", \ "gcc", \
"libsodium-dev", \
"libvpx-dev", \
"linux-headers", \ "linux-headers", \
"musl-dev", \ "musl-dev", \
"libsodium-dev", \
"libvpx-dev", \
"opus-dev", \ "opus-dev", \
"perf"] "perf"]

View File

@ -444,10 +444,7 @@ void generate_event_impl(const std::string& event_name, const std::vector<EventT
f << " Tox_Event event;\n"; f << " Tox_Event event;\n";
f << " event.type = TOX_EVENT_" << str_toupper(event_name) << ";\n"; f << " event.type = TOX_EVENT_" << str_toupper(event_name) << ";\n";
f << " event.data." << event_name_l << " = " << event_name_l << ";\n\n"; f << " event.data." << event_name_l << " = " << event_name_l << ";\n\n";
f << " if (!tox_events_add(events, &event)) {\n"; f << " tox_events_add(events, &event);\n";
f << " tox_event_" << event_name_l << "_free(" << event_name_l << ", mem);\n";
f << " return nullptr;\n";
f << " }\n";
f << " return " << event_name_l << ";\n}\n\n"; f << " return " << event_name_l << ";\n}\n\n";
// unpack // unpack

View File

@ -137,7 +137,7 @@ int main(int argc, char *argv[])
printf("Failed to set status. Error number: %d\n", err); printf("Failed to set status. Error number: %d\n", err);
} }
for (int i = 2; i < argc; i++) { //start at 2 because that is where the tox ids are for (unsigned int i = 2; i < argc; i++) { //start at 2 because that is where the tox ids are
uint8_t *address = hex_string_to_bin(argv[i]); uint8_t *address = hex_string_to_bin(argv[i]);
Tox_Err_Friend_Add friend_err; Tox_Err_Friend_Add friend_err;
tox_friend_add(tox, address, (const uint8_t *)GENERATED_REQUEST_MESSAGE, strlen(GENERATED_REQUEST_MESSAGE), tox_friend_add(tox, address, (const uint8_t *)GENERATED_REQUEST_MESSAGE, strlen(GENERATED_REQUEST_MESSAGE),
@ -145,7 +145,7 @@ int main(int argc, char *argv[])
free(address); free(address);
if (friend_err != TOX_ERR_FRIEND_ADD_OK) { if (friend_err != TOX_ERR_FRIEND_ADD_OK) {
printf("Failed to add friend number %d. Error number: %d\n", i - 1, friend_err); printf("Failed to add friend number %u. Error number: %d\n", i - 1, friend_err);
} }
} }

View File

@ -35,7 +35,7 @@ static int load_file(const char *filename, unsigned char **result)
fseek(f, 0, SEEK_SET); fseek(f, 0, SEEK_SET);
*result = (unsigned char *)malloc(size + 1); *result = (unsigned char *)malloc(size + 1);
if ((size_t)size != fread(*result, sizeof(char), size, f)) { if (size != fread(*result, sizeof(char), size, f)) {
free(*result); free(*result);
fclose(f); fclose(f);
return -2; // -2 means file reading fail return -2; // -2 means file reading fail
@ -55,13 +55,13 @@ int main(int argc, char *argv[])
crypto_sign_ed25519_keypair(pk, sk); crypto_sign_ed25519_keypair(pk, sk);
printf("Public key:\n"); printf("Public key:\n");
for (uint32_t i = 0; i < crypto_sign_ed25519_PUBLICKEYBYTES; ++i) { for (int i = 0; i < crypto_sign_ed25519_PUBLICKEYBYTES; ++i) {
printf("%02X", pk[i]); printf("%02X", pk[i]);
} }
printf("\nSecret key:\n"); printf("\nSecret key:\n");
for (uint32_t i = 0; i < crypto_sign_ed25519_SECRETKEYBYTES; ++i) { for (int i = 0; i < crypto_sign_ed25519_SECRETKEYBYTES; ++i) {
printf("%02X", sk[i]); printf("%02X", sk[i]);
} }

View File

@ -117,7 +117,7 @@ int main(int argc, char *argv[])
#endif #endif
crypto_box_keypair(public_key, secret_key); crypto_box_keypair(public_key, secret_key);
for (uint32_t i = 0; i <= crypto_box_PUBLICKEYBYTES - len; ++i) { for (int i = 0; i <= crypto_box_PUBLICKEYBYTES - len; ++i) {
if (memcmp(public_key + i, desired_bin, len) == 0) { if (memcmp(public_key + i, desired_bin, len) == 0) {
found = 1; found = 1;
break; break;

View File

@ -10,34 +10,22 @@ ENV NO_ARCH_OPT 1
RUN apt-get update && \ RUN apt-get update && \
apt-get -y install --no-install-suggests --no-install-recommends \ apt-get -y install --no-install-suggests --no-install-recommends \
apt-transport-https \
apt-utils \
automake \ automake \
bash-completion \ ninja-build \
bison flex \ bison flex \
build-essential \ build-essential \
ca-certificates \
cmake \
dialog \
git \ git \
gnupg \ python3 python3-dev python3-setuptools python-is-python3 \
gnuplot-nox \
jupp \
less \
libglib2.0-dev \
libpixman-1-dev \
libsodium-dev \
libtool libtool-bin \ libtool libtool-bin \
nano \ libglib2.0-dev \
ninja-build \ wget vim jupp nano bash-completion less \
parallel \ apt-utils apt-transport-https ca-certificates gnupg dialog \
python-is-python3 \ libpixman-1-dev \
python3 \ gnuplot-nox \
python3-dev \
python3-setuptools \
screen \ screen \
vim \ cmake \
wget \ parallel \
libsodium-dev \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main" >> /etc/apt/sources.list && \ RUN echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main" >> /etc/apt/sources.list && \
@ -48,37 +36,12 @@ RUN echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu focal main
RUN apt-get update && apt-get full-upgrade -y && \ RUN apt-get update && apt-get full-upgrade -y && \
apt-get -y install --no-install-suggests --no-install-recommends \ apt-get -y install --no-install-suggests --no-install-recommends \
clang-12 \ gcc-10 g++-10 gcc-10-plugin-dev gcc-10-multilib gcc-multilib gdb lcov \
clang-tools-12 \ clang-12 clang-tools-12 libc++1-12 libc++-12-dev \
g++-10 \ libc++abi1-12 libc++abi-12-dev libclang1-12 libclang-12-dev \
gcc-10 \ libclang-common-12-dev libclang-cpp12 libclang-cpp12-dev liblld-12 \
gcc-10-multilib \ liblld-12-dev liblldb-12 liblldb-12-dev libllvm12 libomp-12-dev \
gcc-10-plugin-dev \ libomp5-12 lld-12 lldb-12 llvm-12 llvm-12-dev llvm-12-runtime llvm-12-tools \
gcc-multilib \
gdb \
lcov \
libc++-12-dev \
libc++1-12 \
libc++abi-12-dev \
libc++abi1-12 \
libclang-12-dev \
libclang-common-12-dev \
libclang-cpp12 \
libclang-cpp12-dev \
libclang1-12 \
liblld-12 \
liblld-12-dev \
liblldb-12 \
liblldb-12-dev \
libllvm12 \
libomp-12-dev \
libomp5-12 \
lld-12 \
lldb-12 \
llvm-12 \
llvm-12-dev \
llvm-12-runtime \
llvm-12-tools \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 0 RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 0

@ -1 +1 @@
Subproject commit 2ac6bca152987c805c04423ebbba4b750585337f Subproject commit 643e6a62d4eb0ec2277de269cda33da02cba2756

View File

@ -42,32 +42,127 @@ cc_library(
) )
cc_library( cc_library(
name = "toxav", name = "bwcontroller",
srcs = glob( srcs = ["bwcontroller.c"],
[ hdrs = ["bwcontroller.h"],
"*.c",
"*.h",
],
exclude = ["toxav.h"],
),
hdrs = ["toxav.h"],
visibility = ["//c-toxcore:__subpackages__"],
deps = [ deps = [
":ring_buffer",
"//c-toxcore/toxcore",
"//c-toxcore/toxcore:Messenger", "//c-toxcore/toxcore:Messenger",
"//c-toxcore/toxcore:ccompat", "//c-toxcore/toxcore:ccompat",
"//c-toxcore/toxcore:logger",
"//c-toxcore/toxcore:mono_time",
"//c-toxcore/toxcore:tox",
"//c-toxcore/toxcore:util",
],
)
cc_library(
name = "rtp",
srcs = ["rtp.c"],
hdrs = ["rtp.h"],
deps = [
":bwcontroller",
"//c-toxcore/toxcore:Messenger",
"//c-toxcore/toxcore:ccompat",
"//c-toxcore/toxcore:logger",
"//c-toxcore/toxcore:mono_time",
"//c-toxcore/toxcore:tox",
"//c-toxcore/toxcore:util",
],
)
cc_test(
name = "rtp_test",
size = "small",
srcs = ["rtp_test.cc"],
deps = [
":rtp",
"//c-toxcore/toxcore:crypto_core",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "audio",
srcs = ["audio.c"],
hdrs = ["audio.h"],
deps = [
":public_api",
":rtp",
"//c-toxcore/toxcore:ccompat",
"//c-toxcore/toxcore:logger",
"//c-toxcore/toxcore:mono_time",
"//c-toxcore/toxcore:util",
"@opus",
],
)
cc_library(
name = "video",
srcs = [
"msi.c",
"video.c",
],
hdrs = [
"msi.h",
"video.h",
],
deps = [
":audio",
":public_api",
":ring_buffer",
":rtp",
"//c-toxcore/toxcore:Messenger",
"//c-toxcore/toxcore:ccompat",
"//c-toxcore/toxcore:logger",
"//c-toxcore/toxcore:mono_time",
"//c-toxcore/toxcore:network",
"//c-toxcore/toxcore:util",
"@libvpx",
],
)
cc_library(
name = "groupav",
srcs = ["groupav.c"],
hdrs = ["groupav.h"],
deps = [
"//c-toxcore/toxcore",
"//c-toxcore/toxcore:ccompat",
"//c-toxcore/toxcore:group", "//c-toxcore/toxcore:group",
"//c-toxcore/toxcore:logger", "//c-toxcore/toxcore:logger",
"//c-toxcore/toxcore:mono_time", "//c-toxcore/toxcore:mono_time",
"//c-toxcore/toxcore:net_crypto",
"//c-toxcore/toxcore:network",
"//c-toxcore/toxcore:tox", "//c-toxcore/toxcore:tox",
"//c-toxcore/toxcore:util", "//c-toxcore/toxcore:util",
"@libsodium",
"@libvpx",
"@opus", "@opus",
], ],
) )
cc_library(
name = "toxav",
srcs = [
"toxav.c",
"toxav_old.c",
],
hdrs = [
"toxav.h",
],
visibility = ["//c-toxcore:__subpackages__"],
deps = [
":groupav",
":rtp",
":video",
"//c-toxcore/toxcore:Messenger",
"//c-toxcore/toxcore:ccompat",
"//c-toxcore/toxcore:logger",
"//c-toxcore/toxcore:mono_time",
"//c-toxcore/toxcore:tox",
"//c-toxcore/toxcore:util",
],
)
sh_library( sh_library(
name = "cimple_files", name = "cimple_files",
srcs = glob([ srcs = glob([

View File

@ -19,7 +19,6 @@ libtoxav_la_SOURCES = ../toxav/rtp.h \
../toxav/ring_buffer.h \ ../toxav/ring_buffer.h \
../toxav/ring_buffer.c \ ../toxav/ring_buffer.c \
../toxav/toxav.h \ ../toxav/toxav.h \
../toxav/toxav_hacks.h \
../toxav/toxav.c \ ../toxav/toxav.c \
../toxav/toxav_old.c ../toxav/toxav_old.c

View File

@ -13,7 +13,6 @@
#include "../toxcore/ccompat.h" #include "../toxcore/ccompat.h"
#include "../toxcore/logger.h" #include "../toxcore/logger.h"
#include "../toxcore/mono_time.h" #include "../toxcore/mono_time.h"
#include "../toxcore/network.h"
static struct JitterBuffer *jbuf_new(uint32_t capacity); static struct JitterBuffer *jbuf_new(uint32_t capacity);
static void jbuf_clear(struct JitterBuffer *q); static void jbuf_clear(struct JitterBuffer *q);
@ -26,8 +25,6 @@ static bool reconfigure_audio_encoder(const Logger *log, OpusEncoder **e, uint32
uint8_t new_ch, uint32_t *old_br, uint32_t *old_sr, uint8_t *old_ch); uint8_t new_ch, uint32_t *old_br, uint32_t *old_sr, uint8_t *old_ch);
static bool reconfigure_audio_decoder(ACSession *ac, uint32_t sampling_rate, uint8_t channels); static bool reconfigure_audio_decoder(ACSession *ac, uint32_t sampling_rate, uint8_t channels);
ACSession *ac_new(Mono_Time *mono_time, const Logger *log, ToxAV *av, uint32_t friend_number, ACSession *ac_new(Mono_Time *mono_time, const Logger *log, ToxAV *av, uint32_t friend_number,
toxav_audio_receive_frame_cb *cb, void *cb_data) toxav_audio_receive_frame_cb *cb, void *cb_data)
{ {
@ -153,9 +150,9 @@ void ac_iterate(ACSession *ac)
ac->lp_channel_count = opus_packet_get_nb_channels(msg->data + 4); ac->lp_channel_count = opus_packet_get_nb_channels(msg->data + 4);
/** NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa, /* NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa,
* it didn't work quite well. * it didn't work quite well.
*/ */
if (!reconfigure_audio_decoder(ac, ac->lp_sampling_rate, ac->lp_channel_count)) { if (!reconfigure_audio_decoder(ac, ac->lp_sampling_rate, ac->lp_channel_count)) {
LOGGER_WARNING(ac->log, "Failed to reconfigure decoder!"); LOGGER_WARNING(ac->log, "Failed to reconfigure decoder!");
free(msg); free(msg);
@ -276,7 +273,6 @@ static struct JitterBuffer *jbuf_new(uint32_t capacity)
q->capacity = capacity; q->capacity = capacity;
return q; return q;
} }
static void jbuf_clear(struct JitterBuffer *q) static void jbuf_clear(struct JitterBuffer *q)
{ {
while (q->bottom != q->top) { while (q->bottom != q->top) {
@ -285,7 +281,6 @@ static void jbuf_clear(struct JitterBuffer *q)
++q->bottom; ++q->bottom;
} }
} }
static void jbuf_free(struct JitterBuffer *q) static void jbuf_free(struct JitterBuffer *q)
{ {
if (q == nullptr) { if (q == nullptr) {
@ -296,11 +291,6 @@ static void jbuf_free(struct JitterBuffer *q)
free(q->queue); free(q->queue);
free(q); free(q);
} }
/*
* if -1 is returned the RTPMessage m needs to be free'd by the caller
* if 0 is returned the RTPMessage m is stored in the ringbuffer and must NOT be freed by the caller
*/
static int jbuf_write(const Logger *log, struct JitterBuffer *q, struct RTPMessage *m) static int jbuf_write(const Logger *log, struct JitterBuffer *q, struct RTPMessage *m)
{ {
const uint16_t sequnum = m->header.sequnum; const uint16_t sequnum = m->header.sequnum;
@ -329,7 +319,6 @@ static int jbuf_write(const Logger *log, struct JitterBuffer *q, struct RTPMessa
return 0; return 0;
} }
static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success) static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
{ {
if (q->top == q->bottom) { if (q->top == q->bottom) {

View File

@ -10,16 +10,12 @@
#include <string.h> #include <string.h>
#include "ring_buffer.h" #include "ring_buffer.h"
#include "toxav_hacks.h"
#include "../toxcore/ccompat.h" #include "../toxcore/ccompat.h"
#include "../toxcore/logger.h" #include "../toxcore/logger.h"
#include "../toxcore/mono_time.h" #include "../toxcore/mono_time.h"
#include "../toxcore/network.h"
#include "../toxcore/tox_private.h"
#include "../toxcore/util.h" #include "../toxcore/util.h"
#define BWC_PACKET_ID 196 #define BWC_PACKET_ID 196
#define BWC_SEND_INTERVAL_MS 950 // 0.95s #define BWC_SEND_INTERVAL_MS 950 // 0.95s
#define BWC_AVG_PKT_COUNT 20 #define BWC_AVG_PKT_COUNT 20
@ -42,8 +38,9 @@ typedef struct BWCRcvPkt {
struct BWController { struct BWController {
m_cb *mcb; m_cb *mcb;
void *mcb_user_data; void *mcb_user_data;
Messenger *m;
Tox *tox; Tox *tox;
const Logger *log;
uint32_t friend_number; uint32_t friend_number;
BWCCycle cycle; BWCCycle cycle;
@ -52,7 +49,6 @@ struct BWController {
uint32_t packet_loss_counted_cycles; uint32_t packet_loss_counted_cycles;
Mono_Time *bwc_mono_time; Mono_Time *bwc_mono_time;
bool bwc_receive_active; /* if this is set to false then incoming bwc packets will not be processed by bwc_handle_data() */
}; };
struct BWCMessage { struct BWCMessage {
@ -60,11 +56,11 @@ struct BWCMessage {
uint32_t recv; uint32_t recv;
}; };
static void bwc_handle_data(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, void *user_data); static int bwc_handle_data(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object);
static int bwc_send_custom_lossy_packet(Tox *tox, int32_t friendnumber, const uint8_t *data, uint32_t length);
static void send_update(BWController *bwc); static void send_update(BWController *bwc);
BWController *bwc_new(Messenger *m, Tox *tox, uint32_t friendnumber, m_cb *mcb, void *mcb_user_data,
BWController *bwc_new(const Logger *log, Tox *tox, uint32_t friendnumber, m_cb *mcb, void *mcb_user_data,
Mono_Time *bwc_mono_time) Mono_Time *bwc_mono_time)
{ {
BWController *retu = (BWController *)calloc(1, sizeof(BWController)); BWController *retu = (BWController *)calloc(1, sizeof(BWController));
@ -73,18 +69,16 @@ BWController *bwc_new(const Logger *log, Tox *tox, uint32_t friendnumber, m_cb *
return nullptr; return nullptr;
} }
LOGGER_DEBUG(log, "Creating bandwidth controller"); LOGGER_DEBUG(m->log, "Creating bandwidth controller");
retu->mcb = mcb; retu->mcb = mcb;
retu->mcb_user_data = mcb_user_data; retu->mcb_user_data = mcb_user_data;
retu->m = m;
retu->friend_number = friendnumber; retu->friend_number = friendnumber;
retu->bwc_mono_time = bwc_mono_time; retu->bwc_mono_time = bwc_mono_time;
const uint64_t now = current_time_monotonic(bwc_mono_time); const uint64_t now = current_time_monotonic(bwc_mono_time);
retu->cycle.last_sent_timestamp = now; retu->cycle.last_sent_timestamp = now;
retu->cycle.last_refresh_timestamp = now; retu->cycle.last_refresh_timestamp = now;
retu->tox = tox; retu->tox = tox;
retu->log = log;
retu->bwc_receive_active = true;
retu->rcvpkt.rb = rb_new(BWC_AVG_PKT_COUNT); retu->rcvpkt.rb = rb_new(BWC_AVG_PKT_COUNT);
retu->cycle.lost = 0; retu->cycle.lost = 0;
retu->cycle.recv = 0; retu->cycle.recv = 0;
@ -95,6 +89,7 @@ BWController *bwc_new(const Logger *log, Tox *tox, uint32_t friendnumber, m_cb *
rb_write(retu->rcvpkt.rb, &retu->rcvpkt.packet_length_array[i]); rb_write(retu->rcvpkt.rb, &retu->rcvpkt.packet_length_array[i]);
} }
m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu);
return retu; return retu;
} }
@ -104,6 +99,7 @@ void bwc_kill(BWController *bwc)
return; return;
} }
m_callback_rtp_packet(bwc->m, bwc->friend_number, BWC_PACKET_ID, nullptr, nullptr);
rb_kill(bwc->rcvpkt.rb); rb_kill(bwc->rcvpkt.rb);
free(bwc); free(bwc);
} }
@ -115,7 +111,7 @@ void bwc_add_lost(BWController *bwc, uint32_t bytes_lost)
} }
if (bytes_lost > 0) { if (bytes_lost > 0) {
LOGGER_DEBUG(bwc->log, "BWC lost(1): %d", (int)bytes_lost); LOGGER_DEBUG(bwc->m->log, "BWC lost(1): %d", (int)bytes_lost);
bwc->cycle.lost += bytes_lost; bwc->cycle.lost += bytes_lost;
send_update(bwc); send_update(bwc);
} }
@ -139,7 +135,7 @@ static void send_update(BWController *bwc)
bwc->packet_loss_counted_cycles = 0; bwc->packet_loss_counted_cycles = 0;
if (bwc->cycle.lost != 0) { if (bwc->cycle.lost != 0) {
LOGGER_DEBUG(bwc->log, "%p Sent update rcv: %u lost: %u percent: %f %%", LOGGER_DEBUG(bwc->m->log, "%p Sent update rcv: %u lost: %u percent: %f %%",
(void *)bwc, bwc->cycle.recv, bwc->cycle.lost, (void *)bwc, bwc->cycle.recv, bwc->cycle.lost,
((double)bwc->cycle.lost / (bwc->cycle.recv + bwc->cycle.lost)) * 100.0); ((double)bwc->cycle.lost / (bwc->cycle.recv + bwc->cycle.lost)) * 100.0);
uint8_t bwc_packet[sizeof(struct BWCMessage) + 1]; uint8_t bwc_packet[sizeof(struct BWCMessage) + 1];
@ -152,11 +148,13 @@ static void send_update(BWController *bwc)
offset += net_pack_u32(bwc_packet + offset, bwc->cycle.recv); offset += net_pack_u32(bwc_packet + offset, bwc->cycle.recv);
assert(offset == sizeof(bwc_packet)); assert(offset == sizeof(bwc_packet));
Tox_Err_Friend_Custom_Packet error; if (bwc_send_custom_lossy_packet(bwc->tox, bwc->friend_number, bwc_packet, sizeof(bwc_packet)) == -1) {
tox_friend_send_lossy_packet(bwc->tox, bwc->friend_number, bwc_packet, sizeof(bwc_packet), &error); char *netstrerror = net_new_strerror(net_error());
char *stdstrerror = net_new_strerror(errno);
if (error != TOX_ERR_FRIEND_CUSTOM_PACKET_OK) { LOGGER_WARNING(bwc->m->log, "BWC send failed (len: %u)! std error: %s, net error %s",
LOGGER_WARNING(bwc->log, "BWC send failed: %d", error); (unsigned)sizeof(bwc_packet), stdstrerror, netstrerror);
net_kill_strerror(stdstrerror);
net_kill_strerror(netstrerror);
} }
} }
@ -168,11 +166,11 @@ static void send_update(BWController *bwc)
static int on_update(BWController *bwc, const struct BWCMessage *msg) static int on_update(BWController *bwc, const struct BWCMessage *msg)
{ {
LOGGER_DEBUG(bwc->log, "%p Got update from peer", (void *)bwc); LOGGER_DEBUG(bwc->m->log, "%p Got update from peer", (void *)bwc);
/* Peers sent update too soon */ /* Peers sent update too soon */
if (bwc->cycle.last_recv_timestamp + BWC_SEND_INTERVAL_MS > current_time_monotonic(bwc->bwc_mono_time)) { if (bwc->cycle.last_recv_timestamp + BWC_SEND_INTERVAL_MS > current_time_monotonic(bwc->bwc_mono_time)) {
LOGGER_INFO(bwc->log, "%p Rejecting extra update", (void *)bwc); LOGGER_INFO(bwc->m->log, "%p Rejecting extra update", (void *)bwc);
return -1; return -1;
} }
@ -182,8 +180,8 @@ static int on_update(BWController *bwc, const struct BWCMessage *msg)
if (lost != 0 && bwc->mcb != nullptr) { if (lost != 0 && bwc->mcb != nullptr) {
const uint32_t recv = msg->recv; const uint32_t recv = msg->recv;
LOGGER_DEBUG(bwc->log, "recved: %u lost: %u percentage: %f %%", recv, lost, LOGGER_DEBUG(bwc->m->log, "recved: %u lost: %u percentage: %f %%", recv, lost,
((double) lost / (recv + lost)) * 100.0); ((double)lost / (recv + lost)) * 100.0);
bwc->mcb(bwc, bwc->friend_number, bwc->mcb(bwc, bwc->friend_number,
(float)lost / (recv + lost), (float)lost / (recv + lost),
bwc->mcb_user_data); bwc->mcb_user_data);
@ -192,41 +190,28 @@ static int on_update(BWController *bwc, const struct BWCMessage *msg)
return 0; return 0;
} }
static void bwc_handle_data(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, void *user_data) /*
* return -1 on failure, 0 on success
*
*/
static int bwc_send_custom_lossy_packet(Tox *tox, int32_t friendnumber, const uint8_t *data, uint32_t length)
{ {
/* get BWController object from Tox and friend number */ Tox_Err_Friend_Custom_Packet error;
ToxAV *toxav = (ToxAV *)tox_get_av_object(tox); tox_friend_send_lossy_packet(tox, friendnumber, data, (size_t)length, &error);
if (toxav == nullptr) { if (error == TOX_ERR_FRIEND_CUSTOM_PACKET_OK) {
// LOGGER_ERROR(log, "Could not get ToxAV object from Tox"); return 0;
return;
} }
const Logger *log = toxav_get_logger(toxav); return -1;
}
static int bwc_handle_data(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object)
{
BWController *bwc = (BWController *)object;
if (length - 1 != sizeof(struct BWCMessage)) { if (length - 1 != sizeof(struct BWCMessage)) {
LOGGER_ERROR(log, "Got BWCMessage of insufficient size."); return -1;
return;
}
const ToxAVCall *call = call_get(toxav, friend_number);
if (call == nullptr) {
LOGGER_ERROR(log, "Could not get ToxAVCall object from ToxAV.");
return;
}
/* get Call object from Tox and friend number */
BWController *bwc = bwc_controller_get(call);
if (bwc == nullptr) {
LOGGER_WARNING(log, "No session!");
return;
}
if (!bwc->bwc_receive_active) {
LOGGER_WARNING(log, "receiving not allowed!");
return;
} }
size_t offset = 1; // Ignore packet id. size_t offset = 1; // Ignore packet id.
@ -235,15 +220,5 @@ static void bwc_handle_data(Tox *tox, uint32_t friend_number, const uint8_t *dat
offset += net_unpack_u32(data + offset, &msg.recv); offset += net_unpack_u32(data + offset, &msg.recv);
assert(offset == length); assert(offset == length);
on_update(bwc, &msg); return on_update(bwc, &msg);
}
void bwc_allow_receiving(Tox *tox)
{
tox_callback_friend_lossy_packet_per_pktid(tox, bwc_handle_data, BWC_PACKET_ID);
}
void bwc_stop_receiving(Tox *tox)
{
tox_callback_friend_lossy_packet_per_pktid(tox, nullptr, BWC_PACKET_ID);
} }

View File

@ -5,24 +5,19 @@
#ifndef C_TOXCORE_TOXAV_BWCONTROLLER_H #ifndef C_TOXCORE_TOXAV_BWCONTROLLER_H
#define C_TOXCORE_TOXAV_BWCONTROLLER_H #define C_TOXCORE_TOXAV_BWCONTROLLER_H
#include <stdint.h> #include "../toxcore/Messenger.h"
#include "../toxcore/logger.h"
#include "../toxcore/mono_time.h"
#include "../toxcore/tox.h" #include "../toxcore/tox.h"
typedef struct BWController BWController; typedef struct BWController BWController;
typedef void m_cb(BWController *bwc, uint32_t friend_number, float loss, void *user_data); typedef void m_cb(BWController *bwc, uint32_t friend_number, float loss, void *user_data);
BWController *bwc_new(const Logger *log, Tox *tox, uint32_t friendnumber, BWController *bwc_new(Messenger *m, Tox *tox, uint32_t friendnumber, m_cb *mcb, void *mcb_user_data,
m_cb *mcb, void *mcb_user_data, Mono_Time *bwc_mono_time); Mono_Time *bwc_mono_time);
void bwc_kill(BWController *bwc); void bwc_kill(BWController *bwc);
void bwc_add_lost(BWController *bwc, uint32_t bytes_lost); void bwc_add_lost(BWController *bwc, uint32_t bytes_lost);
void bwc_add_recv(BWController *bwc, uint32_t recv_bytes); void bwc_add_recv(BWController *bwc, uint32_t recv_bytes);
void bwc_allow_receiving(Tox *tox);
void bwc_stop_receiving(Tox *tox);
#endif /* C_TOXCORE_TOXAV_BWCONTROLLER_H */ #endif /* C_TOXCORE_TOXAV_BWCONTROLLER_H */

View File

@ -476,7 +476,7 @@ int groupchat_enable_av(const Logger *log, Tox *tox, Group_Chats *g_c, uint32_t
return -1; return -1;
} }
for (uint32_t i = 0; i < (uint32_t)numpeers; ++i) { for (uint32_t i = 0; i < numpeers; ++i) {
group_av_peer_new(group_av, conference_number, i); group_av_peer_new(group_av, conference_number, i);
} }
@ -508,7 +508,7 @@ int groupchat_disable_av(const Group_Chats *g_c, uint32_t conference_number)
return -1; return -1;
} }
for (uint32_t i = 0; i < (uint32_t)numpeers; ++i) { for (uint32_t i = 0; i < numpeers; ++i) {
group_av_peer_delete(group_av, conference_number, group_peer_get_object(g_c, conference_number, i)); group_av_peer_delete(group_av, conference_number, group_peer_get_object(g_c, conference_number, i));
group_peer_set_object(g_c, conference_number, i, nullptr); group_peer_set_object(g_c, conference_number, i, nullptr);
} }

View File

@ -9,13 +9,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "toxav_hacks.h"
#include "../toxcore/ccompat.h" #include "../toxcore/ccompat.h"
#include "../toxcore/logger.h" #include "../toxcore/logger.h"
#include "../toxcore/net_crypto.h"
#include "../toxcore/tox.h"
#include "../toxcore/tox_private.h"
#include "../toxcore/util.h" #include "../toxcore/util.h"
#define MSI_MAXMSG_SIZE 256 #define MSI_MAXMSG_SIZE 256
@ -60,20 +55,20 @@ typedef struct MSIMessage {
} MSIMessage; } MSIMessage;
static void msg_init(MSIMessage *dest, MSIRequest request); static void msg_init(MSIMessage *dest, MSIRequest request);
static void kill_call(const Logger *log, MSICall *call);
static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data, uint16_t length); static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data, uint16_t length);
static uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const uint8_t *value, uint8_t value_len, static uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const uint8_t *value, uint8_t value_len,
uint16_t *length); uint16_t *length);
static int send_message(const Logger *log, Tox *tox, uint32_t friend_number, const MSIMessage *msg); static int send_message(const Messenger *m, uint32_t friend_number, const MSIMessage *msg);
static int send_error(const Logger *log, Tox *tox, uint32_t friend_number, MSIError error); static int send_error(const Messenger *m, uint32_t friend_number, MSIError error);
static bool invoke_callback(MSICall *call, MSICallbackID cb);
static MSICall *get_call(MSISession *session, uint32_t friend_number); static MSICall *get_call(MSISession *session, uint32_t friend_number);
static MSICall *new_call(MSISession *session, uint32_t friend_number); static MSICall *new_call(MSISession *session, uint32_t friend_number);
static bool invoke_callback(const Logger *log, MSICall *call, MSICallbackID cb); static void kill_call(MSICall *call);
static void handle_init(const Logger *log, MSICall *call, const MSIMessage *msg); static void on_peer_status(Messenger *m, uint32_t friend_number, bool is_online, void *user_data);
static void handle_push(const Logger *log, MSICall *call, const MSIMessage *msg); static void handle_init(MSICall *call, const MSIMessage *msg);
static void handle_pop(const Logger *log, MSICall *call, const MSIMessage *msg); static void handle_push(MSICall *call, const MSIMessage *msg);
static void handle_msi_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, static void handle_pop(MSICall *call, const MSIMessage *msg);
void *user_data); static void handle_msi_packet(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *user_data);
/* /*
* Public functions * Public functions
@ -104,43 +99,43 @@ void msi_callback_capabilities(MSISession *session, msi_action_cb *callback)
session->capabilities_callback = callback; session->capabilities_callback = callback;
} }
MSISession *msi_new(const Logger *log, Tox *tox) MSISession *msi_new(Messenger *m)
{ {
if (tox == nullptr) { if (m == nullptr) {
return nullptr; return nullptr;
} }
MSISession *retu = (MSISession *)calloc(1, sizeof(MSISession)); MSISession *retu = (MSISession *)calloc(1, sizeof(MSISession));
if (retu == nullptr) { if (retu == nullptr) {
LOGGER_ERROR(log, "Allocation failed! Program might misbehave!"); LOGGER_ERROR(m->log, "Allocation failed! Program might misbehave!");
return nullptr; return nullptr;
} }
if (create_recursive_mutex(retu->mutex) != 0) { if (create_recursive_mutex(retu->mutex) != 0) {
LOGGER_ERROR(log, "Failed to init mutex! Program might misbehave"); LOGGER_ERROR(m->log, "Failed to init mutex! Program might misbehave");
free(retu); free(retu);
return nullptr; return nullptr;
} }
retu->tox = tox; retu->messenger = m;
// register callback m_callback_msi_packet(m, handle_msi_packet, retu);
tox_callback_friend_lossless_packet_per_pktid(tox, handle_msi_packet, PACKET_ID_MSI);
LOGGER_DEBUG(log, "New msi session: %p ", (void *)retu); /* This is called when remote terminates session */
m_callback_connectionstatus_internal_av(m, on_peer_status, retu);
LOGGER_DEBUG(m->log, "New msi session: %p ", (void *)retu);
return retu; return retu;
} }
int msi_kill(MSISession *session, const Logger *log)
int msi_kill(const Logger *log, Tox *tox, MSISession *session)
{ {
if (session == nullptr) { if (session == nullptr) {
LOGGER_ERROR(log, "Tried to terminate non-existing session"); LOGGER_ERROR(log, "Tried to terminate non-existing session");
return -1; return -1;
} }
// UN-register callback m_callback_msi_packet(session->messenger, nullptr, nullptr);
tox_callback_friend_lossless_packet_per_pktid(tox, nullptr, PACKET_ID_MSI);
if (pthread_mutex_trylock(session->mutex) != 0) { if (pthread_mutex_trylock(session->mutex) != 0) {
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex"); LOGGER_ERROR(log, "Failed to acquire lock on msi mutex");
@ -154,10 +149,10 @@ int msi_kill(const Logger *log, Tox *tox, MSISession *session)
MSICall *it = get_call(session, session->calls_head); MSICall *it = get_call(session, session->calls_head);
while (it != nullptr) { while (it != nullptr) {
send_message(log, session->tox, it->friend_number, &msg); send_message(session->messenger, it->friend_number, &msg);
MSICall *temp_it = it; MSICall *temp_it = it;
it = it->next; it = it->next;
kill_call(log, temp_it); /* This will eventually free session->calls */ kill_call(temp_it); /* This will eventually free session->calls */
} }
} }
@ -168,57 +163,21 @@ int msi_kill(const Logger *log, Tox *tox, MSISession *session)
free(session); free(session);
return 0; return 0;
} }
int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities)
/*
* return true if friend is offline and the call was canceled.
*/
bool check_peer_offline_status(const Logger *log, const Tox *tox, MSISession *session, uint32_t friend_number)
{ {
if (tox == nullptr || session == nullptr) {
return false;
}
Tox_Err_Friend_Query f_con_query_error;
const Tox_Connection f_con_status = tox_friend_get_connection_status(tox, friend_number, &f_con_query_error);
if (f_con_status == TOX_CONNECTION_NONE) {
/* Friend is now offline */
LOGGER_DEBUG(log, "Friend %d is now offline", friend_number);
pthread_mutex_lock(session->mutex);
MSICall *call = get_call(session, friend_number);
if (call == nullptr) {
pthread_mutex_unlock(session->mutex);
return true;
}
invoke_callback(log, call, MSI_ON_PEERTIMEOUT); /* Failure is ignored */
kill_call(log, call);
pthread_mutex_unlock(session->mutex);
return true;
}
return false;
}
int msi_invite(const Logger *log, MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities)
{
LOGGER_DEBUG(log, "msi_invite:session:%p", (void *)session);
if (session == nullptr) { if (session == nullptr) {
return -1; return -1;
} }
LOGGER_DEBUG(log, "Session: %p Inviting friend: %u", (void *)session, friend_number); LOGGER_DEBUG(session->messenger->log, "Session: %p Inviting friend: %u", (void *)session, friend_number);
if (pthread_mutex_trylock(session->mutex) != 0) { if (pthread_mutex_trylock(session->mutex) != 0) {
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex"); LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
return -1; return -1;
} }
if (get_call(session, friend_number) != nullptr) { if (get_call(session, friend_number) != nullptr) {
LOGGER_ERROR(log, "Already in a call"); LOGGER_ERROR(session->messenger->log, "Already in a call");
pthread_mutex_unlock(session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return -1;
} }
@ -238,18 +197,17 @@ int msi_invite(const Logger *log, MSISession *session, MSICall **call, uint32_t
msg.capabilities.exists = true; msg.capabilities.exists = true;
msg.capabilities.value = capabilities; msg.capabilities.value = capabilities;
send_message(log, temp->session->tox, temp->friend_number, &msg); send_message(temp->session->messenger, temp->friend_number, &msg);
temp->state = MSI_CALL_REQUESTING; temp->state = MSI_CALL_REQUESTING;
*call = temp; *call = temp;
LOGGER_DEBUG(log, "Invite sent"); LOGGER_DEBUG(session->messenger->log, "Invite sent");
pthread_mutex_unlock(session->mutex); pthread_mutex_unlock(session->mutex);
return 0; return 0;
} }
int msi_hangup(MSICall *call)
int msi_hangup(const Logger *log, MSICall *call)
{ {
if (call == nullptr || call->session == nullptr) { if (call == nullptr || call->session == nullptr) {
return -1; return -1;
@ -257,16 +215,16 @@ int msi_hangup(const Logger *log, MSICall *call)
MSISession *session = call->session; MSISession *session = call->session;
LOGGER_DEBUG(log, "Session: %p Hanging up call with friend: %u", (void *)call->session, LOGGER_DEBUG(session->messenger->log, "Session: %p Hanging up call with friend: %u", (void *)call->session,
call->friend_number); call->friend_number);
if (pthread_mutex_trylock(session->mutex) != 0) { if (pthread_mutex_trylock(session->mutex) != 0) {
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex"); LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
return -1; return -1;
} }
if (call->state == MSI_CALL_INACTIVE) { if (call->state == MSI_CALL_INACTIVE) {
LOGGER_ERROR(log, "Call is in invalid state!"); LOGGER_ERROR(session->messenger->log, "Call is in invalid state!");
pthread_mutex_unlock(session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return -1;
} }
@ -274,14 +232,13 @@ int msi_hangup(const Logger *log, MSICall *call)
MSIMessage msg; MSIMessage msg;
msg_init(&msg, REQU_POP); msg_init(&msg, REQU_POP);
send_message(log, session->tox, call->friend_number, &msg); send_message(session->messenger, call->friend_number, &msg);
kill_call(log, call); kill_call(call);
pthread_mutex_unlock(session->mutex); pthread_mutex_unlock(session->mutex);
return 0; return 0;
} }
int msi_answer(MSICall *call, uint8_t capabilities)
int msi_answer(const Logger *log, MSICall *call, uint8_t capabilities)
{ {
if (call == nullptr || call->session == nullptr) { if (call == nullptr || call->session == nullptr) {
return -1; return -1;
@ -289,18 +246,18 @@ int msi_answer(const Logger *log, MSICall *call, uint8_t capabilities)
MSISession *session = call->session; MSISession *session = call->session;
LOGGER_DEBUG(log, "Session: %p Answering call from: %u", (void *)call->session, LOGGER_DEBUG(session->messenger->log, "Session: %p Answering call from: %u", (void *)call->session,
call->friend_number); call->friend_number);
if (pthread_mutex_trylock(session->mutex) != 0) { if (pthread_mutex_trylock(session->mutex) != 0) {
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex"); LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
return -1; return -1;
} }
if (call->state != MSI_CALL_REQUESTED) { if (call->state != MSI_CALL_REQUESTED) {
/* Though sending in invalid state will not cause anything weird /* Though sending in invalid state will not cause anything weird
* Its better to not do it like a maniac */ * Its better to not do it like a maniac */
LOGGER_ERROR(log, "Call is in invalid state!"); LOGGER_ERROR(session->messenger->log, "Call is in invalid state!");
pthread_mutex_unlock(session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return -1;
} }
@ -313,15 +270,14 @@ int msi_answer(const Logger *log, MSICall *call, uint8_t capabilities)
msg.capabilities.exists = true; msg.capabilities.exists = true;
msg.capabilities.value = capabilities; msg.capabilities.value = capabilities;
send_message(log, session->tox, call->friend_number, &msg); send_message(session->messenger, call->friend_number, &msg);
call->state = MSI_CALL_ACTIVE; call->state = MSI_CALL_ACTIVE;
pthread_mutex_unlock(session->mutex); pthread_mutex_unlock(session->mutex);
return 0; return 0;
} }
int msi_change_capabilities(MSICall *call, uint8_t capabilities)
int msi_change_capabilities(const Logger *log, MSICall *call, uint8_t capabilities)
{ {
if (call == nullptr || call->session == nullptr) { if (call == nullptr || call->session == nullptr) {
return -1; return -1;
@ -329,16 +285,16 @@ int msi_change_capabilities(const Logger *log, MSICall *call, uint8_t capabiliti
MSISession *session = call->session; MSISession *session = call->session;
LOGGER_DEBUG(log, "Session: %p Trying to change capabilities to friend %u", (void *)call->session, LOGGER_DEBUG(session->messenger->log, "Session: %p Trying to change capabilities to friend %u", (void *)call->session,
call->friend_number); call->friend_number);
if (pthread_mutex_trylock(session->mutex) != 0) { if (pthread_mutex_trylock(session->mutex) != 0) {
LOGGER_ERROR(log, "Failed to acquire lock on msi mutex"); LOGGER_ERROR(session->messenger->log, "Failed to acquire lock on msi mutex");
return -1; return -1;
} }
if (call->state != MSI_CALL_ACTIVE) { if (call->state != MSI_CALL_ACTIVE) {
LOGGER_ERROR(log, "Call is in invalid state!"); LOGGER_ERROR(session->messenger->log, "Call is in invalid state!");
pthread_mutex_unlock(session->mutex); pthread_mutex_unlock(session->mutex);
return -1; return -1;
} }
@ -351,7 +307,7 @@ int msi_change_capabilities(const Logger *log, MSICall *call, uint8_t capabiliti
msg.capabilities.exists = true; msg.capabilities.exists = true;
msg.capabilities.value = capabilities; msg.capabilities.value = capabilities;
send_message(log, call->session->tox, call->friend_number, &msg); send_message(call->session->messenger, call->friend_number, &msg);
pthread_mutex_unlock(session->mutex); pthread_mutex_unlock(session->mutex);
return 0; return 0;
@ -395,51 +351,10 @@ static bool check_enum_high(const Logger *log, const uint8_t *bytes, uint8_t enu
return true; return true;
} }
static const uint8_t *msg_parse_one(const Logger *log, MSIMessage *dest, const uint8_t *it, int *size_constraint)
{
switch (*it) {
case ID_REQUEST: {
if (!check_size(log, it, size_constraint, 1) ||
!check_enum_high(log, it, REQU_POP)) {
return nullptr;
}
dest->request.value = (MSIRequest)it[2];
dest->request.exists = true;
return it + 3;
}
case ID_ERROR: {
if (!check_size(log, it, size_constraint, 1) ||
!check_enum_high(log, it, MSI_E_UNDISCLOSED)) {
return nullptr;
}
dest->error.value = (MSIError)it[2];
dest->error.exists = true;
return it + 3;
}
case ID_CAPABILITIES: {
if (!check_size(log, it, size_constraint, 1)) {
return nullptr;
}
dest->capabilities.value = it[2];
dest->capabilities.exists = true;
return it + 3;
}
default: {
LOGGER_ERROR(log, "Invalid id byte: %d", *it);
return nullptr;
}
}
}
static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data, uint16_t length) static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data, uint16_t length)
{ {
/* Parse raw data received from socket into MSIMessage struct */ /* Parse raw data received from socket into MSIMessage struct */
assert(dest != nullptr); assert(dest != nullptr);
if (length == 0 || data[length - 1] != 0) { /* End byte must have value 0 */ if (length == 0 || data[length - 1] != 0) { /* End byte must have value 0 */
@ -453,10 +368,46 @@ static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data
int size_constraint = length; int size_constraint = length;
while (*it != 0) {/* until end byte is hit */ while (*it != 0) {/* until end byte is hit */
it = msg_parse_one(log, dest, it, &size_constraint); switch (*it) {
case ID_REQUEST: {
if (!check_size(log, it, &size_constraint, 1) ||
!check_enum_high(log, it, REQU_POP)) {
return -1;
}
if (it == nullptr) { dest->request.value = (MSIRequest)it[2];
return -1; dest->request.exists = true;
it += 3;
break;
}
case ID_ERROR: {
if (!check_size(log, it, &size_constraint, 1) ||
!check_enum_high(log, it, MSI_E_UNDISCLOSED)) {
return -1;
}
dest->error.value = (MSIError)it[2];
dest->error.exists = true;
it += 3;
break;
}
case ID_CAPABILITIES: {
if (!check_size(log, it, &size_constraint, 1)) {
return -1;
}
dest->capabilities.value = it[2];
dest->capabilities.exists = true;
it += 3;
break;
}
default: {
LOGGER_ERROR(log, "Invalid id byte");
return -1;
}
} }
} }
@ -467,7 +418,6 @@ static int msg_parse_in(const Logger *log, MSIMessage *dest, const uint8_t *data
return 0; return 0;
} }
static uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const uint8_t *value, uint8_t value_len, static uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const uint8_t *value, uint8_t value_len,
uint16_t *length) uint16_t *length)
{ {
@ -487,48 +437,11 @@ static uint8_t *msg_parse_header_out(MSIHeaderID id, uint8_t *dest, const uint8_
return dest + value_len; /* Set to next position ready to be written */ return dest + value_len; /* Set to next position ready to be written */
} }
static int send_message(const Messenger *m, uint32_t friend_number, const MSIMessage *msg)
/* Send an msi packet.
*
* return 1 on success
* return 0 on failure
*/
static int m_msi_packet(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length)
{ {
// TODO(Zoff): make this better later! -------------------
/* we need to prepend 1 byte (packet id) to data
* do this without malloc, memcpy and free in the future
*/
const size_t length_new = (size_t)length + 1;
uint8_t *data_new = (uint8_t *)malloc(length_new);
if (data_new == nullptr) {
return 0;
}
data_new[0] = PACKET_ID_MSI;
if (length != 0) {
memcpy(data_new + 1, data, length);
}
Tox_Err_Friend_Custom_Packet error;
tox_friend_send_lossless_packet(tox, friendnumber, data_new, length_new, &error);
free(data_new);
if (error == TOX_ERR_FRIEND_CUSTOM_PACKET_OK) {
return 1;
}
return 0;
}
static int send_message(const Logger *log, Tox *tox, uint32_t friend_number, const MSIMessage *msg)
{
assert(tox != nullptr);
/* Parse and send message */ /* Parse and send message */
assert(m != nullptr);
uint8_t parsed[MSI_MAXMSG_SIZE]; uint8_t parsed[MSI_MAXMSG_SIZE];
uint8_t *it = parsed; uint8_t *it = parsed;
@ -539,7 +452,7 @@ static int send_message(const Logger *log, Tox *tox, uint32_t friend_number, con
it = msg_parse_header_out(ID_REQUEST, it, &cast, it = msg_parse_header_out(ID_REQUEST, it, &cast,
sizeof(cast), &size); sizeof(cast), &size);
} else { } else {
LOGGER_DEBUG(log, "Must have request field"); LOGGER_DEBUG(m->log, "Must have request field");
return -1; return -1;
} }
@ -555,27 +468,26 @@ static int send_message(const Logger *log, Tox *tox, uint32_t friend_number, con
} }
if (it == parsed) { if (it == parsed) {
LOGGER_WARNING(log, "Parsing message failed; empty message"); LOGGER_WARNING(m->log, "Parsing message failed; empty message");
return -1; return -1;
} }
*it = 0; *it = 0;
++size; ++size;
if (m_msi_packet(tox, friend_number, parsed, size) == 1) { if (m_msi_packet(m, friend_number, parsed, size)) {
LOGGER_DEBUG(log, "Sent message"); LOGGER_DEBUG(m->log, "Sent message");
return 0; return 0;
} }
return -1; return -1;
} }
static int send_error(const Messenger *m, uint32_t friend_number, MSIError error)
static int send_error(const Logger *log, Tox *tox, uint32_t friend_number, MSIError error)
{ {
assert(tox != nullptr);
/* Send error message */ /* Send error message */
LOGGER_DEBUG(log, "Sending error: %d to friend: %d", error, friend_number); assert(m != nullptr);
LOGGER_DEBUG(m->log, "Sending error: %d to friend: %d", error, friend_number);
MSIMessage msg; MSIMessage msg;
msg_init(&msg, REQU_POP); msg_init(&msg, REQU_POP);
@ -583,14 +495,13 @@ static int send_error(const Logger *log, Tox *tox, uint32_t friend_number, MSIEr
msg.error.exists = true; msg.error.exists = true;
msg.error.value = error; msg.error.value = error;
send_message(log, tox, friend_number, &msg); send_message(m, friend_number, &msg);
return 0; return 0;
} }
static int invoke_callback_inner(MSICall *call, MSICallbackID id)
static int invoke_callback_inner(const Logger *log, MSICall *call, MSICallbackID id)
{ {
MSISession *session = call->session; MSISession *session = call->session;
LOGGER_DEBUG(log, "invoking callback function: %d", id); LOGGER_DEBUG(session->messenger->log, "invoking callback function: %d", id);
switch (id) { switch (id) {
case MSI_ON_INVITE: case MSI_ON_INVITE:
@ -612,16 +523,15 @@ static int invoke_callback_inner(const Logger *log, MSICall *call, MSICallbackID
return session->capabilities_callback(session->av, call); return session->capabilities_callback(session->av, call);
} }
LOGGER_FATAL(log, "invalid callback id: %d", id); LOGGER_FATAL(session->messenger->log, "invalid callback id: %d", id);
return -1; return -1;
} }
static bool invoke_callback(MSICall *call, MSICallbackID cb)
static bool invoke_callback(const Logger *log, MSICall *call, MSICallbackID cb)
{ {
assert(call != nullptr); assert(call != nullptr);
if (invoke_callback_inner(log, call, cb) != 0) { if (invoke_callback_inner(call, cb) != 0) {
LOGGER_WARNING(log, LOGGER_WARNING(call->session->messenger->log,
"Callback state handling failed, sending error"); "Callback state handling failed, sending error");
/* If no callback present or error happened while handling, /* If no callback present or error happened while handling,
@ -636,7 +546,6 @@ static bool invoke_callback(const Logger *log, MSICall *call, MSICallbackID cb)
return true; return true;
} }
static MSICall *get_call(MSISession *session, uint32_t friend_number) static MSICall *get_call(MSISession *session, uint32_t friend_number)
{ {
assert(session != nullptr); assert(session != nullptr);
@ -647,7 +556,6 @@ static MSICall *get_call(MSISession *session, uint32_t friend_number)
return session->calls[friend_number]; return session->calls[friend_number];
} }
static MSICall *new_call(MSISession *session, uint32_t friend_number) static MSICall *new_call(MSISession *session, uint32_t friend_number)
{ {
assert(session != nullptr); assert(session != nullptr);
@ -699,8 +607,7 @@ static MSICall *new_call(MSISession *session, uint32_t friend_number)
session->calls[friend_number] = rc; session->calls[friend_number] = rc;
return rc; return rc;
} }
static void kill_call(MSICall *call)
static void kill_call(const Logger *log, MSICall *call)
{ {
/* Assume that session mutex is locked */ /* Assume that session mutex is locked */
if (call == nullptr) { if (call == nullptr) {
@ -709,7 +616,7 @@ static void kill_call(const Logger *log, MSICall *call)
MSISession *session = call->session; MSISession *session = call->session;
LOGGER_DEBUG(log, "Killing call: %p", (void *)call); LOGGER_DEBUG(session->messenger->log, "Killing call: %p", (void *)call);
MSICall *prev = call->prev; MSICall *prev = call->prev;
MSICall *next = call->next; MSICall *next = call->next;
@ -741,12 +648,37 @@ CLEAR_CONTAINER:
free(call); free(call);
session->calls = nullptr; session->calls = nullptr;
} }
static void on_peer_status(Messenger *m, uint32_t friend_number, bool is_online, void *user_data)
static bool try_handle_init(const Logger *log, MSICall *call, const MSIMessage *msg)
{ {
MSISession *session = (MSISession *)user_data;
if (is_online) {
// Friend is online.
return;
}
LOGGER_DEBUG(m->log, "Friend %d is now offline", friend_number);
pthread_mutex_lock(session->mutex);
MSICall *call = get_call(session, friend_number);
if (call == nullptr) {
pthread_mutex_unlock(session->mutex);
return;
}
invoke_callback(call, MSI_ON_PEERTIMEOUT); /* Failure is ignored */
kill_call(call);
pthread_mutex_unlock(session->mutex);
}
static bool try_handle_init(MSICall *call, const MSIMessage *msg)
{
assert(call != nullptr);
LOGGER_DEBUG(call->session->messenger->log,
"Session: %p Handling 'init' friend: %d", (void *)call->session, call->friend_number);
if (!msg->capabilities.exists) { if (!msg->capabilities.exists) {
LOGGER_WARNING(log, "Session: %p Invalid capabilities on 'init'", (void *)call->session); LOGGER_WARNING(call->session->messenger->log, "Session: %p Invalid capabilities on 'init'", (void *)call->session);
call->error = MSI_E_INVALID_MESSAGE; call->error = MSI_E_INVALID_MESSAGE;
return false; return false;
} }
@ -757,7 +689,7 @@ static bool try_handle_init(const Logger *log, MSICall *call, const MSIMessage *
call->peer_capabilities = msg->capabilities.value; call->peer_capabilities = msg->capabilities.value;
call->state = MSI_CALL_REQUESTED; call->state = MSI_CALL_REQUESTED;
if (!invoke_callback(log, call, MSI_ON_INVITE)) { if (!invoke_callback(call, MSI_ON_INVITE)) {
return false; return false;
} }
@ -772,7 +704,7 @@ static bool try_handle_init(const Logger *log, MSICall *call, const MSIMessage *
* we can automatically answer the re-call. * we can automatically answer the re-call.
*/ */
LOGGER_INFO(log, "Friend is recalling us"); LOGGER_INFO(call->session->messenger->log, "Friend is recalling us");
MSIMessage out_msg; MSIMessage out_msg;
msg_init(&out_msg, REQU_PUSH); msg_init(&out_msg, REQU_PUSH);
@ -780,7 +712,7 @@ static bool try_handle_init(const Logger *log, MSICall *call, const MSIMessage *
out_msg.capabilities.exists = true; out_msg.capabilities.exists = true;
out_msg.capabilities.value = call->self_capabilities; out_msg.capabilities.value = call->self_capabilities;
send_message(log, call->session->tox, call->friend_number, &out_msg); send_message(call->session->messenger, call->friend_number, &out_msg);
/* If peer changed capabilities during re-call they will /* If peer changed capabilities during re-call they will
* be handled accordingly during the next step * be handled accordingly during the next step
@ -790,7 +722,7 @@ static bool try_handle_init(const Logger *log, MSICall *call, const MSIMessage *
case MSI_CALL_REQUESTED: // fall-through case MSI_CALL_REQUESTED: // fall-through
case MSI_CALL_REQUESTING: { case MSI_CALL_REQUESTING: {
LOGGER_WARNING(log, "Session: %p Invalid state on 'init'", (void *)call->session); LOGGER_WARNING(call->session->messenger->log, "Session: %p Invalid state on 'init'", (void *)call->session);
call->error = MSI_E_INVALID_STATE; call->error = MSI_E_INVALID_STATE;
return false; return false;
} }
@ -798,28 +730,26 @@ static bool try_handle_init(const Logger *log, MSICall *call, const MSIMessage *
return true; return true;
} }
static void handle_init(MSICall *call, const MSIMessage *msg)
static void handle_init(const Logger *log, MSICall *call, const MSIMessage *msg)
{ {
assert(call != nullptr); assert(call != nullptr);
LOGGER_DEBUG(log, LOGGER_DEBUG(call->session->messenger->log,
"Session: %p Handling 'init' friend: %d", (void *)call->session, call->friend_number); "Session: %p Handling 'init' friend: %d", (void *)call->session, call->friend_number);
if (!try_handle_init(log, call, msg)) { if (!try_handle_init(call, msg)) {
send_error(log, call->session->tox, call->friend_number, call->error); send_error(call->session->messenger, call->friend_number, call->error);
kill_call(log, call); kill_call(call);
} }
} }
static void handle_push(MSICall *call, const MSIMessage *msg)
static void handle_push(const Logger *log, MSICall *call, const MSIMessage *msg)
{ {
assert(call != nullptr); assert(call != nullptr);
LOGGER_DEBUG(log, "Session: %p Handling 'push' friend: %d", (void *)call->session, LOGGER_DEBUG(call->session->messenger->log, "Session: %p Handling 'push' friend: %d", (void *)call->session,
call->friend_number); call->friend_number);
if (!msg->capabilities.exists) { if (!msg->capabilities.exists) {
LOGGER_WARNING(log, "Session: %p Invalid capabilities on 'push'", (void *)call->session); LOGGER_WARNING(call->session->messenger->log, "Session: %p Invalid capabilities on 'push'", (void *)call->session);
call->error = MSI_E_INVALID_MESSAGE; call->error = MSI_E_INVALID_MESSAGE;
goto FAILURE; goto FAILURE;
} }
@ -827,11 +757,12 @@ static void handle_push(const Logger *log, MSICall *call, const MSIMessage *msg)
switch (call->state) { switch (call->state) {
case MSI_CALL_ACTIVE: { case MSI_CALL_ACTIVE: {
if (call->peer_capabilities != msg->capabilities.value) { if (call->peer_capabilities != msg->capabilities.value) {
LOGGER_INFO(log, "Friend is changing capabilities to: %u", msg->capabilities.value); /* Only act if capabilities changed */
LOGGER_INFO(call->session->messenger->log, "Friend is changing capabilities to: %u", msg->capabilities.value);
call->peer_capabilities = msg->capabilities.value; call->peer_capabilities = msg->capabilities.value;
if (!invoke_callback(log, call, MSI_ON_CAPABILITIES)) { if (!invoke_callback(call, MSI_ON_CAPABILITIES)) {
goto FAILURE; goto FAILURE;
} }
} }
@ -840,13 +771,13 @@ static void handle_push(const Logger *log, MSICall *call, const MSIMessage *msg)
} }
case MSI_CALL_REQUESTING: { case MSI_CALL_REQUESTING: {
LOGGER_INFO(log, "Friend answered our call"); LOGGER_INFO(call->session->messenger->log, "Friend answered our call");
/* Call started */ /* Call started */
call->peer_capabilities = msg->capabilities.value; call->peer_capabilities = msg->capabilities.value;
call->state = MSI_CALL_ACTIVE; call->state = MSI_CALL_ACTIVE;
if (!invoke_callback(log, call, MSI_ON_START)) { if (!invoke_callback(call, MSI_ON_START)) {
goto FAILURE; goto FAILURE;
} }
@ -855,7 +786,8 @@ static void handle_push(const Logger *log, MSICall *call, const MSIMessage *msg)
case MSI_CALL_INACTIVE: // fall-through case MSI_CALL_INACTIVE: // fall-through
case MSI_CALL_REQUESTED: { case MSI_CALL_REQUESTED: {
LOGGER_WARNING(log, "Ignoring invalid push"); /* Pushes during initialization state are ignored */
LOGGER_WARNING(call->session->messenger->log, "Ignoring invalid push");
break; break;
} }
} }
@ -863,102 +795,76 @@ static void handle_push(const Logger *log, MSICall *call, const MSIMessage *msg)
return; return;
FAILURE: FAILURE:
send_error(log, call->session->tox, call->friend_number, call->error); send_error(call->session->messenger, call->friend_number, call->error);
kill_call(log, call); kill_call(call);
} }
static void handle_pop(MSICall *call, const MSIMessage *msg)
static void handle_pop(const Logger *log, MSICall *call, const MSIMessage *msg)
{ {
assert(call != nullptr); assert(call != nullptr);
LOGGER_DEBUG(log, "Session: %p Handling 'pop', friend id: %d", (void *)call->session, LOGGER_DEBUG(call->session->messenger->log, "Session: %p Handling 'pop', friend id: %d", (void *)call->session,
call->friend_number); call->friend_number);
/* callback errors are ignored */ /* callback errors are ignored */
if (msg->error.exists) { if (msg->error.exists) {
LOGGER_WARNING(log, "Friend detected an error: %d", msg->error.value); LOGGER_WARNING(call->session->messenger->log, "Friend detected an error: %d", msg->error.value);
call->error = msg->error.value; call->error = msg->error.value;
invoke_callback(log, call, MSI_ON_ERROR); invoke_callback(call, MSI_ON_ERROR);
} else { } else {
switch (call->state) { switch (call->state) {
case MSI_CALL_INACTIVE: { case MSI_CALL_INACTIVE: {
LOGGER_FATAL(log, "Handling what should be impossible case"); LOGGER_FATAL(call->session->messenger->log, "Handling what should be impossible case");
break; break;
} }
case MSI_CALL_ACTIVE: { case MSI_CALL_ACTIVE: {
/* Hangup */ /* Hangup */
LOGGER_INFO(log, "Friend hung up on us"); LOGGER_INFO(call->session->messenger->log, "Friend hung up on us");
invoke_callback(log, call, MSI_ON_END); invoke_callback(call, MSI_ON_END);
break; break;
} }
case MSI_CALL_REQUESTING: { case MSI_CALL_REQUESTING: {
/* Reject */ /* Reject */
LOGGER_INFO(log, "Friend rejected our call"); LOGGER_INFO(call->session->messenger->log, "Friend rejected our call");
invoke_callback(log, call, MSI_ON_END); invoke_callback(call, MSI_ON_END);
break; break;
} }
case MSI_CALL_REQUESTED: { case MSI_CALL_REQUESTED: {
/* Cancel */ /* Cancel */
LOGGER_INFO(log, "Friend canceled call invite"); LOGGER_INFO(call->session->messenger->log, "Friend canceled call invite");
invoke_callback(log, call, MSI_ON_END); invoke_callback(call, MSI_ON_END);
break; break;
} }
} }
} }
kill_call(log, call); kill_call(call);
} }
static void handle_msi_packet(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *user_data)
static void handle_msi_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length,
void *user_data)
{ {
const ToxAV *toxav = (ToxAV *)tox_get_av_object(tox); MSISession *session = (MSISession *)user_data;
if (toxav == nullptr) { LOGGER_DEBUG(m->log, "Got msi message");
return;
}
const Logger *log = toxav_get_logger(toxav);
if (length < 2) {
LOGGER_ERROR(log, "MSI packet is less than 2 bytes in size");
// we need more than the ID byte for MSI messages
return;
}
const uint16_t payload_length = (uint16_t)(length - 1);
// Zoff: do not show the first byte, its always "PACKET_ID_MSI"
const uint8_t *data_strip_id_byte = data + 1;
LOGGER_DEBUG(log, "Got msi message");
MSISession *session = tox_av_msi_get(toxav);
if (session == nullptr) {
return;
}
MSIMessage msg; MSIMessage msg;
if (msg_parse_in(log, &msg, data_strip_id_byte, payload_length) == -1) { if (msg_parse_in(m->log, &msg, data, length) == -1) {
LOGGER_WARNING(log, "Error parsing message"); LOGGER_WARNING(m->log, "Error parsing message");
send_error(log, tox, friend_number, MSI_E_INVALID_MESSAGE); send_error(m, friend_number, MSI_E_INVALID_MESSAGE);
return; return;
} }
LOGGER_DEBUG(log, "Successfully parsed message"); LOGGER_DEBUG(m->log, "Successfully parsed message");
pthread_mutex_lock(session->mutex); pthread_mutex_lock(session->mutex);
MSICall *call = get_call(session, friend_number); MSICall *call = get_call(session, friend_number);
if (call == nullptr) { if (call == nullptr) {
if (msg.request.value != REQU_INIT) { if (msg.request.value != REQU_INIT) {
send_error(log, tox, friend_number, MSI_E_STRAY_MESSAGE); send_error(m, friend_number, MSI_E_STRAY_MESSAGE);
pthread_mutex_unlock(session->mutex); pthread_mutex_unlock(session->mutex);
return; return;
} }
@ -966,7 +872,7 @@ static void handle_msi_packet(Tox *tox, uint32_t friend_number, const uint8_t *d
call = new_call(session, friend_number); call = new_call(session, friend_number);
if (call == nullptr) { if (call == nullptr) {
send_error(log, tox, friend_number, MSI_E_SYSTEM); send_error(m, friend_number, MSI_E_SYSTEM);
pthread_mutex_unlock(session->mutex); pthread_mutex_unlock(session->mutex);
return; return;
} }
@ -974,17 +880,17 @@ static void handle_msi_packet(Tox *tox, uint32_t friend_number, const uint8_t *d
switch (msg.request.value) { switch (msg.request.value) {
case REQU_INIT: { case REQU_INIT: {
handle_init(log, call, &msg); handle_init(call, &msg);
break; break;
} }
case REQU_PUSH: { case REQU_PUSH: {
handle_push(log, call, &msg); handle_push(call, &msg);
break; break;
} }
case REQU_POP: { case REQU_POP: {
handle_pop(log, call, &msg); /* always kills the call */ handle_pop(call, &msg); /* always kills the call */
break; break;
} }
} }

View File

@ -11,6 +11,7 @@
#include "audio.h" #include "audio.h"
#include "video.h" #include "video.h"
#include "../toxcore/Messenger.h"
#include "../toxcore/logger.h" #include "../toxcore/logger.h"
/** /**
@ -41,22 +42,22 @@ typedef enum MSICapabilities {
* Call state identifiers. * Call state identifiers.
*/ */
typedef enum MSICallState { typedef enum MSICallState {
MSI_CALL_INACTIVE = 0, /* Default */ MSI_CALL_INACTIVE, /* Default */
MSI_CALL_ACTIVE = 1, MSI_CALL_ACTIVE,
MSI_CALL_REQUESTING = 2, /* when sending call invite */ MSI_CALL_REQUESTING, /* when sending call invite */
MSI_CALL_REQUESTED = 3, /* when getting call invite */ MSI_CALL_REQUESTED, /* when getting call invite */
} MSICallState; } MSICallState;
/** /**
* Callbacks ids that handle the states * Callbacks ids that handle the states
*/ */
typedef enum MSICallbackID { typedef enum MSICallbackID {
MSI_ON_INVITE = 0, /* Incoming call */ MSI_ON_INVITE, /* Incoming call */
MSI_ON_START = 1, /* Call (RTP transmission) started */ MSI_ON_START, /* Call (RTP transmission) started */
MSI_ON_END = 2, /* Call that was active ended */ MSI_ON_END, /* Call that was active ended */
MSI_ON_ERROR = 3, /* On protocol error */ MSI_ON_ERROR, /* On protocol error */
MSI_ON_PEERTIMEOUT = 4, /* Peer timed out; stop the call */ MSI_ON_PEERTIMEOUT, /* Peer timed out; stop the call */
MSI_ON_CAPABILITIES = 5, /* Peer requested capabilities change */ MSI_ON_CAPABILITIES, /* Peer requested capabilities change */
} MSICallbackID; } MSICallbackID;
/** /**
@ -95,7 +96,7 @@ typedef struct MSISession {
uint32_t calls_head; uint32_t calls_head;
void *av; void *av;
Tox *tox; Messenger *messenger;
pthread_mutex_t mutex[1]; pthread_mutex_t mutex[1];
@ -110,11 +111,11 @@ typedef struct MSISession {
/** /**
* Start the control session. * Start the control session.
*/ */
MSISession *msi_new(const Logger *log, Tox *tox); MSISession *msi_new(Messenger *m);
/** /**
* Terminate control session. NOTE: all calls will be freed * Terminate control session. NOTE: all calls will be freed
*/ */
int msi_kill(const Logger *log, Tox *tox, MSISession *session); int msi_kill(MSISession *session, const Logger *log);
/** /**
* Callback setters. * Callback setters.
*/ */
@ -127,20 +128,18 @@ void msi_callback_capabilities(MSISession *session, msi_action_cb *callback);
/** /**
* Send invite request to friend_number. * Send invite request to friend_number.
*/ */
int msi_invite(const Logger *log, MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities); int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities);
/** /**
* Hangup call. NOTE: `call` will be freed * Hangup call. NOTE: `call` will be freed
*/ */
int msi_hangup(const Logger *log, MSICall *call); int msi_hangup(MSICall *call);
/** /**
* Answer call request. * Answer call request.
*/ */
int msi_answer(const Logger *log, MSICall *call, uint8_t capabilities); int msi_answer(MSICall *call, uint8_t capabilities);
/** /**
* Change capabilities of the call. * Change capabilities of the call.
*/ */
int msi_change_capabilities(const Logger *log, MSICall *call, uint8_t capabilities); int msi_change_capabilities(MSICall *call, uint8_t capabilities);
bool check_peer_offline_status(const Logger *log, const Tox *tox, MSISession *session, uint32_t friend_number);
#endif /* C_TOXCORE_TOXAV_MSI_H */ #endif /* C_TOXCORE_TOXAV_MSI_H */

View File

@ -9,16 +9,12 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sodium.h>
#include "bwcontroller.h" #include "bwcontroller.h"
#include "toxav_hacks.h"
#include "../toxcore/Messenger.h"
#include "../toxcore/ccompat.h" #include "../toxcore/ccompat.h"
#include "../toxcore/logger.h" #include "../toxcore/logger.h"
#include "../toxcore/mono_time.h" #include "../toxcore/mono_time.h"
#include "../toxcore/net_crypto.h"
#include "../toxcore/tox_private.h"
#include "../toxcore/util.h" #include "../toxcore/util.h"
/** /**
@ -27,15 +23,30 @@
*/ */
#define VIDEO_KEEP_KEYFRAME_IN_BUFFER_FOR_MS 15 #define VIDEO_KEEP_KEYFRAME_IN_BUFFER_FOR_MS 15
/**
* return -1 on failure, 0 on success
*
*/
static int rtp_send_custom_lossy_packet(Tox *tox, int32_t friendnumber, const uint8_t *data, uint32_t length)
{
Tox_Err_Friend_Custom_Packet error;
tox_friend_send_lossy_packet(tox, friendnumber, data, (size_t)length, &error);
if (error == TOX_ERR_FRIEND_CUSTOM_PACKET_OK) {
return 0;
}
return -1;
}
// allocate_len is NOT including header! // allocate_len is NOT including header!
static struct RTPMessage *new_message(const Logger *log, const struct RTPHeader *header, size_t allocate_len, static struct RTPMessage *new_message(const struct RTPHeader *header, size_t allocate_len, const uint8_t *data,
const uint8_t *data, uint16_t data_length) uint16_t data_length)
{ {
assert(allocate_len >= data_length); assert(allocate_len >= data_length);
struct RTPMessage *msg = (struct RTPMessage *)calloc(1, sizeof(struct RTPMessage) + allocate_len); struct RTPMessage *msg = (struct RTPMessage *)calloc(1, sizeof(struct RTPMessage) + allocate_len);
if (msg == nullptr) { if (msg == nullptr) {
LOGGER_DEBUG(log, "Could not allocate RTPMessage buffer");
return nullptr; return nullptr;
} }
@ -230,7 +241,7 @@ static struct RTPMessage *process_frame(const Logger *log, struct RTPWorkBufferL
} }
/** /**
* @param log A pointer to the Logger object. * @param log A logger.
* @param wkbl The list of in-progress frames, i.e. all the slots. * @param wkbl The list of in-progress frames, i.e. all the slots.
* @param slot_id The slot we want to fill the data into. * @param slot_id The slot we want to fill the data into.
* @param is_keyframe Whether the data is part of a key frame. * @param is_keyframe Whether the data is part of a key frame.
@ -298,7 +309,7 @@ static bool fill_data_into_slot(const Logger *log, struct RTPWorkBufferList *wkb
return slot->received_len == header->data_length_full; return slot->received_len == header->data_length_full;
} }
static void update_bwc_values(RTPSession *session, const struct RTPMessage *msg) static void update_bwc_values(const Logger *log, RTPSession *session, const struct RTPMessage *msg)
{ {
if (session->first_packets_counter < DISMISS_FIRST_LOST_VIDEO_PACKET_COUNT) { if (session->first_packets_counter < DISMISS_FIRST_LOST_VIDEO_PACKET_COUNT) {
++session->first_packets_counter; ++session->first_packets_counter;
@ -308,7 +319,7 @@ static void update_bwc_values(RTPSession *session, const struct RTPMessage *msg)
bwc_add_recv(session->bwc, data_length_full); bwc_add_recv(session->bwc, data_length_full);
if (received_length_full < data_length_full) { if (received_length_full < data_length_full) {
LOGGER_DEBUG(session->log, "BWC: full length=%u received length=%d", data_length_full, received_length_full); LOGGER_DEBUG(log, "BWC: full length=%u received length=%d", data_length_full, received_length_full);
bwc_add_lost(session->bwc, data_length_full - received_length_full); bwc_add_lost(session->bwc, data_length_full - received_length_full);
} }
} }
@ -336,16 +347,22 @@ static void update_bwc_values(RTPSession *session, const struct RTPMessage *msg)
* @retval -1 on error. * @retval -1 on error.
* @retval 0 on success. * @retval 0 on success.
*/ */
static int handle_video_packet(const Logger *log, RTPSession *session, const struct RTPHeader *header, static int handle_video_packet(RTPSession *session, const struct RTPHeader *header,
const uint8_t *incoming_data, uint16_t incoming_data_length) const uint8_t *incoming_data, uint16_t incoming_data_length, const Logger *log)
{ {
// Full frame length in bytes. The frame may be split into multiple packets, // Full frame length in bytes. The frame may be split into multiple packets,
// but this value is the complete assembled frame size. // but this value is the complete assembled frame size.
const uint32_t full_frame_length = header->data_length_full; const uint32_t full_frame_length = header->data_length_full;
// Current offset in the frame. If this is the first packet of a multipart
// frame or it's not a multipart frame, then this value is 0.
const uint32_t offset = header->offset_full; // without header
// The sender tells us whether this is a key frame. // The sender tells us whether this is a key frame.
const bool is_keyframe = (header->flags & RTP_KEY_FRAME) != 0; const bool is_keyframe = (header->flags & RTP_KEY_FRAME) != 0;
LOGGER_DEBUG(log, "-- handle_video_packet -- full lens=%u len=%u offset=%u is_keyframe=%s",
(unsigned)incoming_data_length, (unsigned)full_frame_length, (unsigned)offset, is_keyframe ? "K" : ".");
LOGGER_DEBUG(log, "wkbl->next_free_entry:003=%d", session->work_buffer_list->next_free_entry); LOGGER_DEBUG(log, "wkbl->next_free_entry:003=%d", session->work_buffer_list->next_free_entry);
const bool is_multipart = full_frame_length != incoming_data_length; const bool is_multipart = full_frame_length != incoming_data_length;
@ -370,13 +387,10 @@ static int handle_video_packet(const Logger *log, RTPSession *session, const str
// get_slot just told us it's full, so process_frame must return non-null. // get_slot just told us it's full, so process_frame must return non-null.
assert(m_new != nullptr); assert(m_new != nullptr);
LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-001a b0=%d b1=%d", (int)m_new->data[0], LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-001a b0=%d b1=%d", (int)m_new->data[0], (int)m_new->data[1]);
(int)m_new->data[1]); update_bwc_values(log, session, m_new);
update_bwc_values(session, m_new);
// Pass ownership of m_new to the callback. // Pass ownership of m_new to the callback.
Mono_Time *mt = toxav_get_av_mono_time(session->toxav); session->mcb(session->m->mono_time, session->cs, m_new);
assert(mt != nullptr);
session->mcb(mt, session->cs, m_new);
// Now we no longer own m_new. // Now we no longer own m_new.
m_new = nullptr; m_new = nullptr;
@ -411,12 +425,9 @@ static int handle_video_packet(const Logger *log, RTPSession *session, const str
struct RTPMessage *m_new = process_frame(log, session->work_buffer_list, slot_id); struct RTPMessage *m_new = process_frame(log, session->work_buffer_list, slot_id);
if (m_new != nullptr) { if (m_new != nullptr) {
LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-003a b0=%d b1=%d", (int)m_new->data[0], LOGGER_DEBUG(log, "-- handle_video_packet -- CALLBACK-003a b0=%d b1=%d", (int)m_new->data[0], (int)m_new->data[1]);
(int)m_new->data[1]); update_bwc_values(log, session, m_new);
update_bwc_values(session, m_new); session->mcb(session->m->mono_time, session->cs, m_new);
Mono_Time *mt = toxav_get_av_mono_time(session->toxav);
assert(mt != nullptr);
session->mcb(mt, session->cs, m_new);
m_new = nullptr; m_new = nullptr;
} }
@ -425,113 +436,80 @@ static int handle_video_packet(const Logger *log, RTPSession *session, const str
} }
/** /**
* receive custom lossypackets and process them. they can be incoming audio or video packets * @retval -1 on error.
* @retval 0 on success.
*/ */
void handle_rtp_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, void *user_data) static int handle_rtp_packet(Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object)
{ {
ToxAV *toxav = (ToxAV *)tox_get_av_object(tox); RTPSession *session = (RTPSession *)object;
if (toxav == nullptr) { if (session == nullptr || length < RTP_HEADER_SIZE + 1) {
// LOGGER_WARNING(log, "ToxAV is NULL!"); LOGGER_WARNING(m->log, "No session or invalid length of received buffer!");
return; return -1;
}
const Logger *log = toxav_get_logger(toxav);
if (length < RTP_HEADER_SIZE + 1) {
LOGGER_WARNING(log, "Invalid length of received buffer!");
return;
}
ToxAVCall *call = call_get(toxav, friend_number);
if (call == nullptr) {
LOGGER_WARNING(log, "ToxAVCall is NULL!");
return;
}
RTPSession *session = rtp_session_get(call, data[0]);
if (session == nullptr) {
LOGGER_WARNING(log, "No session!");
return;
}
if (!session->rtp_receive_active) {
LOGGER_WARNING(log, "receiving not allowed!");
return;
} }
// Get the packet type. // Get the packet type.
const uint8_t packet_type = data[0]; const uint8_t packet_type = data[0];
const uint8_t *payload = &data[1]; ++data;
// TODO(Zoff): is this ok? --length;
const uint16_t payload_size = (uint16_t)length - 1;
// Unpack the header. // Unpack the header.
struct RTPHeader header; struct RTPHeader header;
rtp_header_unpack(payload, &header); rtp_header_unpack(data, &header);
if (header.pt != packet_type % 128) { if (header.pt != packet_type % 128) {
LOGGER_WARNING(log, "RTPHeader packet type and Tox protocol packet type did not agree: %d != %d", LOGGER_WARNING(m->log, "RTPHeader packet type and Tox protocol packet type did not agree: %d != %d",
header.pt, packet_type % 128); header.pt, packet_type % 128);
return; return -1;
} }
if (header.pt != session->payload_type % 128) { if (header.pt != session->payload_type % 128) {
LOGGER_WARNING(log, "RTPHeader packet type does not match this session's payload type: %d != %d", LOGGER_WARNING(m->log, "RTPHeader packet type does not match this session's payload type: %d != %d",
header.pt, session->payload_type % 128); header.pt, session->payload_type % 128);
return; return -1;
} }
if ((header.flags & RTP_LARGE_FRAME) != 0 && header.offset_full >= header.data_length_full) { if ((header.flags & RTP_LARGE_FRAME) != 0 && header.offset_full >= header.data_length_full) {
LOGGER_ERROR(log, "Invalid video packet: frame offset (%u) >= full frame length (%u)", LOGGER_ERROR(m->log, "Invalid video packet: frame offset (%u) >= full frame length (%u)",
(unsigned)header.offset_full, (unsigned)header.data_length_full); (unsigned)header.offset_full, (unsigned)header.data_length_full);
return; return -1;
} }
if (header.offset_lower >= header.data_length_lower) { if (header.offset_lower >= header.data_length_lower) {
LOGGER_ERROR(log, "Invalid old protocol video packet: frame offset (%u) >= full frame length (%u)", LOGGER_ERROR(m->log, "Invalid old protocol video packet: frame offset (%u) >= full frame length (%u)",
(unsigned)header.offset_lower, (unsigned)header.data_length_lower); (unsigned)header.offset_lower, (unsigned)header.data_length_lower);
return; return -1;
} }
LOGGER_DEBUG(log, "header.pt %d, video %d", (uint8_t)header.pt, RTP_TYPE_VIDEO % 128); LOGGER_DEBUG(m->log, "header.pt %d, video %d", (uint8_t)header.pt, RTP_TYPE_VIDEO % 128);
// The sender uses the new large-frame capable protocol and is sending a // The sender uses the new large-frame capable protocol and is sending a
// video packet. // video packet.
if ((header.flags & RTP_LARGE_FRAME) != 0 && header.pt == (RTP_TYPE_VIDEO % 128)) { if ((header.flags & RTP_LARGE_FRAME) != 0 && header.pt == (RTP_TYPE_VIDEO % 128)) {
handle_video_packet(log, session, &header, &payload[RTP_HEADER_SIZE], payload_size - RTP_HEADER_SIZE); return handle_video_packet(session, &header, data + RTP_HEADER_SIZE, length - RTP_HEADER_SIZE, m->log);
return;
} }
// everything below here is for the old 16 bit protocol ------------------ // everything below here is for the old 16 bit protocol ------------------
if (header.data_length_lower == payload_size - RTP_HEADER_SIZE) { if (header.data_length_lower == length - RTP_HEADER_SIZE) {
/* The message is sent in single part */ /* The message is sent in single part */
/* Message is not late; pick up the latest parameters */ /* Message is not late; pick up the latest parameters */
session->rsequnum = header.sequnum; session->rsequnum = header.sequnum;
session->rtimestamp = header.timestamp; session->rtimestamp = header.timestamp;
bwc_add_recv(session->bwc, payload_size); bwc_add_recv(session->bwc, length);
/* Invoke processing of active multiparted message */ /* Invoke processing of active multiparted message */
if (session->mp != nullptr) { if (session->mp != nullptr) {
Mono_Time *mt = toxav_get_av_mono_time(session->toxav); session->mcb(session->m->mono_time, session->cs, session->mp);
assert(mt != nullptr);
session->mcb(mt, session->cs, session->mp);
session->mp = nullptr; session->mp = nullptr;
} }
/* The message came in the allowed time; /* The message came in the allowed time;
*/ */
session->mp = new_message(log, &header, payload_size - RTP_HEADER_SIZE, &payload[RTP_HEADER_SIZE], payload_size - RTP_HEADER_SIZE); return session->mcb(session->m->mono_time, session->cs, new_message(&header, length - RTP_HEADER_SIZE,
Mono_Time *mt = toxav_get_av_mono_time(session->toxav); data + RTP_HEADER_SIZE, length - RTP_HEADER_SIZE));
assert(mt != nullptr);
session->mcb(mt, session->cs, session->mp);
session->mp = nullptr;
return;
} }
/* The message is sent in multiple parts */ /* The message is sent in multiple parts */
@ -549,26 +527,24 @@ void handle_rtp_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, si
/* First case */ /* First case */
/* Make sure we have enough allocated memory */ /* Make sure we have enough allocated memory */
if (session->mp->header.data_length_lower - session->mp->len < payload_size - RTP_HEADER_SIZE || if (session->mp->header.data_length_lower - session->mp->len < length - RTP_HEADER_SIZE ||
session->mp->header.data_length_lower <= header.offset_lower) { session->mp->header.data_length_lower <= header.offset_lower) {
/* There happened to be some corruption on the stream; /* There happened to be some corruption on the stream;
* continue wihtout this part * continue wihtout this part
*/ */
return; return 0;
} }
memcpy(session->mp->data + header.offset_lower, &payload[RTP_HEADER_SIZE], memcpy(session->mp->data + header.offset_lower, data + RTP_HEADER_SIZE,
payload_size - RTP_HEADER_SIZE); length - RTP_HEADER_SIZE);
session->mp->len += payload_size - RTP_HEADER_SIZE; session->mp->len += length - RTP_HEADER_SIZE;
bwc_add_recv(session->bwc, payload_size); bwc_add_recv(session->bwc, length);
if (session->mp->len == session->mp->header.data_length_lower) { if (session->mp->len == session->mp->header.data_length_lower) {
/* Received a full message; now push it for the further /* Received a full message; now push it for the further
* processing. * processing.
*/ */
Mono_Time *mt = toxav_get_av_mono_time(session->toxav); session->mcb(session->m->mono_time, session->cs, session->mp);
assert(mt != nullptr);
session->mcb(mt, session->cs, session->mp);
session->mp = nullptr; session->mp = nullptr;
} }
} else { } else {
@ -577,19 +553,17 @@ void handle_rtp_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, si
/* The received message part is from the old message; /* The received message part is from the old message;
* discard it. * discard it.
*/ */
return; return 0;
} }
/* Push the previous message for processing */ /* Push the previous message for processing */
Mono_Time *mt = toxav_get_av_mono_time(session->toxav); session->mcb(session->m->mono_time, session->cs, session->mp);
assert(mt != nullptr);
session->mcb(mt, session->cs, session->mp);
session->mp = nullptr; session->mp = nullptr;
goto NEW_MULTIPARTED; goto NEW_MULTIPARTED;
} }
} else { } else {
/* In this case treat the message as if it was received in order /* In this case threat the message as if it was received in order
*/ */
/* This is also a point for new multiparted messages */ /* This is also a point for new multiparted messages */
NEW_MULTIPARTED: NEW_MULTIPARTED:
@ -597,21 +571,21 @@ NEW_MULTIPARTED:
/* Message is not late; pick up the latest parameters */ /* Message is not late; pick up the latest parameters */
session->rsequnum = header.sequnum; session->rsequnum = header.sequnum;
session->rtimestamp = header.timestamp; session->rtimestamp = header.timestamp;
bwc_add_recv(session->bwc, payload_size); bwc_add_recv(session->bwc, length);
/* Store message. /* Store message.
*/ */
session->mp = new_message(log, &header, header.data_length_lower, &payload[RTP_HEADER_SIZE], payload_size - RTP_HEADER_SIZE); session->mp = new_message(&header, header.data_length_lower, data + RTP_HEADER_SIZE, length - RTP_HEADER_SIZE);
if (session->mp != nullptr) { if (session->mp != nullptr) {
memmove(session->mp->data + header.offset_lower, session->mp->data, session->mp->len); memmove(session->mp->data + header.offset_lower, session->mp->data, session->mp->len);
} else { } else {
LOGGER_WARNING(log, "new_message() returned a null pointer"); LOGGER_WARNING(m->log, "new_message() returned a null pointer");
return; return -1;
} }
} }
return; return 0;
} }
size_t rtp_header_pack(uint8_t *const rdata, const struct RTPHeader *header) size_t rtp_header_pack(uint8_t *const rdata, const struct RTPHeader *header)
@ -673,29 +647,24 @@ size_t rtp_header_unpack(const uint8_t *data, struct RTPHeader *header)
return p - data; return p - data;
} }
static uint32_t rtp_random_u32(void) RTPSession *rtp_new(int payload_type, Messenger *m, Tox *tox, uint32_t friendnumber,
{
// HINT: uses libsodium function
return randombytes_random();
}
RTPSession *rtp_new(const Logger *log, int payload_type, Tox *tox, ToxAV *toxav, uint32_t friendnumber,
BWController *bwc, void *cs, rtp_m_cb *mcb) BWController *bwc, void *cs, rtp_m_cb *mcb)
{ {
assert(mcb != nullptr); assert(mcb != nullptr);
assert(cs != nullptr); assert(cs != nullptr);
assert(m != nullptr);
RTPSession *session = (RTPSession *)calloc(1, sizeof(RTPSession)); RTPSession *session = (RTPSession *)calloc(1, sizeof(RTPSession));
if (session == nullptr) { if (session == nullptr) {
LOGGER_WARNING(log, "Alloc failed! Program might misbehave!"); LOGGER_WARNING(m->log, "Alloc failed! Program might misbehave!");
return nullptr; return nullptr;
} }
session->work_buffer_list = (struct RTPWorkBufferList *)calloc(1, sizeof(struct RTPWorkBufferList)); session->work_buffer_list = (struct RTPWorkBufferList *)calloc(1, sizeof(struct RTPWorkBufferList));
if (session->work_buffer_list == nullptr) { if (session->work_buffer_list == nullptr) {
LOGGER_ERROR(log, "out of memory while allocating work buffer list"); LOGGER_ERROR(m->log, "out of memory while allocating work buffer list");
free(session); free(session);
return nullptr; return nullptr;
} }
@ -703,12 +672,11 @@ RTPSession *rtp_new(const Logger *log, int payload_type, Tox *tox, ToxAV *toxav,
// First entry is free. // First entry is free.
session->work_buffer_list->next_free_entry = 0; session->work_buffer_list->next_free_entry = 0;
session->ssrc = payload_type == RTP_TYPE_VIDEO ? 0 : rtp_random_u32(); // Zoff: what is this?? session->ssrc = payload_type == RTP_TYPE_VIDEO ? 0 : random_u32(m->rng);
session->payload_type = payload_type; session->payload_type = payload_type;
session->m = m;
session->tox = tox; session->tox = tox;
session->toxav = toxav;
session->friend_number = friendnumber; session->friend_number = friendnumber;
session->rtp_receive_active = true;
// set NULL just in case // set NULL just in case
session->mp = nullptr; session->mp = nullptr;
@ -719,18 +687,26 @@ RTPSession *rtp_new(const Logger *log, int payload_type, Tox *tox, ToxAV *toxav,
session->cs = cs; session->cs = cs;
session->mcb = mcb; session->mcb = mcb;
if (-1 == rtp_allow_receiving(session)) {
LOGGER_WARNING(m->log, "Failed to start rtp receiving mode");
free(session->work_buffer_list);
free(session);
return nullptr;
}
return session; return session;
} }
void rtp_kill(const Logger *log, RTPSession *session) void rtp_kill(RTPSession *session)
{ {
if (session == nullptr) { if (session == nullptr) {
LOGGER_WARNING(log, "No session");
return; return;
} }
LOGGER_DEBUG(log, "Terminated RTP session: %p", (void *)session); LOGGER_DEBUG(session->m->log, "Terminated RTP session: %p", (void *)session);
LOGGER_DEBUG(log, "Terminated RTP session V3 work_buffer_list->next_free_entry: %d", rtp_stop_receiving(session);
LOGGER_DEBUG(session->m->log, "Terminated RTP session V3 work_buffer_list->next_free_entry: %d",
(int)session->work_buffer_list->next_free_entry); (int)session->work_buffer_list->next_free_entry);
for (int8_t i = 0; i < session->work_buffer_list->next_free_entry; ++i) { for (int8_t i = 0; i < session->work_buffer_list->next_free_entry; ++i) {
@ -740,95 +716,36 @@ void rtp_kill(const Logger *log, RTPSession *session)
free(session); free(session);
} }
void rtp_allow_receiving_mark(RTPSession *session) int rtp_allow_receiving(RTPSession *session)
{ {
if (session != nullptr) { if (session == nullptr) {
session->rtp_receive_active = true; return -1;
} }
if (m_callback_rtp_packet(session->m, session->friend_number, session->payload_type,
handle_rtp_packet, session) == -1) {
LOGGER_WARNING(session->m->log, "Failed to register rtp receive handler");
return -1;
}
LOGGER_DEBUG(session->m->log, "Started receiving on session: %p", (void *)session);
return 0;
} }
void rtp_stop_receiving_mark(RTPSession *session) int rtp_stop_receiving(RTPSession *session)
{ {
if (session != nullptr) { if (session == nullptr) {
session->rtp_receive_active = false; return -1;
}
}
void rtp_allow_receiving(Tox *tox)
{
// register callback
tox_callback_friend_lossy_packet_per_pktid(tox, handle_rtp_packet, RTP_TYPE_AUDIO);
tox_callback_friend_lossy_packet_per_pktid(tox, handle_rtp_packet, RTP_TYPE_VIDEO);
}
void rtp_stop_receiving(Tox *tox)
{
// UN-register callback
tox_callback_friend_lossy_packet_per_pktid(tox, nullptr, RTP_TYPE_AUDIO);
tox_callback_friend_lossy_packet_per_pktid(tox, nullptr, RTP_TYPE_VIDEO);
}
static void rtp_send_piece(const Logger *log, Tox *tox, uint32_t friend_number, const struct RTPHeader *header,
const uint8_t *data, uint8_t *rdata, uint16_t length)
{
rtp_header_pack(rdata + 1, header);
memcpy(rdata + 1 + RTP_HEADER_SIZE, data, length);
Tox_Err_Friend_Custom_Packet error;
tox_friend_send_lossy_packet(tox, friend_number,
rdata, length + RTP_HEADER_SIZE + 1, &error);
if (error != TOX_ERR_FRIEND_CUSTOM_PACKET_OK) {
char *netstrerror = net_new_strerror(net_error());
LOGGER_WARNING(log, "RTP send failed (len: %d)! tox error: %d, net error: %s",
length + RTP_HEADER_SIZE + 1, error, netstrerror);
net_kill_strerror(netstrerror);
}
}
static struct RTPHeader rtp_default_header(const RTPSession *session, uint32_t length, bool is_keyframe)
{
uint16_t length_safe = (uint16_t)length;
if (length > UINT16_MAX) {
length_safe = UINT16_MAX;
} }
struct RTPHeader header = {0}; m_callback_rtp_packet(session->m, session->friend_number, session->payload_type, nullptr, nullptr);
if (is_keyframe) { LOGGER_DEBUG(session->m->log, "Stopped receiving on session: %p", (void *)session);
header.flags |= RTP_KEY_FRAME; return 0;
}
if (session->payload_type == RTP_TYPE_VIDEO) {
header.flags |= RTP_LARGE_FRAME;
}
header.ve = 2; // this is unused in toxav
header.pe = 0;
header.xe = 0;
header.cc = 0;
header.ma = 0;
header.pt = session->payload_type % 128;
header.sequnum = session->sequnum;
Mono_Time *mt = toxav_get_av_mono_time(session->toxav);
if (mt != nullptr) {
header.timestamp = current_time_monotonic(mt);
} else {
header.timestamp = 0;
}
header.ssrc = session->ssrc;
header.offset_lower = 0;
header.data_length_lower = length_safe;
header.data_length_full = length; // without header
header.offset_lower = 0;
header.offset_full = 0;
return header;
} }
/** /**
* @brief Send a frame of audio or video data, chunked in @ref RTPMessage instances. * Send a frame of audio or video data, chunked in @ref RTPMessage instances.
* *
* @param session The A/V session to send the data for. * @param session The A/V session to send the data for.
* @param data A byte array of length @p length. * @param data A byte array of length @p length.
@ -836,27 +753,77 @@ static struct RTPHeader rtp_default_header(const RTPSession *session, uint32_t l
* @param is_keyframe Whether this video frame is a key frame. If it is an * @param is_keyframe Whether this video frame is a key frame. If it is an
* audio frame, this parameter is ignored. * audio frame, this parameter is ignored.
*/ */
int rtp_send_data(const Logger *log, RTPSession *session, const uint8_t *data, uint32_t length, int rtp_send_data(RTPSession *session, const uint8_t *data, uint32_t length,
bool is_keyframe) bool is_keyframe, const Logger *log)
{ {
if (session == nullptr) { if (session == nullptr) {
LOGGER_ERROR(log, "No session!");
return -1; return -1;
} }
struct RTPHeader header = {0};
header.ve = 2; // this is unused in toxav
header.pe = 0;
header.xe = 0;
header.cc = 0;
header.ma = 0;
header.pt = session->payload_type % 128;
header.sequnum = session->sequnum;
header.timestamp = current_time_monotonic(session->m->mono_time);
header.ssrc = session->ssrc;
header.offset_lower = 0;
// here the highest bits gets stripped anyway, no need to do keyframe bit magic here!
header.data_length_lower = length;
if (session->payload_type == RTP_TYPE_VIDEO) {
header.flags = RTP_LARGE_FRAME;
}
uint16_t length_safe = (uint16_t)length;
if (length > UINT16_MAX) {
length_safe = UINT16_MAX;
}
header.data_length_lower = length_safe;
header.data_length_full = length; // without header
header.offset_lower = 0;
header.offset_full = 0;
if (is_keyframe) {
header.flags |= RTP_KEY_FRAME;
}
const uint16_t rdata_size = min_u32(length + RTP_HEADER_SIZE + 1, MAX_CRYPTO_DATA_SIZE); const uint16_t rdata_size = min_u32(length + RTP_HEADER_SIZE + 1, MAX_CRYPTO_DATA_SIZE);
VLA(uint8_t, rdata, rdata_size); VLA(uint8_t, rdata, rdata_size);
memset(rdata, 0, rdata_size); memset(rdata, 0, rdata_size);
rdata[0] = session->payload_type; // packet id == payload_type rdata[0] = session->payload_type; // packet id == payload_type
struct RTPHeader header = rtp_default_header(session, length, is_keyframe);
if (MAX_CRYPTO_DATA_SIZE > (length + RTP_HEADER_SIZE + 1)) { if (MAX_CRYPTO_DATA_SIZE > (length + RTP_HEADER_SIZE + 1)) {
/* /*
* The length is lesser than the maximum allowed length (including header) * The length is lesser than the maximum allowed length (including header)
* Send the packet in single piece. * Send the packet in single piece.
*/ */
assert(length < UINT16_MAX); rtp_header_pack(rdata + 1, &header);
rtp_send_piece(log, session->tox, session->friend_number, &header, data, rdata, length); memcpy(rdata + 1 + RTP_HEADER_SIZE, data, length);
if (-1 == rtp_send_custom_lossy_packet(session->tox, session->friend_number, rdata, rdata_size)) {
char *netstrerror = net_new_strerror(net_error());
LOGGER_WARNING(session->m->log, "RTP send failed (len: %u)! net error: %s",
rdata_size, netstrerror);
net_kill_strerror(netstrerror);
}
} else { } else {
/* /*
* The length is greater than the maximum allowed length (including header) * The length is greater than the maximum allowed length (including header)
@ -866,7 +833,16 @@ int rtp_send_data(const Logger *log, RTPSession *session, const uint8_t *data, u
uint16_t piece = MAX_CRYPTO_DATA_SIZE - (RTP_HEADER_SIZE + 1); uint16_t piece = MAX_CRYPTO_DATA_SIZE - (RTP_HEADER_SIZE + 1);
while ((length - sent) + RTP_HEADER_SIZE + 1 > MAX_CRYPTO_DATA_SIZE) { while ((length - sent) + RTP_HEADER_SIZE + 1 > MAX_CRYPTO_DATA_SIZE) {
rtp_send_piece(log, session->tox, session->friend_number, &header, data + sent, rdata, piece); rtp_header_pack(rdata + 1, &header);
memcpy(rdata + 1 + RTP_HEADER_SIZE, data + sent, piece);
if (-1 == rtp_send_custom_lossy_packet(session->tox, session->friend_number,
rdata, piece + RTP_HEADER_SIZE + 1)) {
char *netstrerror = net_new_strerror(net_error());
LOGGER_WARNING(session->m->log, "RTP send failed (len: %d)! net error: %s",
piece + RTP_HEADER_SIZE + 1, netstrerror);
net_kill_strerror(netstrerror);
}
sent += piece; sent += piece;
header.offset_lower = sent; header.offset_lower = sent;
@ -877,7 +853,16 @@ int rtp_send_data(const Logger *log, RTPSession *session, const uint8_t *data, u
piece = length - sent; piece = length - sent;
if (piece != 0) { if (piece != 0) {
rtp_send_piece(log, session->tox, session->friend_number, &header, data + sent, rdata, piece); rtp_header_pack(rdata + 1, &header);
memcpy(rdata + 1 + RTP_HEADER_SIZE, data + sent, piece);
if (-1 == rtp_send_custom_lossy_packet(session->tox, session->friend_number, rdata,
piece + RTP_HEADER_SIZE + 1)) {
char *netstrerror = net_new_strerror(net_error());
LOGGER_WARNING(session->m->log, "RTP send failed (len: %d)! net error: %s",
piece + RTP_HEADER_SIZE + 1, netstrerror);
net_kill_strerror(netstrerror);
}
} }
} }

View File

@ -9,6 +9,7 @@
#include "bwcontroller.h" #include "bwcontroller.h"
#include "../toxcore/Messenger.h"
#include "../toxcore/logger.h" #include "../toxcore/logger.h"
#include "../toxcore/tox.h" #include "../toxcore/tox.h"
@ -35,11 +36,6 @@ typedef enum RTP_Type {
RTP_TYPE_VIDEO = 193, RTP_TYPE_VIDEO = 193,
} RTP_Type; } RTP_Type;
#ifndef TOXAV_DEFINED
#define TOXAV_DEFINED
typedef struct ToxAV ToxAV;
#endif /* TOXAV_DEFINED */
/** /**
* A bit mask (up to 64 bits) specifying features of the current frame affecting * A bit mask (up to 64 bits) specifying features of the current frame affecting
* the behaviour of the decoder. * the behaviour of the decoder.
@ -161,19 +157,14 @@ typedef struct RTPSession {
struct RTPMessage *mp; /* Expected parted message */ struct RTPMessage *mp; /* Expected parted message */
struct RTPWorkBufferList *work_buffer_list; struct RTPWorkBufferList *work_buffer_list;
uint8_t first_packets_counter; /* dismiss first few lost video packets */ uint8_t first_packets_counter; /* dismiss first few lost video packets */
const Logger *log; Messenger *m;
Tox *tox; Tox *tox;
ToxAV *toxav;
uint32_t friend_number; uint32_t friend_number;
bool rtp_receive_active; /* if this is set to false then incoming rtp packets will not be processed by handle_rtp_packet() */
BWController *bwc; BWController *bwc;
void *cs; void *cs;
rtp_m_cb *mcb; rtp_m_cb *mcb;
} RTPSession; } RTPSession;
void handle_rtp_packet(Tox *tox, uint32_t friend_number, const uint8_t *data, size_t length, void *user_data);
/** /**
* Serialise an RTPHeader to bytes to be sent over the network. * Serialise an RTPHeader to bytes to be sent over the network.
* *
@ -192,16 +183,13 @@ size_t rtp_header_pack(uint8_t *rdata, const struct RTPHeader *header);
*/ */
size_t rtp_header_unpack(const uint8_t *data, struct RTPHeader *header); size_t rtp_header_unpack(const uint8_t *data, struct RTPHeader *header);
RTPSession *rtp_new(const Logger *log, int payload_type, Tox *tox, ToxAV *toxav, uint32_t friendnumber, RTPSession *rtp_new(int payload_type, Messenger *m, Tox *tox, uint32_t friendnumber,
BWController *bwc, void *cs, rtp_m_cb *mcb); BWController *bwc, void *cs, rtp_m_cb *mcb);
void rtp_kill(const Logger *log, RTPSession *session); void rtp_kill(RTPSession *session);
void rtp_allow_receiving_mark(RTPSession *session); int rtp_allow_receiving(RTPSession *session);
void rtp_stop_receiving_mark(RTPSession *session); int rtp_stop_receiving(RTPSession *session);
void rtp_allow_receiving(Tox *tox);
void rtp_stop_receiving(Tox *tox);
/** /**
* @brief Send a frame of audio or video data, chunked in @ref RTPMessage instances. * Send a frame of audio or video data, chunked in @ref RTPMessage instances.
* *
* @param session The A/V session to send the data for. * @param session The A/V session to send the data for.
* @param data A byte array of length @p length. * @param data A byte array of length @p length.
@ -209,8 +197,8 @@ void rtp_stop_receiving(Tox *tox);
* @param is_keyframe Whether this video frame is a key frame. If it is an * @param is_keyframe Whether this video frame is a key frame. If it is an
* audio frame, this parameter is ignored. * audio frame, this parameter is ignored.
*/ */
int rtp_send_data(const Logger *log, RTPSession *session, const uint8_t *data, uint32_t length, int rtp_send_data(RTPSession *session, const uint8_t *data, uint32_t length,
bool is_keyframe); bool is_keyframe, const Logger *log);
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@ -12,15 +12,11 @@
#include "msi.h" #include "msi.h"
#include "rtp.h" #include "rtp.h"
#include "toxav_hacks.h"
#include "../toxcore/Messenger.h"
#include "../toxcore/ccompat.h" #include "../toxcore/ccompat.h"
#include "../toxcore/logger.h" #include "../toxcore/logger.h"
#include "../toxcore/mono_time.h" #include "../toxcore/mono_time.h"
#include "../toxcore/net_crypto.h"
#include "../toxcore/network.h"
#include "../toxcore/tox.h"
#include "../toxcore/tox_private.h"
#include "../toxcore/tox_struct.h" #include "../toxcore/tox_struct.h"
#include "../toxcore/util.h" #include "../toxcore/util.h"
@ -40,12 +36,7 @@
// iteration interval that is used when no call is active // iteration interval that is used when no call is active
#define IDLE_ITERATION_INTERVAL_MS 200 #define IDLE_ITERATION_INTERVAL_MS 200
#ifndef TOXAV_CALL_DEFINED typedef struct ToxAVCall {
#define TOXAV_CALL_DEFINED
typedef struct ToxAVCall ToxAVCall;
#endif /* TOXAV_CALL_DEFINED */
struct ToxAVCall {
ToxAV *av; ToxAV *av;
pthread_mutex_t mutex_audio[1]; pthread_mutex_t mutex_audio[1];
@ -72,7 +63,7 @@ struct ToxAVCall {
struct ToxAVCall *prev; struct ToxAVCall *prev;
struct ToxAVCall *next; struct ToxAVCall *next;
}; } ToxAVCall;
/** Decode time statistics */ /** Decode time statistics */
typedef struct DecodeTimeStats { typedef struct DecodeTimeStats {
@ -88,8 +79,8 @@ typedef struct DecodeTimeStats {
} DecodeTimeStats; } DecodeTimeStats;
struct ToxAV { struct ToxAV {
Logger *log;
Tox *tox; Tox *tox;
Messenger *m;
MSISession *msi; MSISession *msi;
/* Two-way storage: first is array of calls and second is list of calls with head and tail */ /* Two-way storage: first is array of calls and second is list of calls with head and tail */
@ -120,8 +111,8 @@ struct ToxAV {
/* keep track of decode times for audio and video */ /* keep track of decode times for audio and video */
DecodeTimeStats audio_stats; DecodeTimeStats audio_stats;
DecodeTimeStats video_stats; DecodeTimeStats video_stats;
/** ToxAV's own mono_time instance */
Mono_Time *toxav_mono_time; // ToxAV's own mono_time instance Mono_Time *toxav_mono_time;
}; };
static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss, void *user_data); static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss, void *user_data);
@ -136,55 +127,11 @@ static bool audio_bit_rate_invalid(uint32_t bit_rate);
static bool video_bit_rate_invalid(uint32_t bit_rate); static bool video_bit_rate_invalid(uint32_t bit_rate);
static bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state); static bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state);
static ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, Toxav_Err_Call *error); static ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, Toxav_Err_Call *error);
static ToxAVCall *call_get(ToxAV *av, uint32_t friend_number);
static ToxAVCall *call_remove(ToxAVCall *call); static ToxAVCall *call_remove(ToxAVCall *call);
static bool call_prepare_transmission(ToxAVCall *call); static bool call_prepare_transmission(ToxAVCall *call);
static void call_kill_transmission(ToxAVCall *call); static void call_kill_transmission(ToxAVCall *call);
MSISession *tox_av_msi_get(const ToxAV *av)
{
if (av == nullptr) {
return nullptr;
}
return av->msi;
}
ToxAVCall *call_get(ToxAV *av, uint32_t friend_number)
{
if (av == nullptr) {
return nullptr;
}
/* Assumes mutex locked */
if (av->calls == nullptr || av->calls_tail < friend_number) {
return nullptr;
}
return av->calls[friend_number];
}
RTPSession *rtp_session_get(ToxAVCall *call, int payload_type)
{
if (call == nullptr) {
return nullptr;
}
if (payload_type == RTP_TYPE_VIDEO) {
return call->video_rtp;
} else {
return call->audio_rtp;
}
}
BWController *bwc_controller_get(const ToxAVCall *call)
{
if (call == nullptr) {
return nullptr;
}
return call->bwc;
}
/** /**
* @brief initialize d with default values * @brief initialize d with default values
* @param d struct to be initialized, must not be nullptr * @param d struct to be initialized, must not be nullptr
@ -208,26 +155,33 @@ ToxAV *toxav_new(Tox *tox, Toxav_Err_New *error)
goto RETURN; goto RETURN;
} }
// TODO(iphydf): Don't rely on toxcore internals.
Messenger *m;
m = tox->m;
if (m->msi_packet != nullptr) {
rc = TOXAV_ERR_NEW_MULTIPLE;
goto RETURN;
}
av = (ToxAV *)calloc(1, sizeof(ToxAV)); av = (ToxAV *)calloc(1, sizeof(ToxAV));
if (av == nullptr) { if (av == nullptr) {
LOGGER_WARNING(m->log, "Allocation failed!");
rc = TOXAV_ERR_NEW_MALLOC; rc = TOXAV_ERR_NEW_MALLOC;
goto RETURN; goto RETURN;
} }
if (create_recursive_mutex(av->mutex) != 0) { if (create_recursive_mutex(av->mutex) != 0) {
LOGGER_WARNING(m->log, "Mutex creation failed!");
rc = TOXAV_ERR_NEW_MALLOC; rc = TOXAV_ERR_NEW_MALLOC;
goto RETURN; goto RETURN;
} }
av->log = tox->m->log;
av->tox = tox; av->tox = tox;
av->msi = msi_new(av->log, av->tox); av->m = m;
rtp_allow_receiving(av->tox);
bwc_allow_receiving(av->tox);
av->toxav_mono_time = mono_time_new(tox->sys.mem, nullptr, nullptr); av->toxav_mono_time = mono_time_new(tox->sys.mem, nullptr, nullptr);
av->msi = msi_new(av->m);
if (av->msi == nullptr) { if (av->msi == nullptr) {
pthread_mutex_destroy(av->mutex); pthread_mutex_destroy(av->mutex);
@ -239,9 +193,6 @@ ToxAV *toxav_new(Tox *tox, Toxav_Err_New *error)
init_decode_time_stats(&av->video_stats); init_decode_time_stats(&av->video_stats);
av->msi->av = av; av->msi->av = av;
// save ToxAV object into toxcore
tox_set_av_object(av->tox, av);
msi_callback_invite(av->msi, callback_invite); msi_callback_invite(av->msi, callback_invite);
msi_callback_start(av->msi, callback_start); msi_callback_start(av->msi, callback_start);
msi_callback_end(av->msi, callback_end); msi_callback_end(av->msi, callback_end);
@ -256,15 +207,12 @@ RETURN:
} }
if (rc != TOXAV_ERR_NEW_OK) { if (rc != TOXAV_ERR_NEW_OK) {
if (av != nullptr) { free(av);
free(av); av = nullptr;
av = nullptr;
}
} }
return av; return av;
} }
void toxav_kill(ToxAV *av) void toxav_kill(ToxAV *av)
{ {
if (av == nullptr) { if (av == nullptr) {
@ -273,16 +221,8 @@ void toxav_kill(ToxAV *av)
pthread_mutex_lock(av->mutex); pthread_mutex_lock(av->mutex);
// unregister callbacks
for (uint8_t i = PACKET_ID_RANGE_LOSSY_AV_START; i <= PACKET_ID_RANGE_LOSSY_AV_END; ++i) {
tox_callback_friend_lossy_packet_per_pktid(av->tox, nullptr, i);
}
rtp_stop_receiving(av->tox);
bwc_stop_receiving(av->tox);
/* To avoid possible deadlocks */ /* To avoid possible deadlocks */
while (av->msi != nullptr && msi_kill(av->log, av->tox, av->msi) != 0) { while (av->msi != nullptr && msi_kill(av->msi, av->m->log) != 0) {
pthread_mutex_unlock(av->mutex); pthread_mutex_unlock(av->mutex);
pthread_mutex_lock(av->mutex); pthread_mutex_lock(av->mutex);
} }
@ -303,22 +243,13 @@ void toxav_kill(ToxAV *av)
pthread_mutex_unlock(av->mutex); pthread_mutex_unlock(av->mutex);
pthread_mutex_destroy(av->mutex); pthread_mutex_destroy(av->mutex);
// set ToxAV object to NULL in toxcore, to signal ToxAV has been shutdown
tox_set_av_object(av->tox, nullptr);
free(av); free(av);
} }
Tox *toxav_get_tox(const ToxAV *av) Tox *toxav_get_tox(const ToxAV *av)
{ {
return av->tox; return av->tox;
} }
const Logger *toxav_get_logger(const ToxAV *av)
{
return av->log;
}
uint32_t toxav_audio_iteration_interval(const ToxAV *av) uint32_t toxav_audio_iteration_interval(const ToxAV *av)
{ {
return av->calls != nullptr ? av->audio_stats.interval : IDLE_ITERATION_INTERVAL_MS; return av->calls != nullptr ? av->audio_stats.interval : IDLE_ITERATION_INTERVAL_MS;
@ -345,11 +276,10 @@ uint32_t toxav_iteration_interval(const ToxAV *av)
static void calc_interval(ToxAV *av, DecodeTimeStats *stats, int32_t frame_time, uint64_t start_time) static void calc_interval(ToxAV *av, DecodeTimeStats *stats, int32_t frame_time, uint64_t start_time)
{ {
stats->interval = frame_time < stats->average ? 0 : (frame_time - stats->average); stats->interval = frame_time < stats->average ? 0 : (frame_time - stats->average);
stats->total += current_time_monotonic(av->toxav_mono_time) - start_time; stats->total += current_time_monotonic(av->m->mono_time) - start_time;
if (++stats->count == 3) { if (++stats->count == 3) {
/* NOTE: Magic Offset for precision */ stats->average = stats->total / 3 + 5; /* NOTE: Magic Offset for precision */
stats->average = stats->total / 3 + 5;
stats->count = 0; stats->count = 0;
stats->total = 0; stats->total = 0;
} }
@ -370,6 +300,7 @@ static void iterate_common(ToxAV *av, bool audio)
} }
const uint64_t start = current_time_monotonic(av->toxav_mono_time); const uint64_t start = current_time_monotonic(av->toxav_mono_time);
// time until the first audio or video frame is over
int32_t frame_time = IDLE_ITERATION_INTERVAL_MS; int32_t frame_time = IDLE_ITERATION_INTERVAL_MS;
for (ToxAVCall *i = av->calls[av->calls_head]; i != nullptr; i = i->next) { for (ToxAVCall *i = av->calls[av->calls_head]; i != nullptr; i = i->next) {
@ -380,15 +311,6 @@ static void iterate_common(ToxAV *av, bool audio)
pthread_mutex_lock(i->toxav_call_mutex); pthread_mutex_lock(i->toxav_call_mutex);
pthread_mutex_unlock(av->mutex); pthread_mutex_unlock(av->mutex);
const uint32_t fid = i->friend_number;
const bool is_offline = check_peer_offline_status(av->log, av->tox, i->msi_call->session, fid);
if (is_offline) {
pthread_mutex_unlock(i->toxav_call_mutex);
pthread_mutex_lock(av->mutex);
break;
}
if (audio) { if (audio) {
ac_iterate(i->audio); ac_iterate(i->audio);
@ -407,6 +329,8 @@ static void iterate_common(ToxAV *av, bool audio)
} }
} }
const uint32_t fid = i->friend_number;
pthread_mutex_unlock(i->toxav_call_mutex); pthread_mutex_unlock(i->toxav_call_mutex);
pthread_mutex_lock(av->mutex); pthread_mutex_lock(av->mutex);
@ -418,10 +342,8 @@ static void iterate_common(ToxAV *av, bool audio)
DecodeTimeStats *stats = audio ? &av->audio_stats : &av->video_stats; DecodeTimeStats *stats = audio ? &av->audio_stats : &av->video_stats;
calc_interval(av, stats, frame_time, start); calc_interval(av, stats, frame_time, start);
pthread_mutex_unlock(av->mutex); pthread_mutex_unlock(av->mutex);
} }
void toxav_audio_iterate(ToxAV *av) void toxav_audio_iterate(ToxAV *av)
{ {
iterate_common(av, true); iterate_common(av, true);
@ -466,7 +388,7 @@ bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint
call->previous_self_capabilities |= audio_bit_rate > 0 ? MSI_CAP_S_AUDIO : 0; call->previous_self_capabilities |= audio_bit_rate > 0 ? MSI_CAP_S_AUDIO : 0;
call->previous_self_capabilities |= video_bit_rate > 0 ? MSI_CAP_S_VIDEO : 0; call->previous_self_capabilities |= video_bit_rate > 0 ? MSI_CAP_S_VIDEO : 0;
if (msi_invite(av->log, av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) { if (msi_invite(av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) {
call_remove(call); call_remove(call);
rc = TOXAV_ERR_CALL_SYNC; rc = TOXAV_ERR_CALL_SYNC;
goto RETURN; goto RETURN;
@ -483,7 +405,6 @@ RETURN:
return rc == TOXAV_ERR_CALL_OK; return rc == TOXAV_ERR_CALL_OK;
} }
void toxav_callback_call(ToxAV *av, toxav_call_cb *callback, void *user_data) void toxav_callback_call(ToxAV *av, toxav_call_cb *callback, void *user_data)
{ {
pthread_mutex_lock(av->mutex); pthread_mutex_lock(av->mutex);
@ -491,7 +412,6 @@ void toxav_callback_call(ToxAV *av, toxav_call_cb *callback, void *user_data)
av->ccb_user_data = user_data; av->ccb_user_data = user_data;
pthread_mutex_unlock(av->mutex); pthread_mutex_unlock(av->mutex);
} }
bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
Toxav_Err_Answer *error) Toxav_Err_Answer *error)
{ {
@ -500,7 +420,7 @@ bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, ui
Toxav_Err_Answer rc = TOXAV_ERR_ANSWER_OK; Toxav_Err_Answer rc = TOXAV_ERR_ANSWER_OK;
ToxAVCall *call; ToxAVCall *call;
if (!tox_friend_exists(av->tox, friend_number)) { if (!m_friend_exists(av->m, friend_number)) {
rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND; rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND;
goto RETURN; goto RETURN;
} }
@ -532,7 +452,7 @@ bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, ui
call->previous_self_capabilities |= audio_bit_rate > 0 ? MSI_CAP_S_AUDIO : 0; call->previous_self_capabilities |= audio_bit_rate > 0 ? MSI_CAP_S_AUDIO : 0;
call->previous_self_capabilities |= video_bit_rate > 0 ? MSI_CAP_S_VIDEO : 0; call->previous_self_capabilities |= video_bit_rate > 0 ? MSI_CAP_S_VIDEO : 0;
if (msi_answer(av->log, call->msi_call, call->previous_self_capabilities) != 0) { if (msi_answer(call->msi_call, call->previous_self_capabilities) != 0) {
rc = TOXAV_ERR_ANSWER_SYNC; rc = TOXAV_ERR_ANSWER_SYNC;
} }
@ -545,7 +465,6 @@ RETURN:
return rc == TOXAV_ERR_ANSWER_OK; return rc == TOXAV_ERR_ANSWER_OK;
} }
void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *callback, void *user_data) void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *callback, void *user_data)
{ {
pthread_mutex_lock(av->mutex); pthread_mutex_lock(av->mutex);
@ -553,7 +472,6 @@ void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *callback, void *u
av->scb_user_data = user_data; av->scb_user_data = user_data;
pthread_mutex_unlock(av->mutex); pthread_mutex_unlock(av->mutex);
} }
static Toxav_Err_Call_Control call_control_handle_resume(const ToxAVCall *call) static Toxav_Err_Call_Control call_control_handle_resume(const ToxAVCall *call)
{ {
/* Only act if paused and had media transfer active before */ /* Only act if paused and had media transfer active before */
@ -561,13 +479,12 @@ static Toxav_Err_Call_Control call_control_handle_resume(const ToxAVCall *call)
return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
} }
if (msi_change_capabilities(call->av->log, call->msi_call, if (msi_change_capabilities(call->msi_call, call->previous_self_capabilities) == -1) {
call->previous_self_capabilities) == -1) {
return TOXAV_ERR_CALL_CONTROL_SYNC; return TOXAV_ERR_CALL_CONTROL_SYNC;
} }
rtp_allow_receiving_mark(call->audio_rtp); rtp_allow_receiving(call->audio_rtp);
rtp_allow_receiving_mark(call->video_rtp); rtp_allow_receiving(call->video_rtp);
return TOXAV_ERR_CALL_CONTROL_OK; return TOXAV_ERR_CALL_CONTROL_OK;
} }
@ -580,12 +497,12 @@ static Toxav_Err_Call_Control call_control_handle_pause(ToxAVCall *call)
call->previous_self_capabilities = call->msi_call->self_capabilities; call->previous_self_capabilities = call->msi_call->self_capabilities;
if (msi_change_capabilities(call->av->log, call->msi_call, 0) == -1) { if (msi_change_capabilities(call->msi_call, 0) == -1) {
return TOXAV_ERR_CALL_CONTROL_SYNC; return TOXAV_ERR_CALL_CONTROL_SYNC;
} }
rtp_stop_receiving_mark(call->audio_rtp); rtp_stop_receiving(call->audio_rtp);
rtp_stop_receiving_mark(call->video_rtp); rtp_stop_receiving(call->video_rtp);
return TOXAV_ERR_CALL_CONTROL_OK; return TOXAV_ERR_CALL_CONTROL_OK;
} }
@ -594,7 +511,7 @@ static Toxav_Err_Call_Control call_control_handle_cancel(ToxAVCall *call)
/* Hang up */ /* Hang up */
pthread_mutex_lock(call->toxav_call_mutex); pthread_mutex_lock(call->toxav_call_mutex);
if (msi_hangup(call->av->log, call->msi_call) != 0) { if (msi_hangup(call->msi_call) != 0) {
pthread_mutex_unlock(call->toxav_call_mutex); pthread_mutex_unlock(call->toxav_call_mutex);
return TOXAV_ERR_CALL_CONTROL_SYNC; return TOXAV_ERR_CALL_CONTROL_SYNC;
} }
@ -614,13 +531,13 @@ static Toxav_Err_Call_Control call_control_handle_mute_audio(const ToxAVCall *ca
return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
} }
if (msi_change_capabilities(call->av->log, call->msi_call, call-> if (msi_change_capabilities(call->msi_call, call->
msi_call->self_capabilities ^ MSI_CAP_R_AUDIO) == -1) { msi_call->self_capabilities ^ MSI_CAP_R_AUDIO) == -1) {
return TOXAV_ERR_CALL_CONTROL_SYNC; return TOXAV_ERR_CALL_CONTROL_SYNC;
} }
rtp_stop_receiving_mark(call->audio_rtp); rtp_stop_receiving(call->audio_rtp);
return TOXAV_ERR_CALL_CONTROL_OK; return TOXAV_ERR_CALL_CONTROL_OK;
} }
static Toxav_Err_Call_Control call_control_handle_unmute_audio(const ToxAVCall *call) static Toxav_Err_Call_Control call_control_handle_unmute_audio(const ToxAVCall *call)
@ -629,12 +546,12 @@ static Toxav_Err_Call_Control call_control_handle_unmute_audio(const ToxAVCall *
return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
} }
if (msi_change_capabilities(call->av->log, call->msi_call, call-> if (msi_change_capabilities(call->msi_call, call->
msi_call->self_capabilities | MSI_CAP_R_AUDIO) == -1) { msi_call->self_capabilities | MSI_CAP_R_AUDIO) == -1) {
return TOXAV_ERR_CALL_CONTROL_SYNC; return TOXAV_ERR_CALL_CONTROL_SYNC;
} }
rtp_allow_receiving_mark(call->audio_rtp); rtp_allow_receiving(call->audio_rtp);
return TOXAV_ERR_CALL_CONTROL_OK; return TOXAV_ERR_CALL_CONTROL_OK;
} }
static Toxav_Err_Call_Control call_control_handle_hide_video(const ToxAVCall *call) static Toxav_Err_Call_Control call_control_handle_hide_video(const ToxAVCall *call)
@ -643,12 +560,12 @@ static Toxav_Err_Call_Control call_control_handle_hide_video(const ToxAVCall *ca
return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
} }
if (msi_change_capabilities(call->av->log, call->msi_call, call-> if (msi_change_capabilities(call->msi_call, call->
msi_call->self_capabilities ^ MSI_CAP_R_VIDEO) == -1) { msi_call->self_capabilities ^ MSI_CAP_R_VIDEO) == -1) {
return TOXAV_ERR_CALL_CONTROL_SYNC; return TOXAV_ERR_CALL_CONTROL_SYNC;
} }
rtp_stop_receiving_mark(call->video_rtp); rtp_stop_receiving(call->video_rtp);
return TOXAV_ERR_CALL_CONTROL_OK; return TOXAV_ERR_CALL_CONTROL_OK;
} }
static Toxav_Err_Call_Control call_control_handle_show_video(const ToxAVCall *call) static Toxav_Err_Call_Control call_control_handle_show_video(const ToxAVCall *call)
@ -657,12 +574,12 @@ static Toxav_Err_Call_Control call_control_handle_show_video(const ToxAVCall *ca
return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION; return TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
} }
if (msi_change_capabilities(call->av->log, call->msi_call, call-> if (msi_change_capabilities(call->msi_call, call->
msi_call->self_capabilities | MSI_CAP_R_VIDEO) == -1) { msi_call->self_capabilities | MSI_CAP_R_VIDEO) == -1) {
return TOXAV_ERR_CALL_CONTROL_SYNC; return TOXAV_ERR_CALL_CONTROL_SYNC;
} }
rtp_allow_receiving_mark(call->video_rtp); rtp_allow_receiving(call->video_rtp);
return TOXAV_ERR_CALL_CONTROL_OK; return TOXAV_ERR_CALL_CONTROL_OK;
} }
static Toxav_Err_Call_Control call_control_handle(ToxAVCall *call, Toxav_Call_Control control) static Toxav_Err_Call_Control call_control_handle(ToxAVCall *call, Toxav_Call_Control control)
@ -694,7 +611,7 @@ static Toxav_Err_Call_Control call_control_handle(ToxAVCall *call, Toxav_Call_Co
} }
static Toxav_Err_Call_Control call_control(ToxAV *av, uint32_t friend_number, Toxav_Call_Control control) static Toxav_Err_Call_Control call_control(ToxAV *av, uint32_t friend_number, Toxav_Call_Control control)
{ {
if (!tox_friend_exists(av->tox, friend_number)) { if (!m_friend_exists(av->m, friend_number)) {
return TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND; return TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND;
} }
@ -720,14 +637,13 @@ bool toxav_call_control(ToxAV *av, uint32_t friend_number, Toxav_Call_Control co
return rc == TOXAV_ERR_CALL_CONTROL_OK; return rc == TOXAV_ERR_CALL_CONTROL_OK;
} }
bool toxav_audio_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_rate, bool toxav_audio_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_rate,
Toxav_Err_Bit_Rate_Set *error) Toxav_Err_Bit_Rate_Set *error)
{ {
Toxav_Err_Bit_Rate_Set rc = TOXAV_ERR_BIT_RATE_SET_OK; Toxav_Err_Bit_Rate_Set rc = TOXAV_ERR_BIT_RATE_SET_OK;
ToxAVCall *call; ToxAVCall *call;
if (!tox_friend_exists(av->tox, friend_number)) { if (!m_friend_exists(av->m, friend_number)) {
rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND; rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND;
goto RETURN; goto RETURN;
} }
@ -746,14 +662,14 @@ bool toxav_audio_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
goto RETURN; goto RETURN;
} }
LOGGER_DEBUG(av->log, "Setting new audio bitrate to: %d", bit_rate); LOGGER_DEBUG(av->m->log, "Setting new audio bitrate to: %d", bit_rate);
if (call->audio_bit_rate == bit_rate) { if (call->audio_bit_rate == bit_rate) {
LOGGER_DEBUG(av->log, "Audio bitrate already set to: %d", bit_rate); LOGGER_DEBUG(av->m->log, "Audio bitrate already set to: %d", bit_rate);
} else if (bit_rate == 0) { } else if (bit_rate == 0) {
LOGGER_DEBUG(av->log, "Turned off audio sending"); LOGGER_DEBUG(av->m->log, "Turned off audio sending");
if (msi_change_capabilities(av->log, call->msi_call, call->msi_call-> if (msi_change_capabilities(call->msi_call, call->msi_call->
self_capabilities ^ MSI_CAP_S_AUDIO) != 0) { self_capabilities ^ MSI_CAP_S_AUDIO) != 0) {
pthread_mutex_unlock(av->mutex); pthread_mutex_unlock(av->mutex);
rc = TOXAV_ERR_BIT_RATE_SET_SYNC; rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
@ -766,10 +682,10 @@ bool toxav_audio_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
pthread_mutex_lock(call->toxav_call_mutex); pthread_mutex_lock(call->toxav_call_mutex);
if (call->audio_bit_rate == 0) { if (call->audio_bit_rate == 0) {
LOGGER_DEBUG(av->log, "Turned on audio sending"); LOGGER_DEBUG(av->m->log, "Turned on audio sending");
/* The audio has been turned off before this */ /* The audio has been turned off before this */
if (msi_change_capabilities(av->log, call->msi_call, call-> if (msi_change_capabilities(call->msi_call, call->
msi_call->self_capabilities | MSI_CAP_S_AUDIO) != 0) { msi_call->self_capabilities | MSI_CAP_S_AUDIO) != 0) {
pthread_mutex_unlock(call->toxav_call_mutex); pthread_mutex_unlock(call->toxav_call_mutex);
pthread_mutex_unlock(av->mutex); pthread_mutex_unlock(av->mutex);
@ -777,7 +693,7 @@ bool toxav_audio_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
goto RETURN; goto RETURN;
} }
} else { } else {
LOGGER_DEBUG(av->log, "Set new audio bit rate %d", bit_rate); LOGGER_DEBUG(av->m->log, "Set new audio bit rate %d", bit_rate);
} }
call->audio_bit_rate = bit_rate; call->audio_bit_rate = bit_rate;
@ -793,14 +709,13 @@ RETURN:
return rc == TOXAV_ERR_BIT_RATE_SET_OK; return rc == TOXAV_ERR_BIT_RATE_SET_OK;
} }
bool toxav_video_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_rate, bool toxav_video_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_rate,
Toxav_Err_Bit_Rate_Set *error) Toxav_Err_Bit_Rate_Set *error)
{ {
Toxav_Err_Bit_Rate_Set rc = TOXAV_ERR_BIT_RATE_SET_OK; Toxav_Err_Bit_Rate_Set rc = TOXAV_ERR_BIT_RATE_SET_OK;
ToxAVCall *call; ToxAVCall *call;
if (!tox_friend_exists(av->tox, friend_number)) { if (!m_friend_exists(av->m, friend_number)) {
rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND; rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND;
goto RETURN; goto RETURN;
} }
@ -819,15 +734,15 @@ bool toxav_video_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
goto RETURN; goto RETURN;
} }
LOGGER_DEBUG(av->log, "Setting new video bitrate to: %d", bit_rate); LOGGER_DEBUG(av->m->log, "Setting new video bitrate to: %d", bit_rate);
if (call->video_bit_rate == bit_rate) { if (call->video_bit_rate == bit_rate) {
LOGGER_DEBUG(av->log, "Video bitrate already set to: %d", bit_rate); LOGGER_DEBUG(av->m->log, "Video bitrate already set to: %d", bit_rate);
} else if (bit_rate == 0) { } else if (bit_rate == 0) {
LOGGER_DEBUG(av->log, "Turned off video sending"); LOGGER_DEBUG(av->m->log, "Turned off video sending");
/* Video sending is turned off; notify peer */ /* Video sending is turned off; notify peer */
if (msi_change_capabilities(av->log, call->msi_call, call->msi_call-> if (msi_change_capabilities(call->msi_call, call->msi_call->
self_capabilities ^ MSI_CAP_S_VIDEO) != 0) { self_capabilities ^ MSI_CAP_S_VIDEO) != 0) {
pthread_mutex_unlock(av->mutex); pthread_mutex_unlock(av->mutex);
rc = TOXAV_ERR_BIT_RATE_SET_SYNC; rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
@ -839,10 +754,10 @@ bool toxav_video_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
pthread_mutex_lock(call->toxav_call_mutex); pthread_mutex_lock(call->toxav_call_mutex);
if (call->video_bit_rate == 0) { if (call->video_bit_rate == 0) {
LOGGER_DEBUG(av->log, "Turned on video sending"); LOGGER_DEBUG(av->m->log, "Turned on video sending");
/* The video has been turned off before this */ /* The video has been turned off before this */
if (msi_change_capabilities(av->log, call->msi_call, call-> if (msi_change_capabilities(call->msi_call, call->
msi_call->self_capabilities | MSI_CAP_S_VIDEO) != 0) { msi_call->self_capabilities | MSI_CAP_S_VIDEO) != 0) {
pthread_mutex_unlock(call->toxav_call_mutex); pthread_mutex_unlock(call->toxav_call_mutex);
pthread_mutex_unlock(av->mutex); pthread_mutex_unlock(av->mutex);
@ -850,7 +765,7 @@ bool toxav_video_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
goto RETURN; goto RETURN;
} }
} else { } else {
LOGGER_DEBUG(av->log, "Set new video bit rate %d", bit_rate); LOGGER_DEBUG(av->m->log, "Set new video bit rate %d", bit_rate);
} }
call->video_bit_rate = bit_rate; call->video_bit_rate = bit_rate;
@ -866,7 +781,6 @@ RETURN:
return rc == TOXAV_ERR_BIT_RATE_SET_OK; return rc == TOXAV_ERR_BIT_RATE_SET_OK;
} }
void toxav_callback_audio_bit_rate(ToxAV *av, toxav_audio_bit_rate_cb *callback, void *user_data) void toxav_callback_audio_bit_rate(ToxAV *av, toxav_audio_bit_rate_cb *callback, void *user_data)
{ {
pthread_mutex_lock(av->mutex); pthread_mutex_lock(av->mutex);
@ -874,7 +788,6 @@ void toxav_callback_audio_bit_rate(ToxAV *av, toxav_audio_bit_rate_cb *callback,
av->abcb_user_data = user_data; av->abcb_user_data = user_data;
pthread_mutex_unlock(av->mutex); pthread_mutex_unlock(av->mutex);
} }
void toxav_callback_video_bit_rate(ToxAV *av, toxav_video_bit_rate_cb *callback, void *user_data) void toxav_callback_video_bit_rate(ToxAV *av, toxav_video_bit_rate_cb *callback, void *user_data)
{ {
pthread_mutex_lock(av->mutex); pthread_mutex_lock(av->mutex);
@ -882,14 +795,13 @@ void toxav_callback_video_bit_rate(ToxAV *av, toxav_video_bit_rate_cb *callback,
av->vbcb_user_data = user_data; av->vbcb_user_data = user_data;
pthread_mutex_unlock(av->mutex); pthread_mutex_unlock(av->mutex);
} }
bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pcm, size_t sample_count, bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pcm, size_t sample_count,
uint8_t channels, uint32_t sampling_rate, Toxav_Err_Send_Frame *error) uint8_t channels, uint32_t sampling_rate, Toxav_Err_Send_Frame *error)
{ {
Toxav_Err_Send_Frame rc = TOXAV_ERR_SEND_FRAME_OK; Toxav_Err_Send_Frame rc = TOXAV_ERR_SEND_FRAME_OK;
ToxAVCall *call; ToxAVCall *call;
if (!tox_friend_exists(av->tox, friend_number)) { if (!m_friend_exists(av->m, friend_number)) {
rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
goto RETURN; goto RETURN;
} }
@ -947,14 +859,14 @@ bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pc
dest + sizeof(sampling_rate), dest_size - sizeof(sampling_rate)); dest + sizeof(sampling_rate), dest_size - sizeof(sampling_rate));
if (vrc < 0) { if (vrc < 0) {
LOGGER_WARNING(av->log, "Failed to encode frame %s", opus_strerror(vrc)); LOGGER_WARNING(av->m->log, "Failed to encode frame %s", opus_strerror(vrc));
pthread_mutex_unlock(call->mutex_audio); pthread_mutex_unlock(call->mutex_audio);
rc = TOXAV_ERR_SEND_FRAME_INVALID; rc = TOXAV_ERR_SEND_FRAME_INVALID;
goto RETURN; goto RETURN;
} }
if (rtp_send_data(av->log, call->audio_rtp, dest, vrc + sizeof(sampling_rate), false) != 0) { if (rtp_send_data(call->audio_rtp, dest, vrc + sizeof(sampling_rate), false, av->m->log) != 0) {
LOGGER_WARNING(av->log, "Failed to send audio packet"); LOGGER_WARNING(av->m->log, "Failed to send audio packet");
rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED; rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
} }
} }
@ -970,7 +882,7 @@ RETURN:
return rc == TOXAV_ERR_SEND_FRAME_OK; return rc == TOXAV_ERR_SEND_FRAME_OK;
} }
static Toxav_Err_Send_Frame send_frames(const ToxAV *av, ToxAVCall *call) static Toxav_Err_Send_Frame send_frames(const Logger *log, ToxAVCall *call)
{ {
vpx_codec_iter_t iter = nullptr; vpx_codec_iter_t iter = nullptr;
@ -988,15 +900,20 @@ static Toxav_Err_Send_Frame send_frames(const ToxAV *av, ToxAVCall *call)
const uint32_t frame_length_in_bytes = pkt->data.frame.sz; const uint32_t frame_length_in_bytes = pkt->data.frame.sz;
const int res = rtp_send_data( const int res = rtp_send_data(
av->log,
call->video_rtp, call->video_rtp,
(const uint8_t *)pkt->data.frame.buf, (const uint8_t *)pkt->data.frame.buf,
frame_length_in_bytes, frame_length_in_bytes,
is_keyframe); is_keyframe,
log);
LOGGER_DEBUG(log, "+ _sending_FRAME_TYPE_==%s bytes=%d frame_len=%d", is_keyframe ? "K" : ".",
(int)pkt->data.frame.sz, (int)frame_length_in_bytes);
const uint8_t *const buf = (const uint8_t *)pkt->data.frame.buf;
LOGGER_DEBUG(log, "+ _sending_FRAME_ b0=%d b1=%d", buf[0], buf[1]);
if (res < 0) { if (res < 0) {
char *netstrerror = net_new_strerror(net_error()); char *netstrerror = net_new_strerror(net_error());
LOGGER_WARNING(av->log, "Could not send video frame: %s", netstrerror); LOGGER_WARNING(log, "Could not send video frame: %s", netstrerror);
net_kill_strerror(netstrerror); net_kill_strerror(netstrerror);
return TOXAV_ERR_SEND_FRAME_RTP_FAILED; return TOXAV_ERR_SEND_FRAME_RTP_FAILED;
} }
@ -1013,7 +930,7 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
int vpx_encode_flags = 0; int vpx_encode_flags = 0;
if (!tox_friend_exists(av->tox, friend_number)) { if (!m_friend_exists(av->m, friend_number)) {
rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
goto RETURN; goto RETURN;
} }
@ -1048,7 +965,7 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
goto RETURN; goto RETURN;
} }
if (vc_reconfigure_encoder(call->video, call->video_bit_rate, width, height, -1) != 0) { if (vc_reconfigure_encoder(call->video, call->video_bit_rate * 1000, width, height, -1) != 0) {
pthread_mutex_unlock(call->mutex_video); pthread_mutex_unlock(call->mutex_video);
rc = TOXAV_ERR_SEND_FRAME_INVALID; rc = TOXAV_ERR_SEND_FRAME_INVALID;
goto RETURN; goto RETURN;
@ -1057,13 +974,13 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
if (call->video_rtp->ssrc < VIDEO_SEND_X_KEYFRAMES_FIRST) { if (call->video_rtp->ssrc < VIDEO_SEND_X_KEYFRAMES_FIRST) {
// Key frame flag for first frames // Key frame flag for first frames
vpx_encode_flags = VPX_EFLAG_FORCE_KF; vpx_encode_flags = VPX_EFLAG_FORCE_KF;
LOGGER_DEBUG(av->log, "I_FRAME_FLAG:%d only-i-frame mode", call->video_rtp->ssrc); LOGGER_DEBUG(av->m->log, "I_FRAME_FLAG:%d only-i-frame mode", call->video_rtp->ssrc);
++call->video_rtp->ssrc; ++call->video_rtp->ssrc;
} else if (call->video_rtp->ssrc == VIDEO_SEND_X_KEYFRAMES_FIRST) { } else if (call->video_rtp->ssrc == VIDEO_SEND_X_KEYFRAMES_FIRST) {
// normal keyframe placement // normal keyframe placement
vpx_encode_flags = 0; vpx_encode_flags = 0;
LOGGER_DEBUG(av->log, "I_FRAME_FLAG:%d normal mode", call->video_rtp->ssrc); LOGGER_DEBUG(av->m->log, "I_FRAME_FLAG:%d normal mode", call->video_rtp->ssrc);
++call->video_rtp->ssrc; ++call->video_rtp->ssrc;
} }
@ -1092,7 +1009,7 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
if (vrc != VPX_CODEC_OK) { if (vrc != VPX_CODEC_OK) {
pthread_mutex_unlock(call->mutex_video); pthread_mutex_unlock(call->mutex_video);
LOGGER_ERROR(av->log, "Could not encode video frame: %s", vpx_codec_err_to_string(vrc)); LOGGER_ERROR(av->m->log, "Could not encode video frame: %s", vpx_codec_err_to_string(vrc));
rc = TOXAV_ERR_SEND_FRAME_INVALID; rc = TOXAV_ERR_SEND_FRAME_INVALID;
goto RETURN; goto RETURN;
} }
@ -1100,7 +1017,7 @@ bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, u
++call->video->frame_counter; ++call->video->frame_counter;
rc = send_frames(av, call); rc = send_frames(av->m->log, call);
pthread_mutex_unlock(call->mutex_video); pthread_mutex_unlock(call->mutex_video);
@ -1146,7 +1063,7 @@ static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss,
ToxAVCall *call = (ToxAVCall *)user_data; ToxAVCall *call = (ToxAVCall *)user_data;
assert(call != nullptr); assert(call != nullptr);
LOGGER_DEBUG(call->av->log, "Reported loss of %f%%", (double)loss * 100); LOGGER_DEBUG(call->av->m->log, "Reported loss of %f%%", (double)loss * 100);
/* if less than 10% data loss we do nothing! */ /* if less than 10% data loss we do nothing! */
if (loss < 0.1F) { if (loss < 0.1F) {
@ -1158,7 +1075,7 @@ static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss,
if (call->video_bit_rate != 0) { if (call->video_bit_rate != 0) {
if (call->av->vbcb == nullptr) { if (call->av->vbcb == nullptr) {
pthread_mutex_unlock(call->av->mutex); pthread_mutex_unlock(call->av->mutex);
LOGGER_WARNING(call->av->log, "No callback to report loss on"); LOGGER_WARNING(call->av->m->log, "No callback to report loss on");
return; return;
} }
@ -1168,7 +1085,7 @@ static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss,
} else if (call->audio_bit_rate != 0) { } else if (call->audio_bit_rate != 0) {
if (call->av->abcb == nullptr) { if (call->av->abcb == nullptr) {
pthread_mutex_unlock(call->av->mutex); pthread_mutex_unlock(call->av->mutex);
LOGGER_WARNING(call->av->log, "No callback to report loss on"); LOGGER_WARNING(call->av->m->log, "No callback to report loss on");
return; return;
} }
@ -1179,7 +1096,6 @@ static void callback_bwc(BWController *bwc, uint32_t friend_number, float loss,
pthread_mutex_unlock(call->av->mutex); pthread_mutex_unlock(call->av->mutex);
} }
static int callback_invite(void *object, MSICall *call) static int callback_invite(void *object, MSICall *call)
{ {
ToxAV *toxav = (ToxAV *)object; ToxAV *toxav = (ToxAV *)object;
@ -1188,7 +1104,7 @@ static int callback_invite(void *object, MSICall *call)
ToxAVCall *av_call = call_new(toxav, call->friend_number, nullptr); ToxAVCall *av_call = call_new(toxav, call->friend_number, nullptr);
if (av_call == nullptr) { if (av_call == nullptr) {
LOGGER_WARNING(toxav->log, "Failed to initialize call..."); LOGGER_WARNING(toxav->m->log, "Failed to initialize call...");
pthread_mutex_unlock(toxav->mutex); pthread_mutex_unlock(toxav->mutex);
return -1; return -1;
} }
@ -1208,7 +1124,6 @@ static int callback_invite(void *object, MSICall *call)
pthread_mutex_unlock(toxav->mutex); pthread_mutex_unlock(toxav->mutex);
return 0; return 0;
} }
static int callback_start(void *object, MSICall *call) static int callback_start(void *object, MSICall *call)
{ {
ToxAV *toxav = (ToxAV *)object; ToxAV *toxav = (ToxAV *)object;
@ -1237,7 +1152,6 @@ static int callback_start(void *object, MSICall *call)
pthread_mutex_unlock(toxav->mutex); pthread_mutex_unlock(toxav->mutex);
return 0; return 0;
} }
static int callback_end(void *object, MSICall *call) static int callback_end(void *object, MSICall *call)
{ {
ToxAV *toxav = (ToxAV *)object; ToxAV *toxav = (ToxAV *)object;
@ -1253,7 +1167,6 @@ static int callback_end(void *object, MSICall *call)
pthread_mutex_unlock(toxav->mutex); pthread_mutex_unlock(toxav->mutex);
return 0; return 0;
} }
static int callback_error(void *object, MSICall *call) static int callback_error(void *object, MSICall *call)
{ {
ToxAV *toxav = (ToxAV *)object; ToxAV *toxav = (ToxAV *)object;
@ -1269,22 +1182,21 @@ static int callback_error(void *object, MSICall *call)
pthread_mutex_unlock(toxav->mutex); pthread_mutex_unlock(toxav->mutex);
return 0; return 0;
} }
static int callback_capabilites(void *object, MSICall *call) static int callback_capabilites(void *object, MSICall *call)
{ {
ToxAV *toxav = (ToxAV *)object; ToxAV *toxav = (ToxAV *)object;
pthread_mutex_lock(toxav->mutex); pthread_mutex_lock(toxav->mutex);
if ((call->peer_capabilities & MSI_CAP_S_AUDIO) != 0) { if ((call->peer_capabilities & MSI_CAP_S_AUDIO) != 0) {
rtp_allow_receiving_mark(call->av_call->audio_rtp); rtp_allow_receiving(call->av_call->audio_rtp);
} else { } else {
rtp_stop_receiving_mark(call->av_call->audio_rtp); rtp_stop_receiving(call->av_call->audio_rtp);
} }
if ((call->peer_capabilities & MSI_CAP_S_VIDEO) != 0) { if ((call->peer_capabilities & MSI_CAP_S_VIDEO) != 0) {
rtp_allow_receiving_mark(call->av_call->video_rtp); rtp_allow_receiving(call->av_call->video_rtp);
} else { } else {
rtp_stop_receiving_mark(call->av_call->video_rtp); rtp_stop_receiving(call->av_call->video_rtp);
} }
invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities); invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities);
@ -1292,7 +1204,6 @@ static int callback_capabilites(void *object, MSICall *call)
pthread_mutex_unlock(toxav->mutex); pthread_mutex_unlock(toxav->mutex);
return 0; return 0;
} }
static bool audio_bit_rate_invalid(uint32_t bit_rate) static bool audio_bit_rate_invalid(uint32_t bit_rate)
{ {
/* Opus RFC 6716 section-2.1.1 dictates the following: /* Opus RFC 6716 section-2.1.1 dictates the following:
@ -1300,7 +1211,6 @@ static bool audio_bit_rate_invalid(uint32_t bit_rate)
*/ */
return bit_rate < 6 || bit_rate > 510; return bit_rate < 6 || bit_rate > 510;
} }
static bool video_bit_rate_invalid(uint32_t bit_rate) static bool video_bit_rate_invalid(uint32_t bit_rate)
{ {
/* https://www.webmproject.org/docs/webm-sdk/structvpx__codec__enc__cfg.html shows the following: /* https://www.webmproject.org/docs/webm-sdk/structvpx__codec__enc__cfg.html shows the following:
@ -1312,7 +1222,6 @@ static bool video_bit_rate_invalid(uint32_t bit_rate)
*/ */
return bit_rate > UINT32_MAX; return bit_rate > UINT32_MAX;
} }
static bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state) static bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state)
{ {
if (av->scb != nullptr) { if (av->scb != nullptr) {
@ -1330,17 +1239,12 @@ static ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, Toxav_Err_Call *er
Toxav_Err_Call rc = TOXAV_ERR_CALL_OK; Toxav_Err_Call rc = TOXAV_ERR_CALL_OK;
ToxAVCall *call = nullptr; ToxAVCall *call = nullptr;
Tox_Err_Friend_Query f_con_query_error; if (!m_friend_exists(av->m, friend_number)) {
Tox_Connection f_con_status = TOX_CONNECTION_NONE;
if (!tox_friend_exists(av->tox, friend_number)) {
rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND; rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND;
goto RETURN; goto RETURN;
} }
f_con_status = tox_friend_get_connection_status(av->tox, friend_number, &f_con_query_error); if (m_get_friend_connectionstatus(av->m, friend_number) < 1) {
if (f_con_status == TOX_CONNECTION_NONE) {
rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED; rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
goto RETURN; goto RETURN;
} }
@ -1419,6 +1323,16 @@ RETURN:
return call; return call;
} }
static ToxAVCall *call_get(ToxAV *av, uint32_t friend_number)
{
/* Assumes mutex locked */
if (av->calls == nullptr || av->calls_tail < friend_number) {
return nullptr;
}
return av->calls[friend_number];
}
static ToxAVCall *call_remove(ToxAVCall *call) static ToxAVCall *call_remove(ToxAVCall *call)
{ {
if (call == nullptr) { if (call == nullptr) {
@ -1485,7 +1399,7 @@ static bool call_prepare_transmission(ToxAVCall *call)
} }
if (call->active) { if (call->active) {
LOGGER_WARNING(av->log, "Call already active!"); LOGGER_WARNING(av->m->log, "Call already active!");
return true; return true;
} }
@ -1498,37 +1412,43 @@ static bool call_prepare_transmission(ToxAVCall *call)
} }
/* Prepare bwc */ /* Prepare bwc */
call->bwc = bwc_new(av->log, av->tox, call->friend_number, callback_bwc, call, av->toxav_mono_time); call->bwc = bwc_new(av->m, av->tox, call->friend_number, callback_bwc, call, av->toxav_mono_time);
{ /* Prepare audio */ if (call->bwc == nullptr) {
call->audio = ac_new(av->toxav_mono_time, av->log, av, call->friend_number, av->acb, av->acb_user_data); LOGGER_ERROR(av->m->log, "Failed to create new bwc");
goto FAILURE;
}
{ /* Prepare audio */
call->audio = ac_new(av->toxav_mono_time, av->m->log, av, call->friend_number, av->acb, av->acb_user_data);
if (call->audio == nullptr) { if (call->audio == nullptr) {
LOGGER_ERROR(av->log, "Failed to create audio codec session"); LOGGER_ERROR(av->m->log, "Failed to create audio codec session");
goto FAILURE; goto FAILURE;
} }
call->audio_rtp = rtp_new(av->log, RTP_TYPE_AUDIO, av->tox, av, call->friend_number, call->bwc, call->audio_rtp = rtp_new(RTP_TYPE_AUDIO, av->m, av->tox, call->friend_number, call->bwc,
call->audio, ac_queue_message); call->audio, ac_queue_message);
if (call->audio_rtp == nullptr) { if (call->audio_rtp == nullptr) {
LOGGER_ERROR(av->log, "Failed to create audio rtp session"); LOGGER_ERROR(av->m->log, "Failed to create audio rtp session");
goto FAILURE; goto FAILURE;
} }
} }
{ /* Prepare video */
call->video = vc_new(av->log, av->toxav_mono_time, av, call->friend_number, av->vcb, av->vcb_user_data); { /* Prepare video */
call->video = vc_new(av->toxav_mono_time, av->m->log, av, call->friend_number, av->vcb, av->vcb_user_data);
if (call->video == nullptr) { if (call->video == nullptr) {
LOGGER_ERROR(av->log, "Failed to create video codec session"); LOGGER_ERROR(av->m->log, "Failed to create video codec session");
goto FAILURE; goto FAILURE;
} }
call->video_rtp = rtp_new(av->log, RTP_TYPE_VIDEO, av->tox, av, call->friend_number, call->bwc, call->video_rtp = rtp_new(RTP_TYPE_VIDEO, av->m, av->tox, call->friend_number, call->bwc,
call->video, vc_queue_message); call->video, vc_queue_message);
if (call->video_rtp == nullptr) { if (call->video_rtp == nullptr) {
LOGGER_ERROR(av->log, "Failed to create video rtp session"); LOGGER_ERROR(av->m->log, "Failed to create video rtp session");
goto FAILURE; goto FAILURE;
} }
} }
@ -1538,11 +1458,11 @@ static bool call_prepare_transmission(ToxAVCall *call)
FAILURE: FAILURE:
bwc_kill(call->bwc); bwc_kill(call->bwc);
rtp_kill(av->log, call->audio_rtp); rtp_kill(call->audio_rtp);
ac_kill(call->audio); ac_kill(call->audio);
call->audio_rtp = nullptr; call->audio_rtp = nullptr;
call->audio = nullptr; call->audio = nullptr;
rtp_kill(av->log, call->video_rtp); rtp_kill(call->video_rtp);
vc_kill(call->video); vc_kill(call->video);
call->video_rtp = nullptr; call->video_rtp = nullptr;
call->video = nullptr; call->video = nullptr;
@ -1569,14 +1489,12 @@ static void call_kill_transmission(ToxAVCall *call)
bwc_kill(call->bwc); bwc_kill(call->bwc);
const ToxAV *av = call->av; rtp_kill(call->audio_rtp);
rtp_kill(av->log, call->audio_rtp);
ac_kill(call->audio); ac_kill(call->audio);
call->audio_rtp = nullptr; call->audio_rtp = nullptr;
call->audio = nullptr; call->audio = nullptr;
rtp_kill(av->log, call->video_rtp); rtp_kill(call->video_rtp);
vc_kill(call->video); vc_kill(call->video);
call->video_rtp = nullptr; call->video_rtp = nullptr;
call->video = nullptr; call->video = nullptr;
@ -1584,12 +1502,3 @@ static void call_kill_transmission(ToxAVCall *call)
pthread_mutex_destroy(call->mutex_audio); pthread_mutex_destroy(call->mutex_audio);
pthread_mutex_destroy(call->mutex_video); pthread_mutex_destroy(call->mutex_video);
} }
Mono_Time *toxav_get_av_mono_time(const ToxAV *av)
{
if (av == nullptr) {
return nullptr;
}
return av->toxav_mono_time;
}

View File

@ -73,8 +73,6 @@ typedef struct Tox Tox;
#endif /* !TOX_DEFINED */ #endif /* !TOX_DEFINED */
#endif /* !APIGEN_IGNORE */ #endif /* !APIGEN_IGNORE */
#ifndef TOXAV_DEFINED
#define TOXAV_DEFINED
/** /**
* @brief The ToxAV instance type. * @brief The ToxAV instance type.
* *
@ -85,7 +83,6 @@ typedef struct Tox Tox;
* notifying peers. * notifying peers.
*/ */
typedef struct ToxAV ToxAV; typedef struct ToxAV ToxAV;
#endif /* TOXAV_DEFINED */
/** @{ /** @{
* @brief Creation and destruction * @brief Creation and destruction
@ -249,9 +246,9 @@ typedef enum Toxav_Err_Call {
* receiving are both enabled by default. * receiving are both enabled by default.
* *
* @param friend_number The friend number of the friend that should be called. * @param friend_number The friend number of the friend that should be called.
* @param audio_bit_rate Audio bit rate in kbit/sec. Set this to 0 to disable * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
* audio sending. * audio sending.
* @param video_bit_rate Video bit rate in kbit/sec. Set this to 0 to disable * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
* video sending. * video sending.
*/ */
bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
@ -318,9 +315,9 @@ typedef enum Toxav_Err_Answer {
* enabled by default. * enabled by default.
* *
* @param friend_number The friend number of the friend that is calling. * @param friend_number The friend number of the friend that is calling.
* @param audio_bit_rate Audio bit rate in kbit/sec. Set this to 0 to disable * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
* audio sending. * audio sending.
* @param video_bit_rate Video bit rate in kbit/sec. Set this to 0 to disable * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
* video sending. * video sending.
*/ */
bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
@ -600,11 +597,11 @@ bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t pcm
uint8_t channels, uint32_t sampling_rate, Toxav_Err_Send_Frame *error); uint8_t channels, uint32_t sampling_rate, Toxav_Err_Send_Frame *error);
/** /**
* Set the bit rate to be used in subsequent audio frames. * Set the bit rate to be used in subsequent video frames.
* *
* @param friend_number The friend number of the friend for which to set the * @param friend_number The friend number of the friend for which to set the
* bit rate. * bit rate.
* @param bit_rate The new audio bit rate in kbit/sec. Set to 0 to disable. * @param bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable.
* *
* @return true on success. * @return true on success.
*/ */
@ -617,7 +614,7 @@ bool toxav_audio_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
* *
* @param friend_number The friend number of the friend for which to set the * @param friend_number The friend number of the friend for which to set the
* bit rate. * bit rate.
* @param audio_bit_rate Suggested maximum audio bit rate in kbit/sec. * @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec.
*/ */
typedef void toxav_audio_bit_rate_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, void *user_data); typedef void toxav_audio_bit_rate_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, void *user_data);
@ -630,10 +627,9 @@ void toxav_callback_audio_bit_rate(ToxAV *av, toxav_audio_bit_rate_cb *callback,
/** /**
* Send a video frame to a friend. * Send a video frame to a friend.
* *
* The video frame needs to be planar YUV420. * Y - plane should be of size: `height * width`
* Y - plane should be of size: `width * height` * U - plane should be of size: `(height/2) * (width/2)`
* U - plane should be of size: `(width/2) * (height/2)` * V - plane should be of size: `(height/2) * (width/2)`
* V - plane should be of size: `(width/2) * (height/2)`
* *
* @param friend_number The friend number of the friend to which to send a video * @param friend_number The friend number of the friend to which to send a video
* frame. * frame.
@ -645,9 +641,9 @@ void toxav_callback_audio_bit_rate(ToxAV *av, toxav_audio_bit_rate_cb *callback,
*/ */
bool toxav_video_send_frame( bool toxav_video_send_frame(
ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height,
const uint8_t y[/*! width * height */], const uint8_t y[/*! height * width */],
const uint8_t u[/*! width/2 * height/2 */], const uint8_t u[/*! height/2 * width/2 */],
const uint8_t v[/*! width/2 * height/2 */], const uint8_t v[/*! height/2 * width/2 */],
Toxav_Err_Send_Frame *error); Toxav_Err_Send_Frame *error);
/** /**
@ -655,7 +651,7 @@ bool toxav_video_send_frame(
* *
* @param friend_number The friend number of the friend for which to set the * @param friend_number The friend number of the friend for which to set the
* bit rate. * bit rate.
* @param bit_rate The new video bit rate in kbit/sec. Set to 0 to disable. * @param bit_rate The new video bit rate in Kb/sec. Set to 0 to disable.
* *
* @return true on success. * @return true on success.
*/ */
@ -668,7 +664,7 @@ bool toxav_video_set_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t bit_ra
* *
* @param friend_number The friend number of the friend for which to set the * @param friend_number The friend number of the friend for which to set the
* bit rate. * bit rate.
* @param video_bit_rate Suggested maximum video bit rate in kbit/sec. * @param video_bit_rate Suggested maximum video bit rate in Kb/sec.
*/ */
typedef void toxav_video_bit_rate_cb(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, void *user_data); typedef void toxav_video_bit_rate_cb(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, void *user_data);

Some files were not shown because too many files have changed in this diff Show More