Merge pull request #649 from AlexandreRouma/backend_abstraction

Backend abstraction and android support
This commit is contained in:
AlexandreRouma 2022-03-28 22:27:38 +02:00 committed by GitHub
commit 80d244003a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
115 changed files with 5747 additions and 1969 deletions

4
.gitignore vendored
View File

@ -14,4 +14,6 @@ Folder.DotSettings.user
CMakeSettings.json
poggers_decoder
m17_decoder/libcorrect
SDR++.app
SDR++.app
android/deps
android/app/assets

View File

@ -7,6 +7,20 @@ else()
set(CMAKE_INSTALL_PREFIX "/usr")
endif()
# Configure toolchain for android
if (ANDROID)
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate"
)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=c++17")
endif (ANDROID)
# Backends
option(OPT_BACKEND_GLFW "Use the GLFW backend" ON)
option(OPT_BACKEND_ANDROID "Use the Android backend" OFF)
# Compatibility Options
option(OPT_OVERRIDE_STD_FILESYSTEM "Use a local version of std::filesystem on systems that don't have it yet" OFF)
@ -28,6 +42,7 @@ option(OPT_BUILD_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencie
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Dependencies: libiio, libad9361)" ON)
# Sinks
option(OPT_BUILD_ANDROID_AUDIO_SINK "Build Android Audio Sink Module (Dependencies: AAudio, only for android)" OFF)
option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Dependencies: rtaudio)" ON)
option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: portaudio)" OFF)
option(OPT_BUILD_NETWORK_SINK "Build Audio Sink Module (no dependencies required)" ON)
@ -35,6 +50,7 @@ option(OPT_BUILD_NEW_PORTAUDIO_SINK "Build the new PortAudio Sink Module (Depend
# Decoders
option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF)
option(OPT_BUILD_KG_SSTV_DECODER "Build the M17 decoder module (no dependencies required)" OFF)
option(OPT_BUILD_M17_DECODER "Build the M17 decoder module (no dependencies required)" OFF)
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
@ -49,7 +65,7 @@ option(OPT_BUILD_SCANNER "Frequency scanner" OFF)
option(OPT_BUILD_SCHEDULER "Build the scheduler" OFF)
# Other options
option(USE_INTERNAL_LIBCORRECT "Use an external version of libcorrect" ON)
option(USE_INTERNAL_LIBCORRECT "Use an internal version of libcorrect" ON)
option(USE_BUNDLE_DEFAULTS "Set the default resource and module directories to the right ones for a MacOS .app" OFF)
# Core of SDR++
@ -118,6 +134,10 @@ endif (OPT_BUILD_PLUTOSDR_SOURCE)
# Sink modules
if (OPT_BUILD_ANDROID_AUDIO_SINK)
add_subdirectory("sink_modules/android_audio_sink")
endif (OPT_BUILD_ANDROID_AUDIO_SINK)
if (OPT_BUILD_AUDIO_SINK)
add_subdirectory("sink_modules/audio_sink")
endif (OPT_BUILD_AUDIO_SINK)
@ -140,6 +160,10 @@ if (OPT_BUILD_FALCON9_DECODER)
add_subdirectory("decoder_modules/falcon9_decoder")
endif (OPT_BUILD_FALCON9_DECODER)
if (OPT_BUILD_KG_SSTV_DECODER)
add_subdirectory("decoder_modules/kg_sstv_decoder")
endif (OPT_BUILD_KG_SSTV_DECODER)
if (OPT_BUILD_M17_DECODER)
add_subdirectory("decoder_modules/m17_decoder")
endif (OPT_BUILD_M17_DECODER)

12
android/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
.cxx
.externalNativeBuild
build/
*.iml
.idea
.gradle
local.properties
# Android Studio puts a Gradle wrapper here, that we don't want:
gradle/
gradlew*

64
android/app/build.gradle Normal file
View File

@ -0,0 +1,64 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 28
buildToolsVersion "30.0.3"
ndkVersion "23.0.7599858"
defaultConfig {
applicationId "org.sdrpp.sdrpp"
minSdkVersion 28
targetSdkVersion 28
versionCode 1
versionName "1.1.0"
externalNativeBuild {
cmake {
arguments "-DOPT_BACKEND_GLFW=OFF", "-DOPT_BACKEND_ANDROID=ON", "-DOPT_BUILD_SOAPY_SOURCE=OFF", "-DOPT_BUILD_ANDROID_AUDIO_SINK=ON", "-DOPT_BUILD_AUDIO_SINK=OFF", "-DOPT_BUILD_DISCORD_PRESENCE=OFF", "-DUSE_INTERNAL_LIBCORRECT=OFF"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
}
}
externalNativeBuild {
cmake {
version "3.18.1"
path "../../CMakeLists.txt"
}
}
sourceSets {
main {
assets.srcDirs += ['assets']
}
}
}
task deleteTempAssets (type: Delete) {
delete 'assets'
}
task copyResources(type: Copy) {
description = 'Copy resources...'
from '../../root/'
into 'assets/'
include('**/*')
}
repositories {
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.2'
}
copyResources.dependsOn deleteTempAssets
preBuild.dependsOn copyResources

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.sdrpp.sdrpp">
<application
android:label="SDR++"
android:allowBackup="false"
android:fullBackupContent="false"
android:hasCode="true">
<activity
android:name="org.sdrpp.sdrpp.MainActivity"
android:icon="@mipmap/ic_launcher"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:configChanges="orientation|keyboardHidden|screenSize">
<meta-data android:name="android.app.lib_name" android:value="sdrpp_core" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -0,0 +1,32 @@
package org.sdrpp.sdrpp;
import android.app.NativeActivity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.hardware.usb.*;
import android.Manifest;
import android.os.Bundle;
import android.view.View;
import android.view.KeyEvent;
import android.view.inputmethod.InputMethodManager;
import android.util.Log;
import android.content.res.AssetManager;
import androidx.core.app.ActivityCompat;
import androidx.core.content.PermissionChecker;
import java.util.concurrent.LinkedBlockingQueue;
import java.io.*;
class DeviceManager {
public fun init() {
}
}

View File

@ -0,0 +1,192 @@
package org.sdrpp.sdrpp;
import android.app.NativeActivity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.hardware.usb.*;
import android.Manifest;
import android.os.Bundle;
import android.view.View;
import android.view.KeyEvent;
import android.view.inputmethod.InputMethodManager;
import android.util.Log;
import android.content.res.AssetManager;
import androidx.core.app.ActivityCompat;
import androidx.core.content.PermissionChecker;
import java.util.concurrent.LinkedBlockingQueue;
import java.io.*;
private const val ACTION_USB_PERMISSION = "org.sdrpp.sdrpp.USB_PERMISSION";
private val usbReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (ACTION_USB_PERMISSION == intent.action) {
synchronized(this) {
var _this = context as MainActivity;
_this.SDR_device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
_this.SDR_conn = _this.usbManager!!.openDevice(_this.SDR_device);
// Save SDR info
_this.SDR_VID = _this.SDR_device!!.getVendorId();
_this.SDR_PID = _this.SDR_device!!.getProductId()
_this.SDR_FD = _this.SDR_conn!!.getFileDescriptor();
}
// Whatever the hell this does
context.unregisterReceiver(this);
// Hide again the system bars
_this.hideSystemBars();
}
}
}
}
class MainActivity : NativeActivity() {
private val TAG : String = "SDR++";
public var usbManager : UsbManager? = null;
public var SDR_device : UsbDevice? = null;
public var SDR_conn : UsbDeviceConnection? = null;
public var SDR_VID : Int = -1;
public var SDR_PID : Int = -1;
public var SDR_FD : Int = -1;
fun checkAndAsk(permission: String) {
if (PermissionChecker.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(permission), 1);
}
}
public fun hideSystemBars() {
val decorView = getWindow().getDecorView();
val uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
}
public override fun onCreate(savedInstanceState: Bundle?) {
// Hide bars
hideSystemBars();
// Ask for required permissions, without these the app cannot run.
checkAndAsk(Manifest.permission.WRITE_EXTERNAL_STORAGE);
checkAndAsk(Manifest.permission.READ_EXTERNAL_STORAGE);
// TODO: Have the main code wait until these two permissions are available
// Register events
usbManager = getSystemService(Context.USB_SERVICE) as UsbManager;
val permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), 0)
val filter = IntentFilter(ACTION_USB_PERMISSION)
registerReceiver(usbReceiver, filter)
// Get permission for all USB devices
val devList = usbManager!!.getDeviceList();
for ((name, dev) in devList) {
usbManager!!.requestPermission(dev, permissionIntent);
}
// Ask for internet permission
checkAndAsk(Manifest.permission.INTERNET);
super.onCreate(savedInstanceState)
}
public override fun onResume() {
// Hide bars again
hideSystemBars();
super.onResume();
}
fun showSoftInput() {
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager;
inputMethodManager.showSoftInput(window.decorView, 0);
}
fun hideSoftInput() {
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager;
inputMethodManager.hideSoftInputFromWindow(window.decorView.windowToken, 0);
hideSystemBars();
}
// Queue for the Unicode characters to be polled from native code (via pollUnicodeChar())
private var unicodeCharacterQueue: LinkedBlockingQueue<Int> = LinkedBlockingQueue()
// We assume dispatchKeyEvent() of the NativeActivity is actually called for every
// KeyEvent and not consumed by any View before it reaches here
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
if (event.action == KeyEvent.ACTION_DOWN) {
unicodeCharacterQueue.offer(event.getUnicodeChar(event.metaState))
}
return super.dispatchKeyEvent(event)
}
fun pollUnicodeChar(): Int {
return unicodeCharacterQueue.poll() ?: 0
}
public fun createIfDoesntExist(path: String) {
// This is a directory, create it in the filesystem
var folder = File(path);
var success = true;
if (!folder.exists()) {
success = folder.mkdirs();
}
if (!success) {
Log.e(TAG, "Could not create folder with path " + path);
}
}
public fun extractDir(aman: AssetManager, local: String, rsrc: String): Int {
val flist = aman.list(rsrc);
var ecount = 0;
for (fp in flist) {
val lpath = local + "/" + fp;
val rpath = rsrc + "/" + fp;
Log.w(TAG, "Extracting '" + rpath + "' to '" + lpath + "'");
// Create local path if non-existent
createIfDoesntExist(local);
// Create if directory
val ext = extractDir(aman, lpath, rpath);
// Extract if file
if (ext == 0) {
// This is a file, extract it
val _os = FileOutputStream(lpath);
val _is = aman.open(rpath);
val ilen = _is.available();
var fbuf = ByteArray(ilen);
_is.read(fbuf, 0, ilen);
_os.write(fbuf);
_os.close();
_is.close();
}
ecount++;
}
return ecount;
}
public fun getAppDir(): String {
val fdir = getFilesDir().getAbsolutePath();
// Extract all resources to the app directory
val aman = getAssets();
extractDir(aman, fdir + "/res", "res");
createIfDoesntExist(fdir + "/modules");
return fdir;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

22
android/build.gradle Normal file
View File

@ -0,0 +1,22 @@
buildscript {
ext.kotlin_version = '1.4.31'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1 @@
android.useAndroidX=true

1
android/settings.gradle Normal file
View File

@ -0,0 +1 @@
include ':app'

View File

@ -17,8 +17,17 @@ if (MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif ()
# Configure backend sources
if (OPT_BACKEND_GLFW)
file(GLOB_RECURSE BACKEND_SRC "backends/glfw/*.cpp" "backends/glfw/*.c")
endif (OPT_BACKEND_GLFW)
if (OPT_BACKEND_ANDROID)
file(GLOB_RECURSE BACKEND_SRC "backends/android/*.cpp" "backends/android/*.c")
set(BACKEND_SRC ${BACKEND_SRC} ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
endif (OPT_BACKEND_ANDROID)
# Add code to dyn lib
add_library(sdrpp_core SHARED ${SRC})
add_library(sdrpp_core SHARED ${SRC} ${BACKEND_SRC})
# Set compiler options
if (MSVC)
@ -36,6 +45,29 @@ target_compile_definitions(sdrpp_core PUBLIC INSTALL_PREFIX="${CMAKE_INSTALL_PRE
target_include_directories(sdrpp_core PUBLIC "src/")
target_include_directories(sdrpp_core PUBLIC "src/imgui")
# Configure backend includes and libraries
if (OPT_BACKEND_GLFW)
target_include_directories(sdrpp_core PUBLIC "backends/glfw")
target_include_directories(sdrpp_core PUBLIC "backends/glfw/imgui")
if (MSVC)
# GLFW3
find_package(glfw3 CONFIG REQUIRED)
target_link_libraries(sdrpp_core PUBLIC glfw)
else()
find_package(PkgConfig)
pkg_check_modules(GLFW3 REQUIRED glfw3)
target_include_directories(sdrpp_core PUBLIC ${GLFW3_INCLUDE_DIRS})
target_link_directories(sdrpp_core PUBLIC ${GLFW3_LIBRARY_DIRS})
target_link_libraries(sdrpp_core PUBLIC ${GLFW3_LIBRARIES})
endif()
endif (OPT_BACKEND_GLFW)
if (OPT_BACKEND_ANDROID)
target_include_directories(sdrpp_core PUBLIC "backends/android")
target_include_directories(sdrpp_core PUBLIC "backends/android/imgui")
endif (OPT_BACKEND_ANDROID)
# Link to libcorrect
if (USE_INTERNAL_LIBCORRECT)
target_include_directories(sdrpp_core PUBLIC "libcorrect/include")
@ -74,7 +106,24 @@ if (MSVC)
# ZSTD
find_package(zstd CONFIG REQUIRED)
target_link_libraries(sdrpp_core PUBLIC zstd::libzstd_shared)
elseif (ANDROID)
target_include_directories(sdrpp_core PUBLIC
../android/deps/volk/jni
../android/deps/fftw3/jni
/mnt/android_sdr/zstd/lib
${ANDROID_NDK}/sources/android/native_app_glue
)
target_link_libraries(sdrpp_core PUBLIC
${PROJECT_SOURCE_DIR}/../android/deps/volk/${ANDROID_ABI}/libcpu_features.a
${PROJECT_SOURCE_DIR}/../android/deps/volk/${ANDROID_ABI}/libvolk.so
${PROJECT_SOURCE_DIR}/../android/deps/fftw3/${ANDROID_ABI}/libfftw3f.a
/mnt/android_sdr/output/zstd/${ANDROID_ABI}/libzstd.so
android
EGL
GLESv3
log
)
else()
find_package(PkgConfig)
find_package(OpenGL REQUIRED)

View File

@ -0,0 +1,17 @@
#pragma once
#include <vector>
#include <stdint.h>
namespace backend {
struct DevVIDPID {
uint16_t vid;
uint16_t pid;
};
extern const std::vector<DevVIDPID> AIRSPY_VIDPIDS;
extern const std::vector<DevVIDPID> AIRSPYHF_VIDPIDS;
extern const std::vector<DevVIDPID> HACKRF_VIDPIDS;
extern const std::vector<DevVIDPID> RTL_SDR_VIDPIDS;
int getDeviceFD(int& vid, int& pid, const std::vector<DevVIDPID>& allowedVidPids);
}

View File

@ -0,0 +1,490 @@
#include <backend.h>
#include "android_backend.h"
#include <core.h>
#include <gui/gui.h>
#include "imgui.h"
#include "imgui_impl_android.h"
#include "imgui_impl_opengl3.h"
#include <android/log.h>
#include <android_native_app_glue.h>
#include <android/asset_manager.h>
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <stdint.h>
#include <spdlog/sinks/android_sink.h>
#include <gui/icons.h>
#include <gui/style.h>
#include <gui/menus/theme.h>
#include <filesystem>
// Credit to the ImGui android OpenGL3 example for a lot of this code!
namespace backend {
struct android_app* app = NULL;
EGLDisplay _EglDisplay = EGL_NO_DISPLAY;
EGLSurface _EglSurface = EGL_NO_SURFACE;
EGLContext _EglContext = EGL_NO_CONTEXT;
bool _Initialized = false;
char _LogTag[] = "SDR++";
bool initialized = false;
bool pauseRendering = false;
bool exited = false;
// Forward declaration
int ShowSoftKeyboardInput();
int PollUnicodeChars();
void doPartialInit() {
std::string root = (std::string)core::args["root"];
backend::init();
style::loadFonts(root + "/res"); // TODO: Don't hardcode, use config
icons::load(root + "/res");
thememenu::applyTheme();
ImGui::GetStyle().ScaleAllSizes(style::uiScale);
gui::mainWindow.setFirstMenuRender();
}
void handleAppCmd(struct android_app* app, int32_t appCmd) {
switch (appCmd) {
case APP_CMD_SAVE_STATE:
spdlog::warn("APP_CMD_SAVE_STATE");
break;
case APP_CMD_INIT_WINDOW:
spdlog::warn("APP_CMD_INIT_WINDOW");
if (pauseRendering && !exited) {
doPartialInit();
pauseRendering = false;
}
exited = false;
break;
case APP_CMD_TERM_WINDOW:
spdlog::warn("APP_CMD_TERM_WINDOW");
pauseRendering = true;
backend::end();
break;
case APP_CMD_GAINED_FOCUS:
spdlog::warn("APP_CMD_GAINED_FOCUS");
break;
case APP_CMD_LOST_FOCUS:
spdlog::warn("APP_CMD_LOST_FOCUS");
break;
}
}
int32_t handleInputEvent(struct android_app* app, AInputEvent* inputEvent) {
return ImGui_ImplAndroid_HandleInputEvent(inputEvent);
}
int aquireWindow() {
while (!app->window) {
spdlog::warn("Waiting on the shitty window thing"); std::this_thread::sleep_for(std::chrono::milliseconds(30));
int out_events;
struct android_poll_source* out_data;
while (ALooper_pollAll(0, NULL, &out_events, (void**)&out_data) >= 0) {
// Process one event
if (out_data != NULL) { out_data->process(app, out_data); }
// Exit the app by returning from within the infinite loop
if (app->destroyRequested != 0) {
return -1;
}
}
}
ANativeWindow_acquire(app->window);
return 0;
}
int init(std::string resDir) {
spdlog::warn("Backend init");
// Get window
aquireWindow();
// EGL Init
{
_EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (_EglDisplay == EGL_NO_DISPLAY)
__android_log_print(ANDROID_LOG_ERROR, _LogTag, "%s", "eglGetDisplay(EGL_DEFAULT_DISPLAY) returned EGL_NO_DISPLAY");
if (eglInitialize(_EglDisplay, 0, 0) != EGL_TRUE)
__android_log_print(ANDROID_LOG_ERROR, _LogTag, "%s", "eglInitialize() returned with an error");
const EGLint egl_attributes[] = { EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 24, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE };
EGLint num_configs = 0;
if (eglChooseConfig(_EglDisplay, egl_attributes, nullptr, 0, &num_configs) != EGL_TRUE)
__android_log_print(ANDROID_LOG_ERROR, _LogTag, "%s", "eglChooseConfig() returned with an error");
if (num_configs == 0)
__android_log_print(ANDROID_LOG_ERROR, _LogTag, "%s", "eglChooseConfig() returned 0 matching config");
// Get the first matching config
EGLConfig egl_config;
eglChooseConfig(_EglDisplay, egl_attributes, &egl_config, 1, &num_configs);
EGLint egl_format;
eglGetConfigAttrib(_EglDisplay, egl_config, EGL_NATIVE_VISUAL_ID, &egl_format);
ANativeWindow_setBuffersGeometry(app->window, 0, 0, egl_format);
const EGLint egl_context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE };
_EglContext = eglCreateContext(_EglDisplay, egl_config, EGL_NO_CONTEXT, egl_context_attributes);
if (_EglContext == EGL_NO_CONTEXT)
__android_log_print(ANDROID_LOG_ERROR, _LogTag, "%s", "eglCreateContext() returned EGL_NO_CONTEXT");
_EglSurface = eglCreateWindowSurface(_EglDisplay, egl_config, app->window, NULL);
eglMakeCurrent(_EglDisplay, _EglSurface, _EglSurface, _EglContext);
}
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
(void)io;
// Disable loading/saving of .ini file from disk.
// FIXME: Consider using LoadIniSettingsFromMemory() / SaveIniSettingsToMemory() to save in appropriate location for Android.
io.IniFilename = NULL;
// Setup Platform/Renderer backends
ImGui_ImplAndroid_Init(app->window);
ImGui_ImplOpenGL3_Init("#version 300 es");
return 0;
}
void beginFrame() {
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplAndroid_NewFrame();
ImGui::NewFrame();
}
void render(bool vsync) {
// Rendering
ImGui::Render();
auto dSize = ImGui::GetIO().DisplaySize;
glViewport(0, 0, dSize.x, dSize.y);
glClearColor(gui::themeManager.clearColor.x, gui::themeManager.clearColor.y, gui::themeManager.clearColor.z, gui::themeManager.clearColor.w);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
eglSwapBuffers(_EglDisplay, _EglSurface);
}
// No screen pos to detect
void getMouseScreenPos(double& x, double& y) { x = 0; y = 0; }
void setMouseScreenPos(double x, double y) {}
int renderLoop() {
spdlog::warn("BRUH: {0}", (void*)backend::app->window);
while (true) {
int out_events;
struct android_poll_source* out_data;
while (ALooper_pollAll(0, NULL, &out_events, (void**)&out_data) >= 0) {
// Process one event
if (out_data != NULL) { out_data->process(app, out_data); }
// Exit the app by returning from within the infinite loop
if (app->destroyRequested != 0) {
spdlog::warn("ASKED TO EXIT");
exited = true;
// Stop SDR
gui::mainWindow.setPlayState(false);
return 0;
}
}
if (_EglDisplay == EGL_NO_DISPLAY) { continue; }
if (!pauseRendering) {
// Initiate a new frame
ImGuiIO& io = ImGui::GetIO();
auto dsize = io.DisplaySize;
// Poll Unicode characters via JNI
// FIXME: do not call this every frame because of JNI overhead
PollUnicodeChars();
// Open on-screen (soft) input if requested by Dear ImGui
static bool WantTextInputLast = false;
if (io.WantTextInput && !WantTextInputLast)
ShowSoftKeyboardInput();
WantTextInputLast = io.WantTextInput;
// Render
beginFrame();
if (dsize.x > 0 && dsize.y > 0) {
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(dsize.x, dsize.y));
gui::mainWindow.draw();
}
render();
}
else {
std::this_thread::sleep_for(std::chrono::milliseconds(30));
}
}
return 0;
}
int end() {
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplAndroid_Shutdown();
ImGui::DestroyContext();
// Destroy all
if (_EglDisplay != EGL_NO_DISPLAY) {
eglMakeCurrent(_EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (_EglContext != EGL_NO_CONTEXT) { eglDestroyContext(_EglDisplay, _EglContext); }
if (_EglSurface != EGL_NO_SURFACE) { eglDestroySurface(_EglDisplay, _EglSurface); }
eglTerminate(_EglDisplay);
}
_EglDisplay = EGL_NO_DISPLAY;
_EglContext = EGL_NO_CONTEXT;
_EglSurface = EGL_NO_SURFACE;
if (app->window) { ANativeWindow_release(app->window); }
return 0;
}
int ShowSoftKeyboardInput() {
JavaVM* java_vm = app->activity->vm;
JNIEnv* java_env = NULL;
jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6);
if (jni_return == JNI_ERR)
return -1;
jni_return = java_vm->AttachCurrentThread(&java_env, NULL);
if (jni_return != JNI_OK)
return -2;
jclass native_activity_clazz = java_env->GetObjectClass(app->activity->clazz);
if (native_activity_clazz == NULL)
return -3;
jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "showSoftInput", "()V");
if (method_id == NULL)
return -4;
java_env->CallVoidMethod(app->activity->clazz, method_id);
jni_return = java_vm->DetachCurrentThread();
if (jni_return != JNI_OK)
return -5;
return 0;
}
int getDeviceFD(int& vid, int& pid, const std::vector<DevVIDPID>& allowedVidPids) {
JavaVM* java_vm = app->activity->vm;
JNIEnv* java_env = NULL;
jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6);
if (jni_return == JNI_ERR)
return -1;
jni_return = java_vm->AttachCurrentThread(&java_env, NULL);
if (jni_return != JNI_OK)
return -1;
jclass native_activity_clazz = java_env->GetObjectClass(app->activity->clazz);
if (native_activity_clazz == NULL)
return -1;
jfieldID fd_field_id = java_env->GetFieldID(native_activity_clazz, "SDR_FD", "I");
jfieldID vid_field_id = java_env->GetFieldID(native_activity_clazz, "SDR_VID", "I");
jfieldID pid_field_id = java_env->GetFieldID(native_activity_clazz, "SDR_PID", "I");
if (!vid_field_id || !vid_field_id || !pid_field_id)
return -1;
int fd = java_env->GetIntField(app->activity->clazz, fd_field_id);
vid = java_env->GetIntField(app->activity->clazz, vid_field_id);
pid = java_env->GetIntField(app->activity->clazz, pid_field_id);
jni_return = java_vm->DetachCurrentThread();
if (jni_return != JNI_OK)
return -1;
// If no vid/pid was given, just return successfully
if (allowedVidPids.empty()) {
return fd;
}
// Otherwise, check that the vid/pid combo is allowed
for (auto const& vp : allowedVidPids) {
if (vp.vid != vid || vp.pid != pid) { continue; }
return fd;
}
return -1;
}
// Unfortunately, the native KeyEvent implementation has no getUnicodeChar() function.
// Therefore, we implement the processing of KeyEvents in MainActivity.kt and poll
// the resulting Unicode characters here via JNI and send them to Dear ImGui.
int PollUnicodeChars() {
JavaVM* java_vm = app->activity->vm;
JNIEnv* java_env = NULL;
jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6);
if (jni_return == JNI_ERR)
return -1;
jni_return = java_vm->AttachCurrentThread(&java_env, NULL);
if (jni_return != JNI_OK)
return -2;
jclass native_activity_clazz = java_env->GetObjectClass(app->activity->clazz);
if (native_activity_clazz == NULL)
return -3;
jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "pollUnicodeChar", "()I");
if (method_id == NULL)
return -4;
// Send the actual characters to Dear ImGui
ImGuiIO& io = ImGui::GetIO();
jint unicode_character;
while ((unicode_character = java_env->CallIntMethod(app->activity->clazz, method_id)) != 0)
io.AddInputCharacter(unicode_character);
jni_return = java_vm->DetachCurrentThread();
if (jni_return != JNI_OK)
return -5;
return 0;
}
std::string getAppFilesDir() {
JavaVM* java_vm = app->activity->vm;
JNIEnv* java_env = NULL;
jint jni_return = java_vm->GetEnv((void**)&java_env, JNI_VERSION_1_6);
if (jni_return == JNI_ERR)
throw std::runtime_error("Could not get JNI environement");
jni_return = java_vm->AttachCurrentThread(&java_env, NULL);
if (jni_return != JNI_OK)
throw std::runtime_error("Could not attach to thread");
jclass native_activity_clazz = java_env->GetObjectClass(app->activity->clazz);
if (native_activity_clazz == NULL)
throw std::runtime_error("Could not get MainActivity class");
jmethodID method_id = java_env->GetMethodID(native_activity_clazz, "getAppDir", "()Ljava/lang/String;");
if (method_id == NULL)
throw std::runtime_error("Could not get methode ID");
jstring jstr = (jstring)java_env->CallObjectMethod(app->activity->clazz, method_id);
const char* _str = java_env->GetStringUTFChars(jstr, NULL);
std::string str(_str);
java_env->ReleaseStringUTFChars(jstr, _str);
jni_return = java_vm->DetachCurrentThread();
if (jni_return != JNI_OK)
throw std::runtime_error("Could not detach from thread");
return str;
}
const std::vector<DevVIDPID> AIRSPY_VIDPIDS = {
{ 0x1d50, 0x60a1 }
};
const std::vector<DevVIDPID> AIRSPYHF_VIDPIDS = {
{ 0x03EB, 0x800C }
};
const std::vector<DevVIDPID> HACKRF_VIDPIDS = {
{ 0x1d50, 0x604b },
{ 0x1d50, 0x6089 },
{ 0x1d50, 0xcc15 }
};
const std::vector<DevVIDPID> RTL_SDR_VIDPIDS = {
{ 0x0bda, 0x2832 },
{ 0x0bda, 0x2838 },
{ 0x0413, 0x6680 },
{ 0x0413, 0x6f0f },
{ 0x0458, 0x707f },
{ 0x0ccd, 0x00a9 },
{ 0x0ccd, 0x00b3 },
{ 0x0ccd, 0x00b4 },
{ 0x0ccd, 0x00b5 },
{ 0x0ccd, 0x00b7 },
{ 0x0ccd, 0x00b8 },
{ 0x0ccd, 0x00b9 },
{ 0x0ccd, 0x00c0 },
{ 0x0ccd, 0x00c6 },
{ 0x0ccd, 0x00d3 },
{ 0x0ccd, 0x00d7 },
{ 0x0ccd, 0x00e0 },
{ 0x1554, 0x5020 },
{ 0x15f4, 0x0131 },
{ 0x15f4, 0x0133 },
{ 0x185b, 0x0620 },
{ 0x185b, 0x0650 },
{ 0x185b, 0x0680 },
{ 0x1b80, 0xd393 },
{ 0x1b80, 0xd394 },
{ 0x1b80, 0xd395 },
{ 0x1b80, 0xd397 },
{ 0x1b80, 0xd398 },
{ 0x1b80, 0xd39d },
{ 0x1b80, 0xd3a4 },
{ 0x1b80, 0xd3a8 },
{ 0x1b80, 0xd3af },
{ 0x1b80, 0xd3b0 },
{ 0x1d19, 0x1101 },
{ 0x1d19, 0x1102 },
{ 0x1d19, 0x1103 },
{ 0x1d19, 0x1104 },
{ 0x1f4d, 0xa803 },
{ 0x1f4d, 0xb803 },
{ 0x1f4d, 0xc803 },
{ 0x1f4d, 0xd286 },
{ 0x1f4d, 0xd803 }
};
}
extern "C" {
void android_main(struct android_app* app) {
// Save app instance
app->onAppCmd = backend::handleAppCmd;
app->onInputEvent = backend::handleInputEvent;
backend::app = app;
// Check if this is the first time we run or not
if (backend::initialized) {
spdlog::warn("android_main called again");
backend::doPartialInit();
backend::pauseRendering = false;
backend::renderLoop();
return;
}
backend::initialized = true;
// prepare spdlog
auto console_sink = std::make_shared<spdlog::sinks::android_sink_st>("SDR++");
auto logger = std::shared_ptr<spdlog::logger>(new spdlog::logger("", { console_sink }));
spdlog::set_default_logger(logger);
// Grab files dir
std::string appdir = backend::getAppFilesDir();
// Call main
char* rootpath = new char[appdir.size() + 1];
strcpy(rootpath, appdir.c_str());
char* dummy[] = { "", "-r", rootpath };
sdrpp_main(3, dummy);
}
}

View File

@ -0,0 +1,278 @@
// dear imgui: Platform Binding for Android native app
// This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3)
// Implemented features:
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// Missing features:
// [ ] Platform: Clipboard support.
// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
// Important:
// - Consider using SDL or GLFW backend on Android, which will be more full-featured than this.
// - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446)
// - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446)
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
// 2021-03-04: Initial version.
#include "imgui.h"
#include "imgui_impl_android.h"
#include <time.h>
#include <android/native_window.h>
#include <android/input.h>
#include <android/keycodes.h>
#include <android/log.h>
// Android data
static double g_Time = 0.0;
static ANativeWindow* g_Window;
static char g_LogTag[] = "ImGuiExample";
static ImGuiKey ImGui_ImplAndroid_KeyCodeToImGuiKey(int32_t key_code)
{
switch (key_code)
{
case AKEYCODE_TAB: return ImGuiKey_Tab;
case AKEYCODE_DPAD_LEFT: return ImGuiKey_LeftArrow;
case AKEYCODE_DPAD_RIGHT: return ImGuiKey_RightArrow;
case AKEYCODE_DPAD_UP: return ImGuiKey_UpArrow;
case AKEYCODE_DPAD_DOWN: return ImGuiKey_DownArrow;
case AKEYCODE_PAGE_UP: return ImGuiKey_PageUp;
case AKEYCODE_PAGE_DOWN: return ImGuiKey_PageDown;
case AKEYCODE_MOVE_HOME: return ImGuiKey_Home;
case AKEYCODE_MOVE_END: return ImGuiKey_End;
case AKEYCODE_INSERT: return ImGuiKey_Insert;
case AKEYCODE_FORWARD_DEL: return ImGuiKey_Delete;
case AKEYCODE_DEL: return ImGuiKey_Backspace;
case AKEYCODE_SPACE: return ImGuiKey_Space;
case AKEYCODE_ENTER: return ImGuiKey_Enter;
case AKEYCODE_ESCAPE: return ImGuiKey_Escape;
case AKEYCODE_APOSTROPHE: return ImGuiKey_Apostrophe;
case AKEYCODE_COMMA: return ImGuiKey_Comma;
case AKEYCODE_MINUS: return ImGuiKey_Minus;
case AKEYCODE_PERIOD: return ImGuiKey_Period;
case AKEYCODE_SLASH: return ImGuiKey_Slash;
case AKEYCODE_SEMICOLON: return ImGuiKey_Semicolon;
case AKEYCODE_EQUALS: return ImGuiKey_Equal;
case AKEYCODE_LEFT_BRACKET: return ImGuiKey_LeftBracket;
case AKEYCODE_BACKSLASH: return ImGuiKey_Backslash;
case AKEYCODE_RIGHT_BRACKET: return ImGuiKey_RightBracket;
case AKEYCODE_GRAVE: return ImGuiKey_GraveAccent;
case AKEYCODE_CAPS_LOCK: return ImGuiKey_CapsLock;
case AKEYCODE_SCROLL_LOCK: return ImGuiKey_ScrollLock;
case AKEYCODE_NUM_LOCK: return ImGuiKey_NumLock;
case AKEYCODE_SYSRQ: return ImGuiKey_PrintScreen;
case AKEYCODE_BREAK: return ImGuiKey_Pause;
case AKEYCODE_NUMPAD_0: return ImGuiKey_Keypad0;
case AKEYCODE_NUMPAD_1: return ImGuiKey_Keypad1;
case AKEYCODE_NUMPAD_2: return ImGuiKey_Keypad2;
case AKEYCODE_NUMPAD_3: return ImGuiKey_Keypad3;
case AKEYCODE_NUMPAD_4: return ImGuiKey_Keypad4;
case AKEYCODE_NUMPAD_5: return ImGuiKey_Keypad5;
case AKEYCODE_NUMPAD_6: return ImGuiKey_Keypad6;
case AKEYCODE_NUMPAD_7: return ImGuiKey_Keypad7;
case AKEYCODE_NUMPAD_8: return ImGuiKey_Keypad8;
case AKEYCODE_NUMPAD_9: return ImGuiKey_Keypad9;
case AKEYCODE_NUMPAD_DOT: return ImGuiKey_KeypadDecimal;
case AKEYCODE_NUMPAD_DIVIDE: return ImGuiKey_KeypadDivide;
case AKEYCODE_NUMPAD_MULTIPLY: return ImGuiKey_KeypadMultiply;
case AKEYCODE_NUMPAD_SUBTRACT: return ImGuiKey_KeypadSubtract;
case AKEYCODE_NUMPAD_ADD: return ImGuiKey_KeypadAdd;
case AKEYCODE_NUMPAD_ENTER: return ImGuiKey_KeypadEnter;
case AKEYCODE_NUMPAD_EQUALS: return ImGuiKey_KeypadEqual;
case AKEYCODE_CTRL_LEFT: return ImGuiKey_LeftCtrl;
case AKEYCODE_SHIFT_LEFT: return ImGuiKey_LeftShift;
case AKEYCODE_ALT_LEFT: return ImGuiKey_LeftAlt;
case AKEYCODE_META_LEFT: return ImGuiKey_LeftSuper;
case AKEYCODE_CTRL_RIGHT: return ImGuiKey_RightCtrl;
case AKEYCODE_SHIFT_RIGHT: return ImGuiKey_RightShift;
case AKEYCODE_ALT_RIGHT: return ImGuiKey_RightAlt;
case AKEYCODE_META_RIGHT: return ImGuiKey_RightSuper;
case AKEYCODE_MENU: return ImGuiKey_Menu;
case AKEYCODE_0: return ImGuiKey_0;
case AKEYCODE_1: return ImGuiKey_1;
case AKEYCODE_2: return ImGuiKey_2;
case AKEYCODE_3: return ImGuiKey_3;
case AKEYCODE_4: return ImGuiKey_4;
case AKEYCODE_5: return ImGuiKey_5;
case AKEYCODE_6: return ImGuiKey_6;
case AKEYCODE_7: return ImGuiKey_7;
case AKEYCODE_8: return ImGuiKey_8;
case AKEYCODE_9: return ImGuiKey_9;
case AKEYCODE_A: return ImGuiKey_A;
case AKEYCODE_B: return ImGuiKey_B;
case AKEYCODE_C: return ImGuiKey_C;
case AKEYCODE_D: return ImGuiKey_D;
case AKEYCODE_E: return ImGuiKey_E;
case AKEYCODE_F: return ImGuiKey_F;
case AKEYCODE_G: return ImGuiKey_G;
case AKEYCODE_H: return ImGuiKey_H;
case AKEYCODE_I: return ImGuiKey_I;
case AKEYCODE_J: return ImGuiKey_J;
case AKEYCODE_K: return ImGuiKey_K;
case AKEYCODE_L: return ImGuiKey_L;
case AKEYCODE_M: return ImGuiKey_M;
case AKEYCODE_N: return ImGuiKey_N;
case AKEYCODE_O: return ImGuiKey_O;
case AKEYCODE_P: return ImGuiKey_P;
case AKEYCODE_Q: return ImGuiKey_Q;
case AKEYCODE_R: return ImGuiKey_R;
case AKEYCODE_S: return ImGuiKey_S;
case AKEYCODE_T: return ImGuiKey_T;
case AKEYCODE_U: return ImGuiKey_U;
case AKEYCODE_V: return ImGuiKey_V;
case AKEYCODE_W: return ImGuiKey_W;
case AKEYCODE_X: return ImGuiKey_X;
case AKEYCODE_Y: return ImGuiKey_Y;
case AKEYCODE_Z: return ImGuiKey_Z;
case AKEYCODE_F1: return ImGuiKey_F1;
case AKEYCODE_F2: return ImGuiKey_F2;
case AKEYCODE_F3: return ImGuiKey_F3;
case AKEYCODE_F4: return ImGuiKey_F4;
case AKEYCODE_F5: return ImGuiKey_F5;
case AKEYCODE_F6: return ImGuiKey_F6;
case AKEYCODE_F7: return ImGuiKey_F7;
case AKEYCODE_F8: return ImGuiKey_F8;
case AKEYCODE_F9: return ImGuiKey_F9;
case AKEYCODE_F10: return ImGuiKey_F10;
case AKEYCODE_F11: return ImGuiKey_F11;
case AKEYCODE_F12: return ImGuiKey_F12;
default: return ImGuiKey_None;
}
}
int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event)
{
ImGuiIO& io = ImGui::GetIO();
int32_t event_type = AInputEvent_getType(input_event);
switch (event_type)
{
case AINPUT_EVENT_TYPE_KEY:
{
int32_t event_key_code = AKeyEvent_getKeyCode(input_event);
int32_t event_scan_code = AKeyEvent_getScanCode(input_event);
int32_t event_action = AKeyEvent_getAction(input_event);
int32_t event_meta_state = AKeyEvent_getMetaState(input_event);
io.AddKeyEvent(ImGuiKey_ModCtrl, (event_meta_state & AMETA_CTRL_ON) != 0);
io.AddKeyEvent(ImGuiKey_ModShift, (event_meta_state & AMETA_SHIFT_ON) != 0);
io.AddKeyEvent(ImGuiKey_ModAlt, (event_meta_state & AMETA_ALT_ON) != 0);
io.AddKeyEvent(ImGuiKey_ModSuper, (event_meta_state & AMETA_META_ON) != 0);
switch (event_action)
{
// FIXME: AKEY_EVENT_ACTION_DOWN and AKEY_EVENT_ACTION_UP occur at once as soon as a touch pointer
// goes up from a key. We use a simple key event queue/ and process one event per key per frame in
// ImGui_ImplAndroid_NewFrame()...or consider using IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787
case AKEY_EVENT_ACTION_DOWN:
case AKEY_EVENT_ACTION_UP:
{
ImGuiKey key = ImGui_ImplAndroid_KeyCodeToImGuiKey(event_key_code);
if (key != ImGuiKey_None && (event_action == AKEY_EVENT_ACTION_DOWN || event_action == AKEY_EVENT_ACTION_UP))
{
io.AddKeyEvent(key, event_action == AKEY_EVENT_ACTION_DOWN);
io.SetKeyEventNativeData(key, event_key_code, event_scan_code);
}
break;
}
default:
break;
}
break;
}
case AINPUT_EVENT_TYPE_MOTION:
{
int32_t event_action = AMotionEvent_getAction(input_event);
int32_t event_pointer_index = (event_action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
event_action &= AMOTION_EVENT_ACTION_MASK;
switch (event_action)
{
case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_UP:
// Physical mouse buttons (and probably other physical devices) also invoke the actions AMOTION_EVENT_ACTION_DOWN/_UP,
// but we have to process them separately to identify the actual button pressed. This is done below via
// AMOTION_EVENT_ACTION_BUTTON_PRESS/_RELEASE. Here, we only process "FINGER" input (and "UNKNOWN", as a fallback).
if((AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER)
|| (AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_STYLUS)
|| (AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_MOUSE)
|| (AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN))
{
io.AddMousePosEvent(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index));
io.AddMouseButtonEvent(0, event_action == AMOTION_EVENT_ACTION_DOWN);
}
break;
case AMOTION_EVENT_ACTION_BUTTON_PRESS:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
{
int32_t button_state = AMotionEvent_getButtonState(input_event);
io.AddMouseButtonEvent(0, (button_state & AMOTION_EVENT_BUTTON_PRIMARY) != 0);
io.AddMouseButtonEvent(1, (button_state & AMOTION_EVENT_BUTTON_SECONDARY) != 0);
io.AddMouseButtonEvent(2, (button_state & AMOTION_EVENT_BUTTON_TERTIARY) != 0);
}
break;
case AMOTION_EVENT_ACTION_HOVER_MOVE: // Hovering: Tool moves while NOT pressed (such as a physical mouse)
case AMOTION_EVENT_ACTION_MOVE: // Touch pointer moves while DOWN
io.AddMousePosEvent(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index));
break;
case AMOTION_EVENT_ACTION_SCROLL:
io.AddMouseWheelEvent(AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index), AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index));
break;
default:
break;
}
}
return 1;
default:
break;
}
return 0;
}
bool ImGui_ImplAndroid_Init(ANativeWindow* window)
{
g_Window = window;
g_Time = 0.0;
// Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendPlatformName = "imgui_impl_android";
return true;
}
void ImGui_ImplAndroid_Shutdown()
{
}
void ImGui_ImplAndroid_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
// Setup display size (every frame to accommodate for window resizing)
int32_t window_width = ANativeWindow_getWidth(g_Window);
int32_t window_height = ANativeWindow_getHeight(g_Window);
int display_width = window_width;
int display_height = window_height;
io.DisplaySize = ImVec2((float)window_width, (float)window_height);
if (window_width > 0 && window_height > 0)
io.DisplayFramebufferScale = ImVec2((float)display_width / window_width, (float)display_height / window_height);
// Setup time step
struct timespec current_timespec;
clock_gettime(CLOCK_MONOTONIC, &current_timespec);
double current_time = (double)(current_timespec.tv_sec) + (current_timespec.tv_nsec / 1000000000.0);
io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f);
g_Time = current_time;
}

View File

@ -0,0 +1,28 @@
// dear imgui: Platform Binding for Android native app
// This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3)
// Implemented features:
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// Missing features:
// [ ] Platform: Clipboard support.
// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
// Important:
// - Consider using SDL or GLFW backend on Android, which will be more full-featured than this.
// - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446)
// - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446)
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#pragma once
struct ANativeWindow;
struct AInputEvent;
IMGUI_IMPL_API bool ImGui_ImplAndroid_Init(ANativeWindow* window);
IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event);
IMGUI_IMPL_API void ImGui_ImplAndroid_Shutdown();
IMGUI_IMPL_API void ImGui_ImplAndroid_NewFrame();

View File

@ -0,0 +1,305 @@
#include <backend.h>
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <GLFW/glfw3.h>
#include <spdlog/spdlog.h>
#include <utils/opengl_include_code.h>
#include <version.h>
#include <core.h>
#include <filesystem>
#include <stb_image.h>
#include <stb_image_resize.h>
#include <gui/gui.h>
namespace backend {
const char* OPENGL_VERSIONS_GLSL[] = {
"#version 120",
"#version 300 es",
"#version 120"
};
const int OPENGL_VERSIONS_MAJOR[] = {
3,
3,
2
};
const int OPENGL_VERSIONS_MINOR[] = {
0,
1,
1
};
const bool OPENGL_VERSIONS_IS_ES[] = {
false,
true,
false
};
#define OPENGL_VERSION_COUNT (sizeof(OPENGL_VERSIONS_GLSL) / sizeof(char*))
bool maximized = false;
bool fullScreen = false;
int winHeight;
int winWidth;
bool _maximized = maximized;
int fsWidth, fsHeight, fsPosX, fsPosY;
int _winWidth, _winHeight;
GLFWwindow* window;
GLFWmonitor* monitor;
static void glfw_error_callback(int error, const char* description) {
spdlog::error("Glfw Error {0}: {1}", error, description);
}
static void maximized_callback(GLFWwindow* window, int n) {
if (n == GLFW_TRUE) {
maximized = true;
}
else {
maximized = false;
}
}
int init(std::string resDir) {
// Load config
core::configManager.acquire();
winWidth = core::configManager.conf["windowSize"]["w"];
winHeight = core::configManager.conf["windowSize"]["h"];
maximized = core::configManager.conf["maximized"];
fullScreen = core::configManager.conf["fullscreen"];
core::configManager.release();
// Setup window
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit()) {
return 1;
}
#ifdef __APPLE__
// GL 3.2 + GLSL 150
const char* glsl_version = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
// Create window with graphics context
monitor = glfwGetPrimaryMonitor();
window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);
if (window == NULL)
return 1;
glfwMakeContextCurrent(window);
#else
const char* glsl_version = "#version 120";
monitor = NULL;
for (int i = 0; i < OPENGL_VERSION_COUNT; i++) {
glsl_version = OPENGL_VERSIONS_GLSL[i];
glfwWindowHint(GLFW_CLIENT_API, OPENGL_VERSIONS_IS_ES[i] ? GLFW_OPENGL_ES_API : GLFW_OPENGL_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_VERSIONS_MAJOR[i]);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_VERSIONS_MINOR[i]);
// Create window with graphics context
monitor = glfwGetPrimaryMonitor();
window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);
if (window == NULL) {
spdlog::info("OpenGL {0}.{1} {2}was not supported", OPENGL_VERSIONS_MAJOR[i], OPENGL_VERSIONS_MINOR[i], OPENGL_VERSIONS_IS_ES[i] ? "ES " : "");
continue;
}
spdlog::info("Using OpenGL {0}.{1}{2}", OPENGL_VERSIONS_MAJOR[i], OPENGL_VERSIONS_MINOR[i], OPENGL_VERSIONS_IS_ES[i] ? " ES" : "");
glfwMakeContextCurrent(window);
break;
}
#endif
// Add callback for max/min if GLFW supports it
#if (GLFW_VERSION_MAJOR == 3) && (GLFW_VERSION_MINOR >= 3)
if (maximized) {
glfwMaximizeWindow(window);
}
glfwSetWindowMaximizeCallback(window, maximized_callback);
#endif
// Load app icon
if (!std::filesystem::is_regular_file(resDir + "/icons/sdrpp.png")) {
spdlog::error("Icon file '{0}' doesn't exist!", resDir + "/icons/sdrpp.png");
return 1;
}
GLFWimage icons[10];
icons[0].pixels = stbi_load(((std::string)(resDir + "/icons/sdrpp.png")).c_str(), &icons[0].width, &icons[0].height, 0, 4);
icons[1].pixels = (unsigned char*)malloc(16 * 16 * 4);
icons[1].width = icons[1].height = 16;
icons[2].pixels = (unsigned char*)malloc(24 * 24 * 4);
icons[2].width = icons[2].height = 24;
icons[3].pixels = (unsigned char*)malloc(32 * 32 * 4);
icons[3].width = icons[3].height = 32;
icons[4].pixels = (unsigned char*)malloc(48 * 48 * 4);
icons[4].width = icons[4].height = 48;
icons[5].pixels = (unsigned char*)malloc(64 * 64 * 4);
icons[5].width = icons[5].height = 64;
icons[6].pixels = (unsigned char*)malloc(96 * 96 * 4);
icons[6].width = icons[6].height = 96;
icons[7].pixels = (unsigned char*)malloc(128 * 128 * 4);
icons[7].width = icons[7].height = 128;
icons[8].pixels = (unsigned char*)malloc(196 * 196 * 4);
icons[8].width = icons[8].height = 196;
icons[9].pixels = (unsigned char*)malloc(256 * 256 * 4);
icons[9].width = icons[9].height = 256;
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[1].pixels, 16, 16, 16 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[2].pixels, 24, 24, 24 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[3].pixels, 32, 32, 32 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[4].pixels, 48, 48, 48 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[5].pixels, 64, 64, 64 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[6].pixels, 96, 96, 96 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[7].pixels, 128, 128, 128 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[8].pixels, 196, 196, 196 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[9].pixels, 256, 256, 256 * 4, 4);
glfwSetWindowIcon(window, 10, icons);
stbi_image_free(icons[0].pixels);
for (int i = 1; i < 10; i++) {
free(icons[i].pixels);
}
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
(void)io;
io.IniFilename = NULL;
// Setup Platform/Renderer bindings
ImGui_ImplGlfw_InitForOpenGL(window, true);
if (!ImGui_ImplOpenGL3_Init(glsl_version)) {
// If init fail, try to fall back on GLSL 1.2
spdlog::warn("Could not init using OpenGL with normal GLSL version, falling back to GLSL 1.2");
if (!ImGui_ImplOpenGL3_Init("#version 120")) {
spdlog::error("Failed to initialize OpenGL with GLSL 1.2");
return -1;
}
}
// Set window size and fullscreen state
glfwGetWindowSize(window, &_winWidth, &_winHeight);
if (fullScreen) {
spdlog::info("Fullscreen: ON");
fsWidth = _winWidth;
fsHeight = _winHeight;
glfwGetWindowPos(window, &fsPosX, &fsPosY);
const GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, 0);
}
// Everything went according to plan
return 0;
}
void beginFrame() {
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
}
void render(bool vsync) {
// Rendering
ImGui::Render();
int display_w, display_h;
glfwGetFramebufferSize(window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClearColor(gui::themeManager.clearColor.x, gui::themeManager.clearColor.y, gui::themeManager.clearColor.z, gui::themeManager.clearColor.w);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapInterval(vsync);
glfwSwapBuffers(window);
}
void getMouseScreenPos(double& x, double& y) {
glfwGetCursorPos(window, &x, &y);
}
void setMouseScreenPos(double x, double y) {
// Tell GLFW to move the cursor and then manually fire the event
glfwSetCursorPos(window, x, y);
ImGui_ImplGlfw_CursorPosCallback(window, x, y);
}
int renderLoop() {
// Main loop
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
beginFrame();
if (_maximized != maximized) {
_maximized = maximized;
core::configManager.acquire();
core::configManager.conf["maximized"] = _maximized;
if (!maximized) {
glfwSetWindowSize(window, core::configManager.conf["windowSize"]["w"], core::configManager.conf["windowSize"]["h"]);
}
core::configManager.release(true);
}
glfwGetWindowSize(window, &_winWidth, &_winHeight);
if (ImGui::IsKeyPressed(GLFW_KEY_F11)) {
fullScreen = !fullScreen;
if (fullScreen) {
spdlog::info("Fullscreen: ON");
fsWidth = _winWidth;
fsHeight = _winHeight;
glfwGetWindowPos(window, &fsPosX, &fsPosY);
const GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, 0);
core::configManager.acquire();
core::configManager.conf["fullscreen"] = true;
core::configManager.release();
}
else {
spdlog::info("Fullscreen: OFF");
glfwSetWindowMonitor(window, nullptr, fsPosX, fsPosY, fsWidth, fsHeight, 0);
core::configManager.acquire();
core::configManager.conf["fullscreen"] = false;
core::configManager.release();
}
}
if ((_winWidth != winWidth || _winHeight != winHeight) && !maximized && _winWidth > 0 && _winHeight > 0) {
winWidth = _winWidth;
winHeight = _winHeight;
core::configManager.acquire();
core::configManager.conf["windowSize"]["w"] = winWidth;
core::configManager.conf["windowSize"]["h"] = winHeight;
core::configManager.release(true);
}
if (winWidth > 0 && winHeight > 0) {
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(_winWidth, _winHeight));
gui::mainWindow.draw();
}
render();
}
return 0;
}
int end() {
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(window);
glfwTerminate();
return 0; // TODO: Int really needed?
}
}

View File

@ -0,0 +1,643 @@
// dear imgui: Platform Backend for GLFW
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
// (Requires: GLFW 3.1+)
// Implemented features:
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-02-07: Added ImGui_ImplGlfw_InstallCallbacks()/ImGui_ImplGlfw_RestoreCallbacks() helpers to facilitate user installing callbacks after iniitializing backend.
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
// 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates.
// 2022-01-12: *BREAKING CHANGE*: Now using glfwSetCursorPosCallback(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetCursorPosCallback() and forward it to the backend via ImGui_ImplGlfw_CursorPosCallback().
// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
// 2022-01-05: Inputs: Converting GLFW untranslated keycodes back to translated keycodes (in the ImGui_ImplGlfw_KeyCallback() function) in order to match the behavior of every other backend, and facilitate the use of GLFW with lettered-shortcuts API.
// 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback().
// 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback().
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors.
// 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor).
// 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown.
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
// 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter().
// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them.
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples.
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()).
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set.
// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
#include "imgui.h"
#include "imgui_impl_glfw.h"
// Clang warnings with -Weverything
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
#if __has_warning("-Wzero-as-null-pointer-constant")
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif
#endif
// GLFW
#include <GLFW/glfw3.h>
#ifdef _WIN32
#undef APIENTRY
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3native.h> // for glfwGetWin32Window
#endif
#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released?
#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR
#else
#define GLFW_HAS_NEW_CURSORS (0)
#endif
#define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetGamepadState() new api
#define GLFW_HAS_GET_KEY_NAME (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwGetKeyName()
// GLFW data
enum GlfwClientApi
{
GlfwClientApi_Unknown,
GlfwClientApi_OpenGL,
GlfwClientApi_Vulkan
};
struct ImGui_ImplGlfw_Data
{
GLFWwindow* Window;
GlfwClientApi ClientApi;
double Time;
GLFWwindow* MouseWindow;
GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT];
ImVec2 LastValidMousePos;
bool InstalledCallbacks;
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
GLFWwindowfocusfun PrevUserCallbackWindowFocus;
GLFWcursorposfun PrevUserCallbackCursorPos;
GLFWcursorenterfun PrevUserCallbackCursorEnter;
GLFWmousebuttonfun PrevUserCallbackMousebutton;
GLFWscrollfun PrevUserCallbackScroll;
GLFWkeyfun PrevUserCallbackKey;
GLFWcharfun PrevUserCallbackChar;
GLFWmonitorfun PrevUserCallbackMonitor;
ImGui_ImplGlfw_Data() { memset(this, 0, sizeof(*this)); }
};
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
// - Because glfwPollEvents() process all windows and some events may be called outside of it, you will need to register your own callbacks
// (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks.
// - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it.
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : NULL;
}
// Functions
static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data)
{
return glfwGetClipboardString((GLFWwindow*)user_data);
}
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_LEFT: return ImGuiKey_LeftArrow;
case GLFW_KEY_RIGHT: return ImGuiKey_RightArrow;
case GLFW_KEY_UP: return ImGuiKey_UpArrow;
case GLFW_KEY_DOWN: return ImGuiKey_DownArrow;
case GLFW_KEY_PAGE_UP: return ImGuiKey_PageUp;
case GLFW_KEY_PAGE_DOWN: return ImGuiKey_PageDown;
case GLFW_KEY_HOME: return ImGuiKey_Home;
case GLFW_KEY_END: return ImGuiKey_End;
case GLFW_KEY_INSERT: return ImGuiKey_Insert;
case GLFW_KEY_DELETE: return ImGuiKey_Delete;
case GLFW_KEY_BACKSPACE: return ImGuiKey_Backspace;
case GLFW_KEY_SPACE: return ImGuiKey_Space;
case GLFW_KEY_ENTER: return ImGuiKey_Enter;
case GLFW_KEY_ESCAPE: return ImGuiKey_Escape;
case GLFW_KEY_APOSTROPHE: return ImGuiKey_Apostrophe;
case GLFW_KEY_COMMA: return ImGuiKey_Comma;
case GLFW_KEY_MINUS: return ImGuiKey_Minus;
case GLFW_KEY_PERIOD: return ImGuiKey_Period;
case GLFW_KEY_SLASH: return ImGuiKey_Slash;
case GLFW_KEY_SEMICOLON: return ImGuiKey_Semicolon;
case GLFW_KEY_EQUAL: return ImGuiKey_Equal;
case GLFW_KEY_LEFT_BRACKET: return ImGuiKey_LeftBracket;
case GLFW_KEY_BACKSLASH: return ImGuiKey_Backslash;
case GLFW_KEY_RIGHT_BRACKET: return ImGuiKey_RightBracket;
case GLFW_KEY_GRAVE_ACCENT: return ImGuiKey_GraveAccent;
case GLFW_KEY_CAPS_LOCK: return ImGuiKey_CapsLock;
case GLFW_KEY_SCROLL_LOCK: return ImGuiKey_ScrollLock;
case GLFW_KEY_NUM_LOCK: return ImGuiKey_NumLock;
case GLFW_KEY_PRINT_SCREEN: return ImGuiKey_PrintScreen;
case GLFW_KEY_PAUSE: return ImGuiKey_Pause;
case GLFW_KEY_KP_0: return ImGuiKey_Keypad0;
case GLFW_KEY_KP_1: return ImGuiKey_Keypad1;
case GLFW_KEY_KP_2: return ImGuiKey_Keypad2;
case GLFW_KEY_KP_3: return ImGuiKey_Keypad3;
case GLFW_KEY_KP_4: return ImGuiKey_Keypad4;
case GLFW_KEY_KP_5: return ImGuiKey_Keypad5;
case GLFW_KEY_KP_6: return ImGuiKey_Keypad6;
case GLFW_KEY_KP_7: return ImGuiKey_Keypad7;
case GLFW_KEY_KP_8: return ImGuiKey_Keypad8;
case GLFW_KEY_KP_9: return ImGuiKey_Keypad9;
case GLFW_KEY_KP_DECIMAL: return ImGuiKey_KeypadDecimal;
case GLFW_KEY_KP_DIVIDE: return ImGuiKey_KeypadDivide;
case GLFW_KEY_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
case GLFW_KEY_KP_SUBTRACT: return ImGuiKey_KeypadSubtract;
case GLFW_KEY_KP_ADD: return ImGuiKey_KeypadAdd;
case GLFW_KEY_KP_ENTER: return ImGuiKey_KeypadEnter;
case GLFW_KEY_KP_EQUAL: return ImGuiKey_KeypadEqual;
case GLFW_KEY_LEFT_SHIFT: return ImGuiKey_LeftShift;
case GLFW_KEY_LEFT_CONTROL: return ImGuiKey_LeftCtrl;
case GLFW_KEY_LEFT_ALT: return ImGuiKey_LeftAlt;
case GLFW_KEY_LEFT_SUPER: return ImGuiKey_LeftSuper;
case GLFW_KEY_RIGHT_SHIFT: return ImGuiKey_RightShift;
case GLFW_KEY_RIGHT_CONTROL: return ImGuiKey_RightCtrl;
case GLFW_KEY_RIGHT_ALT: return ImGuiKey_RightAlt;
case GLFW_KEY_RIGHT_SUPER: return ImGuiKey_RightSuper;
case GLFW_KEY_MENU: return ImGuiKey_Menu;
case GLFW_KEY_0: return ImGuiKey_0;
case GLFW_KEY_1: return ImGuiKey_1;
case GLFW_KEY_2: return ImGuiKey_2;
case GLFW_KEY_3: return ImGuiKey_3;
case GLFW_KEY_4: return ImGuiKey_4;
case GLFW_KEY_5: return ImGuiKey_5;
case GLFW_KEY_6: return ImGuiKey_6;
case GLFW_KEY_7: return ImGuiKey_7;
case GLFW_KEY_8: return ImGuiKey_8;
case GLFW_KEY_9: return ImGuiKey_9;
case GLFW_KEY_A: return ImGuiKey_A;
case GLFW_KEY_B: return ImGuiKey_B;
case GLFW_KEY_C: return ImGuiKey_C;
case GLFW_KEY_D: return ImGuiKey_D;
case GLFW_KEY_E: return ImGuiKey_E;
case GLFW_KEY_F: return ImGuiKey_F;
case GLFW_KEY_G: return ImGuiKey_G;
case GLFW_KEY_H: return ImGuiKey_H;
case GLFW_KEY_I: return ImGuiKey_I;
case GLFW_KEY_J: return ImGuiKey_J;
case GLFW_KEY_K: return ImGuiKey_K;
case GLFW_KEY_L: return ImGuiKey_L;
case GLFW_KEY_M: return ImGuiKey_M;
case GLFW_KEY_N: return ImGuiKey_N;
case GLFW_KEY_O: return ImGuiKey_O;
case GLFW_KEY_P: return ImGuiKey_P;
case GLFW_KEY_Q: return ImGuiKey_Q;
case GLFW_KEY_R: return ImGuiKey_R;
case GLFW_KEY_S: return ImGuiKey_S;
case GLFW_KEY_T: return ImGuiKey_T;
case GLFW_KEY_U: return ImGuiKey_U;
case GLFW_KEY_V: return ImGuiKey_V;
case GLFW_KEY_W: return ImGuiKey_W;
case GLFW_KEY_X: return ImGuiKey_X;
case GLFW_KEY_Y: return ImGuiKey_Y;
case GLFW_KEY_Z: return ImGuiKey_Z;
case GLFW_KEY_F1: return ImGuiKey_F1;
case GLFW_KEY_F2: return ImGuiKey_F2;
case GLFW_KEY_F3: return ImGuiKey_F3;
case GLFW_KEY_F4: return ImGuiKey_F4;
case GLFW_KEY_F5: return ImGuiKey_F5;
case GLFW_KEY_F6: return ImGuiKey_F6;
case GLFW_KEY_F7: return ImGuiKey_F7;
case GLFW_KEY_F8: return ImGuiKey_F8;
case GLFW_KEY_F9: return ImGuiKey_F9;
case GLFW_KEY_F10: return ImGuiKey_F10;
case GLFW_KEY_F11: return ImGuiKey_F11;
case GLFW_KEY_F12: return ImGuiKey_F12;
default: return ImGuiKey_None;
}
}
static void ImGui_ImplGlfw_UpdateKeyModifiers(int mods)
{
ImGuiIO& io = ImGui::GetIO();
io.AddKeyEvent(ImGuiKey_ModCtrl, (mods & GLFW_MOD_CONTROL) != 0);
io.AddKeyEvent(ImGuiKey_ModShift, (mods & GLFW_MOD_SHIFT) != 0);
io.AddKeyEvent(ImGuiKey_ModAlt, (mods & GLFW_MOD_ALT) != 0);
io.AddKeyEvent(ImGuiKey_ModSuper, (mods & GLFW_MOD_SUPER) != 0);
}
void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackMousebutton != NULL && window == bd->Window)
bd->PrevUserCallbackMousebutton(window, button, action, mods);
ImGui_ImplGlfw_UpdateKeyModifiers(mods);
ImGuiIO& io = ImGui::GetIO();
if (button >= 0 && button < ImGuiMouseButton_COUNT)
io.AddMouseButtonEvent(button, action == GLFW_PRESS);
}
void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackScroll != NULL && window == bd->Window)
bd->PrevUserCallbackScroll(window, xoffset, yoffset);
ImGuiIO& io = ImGui::GetIO();
io.AddMouseWheelEvent((float)xoffset, (float)yoffset);
}
static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode)
{
#if GLFW_HAS_GET_KEY_NAME && !defined(__EMSCRIPTEN__)
// GLFW 3.1+ attempts to "untranslate" keys, which goes the opposite of what every other framework does, making using lettered shortcuts difficult.
// (It had reasons to do so: namely GLFW is/was more likely to be used for WASD-type game controls rather than lettered shortcuts, but IHMO the 3.1 change could have been done differently)
// See https://github.com/glfw/glfw/issues/1502 for details.
// Adding a workaround to undo this (so our keys are translated->untranslated->translated, likely a lossy process).
// This won't cover edge cases but this is at least going to cover common cases.
if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_EQUAL)
return key;
const char* key_name = glfwGetKeyName(key, scancode);
if (key_name && key_name[0] != 0 && key_name[1] == 0)
{
const char char_names[] = "`-=[]\\,;\'./";
const int char_keys[] = { GLFW_KEY_GRAVE_ACCENT, GLFW_KEY_MINUS, GLFW_KEY_EQUAL, GLFW_KEY_LEFT_BRACKET, GLFW_KEY_RIGHT_BRACKET, GLFW_KEY_BACKSLASH, GLFW_KEY_COMMA, GLFW_KEY_SEMICOLON, GLFW_KEY_APOSTROPHE, GLFW_KEY_PERIOD, GLFW_KEY_SLASH, 0 };
IM_ASSERT(IM_ARRAYSIZE(char_names) == IM_ARRAYSIZE(char_keys));
if (key_name[0] >= '0' && key_name[0] <= '9') { key = GLFW_KEY_0 + (key_name[0] - '0'); }
else if (key_name[0] >= 'A' && key_name[0] <= 'Z') { key = GLFW_KEY_A + (key_name[0] - 'A'); }
else if (const char* p = strchr(char_names, key_name[0])) { key = char_keys[p - char_names]; }
}
// if (action == GLFW_PRESS) printf("key %d scancode %d name '%s'\n", key, scancode, key_name);
#else
IM_UNUSED(scancode);
#endif
return key;
}
void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackKey != NULL && window == bd->Window)
bd->PrevUserCallbackKey(window, keycode, scancode, action, mods);
if (action != GLFW_PRESS && action != GLFW_RELEASE)
return;
ImGui_ImplGlfw_UpdateKeyModifiers(mods);
keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode);
ImGuiIO& io = ImGui::GetIO();
ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode);
io.AddKeyEvent(imgui_key, (action == GLFW_PRESS));
io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code)
}
void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackWindowFocus != NULL && window == bd->Window)
bd->PrevUserCallbackWindowFocus(window, focused);
ImGuiIO& io = ImGui::GetIO();
io.AddFocusEvent(focused != 0);
}
void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackCursorPos != NULL && window == bd->Window)
bd->PrevUserCallbackCursorPos(window, x, y);
ImGuiIO& io = ImGui::GetIO();
io.AddMousePosEvent((float)x, (float)y);
bd->LastValidMousePos = ImVec2((float)x, (float)y);
}
// Workaround: X11 seems to send spurious Leave/Enter events which would make us lose our position,
// so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984)
void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackCursorEnter != NULL && window == bd->Window)
bd->PrevUserCallbackCursorEnter(window, entered);
ImGuiIO& io = ImGui::GetIO();
if (entered)
{
bd->MouseWindow = window;
io.AddMousePosEvent(bd->LastValidMousePos.x, bd->LastValidMousePos.y);
}
else if (!entered && bd->MouseWindow == window)
{
bd->LastValidMousePos = io.MousePos;
bd->MouseWindow = NULL;
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
}
}
void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackChar != NULL && window == bd->Window)
bd->PrevUserCallbackChar(window, c);
ImGuiIO& io = ImGui::GetIO();
io.AddInputCharacter(c);
}
void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int)
{
// Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too.
}
void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!");
IM_ASSERT(bd->Window == window);
bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback);
bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback);
bd->PrevUserCallbackCursorPos = glfwSetCursorPosCallback(window, ImGui_ImplGlfw_CursorPosCallback);
bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback);
bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback);
bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback);
bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback);
bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback);
bd->InstalledCallbacks = true;
}
void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!");
IM_ASSERT(bd->Window == window);
glfwSetWindowFocusCallback(window, bd->PrevUserCallbackWindowFocus);
glfwSetCursorEnterCallback(window, bd->PrevUserCallbackCursorEnter);
glfwSetCursorPosCallback(window, bd->PrevUserCallbackCursorPos);
glfwSetMouseButtonCallback(window, bd->PrevUserCallbackMousebutton);
glfwSetScrollCallback(window, bd->PrevUserCallbackScroll);
glfwSetKeyCallback(window, bd->PrevUserCallbackKey);
glfwSetCharCallback(window, bd->PrevUserCallbackChar);
glfwSetMonitorCallback(bd->PrevUserCallbackMonitor);
bd->InstalledCallbacks = false;
bd->PrevUserCallbackWindowFocus = NULL;
bd->PrevUserCallbackCursorEnter = NULL;
bd->PrevUserCallbackCursorPos = NULL;
bd->PrevUserCallbackMousebutton = NULL;
bd->PrevUserCallbackScroll = NULL;
bd->PrevUserCallbackKey = NULL;
bd->PrevUserCallbackChar = NULL;
bd->PrevUserCallbackMonitor = NULL;
}
static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!");
// Setup backend capabilities flags
ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)();
io.BackendPlatformUserData = (void*)bd;
io.BackendPlatformName = "imgui_impl_glfw";
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
bd->Window = window;
bd->Time = 0.0;
io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
io.ClipboardUserData = bd->Window;
// Set platform dependent data in viewport
#if defined(_WIN32)
ImGui::GetMainViewport()->PlatformHandleRaw = (void*)glfwGetWin32Window(bd->Window);
#endif
// Create mouse cursors
// (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist,
// GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting.
// Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.)
GLFWerrorfun prev_error_callback = glfwSetErrorCallback(NULL);
bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
#if GLFW_HAS_NEW_CURSORS
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR);
#else
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
#endif
glfwSetErrorCallback(prev_error_callback);
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
if (install_callbacks)
ImGui_ImplGlfw_InstallCallbacks(window);
bd->ClientApi = client_api;
return true;
}
bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks)
{
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL);
}
bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks)
{
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan);
}
bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks)
{
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown);
}
void ImGui_ImplGlfw_Shutdown()
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
if (bd->InstalledCallbacks)
ImGui_ImplGlfw_RestoreCallbacks(bd->Window);
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
glfwDestroyCursor(bd->MouseCursors[cursor_n]);
io.BackendPlatformName = NULL;
io.BackendPlatformUserData = NULL;
IM_DELETE(bd);
}
static void ImGui_ImplGlfw_UpdateMouseData()
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
#ifdef __EMSCRIPTEN__
const bool is_app_focused = true;
#else
const bool is_app_focused = glfwGetWindowAttrib(bd->Window, GLFW_FOCUSED) != 0;
#endif
if (is_app_focused)
{
// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos)
glfwSetCursorPos(bd->Window, (double)io.MousePos.x, (double)io.MousePos.y);
// (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured)
if (is_app_focused && bd->MouseWindow == NULL)
{
double mouse_x, mouse_y;
glfwGetCursorPos(bd->Window, &mouse_x, &mouse_y);
io.AddMousePosEvent((float)mouse_x, (float)mouse_y);
bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y);
}
}
}
static void ImGui_ImplGlfw_UpdateMouseCursor()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
return;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
glfwSetInputMode(bd->Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
}
else
{
// Show OS mouse cursor
// FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here.
glfwSetCursor(bd->Window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]);
glfwSetInputMode(bd->Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
}
}
// Update gamepad inputs
static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; }
static void ImGui_ImplGlfw_UpdateGamepads()
{
ImGuiIO& io = ImGui::GetIO();
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
#if GLFW_HAS_GAMEPAD_API
GLFWgamepadstate gamepad;
if (!glfwGetGamepadState(GLFW_JOYSTICK_1, &gamepad))
return;
#define MAP_BUTTON(KEY_NO, BUTTON_NO, _UNUSED) do { io.AddKeyEvent(KEY_NO, gamepad.buttons[BUTTON_NO] != 0); } while (0)
#define MAP_ANALOG(KEY_NO, AXIS_NO, _UNUSED, V0, V1) do { float v = gamepad.axes[AXIS_NO]; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0)
#else
int axes_count = 0, buttons_count = 0;
const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count);
const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count);
if (axes_count == 0 || buttons_count == 0)
return;
#define MAP_BUTTON(KEY_NO, _UNUSED, BUTTON_NO) do { io.AddKeyEvent(KEY_NO, (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS)); } while (0)
#define MAP_ANALOG(KEY_NO, _UNUSED, AXIS_NO, V0, V1) do { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); io.AddKeyAnalogEvent(KEY_NO, v > 0.10f, Saturate(v)); } while (0)
#endif
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
MAP_BUTTON(ImGuiKey_GamepadStart, GLFW_GAMEPAD_BUTTON_START, 7);
MAP_BUTTON(ImGuiKey_GamepadBack, GLFW_GAMEPAD_BUTTON_BACK, 6);
MAP_BUTTON(ImGuiKey_GamepadFaceDown, GLFW_GAMEPAD_BUTTON_A, 0); // Xbox A, PS Cross
MAP_BUTTON(ImGuiKey_GamepadFaceRight, GLFW_GAMEPAD_BUTTON_B, 1); // Xbox B, PS Circle
MAP_BUTTON(ImGuiKey_GamepadFaceLeft, GLFW_GAMEPAD_BUTTON_X, 2); // Xbox X, PS Square
MAP_BUTTON(ImGuiKey_GamepadFaceUp, GLFW_GAMEPAD_BUTTON_Y, 3); // Xbox Y, PS Triangle
MAP_BUTTON(ImGuiKey_GamepadDpadLeft, GLFW_GAMEPAD_BUTTON_DPAD_LEFT, 13);
MAP_BUTTON(ImGuiKey_GamepadDpadRight, GLFW_GAMEPAD_BUTTON_DPAD_RIGHT, 11);
MAP_BUTTON(ImGuiKey_GamepadDpadUp, GLFW_GAMEPAD_BUTTON_DPAD_UP, 10);
MAP_BUTTON(ImGuiKey_GamepadDpadDown, GLFW_GAMEPAD_BUTTON_DPAD_DOWN, 12);
MAP_BUTTON(ImGuiKey_GamepadL1, GLFW_GAMEPAD_BUTTON_LEFT_BUMPER, 4);
MAP_BUTTON(ImGuiKey_GamepadR1, GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER, 5);
MAP_ANALOG(ImGuiKey_GamepadL2, GLFW_GAMEPAD_AXIS_LEFT_TRIGGER, 4, -0.75f, +1.0f);
MAP_ANALOG(ImGuiKey_GamepadR2, GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER, 5, -0.75f, +1.0f);
MAP_BUTTON(ImGuiKey_GamepadL3, GLFW_GAMEPAD_BUTTON_LEFT_THUMB, 8);
MAP_BUTTON(ImGuiKey_GamepadR3, GLFW_GAMEPAD_BUTTON_RIGHT_THUMB, 9);
MAP_ANALOG(ImGuiKey_GamepadLStickLeft, GLFW_GAMEPAD_AXIS_LEFT_X, 0, -0.25f, -1.0f);
MAP_ANALOG(ImGuiKey_GamepadLStickRight, GLFW_GAMEPAD_AXIS_LEFT_X, 0, +0.25f, +1.0f);
MAP_ANALOG(ImGuiKey_GamepadLStickUp, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, -0.25f, -1.0f);
MAP_ANALOG(ImGuiKey_GamepadLStickDown, GLFW_GAMEPAD_AXIS_LEFT_Y, 1, +0.25f, +1.0f);
MAP_ANALOG(ImGuiKey_GamepadRStickLeft, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, -0.25f, -1.0f);
MAP_ANALOG(ImGuiKey_GamepadRStickRight, GLFW_GAMEPAD_AXIS_RIGHT_X, 2, +0.25f, +1.0f);
MAP_ANALOG(ImGuiKey_GamepadRStickUp, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, -0.25f, -1.0f);
MAP_ANALOG(ImGuiKey_GamepadRStickDown, GLFW_GAMEPAD_AXIS_RIGHT_Y, 3, +0.25f, +1.0f);
#undef MAP_BUTTON
#undef MAP_ANALOG
}
void ImGui_ImplGlfw_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplGlfw_InitForXXX()?");
// Setup display size (every frame to accommodate for window resizing)
int w, h;
int display_w, display_h;
glfwGetWindowSize(bd->Window, &w, &h);
glfwGetFramebufferSize(bd->Window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h);
if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h);
// Setup time step
double current_time = glfwGetTime();
io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);
bd->Time = current_time;
ImGui_ImplGlfw_UpdateMouseData();
ImGui_ImplGlfw_UpdateMouseCursor();
// Update game controllers (if enabled and available)
ImGui_ImplGlfw_UpdateGamepads();
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif

View File

@ -4,9 +4,9 @@
// Implemented features:
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW.
// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE).
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
@ -29,11 +29,16 @@ IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool ins
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
// GLFW callbacks
// - When calling Init with 'install_callbacks=true': GLFW callbacks will be installed for you. They will call user's previously installed callbacks, if any.
// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call those function yourself from your own GLFW callbacks.
IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused);
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered);
// GLFW callbacks (installer)
// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any.
// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks.
IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window);
IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window);
// GLFW callbacks (individual callbacks to call if you didn't install callbacks)
IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87
IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);

12
core/src/backend.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <string>
namespace backend {
int init(std::string resDir = "");
void beginFrame();
void render(bool vsync = true);
void getMouseScreenPos(double& x, double& y);
void setMouseScreenPos(double x, double y);
int renderLoop();
int end();
}

123
core/src/command_args.cpp Normal file
View File

@ -0,0 +1,123 @@
#include "command_args.h"
#include <filesystem>
void CommandArgsParser::defineAll() {
#if defined(_WIN32)
std::string root = ".";
define('c', "con", "Show console on Windows");
#elif defined(IS_MACOS_BUNDLE)
std::string root = (std::string)getenv("HOME") + "/Library/Application Support/sdrpp";
#elif defined(__ANDROID__)
std::string root = "/storage/self/primary/sdrpp";
#else
std::string root = (std::string)getenv("HOME") + "/.config/sdrpp";
#endif
define('a', "addr", "Server mode address", "0.0.0.0");
define('h', "help", "Show help");
define('p', "port", "Server mode port", 5259);
define('r', "root", "Root directory, where all config files are stored", std::filesystem::absolute(root).string());
define('s', "server", "Run in server mode");
}
int CommandArgsParser::parse(int argc, char* argv[]) {
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
// Check for long and short name arguments
if (!arg.rfind("--", 0)) {
arg = arg.substr(2);
}
else if (!arg.rfind("-", 0)) {
if (aliases.find(arg[1]) == aliases.end()) {
printf("Unknown argument\n");
showHelp();
return -1;
}
arg = aliases[arg[1]];
}
else {
printf("Invalid argument\n");
showHelp();
return -1;
}
// Make sure the argument exists
if (args.find(arg) == args.end()) {
printf("Unknown argument\n");
showHelp();
return -1;
}
// Parse depending on type
CLIArg& carg = args[arg];
// If not void, make sure an argument is available and retrieve it
if (carg.type != CLI_ARG_TYPE_VOID && i + 1 >= argc) {
printf("Missing argument\n");
showHelp();
return -1;
}
// Parse void since arg won't be needed
if (carg.type == CLI_ARG_TYPE_VOID) {
carg.bval = true;
continue;
}
// Parse types that require parsing
arg = argv[++i];
if (carg.type == CLI_ARG_TYPE_BOOL) {
// Enforce lower case
for (int i = 0; i < arg.size(); i++) { arg[i] = std::tolower(arg[i]); }
if (arg == "true" || arg == "on" || arg == "1") {
carg.bval = true;
}
else if (arg == "false" || arg == "off" || arg == "0") {
carg.bval = true;
}
else {
printf("Invald argument, expected bool (true, false, on, off, 1, 0)\n");
showHelp();
return -1;
}
}
else if (carg.type == CLI_ARG_TYPE_INT) {
try {
carg.ival = std::stoi(arg);
}
catch (std::exception e) {
printf("Invald argument, failed to parse integer\n");
showHelp();
return -1;
}
}
else if (carg.type == CLI_ARG_TYPE_FLOAT) {
try {
carg.fval = std::stod(arg);
}
catch (std::exception e) {
printf("Invald argument, failed to parse float\n");
showHelp();
return -1;
}
}
else if (carg.type == CLI_ARG_TYPE_STRING) {
carg.sval = arg;
}
}
return 0;
}
void CommandArgsParser::showHelp() {
for (auto const& [ln, arg] : args) {
if (arg.alias) {
printf("-%c --%s\t\t%s\n", arg.alias, ln.c_str(), arg.description.c_str());
}
else {
printf(" --%s\t\t%s\n", ln.c_str(), arg.description.c_str());
}
}
}

153
core/src/command_args.h Normal file
View File

@ -0,0 +1,153 @@
#pragma once
#include <string>
#include <map>
#include <stdexcept>
enum CLIArgType {
CLI_ARG_TYPE_INVALID,
CLI_ARG_TYPE_VOID,
CLI_ARG_TYPE_BOOL,
CLI_ARG_TYPE_INT,
CLI_ARG_TYPE_FLOAT,
CLI_ARG_TYPE_STRING
};
class CommandArgsParser;
class CLIArg {
public:
CLIArg() {
type = CLI_ARG_TYPE_INVALID;
}
CLIArg(char al, std::string desc) {
alias = al;
description = desc;
type = CLI_ARG_TYPE_VOID;
bval = false;
}
CLIArg(char al, std::string desc, bool b) {
alias = al;
description = desc;
type = CLI_ARG_TYPE_BOOL;
bval = b;
}
CLIArg(char al, std::string desc, int i) {
alias = al;
description = desc;
type = CLI_ARG_TYPE_INT;
ival = i;
}
CLIArg(char al, std::string desc, double f) {
alias = al;
description = desc;
type = CLI_ARG_TYPE_FLOAT;
fval = f;
}
CLIArg(char al, std::string desc, std::string s) {
alias = al;
description = desc;
type = CLI_ARG_TYPE_STRING;
sval = s;
}
CLIArg(char al, std::string desc, const char* s) {
alias = al;
description = desc;
type = CLI_ARG_TYPE_STRING;
sval = s;
}
operator bool() const {
if (type != CLI_ARG_TYPE_BOOL && type != CLI_ARG_TYPE_VOID) { throw std::runtime_error("Not a bool"); }
return bval;
}
operator int() const {
if (type != CLI_ARG_TYPE_INT) { throw std::runtime_error("Not an int"); }
return ival;
}
operator float() const {
if (type != CLI_ARG_TYPE_FLOAT) { throw std::runtime_error("Not a float"); }
return (float)fval;
}
operator double() const {
if (type != CLI_ARG_TYPE_FLOAT) { throw std::runtime_error("Not a float"); }
return fval;
}
operator std::string() const {
if (type != CLI_ARG_TYPE_STRING) { throw std::runtime_error("Not a string"); }
return sval;
}
bool b() {
if (type != CLI_ARG_TYPE_BOOL && type != CLI_ARG_TYPE_VOID) { throw std::runtime_error("Not a bool"); }
return bval;
}
int i() {
if (type != CLI_ARG_TYPE_INT) { throw std::runtime_error("Not an int"); }
return ival;
}
float f() {
if (type != CLI_ARG_TYPE_FLOAT) { throw std::runtime_error("Not a float"); }
return (float)fval;
}
double d() {
if (type != CLI_ARG_TYPE_FLOAT) { throw std::runtime_error("Not a float"); }
return fval;
}
const std::string& s() {
if (type != CLI_ARG_TYPE_STRING) { throw std::runtime_error("Not a string"); }
return sval;
}
friend CommandArgsParser;
CLIArgType type;
char alias;
std::string description;
private:
bool bval;
int ival;
std::string sval;
double fval;
};
class CommandArgsParser {
public:
void define(char shortName, std::string name, std::string desc) {
args[name] = CLIArg(shortName, desc);
aliases[shortName] = name;
}
void defineAll();
template<class T>
void define(char shortName, std::string name, std::string desc, T defValue) {
args[name] = CLIArg(shortName, desc, defValue);
aliases[shortName] = name;
}
int parse(int argc, char* argv[]);
void showHelp();
CLIArg operator[](std::string name) {
return args[name];
}
private:
std::map<std::string, CLIArg> args;
std::map<char, std::string> aliases;
};

View File

@ -1,9 +1,6 @@
#include <server.h>
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#include <GLFW/glfw3.h>
#include <gui/main_window.h>
#include <gui/style.h>
#include <gui/gui.h>
@ -14,10 +11,9 @@
#include <stb_image.h>
#include <config.h>
#include <core.h>
#include <glfw_window.h>
#include <options.h>
#include <filesystem>
#include <gui/menus/theme.h>
#include <backend.h>
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include <stb_image_resize.h>
@ -36,41 +32,15 @@
#endif
#endif
const char* OPENGL_VERSIONS_GLSL[] = {
"#version 120",
"#version 300 es",
"#version 120"
};
const int OPENGL_VERSIONS_MAJOR[] = {
3,
3,
2
};
const int OPENGL_VERSIONS_MINOR[] = {
0,
1,
1
};
const bool OPENGL_VERSIONS_IS_ES[] = {
false,
true,
false
};
#define OPENGL_VERSION_COUNT (sizeof(OPENGL_VERSIONS_GLSL) / sizeof(char*))
namespace core {
ConfigManager configManager;
ModuleManager moduleManager;
ModuleComManager modComManager;
GLFWwindow* window;
CommandArgsParser args;
void setInputSampleRate(double samplerate) {
// Forward this to the server
if (options::opts.serverMode) { server::setInputSampleRate(samplerate); return; }
if (args["server"].b()) { server::setInputSampleRate(samplerate); return; }
sigpath::signalPath.sourceSampleRate = samplerate;
double effectiveSr = samplerate / ((double)(1 << sigpath::signalPath.decimation));
@ -84,22 +54,6 @@ namespace core {
}
};
bool maximized = false;
bool fullScreen = false;
static void glfw_error_callback(int error, const char* description) {
spdlog::error("Glfw Error {0}: {1}", error, description);
}
static void maximized_callback(GLFWwindow* window, int n) {
if (n == GLFW_TRUE) {
maximized = true;
}
else {
maximized = false;
}
}
// main
int sdrpp_main(int argc, char* argv[]) {
spdlog::info("SDR++ v" VERSION_STR);
@ -110,25 +64,35 @@ int sdrpp_main(int argc, char* argv[]) {
chdir(execPath.parent_path().string().c_str());
#endif
// Load default options and parse command line
options::loadDefaults();
if (!options::parse(argc, argv)) { return -1; }
// Define command line options and parse arguments
core::args.defineAll();
if (core::args.parse(argc, argv) < 0) { return -1; }
// Show help and exit if requested
if (core::args["help"].b()) {
core::args.showHelp();
return 0;
}
bool serverMode = (bool)core::args["server"];
#ifdef _WIN32
if (!options::opts.showConsole) { FreeConsole(); }
if (!core::args["con"].b() && !serverMode) { FreeConsole(); }
#endif
// Check root directory
if (!std::filesystem::exists(options::opts.root)) {
spdlog::warn("Root directory {0} does not exist, creating it", options::opts.root);
if (!std::filesystem::create_directories(options::opts.root)) {
spdlog::error("Could not create root directory {0}", options::opts.root);
std::string root = (std::string)core::args["root"];
if (!std::filesystem::exists(root)) {
spdlog::warn("Root directory {0} does not exist, creating it", root);
if (!std::filesystem::create_directories(root)) {
spdlog::error("Could not create root directory {0}", root);
return -1;
}
}
if (!std::filesystem::is_directory(options::opts.root)) {
spdlog::error("{0} is not a directory", options::opts.root);
// Check that the path actually is a directory
if (!std::filesystem::is_directory(root)) {
spdlog::error("{0} is not a directory", root);
return -1;
}
@ -230,8 +194,14 @@ int sdrpp_main(int argc, char* argv[]) {
// Themes
defConfig["theme"] = "Dark";
#ifdef __ANDROID__
defConfig["uiScale"] = 3.0f;
#else
defConfig["uiScale"] = 1.0f;
#endif
defConfig["modules"] = json::array();
defConfig["offsetMode"] = (int)0; // Off
defConfig["offset"] = 0.0;
defConfig["showMenu"] = true;
@ -257,6 +227,9 @@ int sdrpp_main(int argc, char* argv[]) {
#elif defined(IS_MACOS_BUNDLE)
defConfig["modulesDirectory"] = "../Plugins";
defConfig["resourcesDirectory"] = "../Resources";
#elif defined(__ANDROID__)
defConfig["modulesDirectory"] = root + "/modules";
defConfig["resourcesDirectory"] = root + "/res";
#else
defConfig["modulesDirectory"] = INSTALL_PREFIX "/lib/sdrpp/plugins";
defConfig["resourcesDirectory"] = INSTALL_PREFIX "/share/sdrpp";
@ -264,12 +237,36 @@ int sdrpp_main(int argc, char* argv[]) {
// Load config
spdlog::info("Loading config");
core::configManager.setPath(options::opts.root + "/config.json");
core::configManager.setPath(root + "/config.json");
core::configManager.load(defConfig);
core::configManager.enableAutoSave();
core::configManager.acquire();
// Android can't load just any .so file. This means we have to hardcode the name of the modules
#ifdef __ANDROID__
int modCount = 0;
core::configManager.conf["modules"] = json::array();
core::configManager.conf["modules"][modCount++] = "airspy_source.so";
core::configManager.conf["modules"][modCount++] = "airspyhf_source.so";
core::configManager.conf["modules"][modCount++] = "hackrf_source.so";
core::configManager.conf["modules"][modCount++] = "plutosdr_source.so";
core::configManager.conf["modules"][modCount++] = "sdrpp_server_source.so";
core::configManager.conf["modules"][modCount++] = "rfspace_source.so";
core::configManager.conf["modules"][modCount++] = "rtl_sdr_source.so";
core::configManager.conf["modules"][modCount++] = "rtl_tcp_source.so";
core::configManager.conf["modules"][modCount++] = "spyserver_source.so";
core::configManager.conf["modules"][modCount++] = "network_sink.so";
core::configManager.conf["modules"][modCount++] = "audio_sink.so";
core::configManager.conf["modules"][modCount++] = "radio.so";
core::configManager.conf["modules"][modCount++] = "frequency_manager.so";
core::configManager.conf["modules"][modCount++] = "recorder.so";
core::configManager.conf["modules"][modCount++] = "rigctl_server.so";
#endif
// Fix missing elements in config
for (auto const& item : defConfig.items()) {
if (!core::configManager.conf.contains(item.key())) {
@ -297,16 +294,14 @@ int sdrpp_main(int argc, char* argv[]) {
core::configManager.conf["moduleInstances"][_name] = newMod;
}
fullScreen = core::configManager.conf["fullscreen"];
// Load UI scaling
style::uiScale = core::configManager.conf["uiScale"];
core::configManager.release(true);
if (options::opts.serverMode) { return server::main(); }
if (serverMode) { return server::main(); }
core::configManager.acquire();
int winWidth = core::configManager.conf["windowSize"]["w"];
int winHeight = core::configManager.conf["windowSize"]["h"];
maximized = core::configManager.conf["maximized"];
std::string resDir = core::configManager.conf["resourcesDirectory"];
json bandColors = core::configManager.conf["bandColors"];
core::configManager.release();
@ -318,122 +313,16 @@ int sdrpp_main(int argc, char* argv[]) {
return 1;
}
// Setup window
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit()) {
return 1;
}
// Initialize backend
int biRes = backend::init(resDir);
if (biRes < 0) { return biRes; }
#ifdef __APPLE__
// GL 3.2 + GLSL 150
const char* glsl_version = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
// Create window with graphics context
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
core::window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);
if (core::window == NULL)
return 1;
glfwMakeContextCurrent(core::window);
#else
const char* glsl_version = "#version 120";
GLFWmonitor* monitor = NULL;
for (int i = 0; i < OPENGL_VERSION_COUNT; i++) {
glsl_version = OPENGL_VERSIONS_GLSL[i];
glfwWindowHint(GLFW_CLIENT_API, OPENGL_VERSIONS_IS_ES[i] ? GLFW_OPENGL_ES_API : GLFW_OPENGL_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_VERSIONS_MAJOR[i]);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_VERSIONS_MINOR[i]);
// Create window with graphics context
monitor = glfwGetPrimaryMonitor();
core::window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);
if (core::window == NULL) {
spdlog::info("OpenGL {0}.{1} {2}was not supported", OPENGL_VERSIONS_MAJOR[i], OPENGL_VERSIONS_MINOR[i], OPENGL_VERSIONS_IS_ES[i] ? "ES " : "");
continue;
}
spdlog::info("Using OpenGL {0}.{1}{2}", OPENGL_VERSIONS_MAJOR[i], OPENGL_VERSIONS_MINOR[i], OPENGL_VERSIONS_IS_ES[i] ? " ES" : "");
glfwMakeContextCurrent(core::window);
break;
}
#endif
// Add callback for max/min if GLFW supports it
#if (GLFW_VERSION_MAJOR == 3) && (GLFW_VERSION_MINOR >= 3)
if (maximized) {
glfwMaximizeWindow(core::window);
}
glfwSetWindowMaximizeCallback(core::window, maximized_callback);
#endif
// Load app icon
if (!std::filesystem::is_regular_file(resDir + "/icons/sdrpp.png")) {
spdlog::error("Icon file '{0}' doesn't exist!", resDir + "/icons/sdrpp.png");
return 1;
}
GLFWimage icons[10];
icons[0].pixels = stbi_load(((std::string)(resDir + "/icons/sdrpp.png")).c_str(), &icons[0].width, &icons[0].height, 0, 4);
icons[1].pixels = (unsigned char*)malloc(16 * 16 * 4);
icons[1].width = icons[1].height = 16;
icons[2].pixels = (unsigned char*)malloc(24 * 24 * 4);
icons[2].width = icons[2].height = 24;
icons[3].pixels = (unsigned char*)malloc(32 * 32 * 4);
icons[3].width = icons[3].height = 32;
icons[4].pixels = (unsigned char*)malloc(48 * 48 * 4);
icons[4].width = icons[4].height = 48;
icons[5].pixels = (unsigned char*)malloc(64 * 64 * 4);
icons[5].width = icons[5].height = 64;
icons[6].pixels = (unsigned char*)malloc(96 * 96 * 4);
icons[6].width = icons[6].height = 96;
icons[7].pixels = (unsigned char*)malloc(128 * 128 * 4);
icons[7].width = icons[7].height = 128;
icons[8].pixels = (unsigned char*)malloc(196 * 196 * 4);
icons[8].width = icons[8].height = 196;
icons[9].pixels = (unsigned char*)malloc(256 * 256 * 4);
icons[9].width = icons[9].height = 256;
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[1].pixels, 16, 16, 16 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[2].pixels, 24, 24, 24 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[3].pixels, 32, 32, 32 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[4].pixels, 48, 48, 48 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[5].pixels, 64, 64, 64 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[6].pixels, 96, 96, 96 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[7].pixels, 128, 128, 128 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[8].pixels, 196, 196, 196 * 4, 4);
stbir_resize_uint8(icons[0].pixels, icons[0].width, icons[0].height, icons[0].width * 4, icons[9].pixels, 256, 256, 256 * 4, 4);
glfwSetWindowIcon(core::window, 10, icons);
stbi_image_free(icons[0].pixels);
for (int i = 1; i < 10; i++) {
free(icons[i].pixels);
}
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
(void)io;
io.IniFilename = NULL;
// Setup Platform/Renderer bindings
ImGui_ImplGlfw_InitForOpenGL(core::window, true);
if (!ImGui_ImplOpenGL3_Init(glsl_version)) {
// If init fail, try to fall back on GLSL 1.2
spdlog::warn("Could not init using OpenGL with normal GLSL version, falling back to GLSL 1.2");
if (!ImGui_ImplOpenGL3_Init("#version 120")) {
spdlog::error("Failed to initialize OpenGL with GLSL 1.2");
return -1;
}
}
// Intialize SmGui in normal mode
SmGui::init(false);
if (!style::loadFonts(resDir)) { return -1; }
thememenu::init(resDir);
LoadingScreen::setWindow(core::window);
LoadingScreen::init();
LoadingScreen::show("Loading icons");
spdlog::info("Loading icons");
@ -447,115 +336,29 @@ int sdrpp_main(int argc, char* argv[]) {
spdlog::info("Loading band plans color table");
bandplan::loadColorTable(bandColors);
bool _maximized = maximized;
int fsWidth, fsHeight, fsPosX, fsPosY;
int _winWidth, _winHeight;
glfwGetWindowSize(core::window, &_winWidth, &_winHeight);
if (fullScreen) {
spdlog::info("Fullscreen: ON");
fsWidth = _winWidth;
fsHeight = _winHeight;
glfwGetWindowPos(core::window, &fsPosX, &fsPosY);
const GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowMonitor(core::window, monitor, 0, 0, mode->width, mode->height, 0);
}
gui::mainWindow.init();
spdlog::info("Ready.");
// Main loop
while (!glfwWindowShouldClose(core::window)) {
glfwPollEvents();
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
//ImGui::ShowDemoWindow();
if (_maximized != maximized) {
_maximized = maximized;
core::configManager.acquire();
core::configManager.conf["maximized"] = _maximized;
if (!maximized) {
glfwSetWindowSize(core::window, core::configManager.conf["windowSize"]["w"], core::configManager.conf["windowSize"]["h"]);
}
core::configManager.release(true);
}
glfwGetWindowSize(core::window, &_winWidth, &_winHeight);
if (ImGui::IsKeyPressed(GLFW_KEY_F11)) {
fullScreen = !fullScreen;
if (fullScreen) {
spdlog::info("Fullscreen: ON");
fsWidth = _winWidth;
fsHeight = _winHeight;
glfwGetWindowPos(core::window, &fsPosX, &fsPosY);
const GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowMonitor(core::window, monitor, 0, 0, mode->width, mode->height, 0);
core::configManager.acquire();
core::configManager.conf["fullscreen"] = true;
core::configManager.release();
}
else {
spdlog::info("Fullscreen: OFF");
glfwSetWindowMonitor(core::window, nullptr, fsPosX, fsPosY, fsWidth, fsHeight, 0);
core::configManager.acquire();
core::configManager.conf["fullscreen"] = false;
core::configManager.release();
}
}
if ((_winWidth != winWidth || _winHeight != winHeight) && !maximized && _winWidth > 0 && _winHeight > 0) {
winWidth = _winWidth;
winHeight = _winHeight;
core::configManager.acquire();
core::configManager.conf["windowSize"]["w"] = winWidth;
core::configManager.conf["windowSize"]["h"] = winHeight;
core::configManager.release(true);
}
if (winWidth > 0 && winHeight > 0) {
ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(ImVec2(_winWidth, _winHeight));
gui::mainWindow.draw();
}
// Rendering
ImGui::Render();
int display_w, display_h;
glfwGetFramebufferSize(core::window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
//glClearColor(0.0666f, 0.0666f, 0.0666f, 1.0f);
glClearColor(gui::themeManager.clearColor.x, gui::themeManager.clearColor.y, gui::themeManager.clearColor.z, gui::themeManager.clearColor.w);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapInterval(1); // Enable vsync
glfwSwapBuffers(core::window);
}
// Run render loop (TODO: CHECK RETURN VALUE)
backend::renderLoop();
// On android, none of this shutdown should happen due to the way the UI works
#ifndef __ANDROID__
// Shut down all modules
for (auto& [name, mod] : core::moduleManager.modules) {
mod.end();
}
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(core::window);
glfwTerminate();
// Terminate backend (TODO: CHECK RETURN VALUE)
backend::end();
sigpath::signalPath.stop();
core::configManager.disableAutoSave();
core::configManager.save();
#endif
spdlog::info("Exiting successfully");
return 0;
}

View File

@ -3,11 +3,13 @@
#include <module.h>
#include <module.h>
#include <module_com.h>
#include "command_args.h"
namespace core {
SDRPP_EXPORT ConfigManager configManager;
SDRPP_EXPORT ModuleManager moduleManager;
SDRPP_EXPORT ModuleComManager modComManager;
SDRPP_EXPORT CommandArgsParser args;
void setInputSampleRate(double samplerate);
};

View File

@ -57,15 +57,20 @@ namespace sdrpp_credits {
"Eric Johnson",
"Ernest Murphy (NH7L)",
"Flinger Films",
"gringogrigio",
"Joe Cupano",
"Kezza",
"Krys Kamieniecki",
"Lee Donaghy",
"Lee KD1SQ",
".lozenge. (Hank Hill)",
"ON4MU",
"Passion-Radio.com",
"Scanner School",
"SignalsEverywhere",
"Syne Ardwin (WI9SYN)",
"W4IPA"
"W4IPA",
"Zipper"
};
const int contributorCount = sizeof(contributors) / sizeof(char*);

View File

@ -1,6 +0,0 @@
#include <GLFW/glfw3.h>
#include <module.h>
namespace core {
SDRPP_EXPORT GLFWwindow* window;
};

View File

@ -8,8 +8,10 @@
namespace credits {
ImFont* bigFont;
ImVec2 imageSize(128.0f, 128.0f);
void init() {
imageSize = ImVec2(128.0f * style::uiScale, 128.0f * style::uiScale);
}
void show() {
@ -25,7 +27,7 @@ namespace credits {
ImGui::TextUnformatted("SDR++ ");
ImGui::PopFont();
ImGui::SameLine();
ImGui::Image(icons::LOGO, ImVec2(128, 128));
ImGui::Image(icons::LOGO, imageSize);
ImGui::Spacing();
ImGui::Spacing();
ImGui::Spacing();

View File

@ -1,26 +1,22 @@
#include <gui/dialogs/loading_screen.h>
#include <gui/main_window.h>
#include <imgui.h>
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <gui/icons.h>
#include <gui/style.h>
#include <credits.h>
#include <gui/gui.h>
#include <backend.h>
namespace LoadingScreen {
GLFWwindow* _win;
ImVec2 imageSize(128.0f, 128.0f);
void setWindow(GLFWwindow* win) {
_win = win;
void init() {
imageSize = ImVec2(128.0f * style::uiScale, 128.0f * style::uiScale);
}
void show(std::string msg) {
glfwPollEvents();
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
backend::beginFrame();
ImGui::NewFrame();
ImGui::Begin("Main", NULL, WINDOW_FLAGS);
@ -33,7 +29,7 @@ namespace LoadingScreen {
ImGui::TextUnformatted("SDR++ ");
ImGui::PopFont();
ImGui::SameLine();
ImGui::Image(icons::LOGO, ImVec2(128, 128));
ImGui::Image(icons::LOGO, imageSize);
ImVec2 origPos = ImGui::GetCursorPos();
ImGui::SetCursorPosY(origPos.y + 50);
@ -46,14 +42,6 @@ namespace LoadingScreen {
ImGui::End();
ImGui::Render();
int display_w, display_h;
glfwGetFramebufferSize(_win, &display_w, &display_h);
glViewport(0, 0, display_w, display_h);
glClearColor(gui::themeManager.clearColor.x, gui::themeManager.clearColor.y, gui::themeManager.clearColor.z, gui::themeManager.clearColor.w);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(_win);
backend::render(false);
}
}

View File

@ -2,9 +2,8 @@
#include <thread>
#include <string>
#include <mutex>
#include <GLFW/glfw3.h>
namespace LoadingScreen {
void setWindow(GLFWwindow* win);
void init();
void show(std::string msg);
};

View File

@ -1,7 +1,6 @@
#include <gui/icons.h>
#include <stdint.h>
#include <config.h>
#include <options.h>
#define STB_IMAGE_IMPLEMENTATION
#include <imgui/stb_image.h>

View File

@ -1,10 +1,7 @@
#include <gui/main_window.h>
#include <gui/gui.h>
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#include <GLFW/glfw3.h>
#include <thread>
#include <complex>
#include <gui/widgets/waterfall.h>
@ -27,7 +24,6 @@
#include <filesystem>
#include <signal_path/source.h>
#include <gui/dialogs/loading_screen.h>
#include <options.h>
#include <gui/colormaps.h>
#include <gui/widgets/snr_meter.h>
#include <gui/tuner.h>
@ -126,10 +122,14 @@ void MainWindow::init() {
// Load additional modules specified through config
for (auto const& path : modules) {
#ifndef __ANDROID__
std::string apath = std::filesystem::absolute(path).string();
spdlog::info("Loading {0}", apath);
LoadingScreen::show("Loading " + std::filesystem::path(path).filename().string());
core::moduleManager.loadModule(apath);
#else
core::moduleManager.loadModule(path);
#endif
}
// Create module instances
@ -354,8 +354,10 @@ void MainWindow::draw() {
}
// To Bar
// ImGui::BeginChild("TopBarChild", ImVec2(0, 49.0f * style::uiScale), false, ImGuiWindowFlags_HorizontalScrollbar);
ImVec2 btnSize(30 * style::uiScale, 30 * style::uiScale);
ImGui::PushID(ImGui::GetID("sdrpp_menu_btn"));
if (ImGui::ImageButton(icons::MENU, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(GLFW_KEY_MENU, false)) {
if (ImGui::ImageButton(icons::MENU, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(ImGuiKey_Menu, false)) {
showMenu = !showMenu;
core::configManager.acquire();
core::configManager.conf["showMenu"] = showMenu;
@ -369,14 +371,14 @@ void MainWindow::draw() {
if (playButtonLocked && !tmpPlaySate) { style::beginDisabled(); }
if (playing) {
ImGui::PushID(ImGui::GetID("sdrpp_stop_btn"));
if (ImGui::ImageButton(icons::STOP, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(GLFW_KEY_END, false)) {
if (ImGui::ImageButton(icons::STOP, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(ImGuiKey_End, false)) {
setPlayState(false);
}
ImGui::PopID();
}
else { // TODO: Might need to check if there even is a device
ImGui::PushID(ImGui::GetID("sdrpp_play_btn"));
if (ImGui::ImageButton(icons::PLAY, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(GLFW_KEY_END, false)) {
if (ImGui::ImageButton(icons::PLAY, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5) || ImGui::IsKeyPressed(ImGuiKey_End, false)) {
setPlayState(true);
}
ImGui::PopID();
@ -384,20 +386,21 @@ void MainWindow::draw() {
if (playButtonLocked && !tmpPlaySate) { style::endDisabled(); }
ImGui::SameLine();
float origY = ImGui::GetCursorPosY();
//ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 8);
sigpath::sinkManager.showVolumeSlider(gui::waterfall.selectedVFO, "##_sdrpp_main_volume_", 248, 30, 5, true);
sigpath::sinkManager.showVolumeSlider(gui::waterfall.selectedVFO, "##_sdrpp_main_volume_", 248 * style::uiScale, btnSize.x, 5, true);
ImGui::SameLine();
ImGui::SetCursorPosY(origY);
gui::freqSelect.draw();
ImGui::SameLine();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 9);
ImGui::SetCursorPosY(origY);
if (tuningMode == tuner::TUNER_MODE_CENTER) {
ImGui::PushID(ImGui::GetID("sdrpp_ena_st_btn"));
if (ImGui::ImageButton(icons::CENTER_TUNING, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) {
if (ImGui::ImageButton(icons::CENTER_TUNING, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5)) {
tuningMode = tuner::TUNER_MODE_NORMAL;
gui::waterfall.VFOMoveSingleClick = false;
core::configManager.acquire();
@ -408,7 +411,7 @@ void MainWindow::draw() {
}
else { // TODO: Might need to check if there even is a device
ImGui::PushID(ImGui::GetID("sdrpp_dis_st_btn"));
if (ImGui::ImageButton(icons::NORMAL_TUNING, ImVec2(30, 30), ImVec2(0, 0), ImVec2(1, 1), 5)) {
if (ImGui::ImageButton(icons::NORMAL_TUNING, btnSize, ImVec2(0, 0), ImVec2(1, 1), 5)) {
tuningMode = tuner::TUNER_MODE_CENTER;
gui::waterfall.VFOMoveSingleClick = true;
tuner::tune(tuner::TUNER_MODE_CENTER, gui::waterfall.selectedVFO, gui::freqSelect.frequency);
@ -421,32 +424,37 @@ void MainWindow::draw() {
ImGui::SameLine();
int snrWidth = std::min<int>(300, ImGui::GetWindowSize().x - ImGui::GetCursorPosX() - 87);
int snrOffset = 87.0f * style::uiScale;
int snrWidth = std::clamp<int>(ImGui::GetWindowSize().x - ImGui::GetCursorPosX() - snrOffset, 100.0f * style::uiScale, 300.0f * style::uiScale);
int snrPos = std::max<int>(ImGui::GetWindowSize().x - (snrWidth + snrOffset), ImGui::GetCursorPosX());
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - (snrWidth + 87));
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5);
ImGui::SetCursorPosX(snrPos);
ImGui::SetCursorPosY(origY + (5.0f * style::uiScale));
ImGui::SetNextItemWidth(snrWidth);
ImGui::SNRMeter((vfo != NULL) ? gui::waterfall.selectedVFOSNR : 0);
// Note: this is what makes the vertical size correct, needs to be fixed
ImGui::SameLine();
// ImGui::EndChild();
// Logo button
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 48);
ImGui::SetCursorPosY(10);
if (ImGui::ImageButton(icons::LOGO, ImVec2(32, 32), ImVec2(0, 0), ImVec2(1, 1), 0)) {
ImGui::SetCursorPosX(ImGui::GetWindowSize().x - (48 * style::uiScale));
ImGui::SetCursorPosY(10.0f * style::uiScale);
if (ImGui::ImageButton(icons::LOGO, ImVec2(32 * style::uiScale, 32 * style::uiScale), ImVec2(0, 0), ImVec2(1, 1), 0)) {
showCredits = true;
}
if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
showCredits = false;
}
if (ImGui::IsKeyPressed(GLFW_KEY_ESCAPE)) {
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
showCredits = false;
}
// Handle menu resize
ImVec2 winSize = ImGui::GetWindowSize();
ImVec2 mousePos = ImGui::GetMousePos();
if (!lockWaterfallControls) {
if (!lockWaterfallControls && showMenu) {
float curY = ImGui::GetCursorPosY();
bool click = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
bool down = ImGui::IsMouseDown(ImGuiMouseButton_Left);
@ -455,7 +463,7 @@ void MainWindow::draw() {
newWidth = std::clamp<float>(newWidth, 250, winSize.x - 250);
ImGui::GetForegroundDrawList()->AddLine(ImVec2(newWidth, curY), ImVec2(newWidth, winSize.y - 10), ImGui::GetColorU32(ImGuiCol_SeparatorActive));
}
if (mousePos.x >= newWidth - 2 && mousePos.x <= newWidth + 2 && mousePos.y > curY) {
if (mousePos.x >= newWidth - (2.0f * style::uiScale) && mousePos.x <= newWidth + (2.0f * style::uiScale) && mousePos.y > curY) {
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
if (click) {
grabbingMenu = true;
@ -479,8 +487,8 @@ void MainWindow::draw() {
if (showMenu) {
ImGui::Columns(3, "WindowColumns", false);
ImGui::SetColumnWidth(0, menuWidth);
ImGui::SetColumnWidth(1, winSize.x - menuWidth - 60);
ImGui::SetColumnWidth(2, 60);
ImGui::SetColumnWidth(1, std::max<int>(winSize.x - menuWidth - (60.0f * style::uiScale), 100.0f * style::uiScale));
ImGui::SetColumnWidth(2, 60.0f * style::uiScale);
ImGui::BeginChild("Left Column");
if (gui::menu.draw(firstMenuRender)) {
@ -538,9 +546,9 @@ void MainWindow::draw() {
else {
// When hiding the menu bar
ImGui::Columns(3, "WindowColumns", false);
ImGui::SetColumnWidth(0, 8);
ImGui::SetColumnWidth(1, winSize.x - 8 - 60);
ImGui::SetColumnWidth(2, 60);
ImGui::SetColumnWidth(0, 8 * style::uiScale);
ImGui::SetColumnWidth(1, winSize.x - ((8 + 60) * style::uiScale));
ImGui::SetColumnWidth(2, 60.0f * style::uiScale);
}
// Right Column
@ -557,12 +565,12 @@ void MainWindow::draw() {
if (!lockWaterfallControls) {
// Handle arrow keys
if (vfo != NULL && (gui::waterfall.mouseInFFT || gui::waterfall.mouseInWaterfall)) {
if (ImGui::IsKeyPressed(GLFW_KEY_LEFT) && !gui::freqSelect.digitHovered) {
if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) && !gui::freqSelect.digitHovered) {
double nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset - vfo->snapInterval;
nfreq = roundl(nfreq / vfo->snapInterval) * vfo->snapInterval;
tuner::tune(tuningMode, gui::waterfall.selectedVFO, nfreq);
}
if (ImGui::IsKeyPressed(GLFW_KEY_RIGHT) && !gui::freqSelect.digitHovered) {
if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && !gui::freqSelect.digitHovered) {
double nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset + vfo->snapInterval;
nfreq = roundl(nfreq / vfo->snapInterval) * vfo->snapInterval;
tuner::tune(tuningMode, gui::waterfall.selectedVFO, nfreq);
@ -602,8 +610,9 @@ void MainWindow::draw() {
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Zoom").x / 2.0));
ImGui::TextUnformatted("Zoom");
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10);
if (ImGui::VSliderFloat("##_7_", ImVec2(20.0, 150.0), &bw, 1.0, 0.0, "")) {
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale);
ImVec2 wfSliderSize(20.0 * style::uiScale, 150.0 * style::uiScale);
if (ImGui::VSliderFloat("##_7_", wfSliderSize, &bw, 1.0, 0.0, "")) {
double factor = (double)bw * (double)bw;
// Map 0.0 -> 1.0 to 1000.0 -> bandwidth
@ -621,8 +630,8 @@ void MainWindow::draw() {
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Max").x / 2.0));
ImGui::TextUnformatted("Max");
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10);
if (ImGui::VSliderFloat("##_8_", ImVec2(20.0, 150.0), &fftMax, 0.0, -160.0f, "")) {
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale);
if (ImGui::VSliderFloat("##_8_", wfSliderSize, &fftMax, 0.0, -160.0f, "")) {
fftMax = std::max<float>(fftMax, fftMin + 10);
core::configManager.acquire();
core::configManager.conf["max"] = fftMax;
@ -633,8 +642,9 @@ void MainWindow::draw() {
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Min").x / 2.0));
ImGui::TextUnformatted("Min");
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10);
if (ImGui::VSliderFloat("##_9_", ImVec2(20.0, 150.0), &fftMin, 0.0, -160.0f, "")) {
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale);
ImGui::SetItemUsingMouseWheel();
if (ImGui::VSliderFloat("##_9_", wfSliderSize, &fftMin, 0.0, -160.0f, "")) {
fftMin = std::min<float>(fftMax - 10, fftMin);
core::configManager.acquire();
core::configManager.conf["min"] = fftMin;
@ -707,4 +717,8 @@ void MainWindow::setFFTWindow(int win) {
bool MainWindow::isPlaying() {
return playing;
}
void MainWindow::setFirstMenuRender() {
firstMenuRender = true;
}

View File

@ -19,6 +19,7 @@ public:
bool sdrIsRunning();
void setFFTSize(int size);
void setFFTWindow(int win);
void setFirstMenuRender();
// TODO: Replace with it's own class
void setVFO(double freq);

View File

@ -35,7 +35,7 @@ namespace bandplanmenu {
}
void draw(void* ctx) {
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
float menuColumnWidth = ImGui::GetContentRegionAvail().x;
ImGui::PushItemWidth(menuColumnWidth);
if (ImGui::Combo("##_bandplan_name_", &bandplanId, bandplan::bandplanNameTxt.c_str())) {
gui::waterfall.bandplan = &bandplan::bandplans[bandplan::bandplanNames[bandplanId]];

View File

@ -7,6 +7,7 @@
#include <gui/main_window.h>
#include <signal_path/signal_path.h>
#include <gui/style.h>
#include <utils/optionlist.h>
namespace displaymenu {
bool showWaterfall;
@ -18,6 +19,10 @@ namespace displaymenu {
std::string colorMapAuthor = "";
int selectedWindow = 0;
int fftRate = 20;
int uiScaleId = 0;
bool restartRequired = false;
OptionList<float, float> uiScales;
const int FFTSizes[] = {
524288,
@ -85,11 +90,18 @@ namespace displaymenu {
selectedWindow = std::clamp<int>((int)core::configManager.conf["fftWindow"], 0, _FFT_WINDOW_COUNT - 1);
gui::mainWindow.setFFTWindow(selectedWindow);
// Define and load UI scales
uiScales.define(1.0f, "100%", 1.0f);
uiScales.define(2.0f, "200%", 2.0f);
uiScales.define(3.0f, "300%", 3.0f);
uiScales.define(4.0f, "400%", 4.0f);
uiScaleId = uiScales.valueId(style::uiScale);
}
void draw(void* ctx) {
float menuWidth = ImGui::GetContentRegionAvailWidth();
bool homePressed = ImGui::IsKeyPressed(GLFW_KEY_HOME, false);
float menuWidth = ImGui::GetContentRegionAvail().x;
bool homePressed = ImGui::IsKeyPressed(ImGuiKey_Home, false);
if (ImGui::Checkbox("Show Waterfall##_sdrpp", &showWaterfall) || homePressed) {
if (homePressed) { showWaterfall = !showWaterfall; }
showWaterfall ? gui::waterfall.showWaterfall() : gui::waterfall.hideWaterfall();
@ -112,6 +124,15 @@ namespace displaymenu {
core::configManager.release(true);
}
ImGui::LeftLabel("High-DPI Scaling");
ImGui::FillWidth();
if (ImGui::Combo("##sdrpp_ui_scale", &uiScaleId, uiScales.txt)) {
core::configManager.acquire();
core::configManager.conf["uiScale"] = uiScales[uiScaleId];
core::configManager.release(true);
restartRequired = true;
}
ImGui::LeftLabel("FFT Framerate");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::InputInt("##sdrpp_fft_rate", &fftRate, 1, 10)) {
@ -153,5 +174,9 @@ namespace displaymenu {
}
ImGui::Text("Color map Author: %s", colorMapAuthor.c_str());
}
if (restartRequired) {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Restart required.");
}
}
}

View File

@ -30,15 +30,22 @@ namespace module_manager_menu {
void draw(void* ctx) {
bool modified = false;
// Calculate delete button size and cell size
ImVec2 cellpad = ImGui::GetStyle().CellPadding;
float lheight = ImGui::GetTextLineHeight();
float cellWidth = lheight - (2.0f * cellpad.y);
float hdiff = cellpad.x - cellpad.y;
ImVec2 btnSize = ImVec2(lheight, lheight - 1);
ImVec2 textOff = ImVec2(3.0f * style::uiScale, -5.0f * style::uiScale);
if (ImGui::BeginTable("Module Manager Table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, ImVec2(0, 200))) {
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Type");
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 10);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, cellWidth);
ImGui::TableSetupScrollFreeze(3, 1);
ImGui::TableHeadersRow();
float height = ImGui::CalcTextSize("-").y;
for (auto& [name, inst] : core::moduleManager.instances) {
ImGui::TableNextRow();
@ -49,13 +56,13 @@ namespace module_manager_menu {
ImGui::TextUnformatted(inst.module.info->name);
ImGui::TableSetColumnIndex(2);
ImVec2 origPos = ImGui::GetCursorPos();
ImGui::SetCursorPos(ImVec2(origPos.x - 3, origPos.y));
if (ImGui::Button(("##module_mgr_" + name).c_str(), ImVec2(height, height))) {
ImVec2 cpos = ImGui::GetCursorPos();
ImGui::SetCursorPos(ImVec2(cpos.x - hdiff, cpos.y + 1));
if (ImGui::Button(("##module_mgr_" + name).c_str(), btnSize)) {
toBeRemoved = name;
confirmOpened = true;
}
ImGui::SetCursorPos(ImVec2(origPos.x + 2, origPos.y - 5));
ImGui::SetCursorPos(ImVec2(cpos.x + textOff.x, cpos.y + textOff.y));
ImGui::TextUnformatted("_");
}
ImGui::EndTable();
@ -73,34 +80,36 @@ namespace module_manager_menu {
});
// Add module row with slightly different settings
ImGui::BeginTable("Module Manager Add Table", 3);
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Type");
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 16);
ImGui::TableNextRow();
if (ImGui::BeginTable("Module Manager Add Table", 3)) {
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Type");
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, cellWidth + cellpad.x);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::InputText("##module_mod_name", modName, 1000);
ImGui::TableSetColumnIndex(0);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x + cellpad.x);
ImGui::InputText("##module_mod_name", modName, 1000);
ImGui::TableSetColumnIndex(1);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::Combo("##module_mgr_type", &modTypeId, modTypesTxt.c_str());
ImGui::TableSetColumnIndex(1);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x + cellpad.x);
ImGui::Combo("##module_mgr_type", &modTypeId, modTypesTxt.c_str());
ImGui::TableSetColumnIndex(2);
if (strlen(modName) == 0) { style::beginDisabled(); }
if (ImGui::Button("+##module_mgr_add_btn", ImVec2(16, 0))) {
if (!core::moduleManager.createInstance(modName, modTypes[modTypeId])) {
core::moduleManager.postInit(modName);
modified = true;
}
else {
errorMessage = "Could not create new instance of " + modTypes[modTypeId];
errorOpen = true;
ImGui::TableSetColumnIndex(2);
if (strlen(modName) == 0) { style::beginDisabled(); }
if (ImGui::Button("+##module_mgr_add_btn", ImVec2(btnSize.x, 0))) {
if (!core::moduleManager.createInstance(modName, modTypes[modTypeId])) {
core::moduleManager.postInit(modName);
modified = true;
}
else {
errorMessage = "Could not create new instance of " + modTypes[modTypeId];
errorOpen = true;
}
}
if (strlen(modName) == 0) { style::endDisabled(); }
ImGui::EndTable();
}
if (strlen(modName) == 0) { style::endDisabled(); }
ImGui::EndTable();
if (modified) {
// Update enabled and disabled modules

View File

@ -156,7 +156,7 @@ namespace sourcemenu {
}
void draw(void* ctx) {
float itemWidth = ImGui::GetContentRegionAvailWidth();
float itemWidth = ImGui::GetContentRegionAvail().x;
bool running = gui::mainWindow.sdrIsRunning();
if (running) { style::beginDisabled(); }

View File

@ -1,6 +1,5 @@
#include <gui/menus/theme.h>
#include <gui/gui.h>
#include <options.h>
#include <core.h>
#include <gui/style.h>
@ -23,8 +22,11 @@ namespace thememenu {
it = std::find(themeNames.begin(), themeNames.end(), "Dark");
selectedThemeName = "Dark";
}
gui::themeManager.applyTheme(selectedThemeName);
themeId = std::distance(themeNames.begin(), it);
applyTheme();
// Apply scaling
ImGui::GetStyle().ScaleAllSizes(style::uiScale);
themeNamesTxt = "";
for (auto name : themeNames) {
@ -33,12 +35,16 @@ namespace thememenu {
}
}
void applyTheme() {
gui::themeManager.applyTheme(themeNames[themeId]);
}
void draw(void* ctx) {
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
ImGui::LeftLabel("Theme");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::Combo("##theme_select_combo", &themeId, themeNamesTxt.c_str())) {
gui::themeManager.applyTheme(themeNames[themeId]);
applyTheme();
core::configManager.acquire();
core::configManager.conf["theme"] = themeNames[themeId];
core::configManager.release(true);

View File

@ -3,5 +3,6 @@
namespace thememenu {
void init(std::string resDir);
void applyTheme();
void draw(void* ctx);
}

View File

@ -81,7 +81,7 @@ namespace vfo_color_menu {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
if (ImGui::Button("Auto Color##vfo_color", ImVec2(ImGui::GetContentRegionAvailWidth(), 0))) {
if (ImGui::Button("Auto Color##vfo_color", ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
float delta = 1.0f / (float)gui::waterfall.vfos.size();
float hue = 0;
for (auto& [name, vfo] : gui::waterfall.vfos) {
@ -99,7 +99,7 @@ namespace vfo_color_menu {
}
ImGui::TableSetColumnIndex(1);
if (ImGui::Button("Clear All##vfo_color", ImVec2(ImGui::GetContentRegionAvailWidth(), 0))) {
if (ImGui::Button("Clear All##vfo_color", ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
for (auto& [name, vfo] : gui::waterfall.vfos) {
vfoColors[name] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
vfo->color = IM_COL32(255, 255, 255, 50);

View File

@ -1,6 +1,5 @@
#include "smgui.h"
#include "style.h"
#include <options.h>
#include <gui/widgets/stepped_slider.h>
#include <gui/gui.h>
@ -25,6 +24,7 @@ namespace SmGui {
std::string diffId = "";
DrawListElem diffValue;
bool nextItemFillWidth = false;
bool serverMode = false;
std::string ImStrToString(const char* imstr) {
int len = 0;
@ -33,6 +33,10 @@ namespace SmGui {
return std::string(imstr, end);
}
void init(bool server) {
serverMode = server;
}
// Rec/Play functions
void setDiff(std::string id, SmGui::DrawListElem value) {
diffId = id;
@ -457,26 +461,26 @@ namespace SmGui {
// Format functions
void FillWidth() {
if (!options::opts.serverMode) {
if (!serverMode) {
nextItemFillWidth = true;
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
return;
}
if (rdl) { rdl->pushStep(DRAW_STEP_FILL_WIDTH, false); }
}
void SameLine() {
if (!options::opts.serverMode) { ImGui::SameLine(); return; }
if (!serverMode) { ImGui::SameLine(); return; }
if (rdl) { rdl->pushStep(DRAW_STEP_SAME_LINE, false); }
}
void BeginDisabled() {
if (!options::opts.serverMode) { style::beginDisabled(); return; }
if (!serverMode) { style::beginDisabled(); return; }
if (rdl) { rdl->pushStep(DRAW_STEP_BEGIN_DISABLED, false); }
}
void EndDisabled() {
if (!options::opts.serverMode) { style::endDisabled(); return; }
if (!serverMode) { style::endDisabled(); return; }
if (rdl) { rdl->pushStep(DRAW_STEP_END_DISABLED, false); }
}
@ -484,7 +488,7 @@ namespace SmGui {
// Widget functions
bool Combo(const char *label, int *current_item, const char *items_separated_by_zeros, int popup_max_height_in_items) {
nextItemFillWidth = false;
if (!options::opts.serverMode) { return ImGui::Combo(label, current_item, items_separated_by_zeros, popup_max_height_in_items); }
if (!serverMode) { return ImGui::Combo(label, current_item, items_separated_by_zeros, popup_max_height_in_items); }
if (rdl) {
rdl->pushStep(DRAW_STEP_COMBO, forceSyncForNext);
rdl->pushString(label);
@ -501,10 +505,10 @@ namespace SmGui {
}
bool Button(const char *label, ImVec2 size) {
if (!options::opts.serverMode) {
if (!serverMode) {
if (nextItemFillWidth) {
nextItemFillWidth = false;
size.x = ImGui::GetContentRegionAvailWidth();
size.x = ImGui::GetContentRegionAvail().x;
}
return ImGui::Button(label, size);
}
@ -519,7 +523,7 @@ namespace SmGui {
}
void Columns(int count, const char *id, bool border) {
if (!options::opts.serverMode) { ImGui::Columns(count, id, border); return; }
if (!serverMode) { ImGui::Columns(count, id, border); return; }
if (rdl) {
rdl->pushStep(DRAW_STEP_COLUMNS, forceSyncForNext);
rdl->pushInt(count);
@ -530,12 +534,12 @@ namespace SmGui {
}
void NextColumn() {
if (!options::opts.serverMode) { ImGui::NextColumn(); return; }
if (!serverMode) { ImGui::NextColumn(); return; }
if (rdl) { rdl->pushStep(DRAW_STEP_NEXT_COLUMN, false); }
}
bool RadioButton(const char *label, bool active) {
if (!options::opts.serverMode) { return ImGui::RadioButton(label, active); }
if (!serverMode) { return ImGui::RadioButton(label, active); }
if (rdl) {
rdl->pushStep(DRAW_STEP_RADIO_BUTTON, forceSyncForNext);
rdl->pushString(label);
@ -546,17 +550,17 @@ namespace SmGui {
}
void BeginGroup() {
if (!options::opts.serverMode) { ImGui::BeginGroup(); return; }
if (!serverMode) { ImGui::BeginGroup(); return; }
if (rdl) { rdl->pushStep(DRAW_STEP_BEGIN_GROUP, false); }
}
void EndGroup() {
if (!options::opts.serverMode) { ImGui::EndGroup(); return; }
if (!serverMode) { ImGui::EndGroup(); return; }
if (rdl) { rdl->pushStep(DRAW_STEP_END_GROUP, false); }
}
void LeftLabel(const char *text) {
if (!options::opts.serverMode) { ImGui::LeftLabel(text); return; }
if (!serverMode) { ImGui::LeftLabel(text); return; }
if (rdl) {
rdl->pushStep(DRAW_STEP_LEFT_LABEL, forceSyncForNext);
rdl->pushString(text);
@ -566,7 +570,7 @@ namespace SmGui {
bool SliderInt(const char *label, int *v, int v_min, int v_max, FormatString format, ImGuiSliderFlags flags) {
nextItemFillWidth = false;
if (!options::opts.serverMode) { return ImGui::SliderInt(label, v, v_min, v_max, fmtStr[format], flags); }
if (!serverMode) { return ImGui::SliderInt(label, v, v_min, v_max, fmtStr[format], flags); }
if (rdl) {
rdl->pushStep(DRAW_STEP_SLIDER_INT, forceSyncForNext);
rdl->pushString(label);
@ -586,7 +590,7 @@ namespace SmGui {
bool SliderFloatWithSteps(const char *label, float *v, float v_min, float v_max, float v_step, FormatString display_format) {
nextItemFillWidth = false;
if (!options::opts.serverMode) { return ImGui::SliderFloatWithSteps(label, v, v_min, v_max, v_step, fmtStr[display_format]); }
if (!serverMode) { return ImGui::SliderFloatWithSteps(label, v, v_min, v_max, v_step, fmtStr[display_format]); }
if (rdl) {
rdl->pushStep(DRAW_STEP_SLIDER_FLOAT_WITH_STEPS, forceSyncForNext);
rdl->pushString(label);
@ -606,7 +610,7 @@ namespace SmGui {
bool InputInt(const char *label, int *v, int step, int step_fast, ImGuiInputTextFlags flags) {
nextItemFillWidth = false;
if (!options::opts.serverMode) { return ImGui::InputInt(label, v, step, step_fast, flags); }
if (!serverMode) { return ImGui::InputInt(label, v, step, step_fast, flags); }
if (rdl) {
rdl->pushStep(DRAW_STEP_INPUT_INT, forceSyncForNext);
rdl->pushString(label);
@ -624,7 +628,7 @@ namespace SmGui {
}
bool Checkbox(const char *label, bool *v) {
if (!options::opts.serverMode) { return ImGui::Checkbox(label, v); }
if (!serverMode) { return ImGui::Checkbox(label, v); }
if (rdl) {
rdl->pushStep(DRAW_STEP_CHECKBOX, forceSyncForNext);
rdl->pushString(label);
@ -640,7 +644,7 @@ namespace SmGui {
bool SliderFloat(const char *label, float *v, float v_min, float v_max, FormatString format, ImGuiSliderFlags flags) {
nextItemFillWidth = false;
if (!options::opts.serverMode) { return ImGui::SliderFloat(label, v, v_min, v_max, fmtStr[format], flags); }
if (!serverMode) { return ImGui::SliderFloat(label, v, v_min, v_max, fmtStr[format], flags); }
if (rdl) {
rdl->pushStep(DRAW_STEP_SLIDER_FLOAT, forceSyncForNext);
rdl->pushString(label);
@ -660,7 +664,7 @@ namespace SmGui {
bool InputText(const char *label, char *buf, size_t buf_size, ImGuiInputTextFlags flags) {
nextItemFillWidth = false;
if (!options::opts.serverMode) { return ImGui::InputText(label, buf, buf_size, flags); }
if (!serverMode) { return ImGui::InputText(label, buf, buf_size, flags); }
if (rdl) {
rdl->pushStep(DRAW_STEP_INPUT_TEXT, forceSyncForNext);
rdl->pushString(label);
@ -677,7 +681,7 @@ namespace SmGui {
}
void Text(const char* str) {
if (!options::opts.serverMode) { ImGui::TextUnformatted(str); return; }
if (!serverMode) { ImGui::TextUnformatted(str); return; }
if (rdl) {
rdl->pushStep(DRAW_STEP_TEXT, false);
rdl->pushString(str);
@ -685,7 +689,7 @@ namespace SmGui {
}
void TextColored(const ImVec4 &col, const char *str) {
if (!options::opts.serverMode) { ImGui::TextColored(col, "%s", str); return; }
if (!serverMode) { ImGui::TextColored(col, "%s", str); return; }
if (rdl) {
rdl->pushStep(DRAW_STEP_TEXT_COLORED, false);
rdl->pushFloat(col.x);
@ -697,7 +701,7 @@ namespace SmGui {
}
void OpenPopup(const char *str_id, ImGuiPopupFlags popup_flags) {
if (!options::opts.serverMode) { ImGui::OpenPopup(str_id, popup_flags); return; }
if (!serverMode) { ImGui::OpenPopup(str_id, popup_flags); return; }
if (rdl) {
rdl->pushStep(DRAW_STEP_OPEN_POPUP, false);
rdl->pushString(str_id);
@ -706,7 +710,7 @@ namespace SmGui {
}
bool BeginPopup(const char *str_id, ImGuiWindowFlags flags) {
if (!options::opts.serverMode) { return ImGui::BeginPopup(str_id, flags); }
if (!serverMode) { return ImGui::BeginPopup(str_id, flags); }
if (rdl) {
rdl->pushStep(DRAW_STEP_BEGIN_POPUP, false);
rdl->pushString(str_id);
@ -716,14 +720,14 @@ namespace SmGui {
}
void EndPopup() {
if (!options::opts.serverMode) { ImGui::EndPopup(); return; }
if (!serverMode) { ImGui::EndPopup(); return; }
if (rdl) {
rdl->pushStep(DRAW_STEP_END_POPUP, false);
}
}
bool BeginTable(const char *str_id, int column, ImGuiTableFlags flags, const ImVec2 &outer_size, float inner_width) {
if (!options::opts.serverMode) { return ImGui::BeginTable(str_id, column, flags, outer_size, inner_width); }
if (!serverMode) { return ImGui::BeginTable(str_id, column, flags, outer_size, inner_width); }
if (rdl) {
rdl->pushStep(DRAW_STEP_BEGIN_TABLE, false);
rdl->pushString(str_id);
@ -737,14 +741,14 @@ namespace SmGui {
}
void EndTable() {
if (!options::opts.serverMode) { ImGui::EndTable(); return; }
if (!serverMode) { ImGui::EndTable(); return; }
if (rdl) {
rdl->pushStep(DRAW_STEP_END_TABLE, false);
}
}
void TableNextRow(ImGuiTableRowFlags row_flags, float min_row_height) {
if (!options::opts.serverMode) { ImGui::TableNextRow(row_flags, min_row_height); return; }
if (!serverMode) { ImGui::TableNextRow(row_flags, min_row_height); return; }
if (rdl) {
rdl->pushStep(DRAW_STEP_TABLE_NEXT_ROW, false);
rdl->pushInt(row_flags);
@ -753,7 +757,7 @@ namespace SmGui {
}
void TableSetColumnIndex(int column_n) {
if (!options::opts.serverMode) { ImGui::TableSetColumnIndex(column_n); return; }
if (!serverMode) { ImGui::TableSetColumnIndex(column_n); return; }
if (rdl) {
rdl->pushStep(DRAW_STEP_TABLE_SET_COLUMN_INDEX, false);
rdl->pushInt(column_n);
@ -761,7 +765,7 @@ namespace SmGui {
}
void SetNextItemWidth(float item_width) {
if (!options::opts.serverMode) { ImGui::SetNextItemWidth(item_width); return; }
if (!serverMode) { ImGui::SetNextItemWidth(item_width); return; }
if (rdl) {
rdl->pushStep(DRAW_STEP_SET_NEXT_ITEM_WIDTH, false);
rdl->pushFloat(item_width);

View File

@ -100,6 +100,7 @@ namespace SmGui {
// Rec/Play functions
// TODO: Maybe move verification to the load function instead of checking in drawFrame
void init(bool server);
void setDiff(std::string id, SmGui::DrawListElem value);
void startRecord(DrawList* dl);
void stopRecord();

View File

@ -2,7 +2,6 @@
#include <imgui.h>
#include <imgui_internal.h>
#include <config.h>
#include <options.h>
#include <spdlog/spdlog.h>
#include <filesystem>
@ -10,6 +9,14 @@ namespace style {
ImFont* baseFont;
ImFont* bigFont;
ImFont* hugeFont;
ImVector<ImWchar> ranges;
ImFontGlyphRangesBuilder builder;
#ifndef __ANDROID__
float uiScale = 1.0f;
#else
float uiScale = 3.0f;
#endif
bool loadFonts(std::string resDir) {
if (!std::filesystem::is_directory(resDir)) {
@ -17,9 +24,16 @@ namespace style {
return false;
}
baseFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 16.0f);
bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 45.0f);
hugeFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 128.0f);
// Create font range
ImFontAtlas* fonts = ImGui::GetIO().Fonts;
builder.AddRanges(fonts->GetGlyphRangesDefault());
builder.AddRanges(fonts->GetGlyphRangesCyrillic());
builder.BuildRanges(&ranges);
// Add bigger fonts for frequency select and title
baseFont = fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 16.0f * uiScale, NULL, ranges.Data);
bigFont = fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 45.0f * uiScale);
hugeFont = fonts->AddFontFromFileTTF(((std::string)(resDir + "/fonts/Roboto-Medium.ttf")).c_str(), 128.0f * uiScale);
return true;
}
@ -55,6 +69,6 @@ namespace ImGui {
}
void FillWidth() {
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
}
}

View File

@ -7,6 +7,8 @@ namespace style {
extern ImFont* bigFont;
extern ImFont* hugeFont;
extern float uiScale;
bool setDefaultStyle(std::string resDir);
bool loadFonts(std::string resDir);
void beginDisabled();

View File

@ -1,17 +1,18 @@
#include <gui/widgets/file_select.h>
#include <regex>
#include <options.h>
#include <filesystem>
#include <gui/file_dialogs.h>
#include <core.h>
FileSelect::FileSelect(std::string defaultPath, std::vector<std::string> filter) {
_filter = filter;
root = (std::string)core::args["root"];
setPath(defaultPath);
}
bool FileSelect::render(std::string id) {
bool _pathChanged = false;
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
float menuColumnWidth = ImGui::GetContentRegionAvail().x;
float buttonWidth = ImGui::CalcTextSize("...").x + 20.0f;
bool lastPathValid = pathValid;
@ -54,7 +55,7 @@ void FileSelect::setPath(std::string path, bool markChanged) {
}
std::string FileSelect::expandString(std::string input) {
input = std::regex_replace(input, std::regex("%ROOT%"), options::opts.root);
input = std::regex_replace(input, std::regex("%ROOT%"), root);
return std::regex_replace(input, std::regex("//"), "/");
}

View File

@ -22,6 +22,7 @@ private:
void worker();
std::thread workerThread;
std::vector<std::string> _filter;
std::string root = "";
bool pathValid = false;
bool dialogOpen = false;

View File

@ -1,16 +1,17 @@
#include <gui/widgets/folder_select.h>
#include <regex>
#include <options.h>
#include <filesystem>
#include <gui/file_dialogs.h>
#include <core.h>
FolderSelect::FolderSelect(std::string defaultPath) {
root = (std::string)core::args["root"];
setPath(defaultPath);
}
bool FolderSelect::render(std::string id) {
bool _pathChanged = false;
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
float menuColumnWidth = ImGui::GetContentRegionAvail().x;
float buttonWidth = ImGui::CalcTextSize("...").x + 20.0f;
bool lastPathValid = pathValid;
@ -53,7 +54,7 @@ void FolderSelect::setPath(std::string path, bool markChanged) {
}
std::string FolderSelect::expandString(std::string input) {
input = std::regex_replace(input, std::regex("%ROOT%"), options::opts.root);
input = std::regex_replace(input, std::regex("%ROOT%"), root);
return std::regex_replace(input, std::regex("//"), "/");
}

View File

@ -20,6 +20,7 @@ public:
private:
void worker();
std::thread workerThread;
std::string root = "";
bool pathValid = false;
bool dialogOpen = false;

View File

@ -2,8 +2,7 @@
#include <config.h>
#include <gui/style.h>
#include <gui/gui.h>
#include <glfw_window.h>
#include <GLFW/glfw3.h>
#include <backend.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
@ -42,9 +41,6 @@ void FrequencySelect::onPosChange() {
}
}
void FrequencySelect::onResize() {
}
void FrequencySelect::incrementDigit(int i) {
if (i < 0) {
return;
@ -87,40 +83,33 @@ void FrequencySelect::decrementDigit(int i) {
void FrequencySelect::moveCursorToDigit(int i) {
double xpos, ypos;
glfwGetCursorPos(core::window, &xpos, &ypos);
float nxpos = (digitTopMaxs[i].x + digitTopMins[i].x) / 2.0f;
glfwSetCursorPos(core::window, nxpos, ypos);
backend::getMouseScreenPos(xpos, ypos);
double nxpos = (digitTopMaxs[i].x + digitTopMins[i].x) / 2.0;
backend::setMouseScreenPos(nxpos, ypos);
}
void FrequencySelect::draw() {
window = ImGui::GetCurrentWindow();
auto window = ImGui::GetCurrentWindow();
widgetPos = ImGui::GetWindowContentRegionMin();
widgetEndPos = ImGui::GetWindowContentRegionMax();
ImVec2 cursorPos = ImGui::GetCursorPos();
widgetPos.x += window->Pos.x + cursorPos.x;
widgetPos.y += window->Pos.y - 3;
widgetEndPos.x += window->Pos.x + cursorPos.x;
widgetEndPos.y += window->Pos.y - 3;
widgetSize = ImVec2(widgetEndPos.x - widgetPos.x, widgetEndPos.y - widgetPos.y);
ImGui::PushFont(style::bigFont);
ImVec2 digitSz = ImGui::CalcTextSize("0");
ImVec2 commaSz = ImGui::CalcTextSize(".");
widgetPos.y = window->Pos.y + cursorPos.y - ((digitSz.y / 2.0f) - ceilf(15 * style::uiScale) - 5);
if (widgetPos.x != lastWidgetPos.x || widgetPos.y != lastWidgetPos.y) {
lastWidgetPos = widgetPos;
onPosChange();
}
if (widgetSize.x != lastWidgetSize.x || widgetSize.y != lastWidgetSize.y) {
lastWidgetSize = widgetSize;
onResize();
}
ImU32 disabledColor = ImGui::GetColorU32(ImGuiCol_Text, 0.3f);
ImU32 textColor = ImGui::GetColorU32(ImGuiCol_Text);
ImVec2 digitSz = ImGui::CalcTextSize("0");
ImVec2 commaSz = ImGui::CalcTextSize(".");
int digitWidth = digitSz.x;
int commaOffset = 0;
float textOffset = 11.0f * style::uiScale;
bool zeros = true;
ImGui::ItemSize(ImRect(digitTopMins[0], ImVec2(digitBottomMaxs[11].x + 15, digitBottomMaxs[11].y)));
@ -134,7 +123,7 @@ void FrequencySelect::draw() {
zeros ? disabledColor : textColor, buf);
if ((i + 1) % 3 == 0 && i < 11) {
commaOffset += commaSz.x;
window->DrawList->AddText(ImVec2(widgetPos.x + (i * digitWidth) + commaOffset + 11, widgetPos.y),
window->DrawList->AddText(ImVec2(widgetPos.x + (i * digitWidth) + commaOffset + textOffset, widgetPos.y),
zeros ? disabledColor : textColor, ".");
}
}
@ -165,23 +154,23 @@ void FrequencySelect::draw() {
}
if (onDigit) {
hovered = true;
if (rightClick || (ImGui::IsKeyPressed(GLFW_KEY_DELETE) || ImGui::IsKeyPressed(GLFW_KEY_ENTER) || ImGui::IsKeyPressed(GLFW_KEY_KP_ENTER))) {
if (rightClick || (ImGui::IsKeyPressed(ImGuiKey_Delete) || ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))) {
for (int j = i; j < 12; j++) {
digits[j] = 0;
}
frequencyChanged = true;
}
if (ImGui::IsKeyPressed(GLFW_KEY_UP)) {
if (ImGui::IsKeyPressed(ImGuiKey_UpArrow)) {
incrementDigit(i);
}
if (ImGui::IsKeyPressed(GLFW_KEY_DOWN)) {
if (ImGui::IsKeyPressed(ImGuiKey_DownArrow)) {
decrementDigit(i);
}
if ((ImGui::IsKeyPressed(GLFW_KEY_LEFT) || ImGui::IsKeyPressed(GLFW_KEY_BACKSPACE)) && i > 0) {
if ((ImGui::IsKeyPressed(ImGuiKey_LeftArrow) || ImGui::IsKeyPressed(ImGuiKey_Backspace)) && i > 0) {
moveCursorToDigit(i - 1);
}
if (ImGui::IsKeyPressed(GLFW_KEY_RIGHT) && i < 11) {
if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && i < 11) {
moveCursorToDigit(i + 1);
}
@ -223,9 +212,7 @@ void FrequencySelect::draw() {
ImGui::PopFont();
ImGui::SetCursorPosX(digitBottomMaxs[11].x + 17);
//ImGui::NewLine();
ImGui::SetCursorPosX(digitBottomMaxs[11].x + (17.0f * style::uiScale));
}
void FrequencySelect::setFrequency(int64_t freq) {

View File

@ -26,13 +26,7 @@ private:
void moveCursorToDigit(int i);
ImVec2 widgetPos;
ImVec2 widgetEndPos;
ImVec2 widgetSize;
ImVec2 lastWidgetPos;
ImVec2 lastWidgetSize;
ImGuiWindow* window;
int digits[12];
ImVec2 digitBottomMins[12];

View File

@ -26,8 +26,9 @@ void Menu::removeEntry(std::string name) {
bool Menu::draw(bool updateStates) {
bool changed = false;
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
ImGuiWindow* window = ImGui::GetCurrentWindow();
ImVec2 checkboxOffset = ImVec2(menuWidth - ImGui::GetTextLineHeight() - (6.0f * style::uiScale), - ImGui::GetTextLineHeight() - (10.0f * style::uiScale));
int displayedCount = 0;
int rawId = 0;
@ -46,6 +47,7 @@ bool Menu::draw(bool updateStates) {
continue;
}
// Draw dragged menu item
if (displayedCount == insertBefore && !draggedMenuName.empty()) {
if (updateStates) { ImGui::SetNextItemOpen(false); }
ImVec2 posMin = ImGui::GetCursorScreenPos();
@ -56,14 +58,14 @@ bool Menu::draw(bool updateStates) {
if (items[draggedOpt.name].inst != NULL) {
window->WorkRect = orignalRect;
ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6);
ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight());
ImGui::SetCursorPosX(pos.x + checkboxOffset.x);
ImGui::SetCursorPosY(pos.y + checkboxOffset.y);
bool enabled = items[draggedOpt.name].inst->isEnabled();
ImGui::Checkbox(("##_menu_checkbox_" + draggedOpt.name).c_str(), &enabled);
ImGui::SetCursorPos(pos);
}
style::endDisabled();
window->DrawList->AddRect(posMin, posMax, textColor);
window->DrawList->AddRect(posMin, posMax, textColor, 0.0f, 0, style::uiScale);
}
displayedCount++;
@ -72,7 +74,7 @@ bool Menu::draw(bool updateStates) {
ImRect orginalRect = window->WorkRect;
if (item.inst != NULL) {
window->WorkRect = ImRect(orginalRect.Min, ImVec2(orginalRect.Max.x - ImGui::GetTextLineHeight() - 6, orginalRect.Max.y));
window->WorkRect = ImRect(orginalRect.Min, ImVec2(orginalRect.Max.x - ImGui::GetTextLineHeight() - (6.0f * style::uiScale), orginalRect.Max.y));
}
ImVec2 posMin = ImGui::GetCursorScreenPos();
@ -93,13 +95,14 @@ bool Menu::draw(bool updateStates) {
continue;
}
// Draw menu header and menu content. There is a lot of boilerplate because the checkbox has to be drawn before the menu, TODO: fix
if (updateStates) { ImGui::SetNextItemOpen(opt.open); }
if (ImGui::CollapsingHeader((opt.name + "##sdrpp_main_menu").c_str())) {
if (item.inst != NULL) {
window->WorkRect = orginalRect;
ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6);
ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight());
ImGui::SetCursorPosX(pos.x + checkboxOffset.x);
ImGui::SetCursorPosY(pos.y + checkboxOffset.y);
bool enabled = item.inst->isEnabled();
if (ImGui::Checkbox(("##_menu_checkbox_" + opt.name).c_str(), &enabled)) {
enabled ? item.inst->enable() : item.inst->disable();
@ -120,8 +123,8 @@ bool Menu::draw(bool updateStates) {
else if (item.inst != NULL) {
window->WorkRect = orginalRect;
ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6);
ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight());
ImGui::SetCursorPosX(pos.x + checkboxOffset.x);
ImGui::SetCursorPosY(pos.y + checkboxOffset.y);
bool enabled = item.inst->isEnabled();
if (ImGui::Checkbox(("##_menu_checkbox_" + opt.name).c_str(), &enabled)) {
enabled ? item.inst->enable() : item.inst->disable();
@ -168,7 +171,7 @@ bool Menu::draw(bool updateStates) {
insertBefore = -1;
}
// TODO: Figure out why the hell this is needed
if (insertBefore == displayedCount && !draggedMenuName.empty()) {
if (updateStates) { ImGui::SetNextItemOpen(false); }
ImVec2 posMin = ImGui::GetCursorScreenPos();
@ -179,14 +182,14 @@ bool Menu::draw(bool updateStates) {
if (items[draggedOpt.name].inst != NULL) {
window->WorkRect = orignalRect;
ImVec2 pos = ImGui::GetCursorPos();
ImGui::SetCursorPosX(pos.x + menuWidth - ImGui::GetTextLineHeight() - 6);
ImGui::SetCursorPosY(pos.y - 10 - ImGui::GetTextLineHeight());
ImGui::SetCursorPosX(pos.x + checkboxOffset.x);
ImGui::SetCursorPosY(pos.y + checkboxOffset.y);
bool enabled = items[draggedOpt.name].inst->isEnabled();
ImGui::Checkbox(("##_menu_checkbox_" + draggedOpt.name).c_str(), &enabled);
ImGui::SetCursorPos(pos);
}
style::endDisabled();
window->DrawList->AddRect(posMin, posMax, textColor);
window->DrawList->AddRect(posMin, posMax, textColor, 0.0f, 0, style::uiScale);
}
if (!draggedMenuName.empty()) {

View File

@ -1,5 +1,6 @@
#include <gui/widgets/volume_meter.h>
#include <algorithm>
#include <gui/style.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
@ -27,15 +28,15 @@ namespace ImGui {
float it = size.x / 9;
char buf[32];
window->DrawList->AddRectFilled(min + ImVec2(0, 1), min + ImVec2(roundf((float)val * ratio), 10), IM_COL32(0, 136, 255, 255));
window->DrawList->AddLine(min, min + ImVec2(0, 9), text);
window->DrawList->AddLine(min + ImVec2(0, 9), min + ImVec2(size.x + 1, 9), text);
window->DrawList->AddRectFilled(min + ImVec2(0, 1), min + ImVec2(roundf((float)val * ratio), 10 * style::uiScale), IM_COL32(0, 136, 255, 255));
window->DrawList->AddLine(min, min + ImVec2(0, (10.0f * style::uiScale) - 1), text, style::uiScale);
window->DrawList->AddLine(min + ImVec2(0, (10.0f * style::uiScale) - 1), min + ImVec2(size.x + 1, (10.0f * style::uiScale) - 1), text, style::uiScale);
for (int i = 0; i < 10; i++) {
window->DrawList->AddLine(min + ImVec2(roundf((float)i * it), 9), min + ImVec2(roundf((float)i * it), 14), text);
window->DrawList->AddLine(min + ImVec2(roundf((float)i * it), (10.0f * style::uiScale) - 1), min + ImVec2(roundf((float)i * it), (15.0f * style::uiScale) - 1), text, style::uiScale);
sprintf(buf, "%d", i * 10);
ImVec2 sz = ImGui::CalcTextSize(buf);
window->DrawList->AddText(min + ImVec2(roundf(((float)i * it) - (sz.x / 2.0)) + 1, 16), text, buf);
window->DrawList->AddText(min + ImVec2(roundf(((float)i * it) - (sz.x / 2.0)) + 1, 16.0f * style::uiScale), text, buf);
}
}
}

View File

@ -4,9 +4,9 @@
#include <imutils.h>
#include <algorithm>
#include <volk/volk.h>
#include <GLFW/glfw3.h>
#include <spdlog/spdlog.h>
#include <gui/gui.h>
#include <gui/style.h>
float DEFAULT_COLOR_MAP[][3] = {
{ 0x00, 0x00, 0x20 },
@ -99,51 +99,53 @@ namespace ImGui {
ImU32 trace = ImGui::GetColorU32(ImGuiCol_PlotLines);
ImU32 shadow = ImGui::GetColorU32(ImGuiCol_PlotLines, 0.2);
ImU32 text = ImGui::GetColorU32(ImGuiCol_Text);
float textVOffset = 10.0f * style::uiScale;
// Vertical scale
for (float line = startLine; line > fftMin; line -= vRange) {
float yPos = widgetPos.y + fftHeight + 10 - ((line - fftMin) * scaleFactor);
window->DrawList->AddLine(ImVec2(roundf(widgetPos.x + 50), roundf(yPos)),
ImVec2(roundf(widgetPos.x + dataWidth + 50), roundf(yPos)),
IM_COL32(50, 50, 50, 255), 1.0);
float yPos = fftAreaMax.y - ((line - fftMin) * scaleFactor);
window->DrawList->AddLine(ImVec2(fftAreaMin.x, roundf(yPos)),
ImVec2(fftAreaMax.x, roundf(yPos)),
IM_COL32(50, 50, 50, 255), style::uiScale);
sprintf(buf, "%d", (int)line);
ImVec2 txtSz = ImGui::CalcTextSize(buf);
window->DrawList->AddText(ImVec2(widgetPos.x + 40 - txtSz.x, roundf(yPos - (txtSz.y / 2.0))), text, buf);
window->DrawList->AddText(ImVec2(fftAreaMin.x - txtSz.x - textVOffset, roundf(yPos - (txtSz.y / 2.0))), text, buf);
}
// Horizontal scale
double startFreq = ceilf(lowerFreq / range) * range;
double horizScale = (double)dataWidth / viewBandwidth;
float scaleVOfsset = 7 * style::uiScale;
for (double freq = startFreq; freq < upperFreq; freq += range) {
double xPos = widgetPos.x + 50 + ((freq - lowerFreq) * horizScale);
window->DrawList->AddLine(ImVec2(roundf(xPos), widgetPos.y + 10),
ImVec2(roundf(xPos), widgetPos.y + fftHeight + 10),
IM_COL32(50, 50, 50, 255), 1.0);
window->DrawList->AddLine(ImVec2(roundf(xPos), widgetPos.y + fftHeight + 10),
ImVec2(roundf(xPos), widgetPos.y + fftHeight + 17),
text, 1.0);
double xPos = fftAreaMin.x + ((freq - lowerFreq) * horizScale);
window->DrawList->AddLine(ImVec2(roundf(xPos), fftAreaMin.y + 1),
ImVec2(roundf(xPos), fftAreaMax.y),
IM_COL32(50, 50, 50, 255), style::uiScale);
window->DrawList->AddLine(ImVec2(roundf(xPos), fftAreaMax.y),
ImVec2(roundf(xPos), fftAreaMax.y + scaleVOfsset),
text, style::uiScale);
printAndScale(freq, buf);
ImVec2 txtSz = ImGui::CalcTextSize(buf);
window->DrawList->AddText(ImVec2(roundf(xPos - (txtSz.x / 2.0)), widgetPos.y + fftHeight + 10 + txtSz.y), text, buf);
window->DrawList->AddText(ImVec2(roundf(xPos - (txtSz.x / 2.0)), fftAreaMax.y + txtSz.y), text, buf);
}
// Data
if (latestFFT != NULL && fftLines != 0) {
for (int i = 1; i < dataWidth; i++) {
double aPos = widgetPos.y + fftHeight + 10 - ((latestFFT[i - 1] - fftMin) * scaleFactor);
double bPos = widgetPos.y + fftHeight + 10 - ((latestFFT[i] - fftMin) * scaleFactor);
aPos = std::clamp<double>(aPos, widgetPos.y + 10, widgetPos.y + fftHeight + 10);
bPos = std::clamp<double>(bPos, widgetPos.y + 10, widgetPos.y + fftHeight + 10);
window->DrawList->AddLine(ImVec2(widgetPos.x + 49 + i, roundf(aPos)),
ImVec2(widgetPos.x + 50 + i, roundf(bPos)), trace, 1.0);
window->DrawList->AddLine(ImVec2(widgetPos.x + 50 + i, roundf(bPos)),
ImVec2(widgetPos.x + 50 + i, widgetPos.y + fftHeight + 10), shadow, 1.0);
double aPos = fftAreaMax.y - ((latestFFT[i - 1] - fftMin) * scaleFactor);
double bPos = fftAreaMax.y - ((latestFFT[i] - fftMin) * scaleFactor);
aPos = std::clamp<double>(aPos, fftAreaMin.y + 1, fftAreaMax.y);
bPos = std::clamp<double>(bPos, fftAreaMin.y + 1, fftAreaMax.y);
window->DrawList->AddLine(ImVec2(fftAreaMin.x + i - 1, roundf(aPos)),
ImVec2(fftAreaMin.x + i, roundf(bPos)), trace, 1.0);
window->DrawList->AddLine(ImVec2(fftAreaMin.x + i, roundf(bPos)),
ImVec2(fftAreaMin.x + i, fftAreaMax.y), shadow, 1.0);
}
}
FFTRedrawArgs args;
args.min = ImVec2(widgetPos.x + 50, widgetPos.y + 9);
args.max = ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 10);
args.min = fftAreaMin;
args.max = fftAreaMax;
args.lowFreq = lowerFreq;
args.highFreq = upperFreq;
args.freqToPixelRatio = horizScale;
@ -152,13 +154,13 @@ namespace ImGui {
onFFTRedraw.emit(args);
// X Axis
window->DrawList->AddLine(ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 10),
ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 10),
text, 1.0);
window->DrawList->AddLine(ImVec2(fftAreaMin.x, fftAreaMax.y),
ImVec2(fftAreaMax.x, fftAreaMax.y),
text, style::uiScale);
// Y Axis
window->DrawList->AddLine(ImVec2(widgetPos.x + 50, widgetPos.y + 9),
ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 9),
text, 1.0);
window->DrawList->AddLine(ImVec2(fftAreaMin.x, fftAreaMin.y),
ImVec2(fftAreaMin.x, fftAreaMax.y - 1),
text, style::uiScale);
}
void WaterFall::drawWaterfall() {
@ -173,7 +175,7 @@ namespace ImGui {
for (auto const& [name, vfo] : vfos) {
window->DrawList->AddRectFilled(vfo->wfRectMin, vfo->wfRectMax, vfo->color);
if (!vfo->lineVisible) { continue; }
window->DrawList->AddLine(vfo->wfLineMin, vfo->wfLineMax, (name == selectedVFO) ? IM_COL32(255, 0, 0, 255) : IM_COL32(255, 255, 0, 255));
window->DrawList->AddLine(vfo->wfLineMin, vfo->wfLineMax, (name == selectedVFO) ? IM_COL32(255, 0, 0, 255) : IM_COL32(255, 255, 0, 255), style::uiScale);
}
}
}
@ -212,7 +214,7 @@ namespace ImGui {
bool mouseClicked = ImGui::ButtonBehavior(ImRect(fftAreaMin, wfMax), GetID("WaterfallID"), &mouseHovered, &mouseHeld,
ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_PressedOnClick);
mouseInFFTResize = (dragOrigin.x > widgetPos.x && dragOrigin.x < widgetPos.x + widgetSize.x && dragOrigin.y >= widgetPos.y + newFFTAreaHeight - 2 && dragOrigin.y <= widgetPos.y + newFFTAreaHeight + 2);
mouseInFFTResize = (dragOrigin.x > widgetPos.x && dragOrigin.x < widgetPos.x + widgetSize.x && dragOrigin.y >= widgetPos.y + newFFTAreaHeight - (2.0f * style::uiScale) && dragOrigin.y <= widgetPos.y + newFFTAreaHeight + (2.0f * style::uiScale));
mouseInFreq = IS_IN_AREA(dragOrigin, freqAreaMin, freqAreaMax);
mouseInFFT = IS_IN_AREA(dragOrigin, fftAreaMin, fftAreaMax);
mouseInWaterfall = IS_IN_AREA(dragOrigin, wfMin, wfMax);
@ -304,7 +306,7 @@ namespace ImGui {
newFFTAreaHeight = mousePos.y - widgetPos.y;
newFFTAreaHeight = std::clamp<float>(newFFTAreaHeight, 150, widgetSize.y - 50);
ImGui::GetForegroundDrawList()->AddLine(ImVec2(widgetPos.x, newFFTAreaHeight + widgetPos.y), ImVec2(widgetEndPos.x, newFFTAreaHeight + widgetPos.y),
ImGui::GetColorU32(ImGuiCol_SeparatorActive));
ImGui::GetColorU32(ImGuiCol_SeparatorActive), style::uiScale);
return;
}
@ -387,8 +389,8 @@ namespace ImGui {
}
// If the left and right keys are pressed while hovering the freq scale, move it too
bool leftKeyPressed = ImGui::IsKeyPressed(GLFW_KEY_LEFT);
if ((leftKeyPressed || ImGui::IsKeyPressed(GLFW_KEY_RIGHT)) && mouseInFreq) {
bool leftKeyPressed = ImGui::IsKeyPressed(ImGuiKey_LeftArrow);
if ((leftKeyPressed || ImGui::IsKeyPressed(ImGuiKey_RightArrow)) && mouseInFreq) {
viewOffset += leftKeyPressed ? (viewBandwidth / 20.0) : (-viewBandwidth / 20.0);
if (viewOffset + (viewBandwidth / 2.0) > wholeBandwidth / 2.0) {
@ -417,7 +419,7 @@ namespace ImGui {
// Finally, if nothing else was selected, just move the VFO
if ((VFOMoveSingleClick ? ImGui::IsMouseClicked(ImGuiMouseButton_Left) : ImGui::IsMouseDown(ImGuiMouseButton_Left)) && (mouseInFFT | mouseInWaterfall) && (mouseMoved || hoveredVFOName == "")) {
if (selVfo != NULL) {
int refCenter = mousePos.x - (widgetPos.x + 50);
int refCenter = mousePos.x - fftAreaMin.x;
if (refCenter >= 0 && refCenter < dataWidth) {
double off = ((((double)refCenter / ((double)dataWidth / 2.0)) - 1.0) * (viewBandwidth / 2.0)) + viewOffset;
off += centerFreq;
@ -435,7 +437,7 @@ namespace ImGui {
ImGui::TextUnformatted(name.c_str());
if (ImGui::IsKeyDown(GLFW_KEY_LEFT_CONTROL) || ImGui::IsKeyDown(GLFW_KEY_RIGHT_CONTROL)) {
if (ImGui::GetIO().KeyCtrl) {
ImGui::Separator();
printAndScale(_vfo->generalOffset + centerFreq, buf);
ImGui::Text("Frequency: %sHz", buf);
@ -461,7 +463,7 @@ namespace ImGui {
}
// Handle Page Up to cycle through VFOs
if (ImGui::IsKeyPressed(GLFW_KEY_PAGE_UP) && selVfo != NULL) {
if (ImGui::IsKeyPressed(ImGuiKey_PageUp) && selVfo != NULL) {
std::string next = (--vfos.end())->first;
std::string lowest = "";
double lowestOffset = INFINITY;
@ -484,7 +486,7 @@ namespace ImGui {
}
// Handle Page Down to cycle through VFOs
if (ImGui::IsKeyPressed(GLFW_KEY_PAGE_DOWN) && selVfo != NULL) {
if (ImGui::IsKeyPressed(ImGuiKey_PageDown) && selVfo != NULL) {
std::string next = (--vfos.end())->first;
std::string highest = "";
double highestOffset = -INFINITY;
@ -599,10 +601,10 @@ namespace ImGui {
float bpBottom;
if (bandPlanPos == BANDPLAN_POS_BOTTOM) {
bpBottom = widgetPos.y + fftHeight + 10;
bpBottom = fftAreaMax.y;
}
else {
bpBottom = widgetPos.y + height + 10;
bpBottom = fftAreaMin.y + height + 1;
}
@ -620,9 +622,9 @@ namespace ImGui {
start = std::clamp<double>(start, lowerFreq, upperFreq);
end = std::clamp<double>(end, lowerFreq, upperFreq);
center = (start + end) / 2.0;
aPos = widgetPos.x + 50 + ((start - lowerFreq) * horizScale);
bPos = widgetPos.x + 50 + ((end - lowerFreq) * horizScale);
cPos = widgetPos.x + 50 + ((center - lowerFreq) * horizScale);
aPos = fftAreaMin.x + ((start - lowerFreq) * horizScale);
bPos = fftAreaMin.x + ((end - lowerFreq) * horizScale);
cPos = fftAreaMin.x + ((center - lowerFreq) * horizScale);
width = bPos - aPos;
txtSz = ImGui::CalcTextSize(bandplan->bands[i].name.c_str());
if (bandplan::colorTable.find(bandplan->bands[i].type.c_str()) != bandplan::colorTable.end()) {
@ -633,22 +635,22 @@ namespace ImGui {
color = IM_COL32(255, 255, 255, 255);
colorTrans = IM_COL32(255, 255, 255, 100);
}
if (aPos <= widgetPos.x + 50) {
aPos = widgetPos.x + 51;
if (aPos <= fftAreaMin.x) {
aPos = fftAreaMin.x + 1;
}
if (bPos <= widgetPos.x + 50) {
bPos = widgetPos.x + 51;
if (bPos <= fftAreaMin.x) {
bPos = fftAreaMin.x + 1;
}
if (width >= 1.0) {
window->DrawList->AddRectFilled(ImVec2(roundf(aPos), bpBottom - height),
ImVec2(roundf(bPos), bpBottom), colorTrans);
if (startVis) {
window->DrawList->AddLine(ImVec2(roundf(aPos), bpBottom - height - 1),
ImVec2(roundf(aPos), bpBottom - 1), color);
ImVec2(roundf(aPos), bpBottom - 1), color, style::uiScale);
}
if (endVis) {
window->DrawList->AddLine(ImVec2(roundf(bPos), bpBottom - height - 1),
ImVec2(roundf(bPos), bpBottom - 1), color);
ImVec2(roundf(bPos), bpBottom - 1), color, style::uiScale);
}
}
if (txtSz.x <= width) {
@ -679,15 +681,15 @@ namespace ImGui {
int lastWaterfallHeight = waterfallHeight;
if (waterfallVisible) {
FFTAreaHeight = std::min<int>(FFTAreaHeight, widgetSize.y - 50);
FFTAreaHeight = std::min<int>(FFTAreaHeight, widgetSize.y - (50.0f * style::uiScale));
newFFTAreaHeight = FFTAreaHeight;
fftHeight = FFTAreaHeight - 50;
waterfallHeight = widgetSize.y - fftHeight - 52;
fftHeight = FFTAreaHeight - (50.0f * style::uiScale);
waterfallHeight = widgetSize.y - fftHeight - (50.0f * style::uiScale) - 2;
}
else {
fftHeight = widgetSize.y - 50;
fftHeight = widgetSize.y - (50.0f * style::uiScale);
}
dataWidth = widgetSize.x - 60.0;
dataWidth = widgetSize.x - (60.0f * style::uiScale);
if (waterfallVisible) {
// Raw FFT resize
@ -724,12 +726,14 @@ namespace ImGui {
latestFFT[i] = -1000.0; // Hide everything
}
fftAreaMin = ImVec2(widgetPos.x + 50, widgetPos.y + 9);
fftAreaMax = ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 10);
freqAreaMin = ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 11);
freqAreaMax = ImVec2(widgetPos.x + dataWidth + 50, widgetPos.y + fftHeight + 50);
wfMin = ImVec2(widgetPos.x + 50, widgetPos.y + fftHeight + 51);
wfMax = ImVec2(widgetPos.x + 50 + dataWidth, widgetPos.y + fftHeight + 51 + waterfallHeight);
fftAreaMin = ImVec2(widgetPos.x + (50.0f * style::uiScale), widgetPos.y + (9.0f * style::uiScale));
fftAreaMax = ImVec2(fftAreaMin.x + dataWidth, fftAreaMin.y + fftHeight + 1);
freqAreaMin = ImVec2(fftAreaMin.x, fftAreaMax.y + 1);
freqAreaMax = ImVec2(fftAreaMax.x, fftAreaMax.y + (40.0f * style::uiScale));
wfMin = ImVec2(fftAreaMin.x, freqAreaMax.y + 1);
wfMax = ImVec2(fftAreaMin.x + dataWidth, wfMin.y + waterfallHeight);
maxHSteps = dataWidth / (ImGui::CalcTextSize("000.000").x + 10);
maxVSteps = fftHeight / (ImGui::CalcTextSize("000.000").y);
@ -769,8 +773,8 @@ namespace ImGui {
//window->DrawList->AddRectFilled(widgetPos, widgetEndPos, IM_COL32( 0, 0, 0, 255 ));
ImU32 bg = ImGui::ColorConvertFloat4ToU32(gui::themeManager.waterfallBg);
window->DrawList->AddRectFilled(widgetPos, widgetEndPos, bg);
window->DrawList->AddRect(widgetPos, widgetEndPos, IM_COL32(50, 50, 50, 255));
window->DrawList->AddLine(ImVec2(widgetPos.x, widgetPos.y + fftHeight + 50), ImVec2(widgetPos.x + widgetSize.x, widgetPos.y + fftHeight + 50), IM_COL32(50, 50, 50, 255), 1.0);
window->DrawList->AddRect(widgetPos, widgetEndPos, IM_COL32(50, 50, 50, 255), 0.0, 0, style::uiScale);
window->DrawList->AddLine(ImVec2(widgetPos.x, freqAreaMax.y), ImVec2(widgetPos.x + widgetSize.x, freqAreaMax.y), IM_COL32(50, 50, 50, 255), style::uiScale);
if (!gui::mainWindow.lockWaterfallControls) {
inputHandled = false;
@ -1041,8 +1045,8 @@ namespace ImGui {
vfo->updateDrawingVars(viewBandwidth, dataWidth, viewOffset, widgetPos, fftHeight);
vfo->wfRectMin = ImVec2(vfo->rectMin.x, wfMin.y);
vfo->wfRectMax = ImVec2(vfo->rectMax.x, wfMax.y);
vfo->wfLineMin = ImVec2(vfo->lineMin.x, wfMin.y);
vfo->wfLineMax = ImVec2(vfo->lineMax.x, wfMax.y);
vfo->wfLineMin = ImVec2(vfo->lineMin.x, wfMin.y - 1);
vfo->wfLineMax = ImVec2(vfo->lineMax.x, wfMax.y - 1);
vfo->wfLbwSelMin = ImVec2(vfo->wfRectMin.x - 2, vfo->wfRectMin.y);
vfo->wfLbwSelMax = ImVec2(vfo->wfRectMin.x + 2, vfo->wfRectMax.y);
vfo->wfRbwSelMin = ImVec2(vfo->wfRectMax.x - 2, vfo->wfRectMin.y);
@ -1175,16 +1179,16 @@ namespace ImGui {
// Calculate the position of the line
if (reference == REF_LOWER) {
lineMin = ImVec2(widgetPos.x + 50 + left, widgetPos.y + 9);
lineMax = ImVec2(widgetPos.x + 50 + left, widgetPos.y + fftHeight + 9);
lineMin = ImVec2(gui::waterfall.fftAreaMin.x + left, gui::waterfall.fftAreaMin.y);
lineMax = ImVec2(gui::waterfall.fftAreaMin.x + left, gui::waterfall.fftAreaMax.y - 1);
}
else if (reference == REF_CENTER) {
lineMin = ImVec2(widgetPos.x + 50 + center, widgetPos.y + 9);
lineMax = ImVec2(widgetPos.x + 50 + center, widgetPos.y + fftHeight + 9);
lineMin = ImVec2(gui::waterfall.fftAreaMin.x + center, gui::waterfall.fftAreaMin.y);
lineMax = ImVec2(gui::waterfall.fftAreaMin.x + center, gui::waterfall.fftAreaMax.y - 1);
}
else if (reference == REF_UPPER) {
lineMin = ImVec2(widgetPos.x + 50 + right, widgetPos.y + 9);
lineMax = ImVec2(widgetPos.x + 50 + right, widgetPos.y + fftHeight + 9);
lineMin = ImVec2(gui::waterfall.fftAreaMin.x + right, gui::waterfall.fftAreaMin.y);
lineMax = ImVec2(gui::waterfall.fftAreaMin.x + right, gui::waterfall.fftAreaMax.y - 1);
}
int _left = left;
@ -1194,22 +1198,23 @@ namespace ImGui {
leftClamped = (left != _left);
rightClamped = (right != _right);
rectMin = ImVec2(widgetPos.x + 50 + left, widgetPos.y + 10);
rectMax = ImVec2(widgetPos.x + 51 + right, widgetPos.y + fftHeight + 10);
rectMin = ImVec2(gui::waterfall.fftAreaMin.x + left, gui::waterfall.fftAreaMin.y + 1);
rectMax = ImVec2(gui::waterfall.fftAreaMin.x + right + 1, gui::waterfall.fftAreaMax.y);
lbwSelMin = ImVec2(rectMin.x - 2, rectMin.y);
lbwSelMax = ImVec2(rectMin.x + 2, rectMax.y);
rbwSelMin = ImVec2(rectMax.x - 2, rectMin.y);
rbwSelMax = ImVec2(rectMax.x + 2, rectMax.y);
float gripSize = 2.0f * style::uiScale;
lbwSelMin = ImVec2(rectMin.x - gripSize, rectMin.y);
lbwSelMax = ImVec2(rectMin.x + gripSize, rectMax.y);
rbwSelMin = ImVec2(rectMax.x - gripSize, rectMin.y);
rbwSelMax = ImVec2(rectMax.x + gripSize, rectMax.y);
notchMin = ImVec2(widgetPos.x + 50 + notch - 2, widgetPos.y + 9);
notchMax = ImVec2(widgetPos.x + 50 + notch + 2, widgetPos.y + fftHeight + 9);
notchMin = ImVec2(gui::waterfall.fftAreaMin.x + notch - gripSize, gui::waterfall.fftAreaMin.y);
notchMax = ImVec2(gui::waterfall.fftAreaMin.x + notch + gripSize, gui::waterfall.fftAreaMax.y - 1);
}
void WaterfallVFO::draw(ImGuiWindow* window, bool selected) {
window->DrawList->AddRectFilled(rectMin, rectMax, color);
if (lineVisible) {
window->DrawList->AddLine(lineMin, lineMax, selected ? IM_COL32(255, 0, 0, 255) : IM_COL32(255, 255, 0, 255));
window->DrawList->AddLine(lineMin, lineMax, selected ? IM_COL32(255, 0, 0, 255) : IM_COL32(255, 255, 0, 255), style::uiScale);
}
if (notchVisible) {

View File

@ -236,6 +236,13 @@ namespace ImGui {
_BANDPLAN_POS_COUNT
};
ImVec2 fftAreaMin;
ImVec2 fftAreaMax;
ImVec2 freqAreaMin;
ImVec2 freqAreaMax;
ImVec2 wfMin;
ImVec2 wfMax;
private:
void drawWaterfall();
void drawFFT();
@ -260,13 +267,6 @@ namespace ImGui {
ImVec2 lastWidgetPos;
ImVec2 lastWidgetSize;
ImVec2 fftAreaMin;
ImVec2 fftAreaMax;
ImVec2 freqAreaMin;
ImVec2 freqAreaMax;
ImVec2 wfMin;
ImVec2 wfMax;
ImGuiWindow* window;
GLuint textureId;

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// dear imgui, v1.86
// dear imgui, v1.87
// (headers)
// Help:
@ -35,6 +35,7 @@ Index of this file:
// [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData)
// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont)
// [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport)
// [SECTION] Platform Dependent Interfaces (ImGuiPlatformImeData)
// [SECTION] Obsolete functions and types
*/
@ -63,8 +64,8 @@ Index of this file:
// Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
#define IMGUI_VERSION "1.86"
#define IMGUI_VERSION_NUM 18600
#define IMGUI_VERSION "1.87"
#define IMGUI_VERSION_NUM 18700
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
#define IMGUI_HAS_TABLE
@ -85,11 +86,7 @@ Index of this file:
#endif
#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers!
#define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds.
#if (__cplusplus >= 201100) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201100)
#define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in C++11
#else
#define IM_OFFSETOF(_TYPE,_MEMBER) ((size_t)&(((_TYPE*)0)->_MEMBER)) // Offset of _MEMBER within _TYPE. Old style macro.
#endif
// Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions.
#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__)
@ -104,7 +101,7 @@ Index of this file:
#endif
// Disable some of MSVC most aggressive Debug runtime checks in function header/footer (used in some simple/low-level functions)
#if defined(_MSC_VER) && !defined(__clang__) && !defined(IMGUI_DEBUG_PARANOID)
#if defined(_MSC_VER) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(IMGUI_DEBUG_PARANOID)
#define IM_MSVC_RUNTIME_CHECKS_OFF __pragma(runtime_checks("",off)) __pragma(check_stack(off)) __pragma(strict_gs_check(push,off))
#define IM_MSVC_RUNTIME_CHECKS_RESTORE __pragma(runtime_checks("",restore)) __pragma(check_stack()) __pragma(strict_gs_check(pop))
#else
@ -151,9 +148,11 @@ struct ImColor; // Helper functions to create a color that c
struct ImGuiContext; // Dear ImGui context (opaque structure, unless including imgui_internal.h)
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 ImGuiKeyData; // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions.
struct ImGuiListClipper; // Helper to manually clip large list of items
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 ImGuiPlatformImeData; // Platform IME data for io.SetPlatformImeDataFn() function.
struct ImGuiSizeCallbackData; // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use)
struct ImGuiStorage; // Helper for key->value storage
struct ImGuiStyle; // Runtime data for styling/colors
@ -171,7 +170,7 @@ typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A
typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for many Set*() functions
typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type
typedef int ImGuiDir; // -> enum ImGuiDir_ // Enum: A cardinal direction
typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A key identifier (ImGui-side enum)
typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A key identifier
typedef int ImGuiNavInput; // -> enum ImGuiNavInput_ // Enum: An input identifier for navigation
typedef int ImGuiMouseButton; // -> enum ImGuiMouseButton_ // Enum: A mouse button identifier (0=left, 1=right, 2=middle)
typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor identifier
@ -225,17 +224,8 @@ typedef signed short ImS16; // 16-bit signed integer
typedef unsigned short ImU16; // 16-bit unsigned integer
typedef signed int ImS32; // 32-bit signed integer == int
typedef unsigned int ImU32; // 32-bit unsigned integer (often used to store packed colors)
#if defined(_MSC_VER) && !defined(__clang__)
typedef signed __int64 ImS64; // 64-bit signed integer (pre and post C++11 with Visual Studio)
typedef unsigned __int64 ImU64; // 64-bit unsigned integer (pre and post C++11 with Visual Studio)
#elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100)
#include <stdint.h>
typedef int64_t ImS64; // 64-bit signed integer (pre C++11)
typedef uint64_t ImU64; // 64-bit unsigned integer (pre C++11)
#else
typedef signed long long ImS64; // 64-bit signed integer (post C++11)
typedef unsigned long long ImU64; // 64-bit unsigned integer (post C++11)
#endif
typedef signed long long ImS64; // 64-bit signed integer
typedef unsigned long long ImU64; // 64-bit unsigned integer
// Character types
// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display)
@ -524,7 +514,8 @@ namespace ImGui
// Widgets: Drag Sliders
// - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.
// - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x
// - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v',
// the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x
// - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc.
// - Format string may also be set to NULL or use the default format ("%f" or "%d").
// - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision).
@ -690,6 +681,7 @@ namespace ImGui
// - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options).
// - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup().
// - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened.
// - IMPORTANT: Notice that for OpenPopupOnItemClick() we exceptionally default flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter
IMGUI_API void OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!).
IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks
IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors)
@ -699,7 +691,7 @@ namespace ImGui
// - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking.
// - They are convenient to easily create context menus, hence the name.
// - IMPORTANT: Notice that BeginPopupContextXXX takes ImGuiPopupFlags just like OpenPopup() and unlike BeginPopup(). For full consistency, we may add ImGuiWindowFlags to the BeginPopupContextXXX functions in the future.
// - IMPORTANT: we exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter, so if you add other flags remember to re-add the ImGuiPopupFlags_MouseButtonRight.
// - IMPORTANT: Notice that we exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter, so if you add other flags remember to re-add the ImGuiPopupFlags_MouseButtonRight.
IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp!
IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window.
IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1); // open+begin popup when clicked in void (where there are no windows).
@ -711,7 +703,6 @@ namespace ImGui
IMGUI_API bool IsPopupOpen(const char* str_id, ImGuiPopupFlags flags = 0); // return true if the popup is open.
// Tables
// [BETA API] API may evolve slightly! If you use this, please update to the next version when it comes out!
// - Full-featured replacement for old Columns API.
// - See Demo->Tables for demo code.
// - See top of imgui_tables.cpp for general commentary.
@ -807,7 +798,7 @@ namespace ImGui
// - If you stop calling BeginDragDropSource() the payload is preserved however it won't have a preview tooltip (we currently display a fallback "..." tooltip, see #1725)
// - An item can be both drag source and drop target.
IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call after submitting an item which may be dragged. when this return true, you can call SetDragDropPayload() + EndDragDropSource()
IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui.
IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0); // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. Return true when payload has been accepted.
IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true!
IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget()
IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released.
@ -882,13 +873,16 @@ namespace ImGui
IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b);
// Inputs Utilities: Keyboard
// - For 'int user_key_index' you can use your own indices/enums according to how your backend/engine stored them in io.KeysDown[].
// - We don't know the meaning of those value. You can use GetKeyIndex() to map a ImGuiKey_ value into the user index.
IMGUI_API int GetKeyIndex(ImGuiKey imgui_key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key]
IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index].
IMGUI_API bool IsKeyPressed(int user_key_index, bool repeat = true); // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate
IMGUI_API bool IsKeyReleased(int user_key_index); // was key released (went from Down to !Down)?
IMGUI_API int GetKeyPressedAmount(int key_index, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate
// Without IMGUI_DISABLE_OBSOLETE_KEYIO: (legacy support)
// - For 'ImGuiKey key' you can still use your legacy native/user indices according to how your backend/engine stored them in io.KeysDown[].
// With IMGUI_DISABLE_OBSOLETE_KEYIO: (this is the way forward)
// - Any use of 'ImGuiKey' will assert when key < 512 will be passed, previously reserved as native/user keys indices
// - GetKeyIndex() is pass-through and therefore deprecated (gone if IMGUI_DISABLE_OBSOLETE_KEYIO is defined)
IMGUI_API bool IsKeyDown(ImGuiKey key); // is key being held.
IMGUI_API bool IsKeyPressed(ImGuiKey key, bool repeat = true); // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate
IMGUI_API bool IsKeyReleased(ImGuiKey key); // was key released (went from Down to !Down)?
IMGUI_API int GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate
IMGUI_API const char* GetKeyName(ImGuiKey key); // [DEBUG] returns English name of the key. Those names a provided for debugging purpose and are not meant to be saved persistently not compared.
IMGUI_API void CaptureKeyboardFromApp(bool want_capture_keyboard_value = true); // attention: misleading name! manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application to handle). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard_value"; after the next NewFrame() call.
// Inputs Utilities: Mouse
@ -902,7 +896,7 @@ namespace ImGui
IMGUI_API int GetMouseClickedCount(ImGuiMouseButton button); // return the number of successive mouse-clicks at the time where a click happen (otherwise 0).
IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block.
IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available
IMGUI_API bool IsAnyMouseDown(); // is any mouse button held?
IMGUI_API bool IsAnyMouseDown(); // [WILL OBSOLETE] is any mouse button held? This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid.
IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls
IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve mouse position at the time of opening popup we have BeginPopup() into (helper to avoid user backing that value themselves)
IMGUI_API bool IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f); // is mouse dragging? (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold)
@ -980,9 +974,7 @@ enum ImGuiWindowFlags_
ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup()
ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal()
ImGuiWindowFlags_ChildMenu = 1 << 28 // Don't use! For internal use by BeginMenu()
// [Obsolete]
//ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // --> Set io.ConfigWindowsResizeFromEdges=true and make sure mouse cursors are supported by backend (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors)
//ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // [Obsolete] --> Set io.ConfigWindowsResizeFromEdges=true and make sure mouse cursors are supported by backend (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors)
};
// Flags for ImGui::InputText()
@ -1117,8 +1109,7 @@ enum ImGuiTabItemFlags_
};
// Flags for ImGui::BeginTable()
// [BETA API] API may evolve slightly! If you use this, please update to the next version when it comes out!
// - Important! Sizing policies have complex and subtle side effects, more so than you would expect.
// - Important! Sizing policies have complex and subtle side effects, much more so than you would expect.
// Read comments/demos carefully + experiment with live demos to get acquainted with them.
// - The DEFAULT sizing policies are:
// - Default to ImGuiTableFlags_SizingFixedFit if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize.
@ -1126,8 +1117,8 @@ enum ImGuiTabItemFlags_
// - When ScrollX is off:
// - Table defaults to ImGuiTableFlags_SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch with same weight.
// - Columns sizing policy allowed: Stretch (default), Fixed/Auto.
// - Fixed Columns will generally obtain their requested width (unless the table cannot fit them all).
// - Stretch Columns will share the remaining width.
// - Fixed Columns (if any) will generally obtain their requested width (unless the table cannot fit them all).
// - Stretch Columns will share the remaining width according to their respective weight.
// - Mixed Fixed/Stretch columns is possible but has various side-effects on resizing behaviors.
// The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.
// (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing).
@ -1241,7 +1232,7 @@ enum ImGuiTableColumnFlags_
enum ImGuiTableRowFlags_
{
ImGuiTableRowFlags_None = 0,
ImGuiTableRowFlags_Headers = 1 << 0 // Identify header row (set default background color + width of its contents accounted different for auto column width)
ImGuiTableRowFlags_Headers = 1 << 0 // Identify header row (set default background color + width of its contents accounted differently for auto column width)
};
// Enum for ImGui::TableSetBgColor()
@ -1350,10 +1341,11 @@ enum ImGuiSortDirection_
ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc.
};
// User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array
enum ImGuiKey_
{
ImGuiKey_Tab,
// Keyboard
ImGuiKey_None = 0,
ImGuiKey_Tab = 512, // == ImGuiKey_NamedKey_BEGIN
ImGuiKey_LeftArrow,
ImGuiKey_RightArrow,
ImGuiKey_UpArrow,
@ -1368,65 +1360,148 @@ enum ImGuiKey_
ImGuiKey_Space,
ImGuiKey_Enter,
ImGuiKey_Escape,
ImGuiKey_KeyPadEnter,
ImGuiKey_A, // for text edit CTRL+A: select all
ImGuiKey_C, // for text edit CTRL+C: copy
ImGuiKey_V, // for text edit CTRL+V: paste
ImGuiKey_X, // for text edit CTRL+X: cut
ImGuiKey_Y, // for text edit CTRL+Y: redo
ImGuiKey_Z, // for text edit CTRL+Z: undo
ImGuiKey_COUNT
ImGuiKey_LeftCtrl, ImGuiKey_LeftShift, ImGuiKey_LeftAlt, ImGuiKey_LeftSuper,
ImGuiKey_RightCtrl, ImGuiKey_RightShift, ImGuiKey_RightAlt, ImGuiKey_RightSuper,
ImGuiKey_Menu,
ImGuiKey_0, ImGuiKey_1, ImGuiKey_2, ImGuiKey_3, ImGuiKey_4, ImGuiKey_5, ImGuiKey_6, ImGuiKey_7, ImGuiKey_8, ImGuiKey_9,
ImGuiKey_A, ImGuiKey_B, ImGuiKey_C, ImGuiKey_D, ImGuiKey_E, ImGuiKey_F, ImGuiKey_G, ImGuiKey_H, ImGuiKey_I, ImGuiKey_J,
ImGuiKey_K, ImGuiKey_L, ImGuiKey_M, ImGuiKey_N, ImGuiKey_O, ImGuiKey_P, ImGuiKey_Q, ImGuiKey_R, ImGuiKey_S, ImGuiKey_T,
ImGuiKey_U, ImGuiKey_V, ImGuiKey_W, ImGuiKey_X, ImGuiKey_Y, ImGuiKey_Z,
ImGuiKey_F1, ImGuiKey_F2, ImGuiKey_F3, ImGuiKey_F4, ImGuiKey_F5, ImGuiKey_F6,
ImGuiKey_F7, ImGuiKey_F8, ImGuiKey_F9, ImGuiKey_F10, ImGuiKey_F11, ImGuiKey_F12,
ImGuiKey_Apostrophe, // '
ImGuiKey_Comma, // ,
ImGuiKey_Minus, // -
ImGuiKey_Period, // .
ImGuiKey_Slash, // /
ImGuiKey_Semicolon, // ;
ImGuiKey_Equal, // =
ImGuiKey_LeftBracket, // [
ImGuiKey_Backslash, // \ (this text inhibit multiline comment caused by backslash)
ImGuiKey_RightBracket, // ]
ImGuiKey_GraveAccent, // `
ImGuiKey_CapsLock,
ImGuiKey_ScrollLock,
ImGuiKey_NumLock,
ImGuiKey_PrintScreen,
ImGuiKey_Pause,
ImGuiKey_Keypad0, ImGuiKey_Keypad1, ImGuiKey_Keypad2, ImGuiKey_Keypad3, ImGuiKey_Keypad4,
ImGuiKey_Keypad5, ImGuiKey_Keypad6, ImGuiKey_Keypad7, ImGuiKey_Keypad8, ImGuiKey_Keypad9,
ImGuiKey_KeypadDecimal,
ImGuiKey_KeypadDivide,
ImGuiKey_KeypadMultiply,
ImGuiKey_KeypadSubtract,
ImGuiKey_KeypadAdd,
ImGuiKey_KeypadEnter,
ImGuiKey_KeypadEqual,
// Gamepad (some of those are analog values, 0.0f to 1.0f) // NAVIGATION action
ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) // --
ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) // --
ImGuiKey_GamepadFaceUp, // Y (Xbox) X (Switch) Triangle (PS) // -> ImGuiNavInput_Input
ImGuiKey_GamepadFaceDown, // A (Xbox) B (Switch) Cross (PS) // -> ImGuiNavInput_Activate
ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // -> ImGuiNavInput_Menu
ImGuiKey_GamepadFaceRight, // B (Xbox) A (Switch) Circle (PS) // -> ImGuiNavInput_Cancel
ImGuiKey_GamepadDpadUp, // D-pad Up // -> ImGuiNavInput_DpadUp
ImGuiKey_GamepadDpadDown, // D-pad Down // -> ImGuiNavInput_DpadDown
ImGuiKey_GamepadDpadLeft, // D-pad Left // -> ImGuiNavInput_DpadLeft
ImGuiKey_GamepadDpadRight, // D-pad Right // -> ImGuiNavInput_DpadRight
ImGuiKey_GamepadL1, // L Bumper (Xbox) L (Switch) L1 (PS) // -> ImGuiNavInput_FocusPrev + ImGuiNavInput_TweakSlow
ImGuiKey_GamepadR1, // R Bumper (Xbox) R (Switch) R1 (PS) // -> ImGuiNavInput_FocusNext + ImGuiNavInput_TweakFast
ImGuiKey_GamepadL2, // L Trigger (Xbox) ZL (Switch) L2 (PS) [Analog]
ImGuiKey_GamepadR2, // R Trigger (Xbox) ZR (Switch) R2 (PS) [Analog]
ImGuiKey_GamepadL3, // L Thumbstick (Xbox) L3 (Switch) L3 (PS)
ImGuiKey_GamepadR3, // R Thumbstick (Xbox) R3 (Switch) R3 (PS)
ImGuiKey_GamepadLStickUp, // [Analog] // -> ImGuiNavInput_LStickUp
ImGuiKey_GamepadLStickDown, // [Analog] // -> ImGuiNavInput_LStickDown
ImGuiKey_GamepadLStickLeft, // [Analog] // -> ImGuiNavInput_LStickLeft
ImGuiKey_GamepadLStickRight, // [Analog] // -> ImGuiNavInput_LStickRight
ImGuiKey_GamepadRStickUp, // [Analog]
ImGuiKey_GamepadRStickDown, // [Analog]
ImGuiKey_GamepadRStickLeft, // [Analog]
ImGuiKey_GamepadRStickRight, // [Analog]
// Keyboard Modifiers
// - This is mirroring the data also written to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper, in a format allowing
// them to be accessed via standard key API, allowing calls such as IsKeyPressed(), IsKeyReleased(), querying duration etc.
// - Code polling every keys (e.g. an interface to detect a key press for input mapping) might want to ignore those
// and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiKey_ModCtrl).
// - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys.
// In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and
// backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user...
ImGuiKey_ModCtrl,
ImGuiKey_ModShift,
ImGuiKey_ModAlt,
ImGuiKey_ModSuper,
ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value
// [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + a io.KeyMap[] array.
// We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE)
ImGuiKey_NamedKey_BEGIN = 512,
ImGuiKey_NamedKey_END = ImGuiKey_COUNT,
ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN,
#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
ImGuiKey_KeysData_SIZE = ImGuiKey_NamedKey_COUNT, // Size of KeysData[]: only hold named keys
ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN // First key stored in KeysData[0]
#else
ImGuiKey_KeysData_SIZE = ImGuiKey_COUNT, // Size of KeysData[]: hold legacy 0..512 keycodes + named keys
ImGuiKey_KeysData_OFFSET = 0 // First key stored in KeysData[0]
#endif
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
, ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter // Renamed in 1.87
#endif
};
// To test io.KeyMods (which is a combination of individual fields io.KeyCtrl, io.KeyShift, io.KeyAlt set by user/backend)
// Helper "flags" version of key-mods to store and compare multiple key-mods easily. Sometimes used for storage (e.g. io.KeyMods) but otherwise not much used in public API.
enum ImGuiKeyModFlags_
{
ImGuiKeyModFlags_None = 0,
ImGuiKeyModFlags_Ctrl = 1 << 0,
ImGuiKeyModFlags_Shift = 1 << 1,
ImGuiKeyModFlags_Alt = 1 << 2,
ImGuiKeyModFlags_Super = 1 << 3
ImGuiKeyModFlags_None = 0,
ImGuiKeyModFlags_Ctrl = 1 << 0,
ImGuiKeyModFlags_Shift = 1 << 1,
ImGuiKeyModFlags_Alt = 1 << 2,
ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key
};
// Gamepad/Keyboard navigation
// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays.
// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls.
// Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame().
// Read instructions in imgui.cpp for more details. Download PNG/PSD at http://dearimgui.org/controls_sheets.
enum ImGuiNavInput_
{
// Gamepad Mapping
ImGuiNavInput_Activate, // activate / open / toggle / tweak value // e.g. Cross (PS4), A (Xbox), A (Switch), Space (Keyboard)
ImGuiNavInput_Cancel, // cancel / close / exit // e.g. Circle (PS4), B (Xbox), B (Switch), Escape (Keyboard)
ImGuiNavInput_Input, // text input / on-screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch), Return (Keyboard)
ImGuiNavInput_Menu, // tap: toggle menu / hold: focus, move, resize // e.g. Square (PS4), X (Xbox), Y (Switch), Alt (Keyboard)
ImGuiNavInput_DpadLeft, // move / tweak / resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down (Gamepads), Arrow keys (Keyboard)
ImGuiNavInput_Activate, // Activate / Open / Toggle / Tweak value // e.g. Cross (PS4), A (Xbox), A (Switch), Space (Keyboard)
ImGuiNavInput_Cancel, // Cancel / Close / Exit // e.g. Circle (PS4), B (Xbox), B (Switch), Escape (Keyboard)
ImGuiNavInput_Input, // Text input / On-Screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch), Return (Keyboard)
ImGuiNavInput_Menu, // Tap: Toggle menu / Hold: Focus, Move, Resize // e.g. Square (PS4), X (Xbox), Y (Switch), Alt (Keyboard)
ImGuiNavInput_DpadLeft, // Move / Tweak / Resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down (Gamepads), Arrow keys (Keyboard)
ImGuiNavInput_DpadRight, //
ImGuiNavInput_DpadUp, //
ImGuiNavInput_DpadDown, //
ImGuiNavInput_LStickLeft, // scroll / move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down
ImGuiNavInput_LStickLeft, // Scroll / Move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down
ImGuiNavInput_LStickRight, //
ImGuiNavInput_LStickUp, //
ImGuiNavInput_LStickDown, //
ImGuiNavInput_FocusPrev, // next window (w/ PadMenu) // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch)
ImGuiNavInput_FocusNext, // prev window (w/ PadMenu) // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch)
ImGuiNavInput_TweakSlow, // slower tweaks // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch)
ImGuiNavInput_TweakFast, // faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch)
ImGuiNavInput_FocusPrev, // Focus Next window (w/ PadMenu) // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch)
ImGuiNavInput_FocusNext, // Focus Prev window (w/ PadMenu) // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch)
ImGuiNavInput_TweakSlow, // Slower tweaks // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch)
ImGuiNavInput_TweakFast, // Faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch)
// [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them.
// Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[].
ImGuiNavInput_KeyLeft_, // move left // = Arrow keys
ImGuiNavInput_KeyRight_, // move right
ImGuiNavInput_KeyUp_, // move up
ImGuiNavInput_KeyDown_, // move down
ImGuiNavInput_COUNT,
ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyLeft_
// Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from keyboard keys instead of io.NavInputs[].
ImGuiNavInput_KeyLeft_, // Move left // = Arrow keys
ImGuiNavInput_KeyRight_, // Move right
ImGuiNavInput_KeyUp_, // Move up
ImGuiNavInput_KeyDown_, // Move down
ImGuiNavInput_COUNT
};
// Configuration flags stored in io.ConfigFlags. Set by user/application.
enum ImGuiConfigFlags_
{
ImGuiConfigFlags_None = 0,
ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeysDown[].
ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.AddKeyEvent() calls
ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui backend to fill io.NavInputs[]. Backend also needs to set ImGuiBackendFlags_HasGamepad.
ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your backend, otherwise ImGui will react as if the mouse is jumping around back and forth.
ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set.
@ -1599,9 +1674,7 @@ enum ImGuiColorEditFlags_
ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV
// Obsolete names (will be removed)
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
, ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69]
#endif
// ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69]
};
// Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
@ -1817,6 +1890,16 @@ struct ImGuiStyle
// Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage.
//-----------------------------------------------------------------------------
// [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.
struct ImGuiKeyData
{
bool Down; // True for if key is down
float DownDuration; // Duration the key has been down (<0.0f: not pressed, 0.0f: just pressed, >0.0f: time held)
float DownDurationPrev; // Last frame duration the key has been down
float AnalogValue; // 0.0f..1.0f for gamepad values
};
struct ImGuiIO
{
//------------------------------------------------------------------
@ -1833,7 +1916,6 @@ struct ImGuiIO
float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds.
float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels.
float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging.
int KeyMap[ImGuiKey_COUNT]; // <unset> // Map of indices into the KeysDown[512] entries array which represent your "native" keyboard state.
float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.).
float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds.
void* UserData; // = NULL // Store your own data for retrieval by callbacks.
@ -1847,6 +1929,7 @@ struct ImGuiIO
// Miscellaneous options
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__) // 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 ConfigInputTrickleEventQueue; // = true // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.
bool ConfigInputTextCursorBlink; // = true // Enable blinking cursor (optional as some users consider it to be distracting).
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)
@ -1873,31 +1956,31 @@ struct ImGuiIO
// 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 (*ImeSetInputScreenPosFn)(int x, int y);
void* ImeWindowHandle; // = NULL // (Windows) Set this to your HWND to get automatic IME cursor positioning.
void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data);
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
void* ImeWindowHandle; // = NULL // [Obsolete] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning.
#else
void* _UnusedPadding; // Unused field to keep data structure the same size.
#endif
//------------------------------------------------------------------
// Input - Fill before calling NewFrame()
// Input - Call before calling NewFrame()
//------------------------------------------------------------------
ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.)
bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API.
float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text.
float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends.
bool KeyCtrl; // Keyboard modifier pressed: Control
bool KeyShift; // Keyboard modifier pressed: Shift
bool KeyAlt; // Keyboard modifier pressed: Alt
bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows
bool KeysDown[512]; // Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys).
float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame().
// Input Functions
IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)
IMGUI_API void AddKeyAnalogEvent(ImGuiKey key, bool down, float v); // Queue a new key down/up event for analog values (e.g. ImGuiKey_Gamepad_ values). Dead-zones should be handled by the backend.
IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered)
IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change
IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update
IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window)
IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input
IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate
IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string
// Functions
IMGUI_API void AddInputCharacter(unsigned int c); // Queue new character input
IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue new character input from an UTF-16 character, it can be a surrogate
IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue new characters input from an UTF-8 string
IMGUI_API void AddFocusEvent(bool focused); // Notifies Dear ImGui when hosting platform windows lose or gain input focus
IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually
IMGUI_API void ClearInputKeys(); // [Internal] Release all keys
IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually
IMGUI_API void ClearInputKeys(); // [Internal] Release all keys
IMGUI_API void SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode.
//------------------------------------------------------------------
// Output - Updated by NewFrame() or EndFrame()/Render()
@ -1905,50 +1988,71 @@ struct ImGuiIO
// generally easier and more correct to use their state BEFORE calling NewFrame(). See FAQ for details!)
//------------------------------------------------------------------
bool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.).
bool WantCaptureKeyboard; // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.).
bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active).
bool WantSetMousePos; // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled.
bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving!
bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag.
bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events).
float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames.
int MetricsRenderVertices; // Vertices output during last call to Render()
int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3
int MetricsRenderWindows; // Number of visible windows
int MetricsActiveWindows; // Number of active windows
int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts.
ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta.
bool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.).
bool WantCaptureKeyboard; // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.).
bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active).
bool WantSetMousePos; // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled.
bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving!
bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag.
bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events).
float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames.
int MetricsRenderVertices; // Vertices output during last call to Render()
int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3
int MetricsRenderWindows; // Number of visible windows
int MetricsActiveWindows; // Number of active windows
int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts.
ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta.
// Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame.
// This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent().
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512.
bool KeysDown[512]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys).
#endif
//------------------------------------------------------------------
// [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed!
//------------------------------------------------------------------
bool WantCaptureMouseUnlessPopupClose;// Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.
ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame()
ImGuiKeyModFlags KeyModsPrev; // Previous key mods
ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)
ImVec2 MouseClickedPos[5]; // Position at time of clicking
double MouseClickedTime[5]; // Time of last click (used to figure out double-click)
bool MouseClicked[5]; // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0)
bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2)
ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down
ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done.
bool MouseReleased[5]; // Mouse button went from Down to !Down
bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds.
bool MouseDownOwnedUnlessPopupClose[5];//Track if button was clicked inside a dear imgui window.
float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked)
float MouseDownDurationPrev[5]; // Previous time the mouse button has been down
ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point
float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point
float KeysDownDuration[512]; // Duration the keyboard key has been down (0.0f == just pressed)
float KeysDownDurationPrev[512]; // Previous duration the key has been down
// Main Input State
// (this block used to be written by backend, since 1.87 it is best to NOT write to those directly, call the AddXXX functions above instead)
// (reading from those variables is fair game, as they are extremely unlikely to be moving anywhere)
ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.)
bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API.
float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text.
float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends.
bool KeyCtrl; // Keyboard modifier down: Control
bool KeyShift; // Keyboard modifier down: Shift
bool KeyAlt; // Keyboard modifier down: Alt
bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows
float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame().
// Other state maintained from data above + IO function calls
ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame()
ImGuiKeyModFlags KeyModsPrev; // Key mods flags (from previous frame)
ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this.
bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.
ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)
ImVec2 MouseClickedPos[5]; // Position at time of clicking
double MouseClickedTime[5]; // Time of last click (used to figure out double-click)
bool MouseClicked[5]; // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0)
bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2)
ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down
ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done.
bool MouseReleased[5]; // Mouse button went from Down to !Down
bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds.
bool MouseDownOwnedUnlessPopupClose[5]; //Track if button was clicked inside a dear imgui window.
float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked)
float MouseDownDurationPrev[5]; // Previous time the mouse button has been down
float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds)
float NavInputsDownDuration[ImGuiNavInput_COUNT];
float NavInputsDownDurationPrev[ImGuiNavInput_COUNT];
float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui.
float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui.
bool AppFocusLost;
ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16
ImVector<ImWchar> InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper.
ImS8 BackendUsingLegacyKeyArrays; // -1: unknown, 0: using AddKeyEvent(), 1: using legacy io.KeysDown[]
bool BackendUsingLegacyNavInputArray; // 0: using AddKeyAnalogEvent(), 1: writing to legacy io.NavInputs[] directly
ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16()
ImVector<ImWchar> InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper.
IMGUI_API ImGuiIO();
};
@ -2283,16 +2387,16 @@ typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* c
#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-1)
// Typically, 1 command = 1 GPU draw call (unless command is a callback)
// - VtxOffset/IdxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled,
// those fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices.
// Pre-1.71 backends will typically ignore the VtxOffset/IdxOffset fields.
// - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled,
// this fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices.
// Backends made for <1.71. will typically ignore the VtxOffset fields.
// - The ClipRect/TextureId/VtxOffset fields must be contiguous as we memcmp() them together (this is asserted for).
struct ImDrawCmd
{
ImVec4 ClipRect; // 4*4 // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in "viewport" coordinates
ImTextureID TextureId; // 4-8 // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas.
unsigned int VtxOffset; // 4 // Start offset in vertex buffer. ImGuiBackendFlags_RendererHasVtxOffset: always 0, otherwise may be >0 to support meshes larger than 64K vertices with 16-bit indices.
unsigned int IdxOffset; // 4 // Start offset in index buffer. Always equal to sum of ElemCount drawn so far.
unsigned int IdxOffset; // 4 // Start offset in index buffer.
unsigned int ElemCount; // 4 // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[].
ImDrawCallback UserCallback; // 4-8 // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally.
void* UserCallbackData; // 4-8 // The draw callback code can access this.
@ -2498,8 +2602,8 @@ struct ImDrawList
inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } // Write vertex with unique index
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
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); }
inline void PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); }
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)
#endif
// [Internal helpers]
@ -2728,10 +2832,9 @@ struct ImFontAtlas
int PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors
int PackIdLines; // Custom texture rectangle ID for baked anti-aliased lines
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+
// [Obsolete]
//typedef ImFontAtlasCustomRect CustomRect; // OBSOLETED in 1.72+
//typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+
#endif
};
// Font runtime data and rendering
@ -2815,6 +2918,9 @@ struct ImGuiViewport
ImVec2 WorkPos; // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos)
ImVec2 WorkSize; // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size)
// Platform/Backend Dependent Data
void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (under Win32 this is expected to be a HWND, unused for other platforms)
ImGuiViewport() { memset(this, 0, sizeof(*this)); }
// Helpers
@ -2822,12 +2928,35 @@ struct ImGuiViewport
ImVec2 GetWorkCenter() const { return ImVec2(WorkPos.x + WorkSize.x * 0.5f, WorkPos.y + WorkSize.y * 0.5f); }
};
//-----------------------------------------------------------------------------
// [SECTION] Platform Dependent Interfaces
//-----------------------------------------------------------------------------
// (Optional) Support for IME (Input Method Editor) via the io.SetPlatformImeDataFn() function.
struct ImGuiPlatformImeData
{
bool WantVisible; // A widget wants the IME to be visible
ImVec2 InputPos; // Position of the input cursor
float InputLineHeight; // Line height
ImGuiPlatformImeData() { memset(this, 0, sizeof(*this)); }
};
//-----------------------------------------------------------------------------
// [SECTION] Obsolete functions and types
// (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details)
// Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead.
//-----------------------------------------------------------------------------
namespace ImGui
{
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
IMGUI_API int GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]
#else
static inline int GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "ImGuiKey and native_index was merged together and native_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey."); return key; }
#endif
}
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
namespace ImGui
{
@ -2858,24 +2987,21 @@ namespace ImGui
static inline bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); }
// OBSOLETED in 1.77 (from June 2020)
static inline bool BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); }
// OBSOLETED in 1.72 (from April 2019)
static inline void TreeAdvanceToLabelPos() { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); }
// OBSOLETED in 1.71 (from June 2019)
static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); }
// OBSOLETED in 1.70 (from May 2019)
static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; }
// Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE)
//static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019)
//static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018)
//static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018)
//static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018)
//static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018)
//static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
//static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
//static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
//static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
//static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
//static inline void TreeAdvanceToLabelPos() { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); } // OBSOLETED in 1.72 (from July 2019)
//static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } // OBSOLETED in 1.71 (from June 2019)
//static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; } // OBSOLETED in 1.70 (from May 2019)
//static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019)
//static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018)
//static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018)
//static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018)
//static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018)
//static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
//static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
//static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
//static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
//static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
}
// OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect()

View File

@ -1,4 +1,4 @@
// dear imgui, v1.86
// dear imgui, v1.87
// (demo code)
// Help:
@ -92,6 +92,7 @@ Index of this file:
// Visual Studio warnings
#ifdef _MSC_VER
#pragma warning (disable: 4127) // condition expression is constant
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
#endif
@ -228,7 +229,6 @@ void ImGui::ShowUserGuide()
ImGui::BulletText("CTRL+X/C/V to use clipboard cut/copy/paste.");
ImGui::BulletText("CTRL+Z,CTRL+Y to undo/redo.");
ImGui::BulletText("ESCAPE to revert.");
ImGui::BulletText("You can apply arithmetic operators +,*,/ on numerical values.\nUse +- to subtract.");
ImGui::Unindent();
ImGui::BulletText("With keyboard navigation enabled:");
ImGui::Indent();
@ -454,13 +454,15 @@ void ImGui::ShowDemoWindow(bool* p_open)
ImGui::SameLine();
ImGui::Text("<<PRESS SPACE TO DISABLE>>");
}
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Space)))
if (ImGui::IsKeyPressed(ImGuiKey_Space))
io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
}
ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange);
ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility.");
ImGui::Checkbox("io.ConfigInputTrickleEventQueue", &io.ConfigInputTrickleEventQueue);
ImGui::SameLine(); HelpMarker("Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.");
ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink);
ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting)");
ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting).");
ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText);
ImGui::SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).");
ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges);
@ -481,6 +483,7 @@ void ImGui::ShowDemoWindow(bool* p_open)
"Here we expose them as read-only fields to avoid breaking interactions with your backend.");
// Make a local copy to avoid modifying actual backend flags.
// FIXME: We don't use BeginDisabled() to keep label bright, maybe we need a BeginReadonly() equivalent..
ImGuiBackendFlags backend_flags = io.BackendFlags;
ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &backend_flags, ImGuiBackendFlags_HasGamepad);
ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &backend_flags, ImGuiBackendFlags_HasMouseCursors);
@ -673,10 +676,6 @@ static void ShowDemoWindowWidgets()
IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat");
static int i0 = 123;
ImGui::InputInt("input int", &i0);
ImGui::SameLine(); HelpMarker(
"You can apply arithmetic operators +,*,/ on numerical values.\n"
" e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\n"
"Use +- to subtract.");
static float f0 = 0.001f;
ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f");
@ -5639,6 +5638,8 @@ static void ShowDemoWindowColumns()
ImGui::TreePop();
}
namespace ImGui { extern ImGuiKeyData* GetKeyData(ImGuiKey key); }
static void ShowDemoWindowMisc()
{
IMGUI_DEMO_MARKER("Filtering");
@ -5692,18 +5693,75 @@ static void ShowDemoWindowMisc()
}
// Display Keyboard/Mouse state
IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Keyboard & Navigation State");
if (ImGui::TreeNode("Keyboard & Navigation State"))
IMGUI_DEMO_MARKER("Inputs, Navigation & Focus/Keyboard, Gamepad & Navigation State");
if (ImGui::TreeNode("Keyboard, Gamepad & Navigation State"))
{
ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyDown(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X) (%.02f secs)", i, i, io.KeysDownDuration[i]); }
ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X)", i, i); }
ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d (0x%X)", i, i); }
// We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allow displaying the data for old/new backends.
// User code should never have to go through such hoops: old code may use native keycodes, new code may use ImGuiKey codes.
#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
const ImGuiKey key_first = ImGuiKey_NamedKey_BEGIN;
#else
struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array
const ImGuiKey key_first = 0;
#endif
ImGui::Text("Keys down:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyDown(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d (%.02f secs)", ImGui::GetKeyName(key), key, ImGui::GetKeyData(key)->DownDuration); } }
ImGui::Text("Keys pressed:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyPressed(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } }
ImGui::Text("Keys released:"); for (ImGuiKey key = key_first; key < ImGuiKey_COUNT; key++) { if (funcs::IsLegacyNativeDupe(key)) continue; if (ImGui::IsKeyReleased(key)) { ImGui::SameLine(); ImGui::Text("\"%s\" %d", ImGui::GetKeyName(key), key); } }
ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f (%.02f secs)", i, io.NavInputs[i], io.NavInputsDownDuration[i]); }
ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); }
// Draw an arbitrary US keyboard layout to visualize translated keys
{
const ImVec2 key_size = ImVec2(35.0f, 35.0f);
const float key_rounding = 3.0f;
const ImVec2 key_face_size = ImVec2(25.0f, 25.0f);
const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f);
const float key_face_rounding = 2.0f;
const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f);
const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f);
const float key_row_offset = 9.0f;
ImVec2 board_min = ImGui::GetCursorScreenPos();
ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f);
ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y);
struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; };
const KeyLayoutData keys_to_display[] =
{
{ 0, 0, "", ImGuiKey_Tab }, { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R },
{ 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F },
{ 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V }
};
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->PushClipRect(board_min, board_max, true);
for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++)
{
const KeyLayoutData* key_data = &keys_to_display[n];
ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y);
ImVec2 key_max = ImVec2(key_min.x + key_size.x, key_min.y + key_size.y);
draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding);
draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding);
ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y);
ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y);
draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f);
draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding);
ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y);
draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label);
if (ImGui::IsKeyDown(key_data->Key))
draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding);
}
draw_list->PopClipRect();
ImGui::Dummy(ImVec2(board_max.x - board_min.x, board_max.y - board_min.y));
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Capture override"))
{
ImGui::Button("Hovering me sets the\nkeyboard capture flag");
if (ImGui::IsItemHovered())
ImGui::CaptureKeyboardFromApp(true);
@ -5868,6 +5926,9 @@ void ImGui::ShowAboutWindow(bool* p_open)
#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS");
#endif
#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO
ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_KEYIO");
#endif
#ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS");
#endif
@ -5981,7 +6042,7 @@ void ImGui::ShowAboutWindow(bool* p_open)
namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); }
// Demo helper function to select among loaded fonts.
// Here we use the regular BeginCombo()/EndCombo() api which is more the more flexible one.
// Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one.
void ImGui::ShowFontSelector(const char* label)
{
ImGuiIO& io = ImGui::GetIO();
@ -7509,8 +7570,8 @@ static void ShowExampleAppCustomRendering(bool* p_open)
// Context menu (under default mouse threshold)
ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
if (opt_enable_context_menu && ImGui::IsMouseReleased(ImGuiMouseButton_Right) && drag_delta.x == 0.0f && drag_delta.y == 0.0f)
ImGui::OpenPopupOnItemClick("context");
if (opt_enable_context_menu && drag_delta.x == 0.0f && drag_delta.y == 0.0f)
ImGui::OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
if (ImGui::BeginPopup("context"))
{
if (adding_line)

View File

@ -1,4 +1,4 @@
// dear imgui, v1.86
// dear imgui, v1.87
// (drawing and font code)
/*
@ -402,10 +402,9 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
void ImDrawList::_ResetForNewFrame()
{
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
// (those should be IM_STATIC_ASSERT() in theory but with our pre C++11 setup the whole check doesn't compile with GCC)
IM_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0);
IM_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4));
IM_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0);
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4));
IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));
CmdBuffer.resize(0);
IdxBuffer.resize(0);
@ -488,9 +487,10 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
}
// Compare ClipRect, TextureId and VtxOffset with a single memcmp()
#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset
#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset
#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset
#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset
#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset)
// Try to merge two last draw commands
void ImDrawList::_TryMergeDrawCmds()
@ -498,7 +498,7 @@ void ImDrawList::_TryMergeDrawCmds()
IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
ImDrawCmd* prev_cmd = curr_cmd - 1;
if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL)
if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL)
{
prev_cmd->ElemCount += curr_cmd->ElemCount;
CmdBuffer.pop_back();
@ -521,7 +521,7 @@ void ImDrawList::_OnChangedClipRect()
// Try to merge with previous command if it matches, else use current command
ImDrawCmd* prev_cmd = curr_cmd - 1;
if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && prev_cmd->UserCallback == NULL)
if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL)
{
CmdBuffer.pop_back();
return;
@ -544,7 +544,7 @@ void ImDrawList::_OnChangedTextureID()
// Try to merge with previous command if it matches, else use current command
ImDrawCmd* prev_cmd = curr_cmd - 1;
if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && prev_cmd->UserCallback == NULL)
if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL)
{
CmdBuffer.pop_back();
return;
@ -1733,13 +1733,13 @@ void ImDrawListSplitter::Merge(ImDrawList* draw_list)
for (int i = 1; i < _Count; i++)
{
ImDrawChannel& ch = _Channels[i];
// Equivalent of PopUnusedDrawCmd() for this channel's cmdbuffer and except we don't need to test for UserCallback.
if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0)
if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0 && ch._CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd()
ch._CmdBuffer.pop_back();
if (ch._CmdBuffer.Size > 0 && last_cmd != NULL)
{
// Do not include ImDrawCmd_AreSequentialIdxOffset() in the compare as we rebuild IdxOffset values ourselves.
// Manipulating IdxOffset (e.g. by reordering draw commands like done by RenderDimmedBackgroundBehindWindow()) is not supported within a splitter.
ImDrawCmd* next_cmd = &ch._CmdBuffer[0];
if (ImDrawCmd_HeaderCompare(last_cmd, next_cmd) == 0 && last_cmd->UserCallback == NULL && next_cmd->UserCallback == NULL)
{

View File

@ -1,455 +0,0 @@
// dear imgui: Platform Backend for GLFW
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
// (Requires: GLFW 3.1+)
// Implemented features:
// [X] Platform: Clipboard support.
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE).
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2021-08-17: *BREAKING CHANGE*: Now using glfwSetWindowFocusCallback() to calling io.AddFocusEvent(). If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() and forward it to the backend via ImGui_ImplGlfw_WindowFocusCallback().
// 2021-07-29: *BREAKING CHANGE*: Now using glfwSetCursorEnterCallback(). MousePos is correctly reported when the host platform window is hovered but not focused. If you called ImGui_ImplGlfw_InitXXX() with install_callbacks = false, you MUST install glfwSetWindowFocusCallback() callback and forward it to the backend via ImGui_ImplGlfw_CursorEnterCallback().
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors.
// 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor).
// 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown.
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
// 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter().
// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them.
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples.
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()).
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set.
// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
#include "imgui.h"
#include "imgui_impl_glfw.h"
// GLFW
#include <GLFW/glfw3.h>
#ifdef _WIN32
#undef APIENTRY
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3native.h> // for glfwGetWin32Window
#endif
#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING
#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED
#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity
#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale
#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface
#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released?
#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR
#else
#define GLFW_HAS_NEW_CURSORS (0)
#endif
// GLFW data
enum GlfwClientApi
{
GlfwClientApi_Unknown,
GlfwClientApi_OpenGL,
GlfwClientApi_Vulkan
};
struct ImGui_ImplGlfw_Data
{
GLFWwindow* Window;
GlfwClientApi ClientApi;
double Time;
GLFWwindow* MouseWindow;
bool MouseJustPressed[ImGuiMouseButton_COUNT];
GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT];
bool InstalledCallbacks;
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
GLFWwindowfocusfun PrevUserCallbackWindowFocus;
GLFWcursorenterfun PrevUserCallbackCursorEnter;
GLFWmousebuttonfun PrevUserCallbackMousebutton;
GLFWscrollfun PrevUserCallbackScroll;
GLFWkeyfun PrevUserCallbackKey;
GLFWcharfun PrevUserCallbackChar;
GLFWmonitorfun PrevUserCallbackMonitor;
ImGui_ImplGlfw_Data() { memset(this, 0, sizeof(*this)); }
};
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
// - Because glfwPollEvents() process all windows and some events may be called outside of it, you will need to register your own callbacks
// (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks.
// - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it.
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : NULL;
}
// Functions
static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data)
{
return glfwGetClipboardString((GLFWwindow*)user_data);
}
static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text)
{
glfwSetClipboardString((GLFWwindow*)user_data, text);
}
void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackMousebutton != NULL && window == bd->Window)
bd->PrevUserCallbackMousebutton(window, button, action, mods);
if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(bd->MouseJustPressed))
bd->MouseJustPressed[button] = true;
}
void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackScroll != NULL && window == bd->Window)
bd->PrevUserCallbackScroll(window, xoffset, yoffset);
ImGuiIO& io = ImGui::GetIO();
io.MouseWheelH += (float)xoffset;
io.MouseWheel += (float)yoffset;
}
void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackKey != NULL && window == bd->Window)
bd->PrevUserCallbackKey(window, key, scancode, action, mods);
ImGuiIO& io = ImGui::GetIO();
if (key >= 0 && key < IM_ARRAYSIZE(io.KeysDown))
{
if (action == GLFW_PRESS)
io.KeysDown[key] = true;
if (action == GLFW_RELEASE)
io.KeysDown[key] = false;
}
// Modifiers are not reliable across systems
io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL];
io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT];
io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT];
#ifdef _WIN32
io.KeySuper = false;
#else
io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER];
#endif
}
void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackWindowFocus != NULL && window == bd->Window)
bd->PrevUserCallbackWindowFocus(window, focused);
ImGuiIO& io = ImGui::GetIO();
io.AddFocusEvent(focused != 0);
}
void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackCursorEnter != NULL && window == bd->Window)
bd->PrevUserCallbackCursorEnter(window, entered);
if (entered)
bd->MouseWindow = window;
if (!entered && bd->MouseWindow == window)
bd->MouseWindow = NULL;
}
void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if (bd->PrevUserCallbackChar != NULL && window == bd->Window)
bd->PrevUserCallbackChar(window, c);
ImGuiIO& io = ImGui::GetIO();
io.AddInputCharacter(c);
}
void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int)
{
// Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too.
}
static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!");
// Setup backend capabilities flags
ImGui_ImplGlfw_Data* bd = IM_NEW(ImGui_ImplGlfw_Data)();
io.BackendPlatformUserData = (void*)bd;
io.BackendPlatformName = "imgui_impl_glfw";
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
bd->Window = window;
bd->Time = 0.0;
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT;
io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE;
io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
io.KeyMap[ImGuiKey_KeyPadEnter] = GLFW_KEY_KP_ENTER;
io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
io.ClipboardUserData = bd->Window;
#if defined(_WIN32)
io.ImeWindowHandle = (void*)glfwGetWin32Window(bd->Window);
#endif
// Create mouse cursors
// (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist,
// GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting.
// Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.)
GLFWerrorfun prev_error_callback = glfwSetErrorCallback(NULL);
bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
#if GLFW_HAS_NEW_CURSORS
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR);
#else
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
#endif
glfwSetErrorCallback(prev_error_callback);
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
bd->PrevUserCallbackWindowFocus = NULL;
bd->PrevUserCallbackCursorEnter = NULL;
bd->PrevUserCallbackMousebutton = NULL;
bd->PrevUserCallbackScroll = NULL;
bd->PrevUserCallbackKey = NULL;
bd->PrevUserCallbackChar = NULL;
bd->PrevUserCallbackMonitor = NULL;
if (install_callbacks)
{
bd->InstalledCallbacks = true;
bd->PrevUserCallbackWindowFocus = glfwSetWindowFocusCallback(window, ImGui_ImplGlfw_WindowFocusCallback);
bd->PrevUserCallbackCursorEnter = glfwSetCursorEnterCallback(window, ImGui_ImplGlfw_CursorEnterCallback);
bd->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback);
bd->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback);
bd->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback);
bd->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback);
bd->PrevUserCallbackMonitor = glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback);
}
bd->ClientApi = client_api;
return true;
}
bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks)
{
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL);
}
bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks)
{
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan);
}
bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks)
{
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown);
}
void ImGui_ImplGlfw_Shutdown()
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
if (bd->InstalledCallbacks)
{
glfwSetWindowFocusCallback(bd->Window, bd->PrevUserCallbackWindowFocus);
glfwSetCursorEnterCallback(bd->Window, bd->PrevUserCallbackCursorEnter);
glfwSetMouseButtonCallback(bd->Window, bd->PrevUserCallbackMousebutton);
glfwSetScrollCallback(bd->Window, bd->PrevUserCallbackScroll);
glfwSetKeyCallback(bd->Window, bd->PrevUserCallbackKey);
glfwSetCharCallback(bd->Window, bd->PrevUserCallbackChar);
glfwSetMonitorCallback(bd->PrevUserCallbackMonitor);
}
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
glfwDestroyCursor(bd->MouseCursors[cursor_n]);
io.BackendPlatformName = NULL;
io.BackendPlatformUserData = NULL;
IM_DELETE(bd);
}
static void ImGui_ImplGlfw_UpdateMousePosAndButtons()
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
const ImVec2 mouse_pos_prev = io.MousePos;
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
// Update mouse buttons
// (if a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame)
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
{
io.MouseDown[i] = bd->MouseJustPressed[i] || glfwGetMouseButton(bd->Window, i) != 0;
bd->MouseJustPressed[i] = false;
}
#ifdef __EMSCRIPTEN__
const bool focused = true;
#else
const bool focused = glfwGetWindowAttrib(bd->Window, GLFW_FOCUSED) != 0;
#endif
GLFWwindow* mouse_window = (bd->MouseWindow == bd->Window || focused) ? bd->Window : NULL;
// Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos && focused)
glfwSetCursorPos(bd->Window, (double)mouse_pos_prev.x, (double)mouse_pos_prev.y);
// Set Dear ImGui mouse position from OS position
if (mouse_window != NULL)
{
double mouse_x, mouse_y;
glfwGetCursorPos(mouse_window, &mouse_x, &mouse_y);
io.MousePos = ImVec2((float)mouse_x, (float)mouse_y);
}
}
static void ImGui_ImplGlfw_UpdateMouseCursor()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
return;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
glfwSetInputMode(bd->Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
}
else
{
// Show OS mouse cursor
// FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here.
glfwSetCursor(bd->Window, bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]);
glfwSetInputMode(bd->Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
}
}
static void ImGui_ImplGlfw_UpdateGamepads()
{
ImGuiIO& io = ImGui::GetIO();
memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
// Update gamepad inputs
#define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; }
#define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; }
int axes_count = 0, buttons_count = 0;
const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count);
const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count);
MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A
MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B
MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X
MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y
MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left
MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right
MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up
MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down
MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB
MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB
MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB
MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB
MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f);
MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f);
MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f);
MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f);
#undef MAP_BUTTON
#undef MAP_ANALOG
if (axes_count > 0 && buttons_count > 0)
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
else
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
}
void ImGui_ImplGlfw_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplGlfw_InitForXXX()?");
// Setup display size (every frame to accommodate for window resizing)
int w, h;
int display_w, display_h;
glfwGetWindowSize(bd->Window, &w, &h);
glfwGetFramebufferSize(bd->Window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h);
if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);
// Setup time step
double current_time = glfwGetTime();
io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);
bd->Time = current_time;
ImGui_ImplGlfw_UpdateMousePosAndButtons();
ImGui_ImplGlfw_UpdateMouseCursor();
// Update game controllers (if enabled and available)
ImGui_ImplGlfw_UpdateGamepads();
}

View File

@ -96,9 +96,23 @@
#include <stdint.h> // intptr_t
#endif
// Clang warnings with -Weverything
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
#if __has_warning("-Wzero-as-null-pointer-constant")
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif
#endif
// GL includes
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <GLES2/gl2.h>
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
#include <OpenGLES/ES2/gl.h> // Use GL ES 2
#else
#include <GLES2/gl2.h> // Use GL ES 2
#endif
#if defined(__EMSCRIPTEN__)
#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
@ -464,7 +478,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
continue;
// Apply scissor/clipping rectangle (Y is inverted in OpenGL)
glScissor((int)clip_min.x, (int)(fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y));
glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y));
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
@ -791,3 +805,7 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects()
if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; }
ImGui_ImplOpenGL3_DestroyFontsTexture();
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif

View File

@ -437,7 +437,7 @@ GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc);
/* gl3w internal state */
union GL3WProcs {
GL3WglProc ptr[53];
GL3WglProc ptr[54];
struct {
PFNGLACTIVETEXTUREPROC ActiveTexture;
PFNGLATTACHSHADERPROC AttachShader;

View File

@ -1,4 +1,4 @@
// dear imgui, v1.86
// dear imgui, v1.87
// (internal structures/api)
// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility!
@ -18,6 +18,7 @@ Index of this file:
// [SECTION] Generic helpers
// [SECTION] ImDrawList support
// [SECTION] Widgets support: flags, enums, data structures
// [SECTION] Inputs support
// [SECTION] Clipper support
// [SECTION] Navigation support
// [SECTION] Columns support
@ -199,15 +200,13 @@ namespace ImStb
// Debug Logging for selected systems. Remove the '((void)0) //' to enable.
//#define IMGUI_DEBUG_LOG_POPUP IMGUI_DEBUG_LOG // Enable log
//#define IMGUI_DEBUG_LOG_NAV IMGUI_DEBUG_LOG // Enable log
//#define IMGUI_DEBUG_LOG_IO IMGUI_DEBUG_LOG // Enable log
#define IMGUI_DEBUG_LOG_POPUP(...) ((void)0) // Disable log
#define IMGUI_DEBUG_LOG_NAV(...) ((void)0) // Disable log
#define IMGUI_DEBUG_LOG_IO(...) ((void)0) // Disable log
// Static Asserts
#if (__cplusplus >= 201100) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201100)
#define IM_STATIC_ASSERT(_COND) static_assert(_COND, "")
#else
#define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1]
#endif
// "Paranoid" Debug Asserts are meant to only be enabled during specific debugging/work, otherwise would slow down the code too much.
// We currently don't have many of those so the effect is currently negligible, but onward intent to add more aggressive ones in the code.
@ -441,8 +440,9 @@ static inline float ImLengthSqr(const ImVec2& lhs)
static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); }
static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; }
static inline float ImFloor(float f) { return (float)(int)(f); }
static inline float ImFloorSigned(float f) { return (float)((f >= 0 || (int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
static inline float ImFloorSigned(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }
static inline ImVec2 ImFloorSigned(const ImVec2& v) { return ImVec2(ImFloorSigned(v.x), ImFloorSigned(v.y)); }
static inline int ImModPositive(int a, int b) { return (a + b) % b; }
static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; }
static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }
@ -540,17 +540,18 @@ inline void ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on ran
// Helper: ImBitArray class (wrapper over ImBitArray functions)
// Store 1-bit per value.
template<int BITCOUNT>
struct IMGUI_API ImBitArray
template<int BITCOUNT, int OFFSET = 0>
struct ImBitArray
{
ImU32 Storage[(BITCOUNT + 31) >> 5];
ImBitArray() { ClearAllBits(); }
void ClearAllBits() { memset(Storage, 0, sizeof(Storage)); }
void SetAllBits() { memset(Storage, 255, sizeof(Storage)); }
bool TestBit(int n) const { IM_ASSERT(n < BITCOUNT); return ImBitArrayTestBit(Storage, n); }
void SetBit(int n) { IM_ASSERT(n < BITCOUNT); ImBitArraySetBit(Storage, n); }
void ClearBit(int n) { IM_ASSERT(n < BITCOUNT); ImBitArrayClearBit(Storage, n); }
void SetBitRange(int n, int n2) { ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2)
bool TestBit(int n) const { IM_ASSERT(n + OFFSET < BITCOUNT); return ImBitArrayTestBit(Storage, n + OFFSET); }
void SetBit(int n) { IM_ASSERT(n + OFFSET < BITCOUNT); ImBitArraySetBit(Storage, n + OFFSET); }
void ClearBit(int n) { IM_ASSERT(n + OFFSET < BITCOUNT); ImBitArrayClearBit(Storage, n + OFFSET); }
void SetBitRange(int n, int n2) { ImBitArraySetBitRange(Storage, n + OFFSET, n2 + OFFSET); } // Works on range [n..n2)
bool operator[](int n) const { IM_ASSERT(n + OFFSET < BITCOUNT); return ImBitArrayTestBit(Storage, n + OFFSET); }
};
// Helper: ImBitVector
@ -621,7 +622,7 @@ struct ImSpanAllocator
// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object.
typedef int ImPoolIdx;
template<typename T>
struct IMGUI_API ImPool
struct ImPool
{
ImVector<T> Buf; // Contiguous data
ImGuiStorage Map; // ID->Index
@ -658,7 +659,7 @@ struct IMGUI_API ImPool
// We store the chunk size first, and align the final size on 4 bytes boundaries.
// The tedious/zealous amount of casting is to avoid -Wcast-align warnings.
template<typename T>
struct IMGUI_API ImChunkStream
struct ImChunkStream
{
ImVector<char> Buf;
@ -897,28 +898,6 @@ enum ImGuiPlotType
ImGuiPlotType_Histogram
};
enum ImGuiInputSource
{
ImGuiInputSource_None = 0,
ImGuiInputSource_Mouse,
ImGuiInputSource_Keyboard,
ImGuiInputSource_Gamepad,
ImGuiInputSource_Nav, // Stored in g.ActiveIdSource only
ImGuiInputSource_Clipboard, // Currently only used by InputText()
ImGuiInputSource_COUNT
};
// FIXME-NAV: Clarify/expose various repeat delay/rate
enum ImGuiInputReadMode
{
ImGuiInputReadMode_Down,
ImGuiInputReadMode_Pressed,
ImGuiInputReadMode_Released,
ImGuiInputReadMode_Repeat,
ImGuiInputReadMode_RepeatSlow,
ImGuiInputReadMode_RepeatFast
};
enum ImGuiPopupPositionPolicy
{
ImGuiPopupPositionPolicy_Default,
@ -1171,6 +1150,81 @@ struct ImGuiPtrOrIndex
ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; }
};
//-----------------------------------------------------------------------------
// [SECTION] Inputs support
//-----------------------------------------------------------------------------
typedef ImBitArray<ImGuiKey_NamedKey_COUNT, -ImGuiKey_NamedKey_BEGIN> ImBitArrayForNamedKeys;
enum ImGuiKeyPrivate_
{
ImGuiKey_LegacyNativeKey_BEGIN = 0,
ImGuiKey_LegacyNativeKey_END = 512,
ImGuiKey_Gamepad_BEGIN = ImGuiKey_GamepadStart,
ImGuiKey_Gamepad_END = ImGuiKey_GamepadRStickRight + 1
};
enum ImGuiInputEventType
{
ImGuiInputEventType_None = 0,
ImGuiInputEventType_MousePos,
ImGuiInputEventType_MouseWheel,
ImGuiInputEventType_MouseButton,
ImGuiInputEventType_Key,
ImGuiInputEventType_Char,
ImGuiInputEventType_Focus,
ImGuiInputEventType_COUNT
};
enum ImGuiInputSource
{
ImGuiInputSource_None = 0,
ImGuiInputSource_Mouse,
ImGuiInputSource_Keyboard,
ImGuiInputSource_Gamepad,
ImGuiInputSource_Clipboard, // Currently only used by InputText()
ImGuiInputSource_Nav, // Stored in g.ActiveIdSource only
ImGuiInputSource_COUNT
};
// FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension?
// Using ImVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor'
struct ImGuiInputEventMousePos { float PosX, PosY; };
struct ImGuiInputEventMouseWheel { float WheelX, WheelY; };
struct ImGuiInputEventMouseButton { int Button; bool Down; };
struct ImGuiInputEventKey { ImGuiKey Key; bool Down; float AnalogValue; };
struct ImGuiInputEventText { unsigned int Char; };
struct ImGuiInputEventAppFocused { bool Focused; };
struct ImGuiInputEvent
{
ImGuiInputEventType Type;
ImGuiInputSource Source;
union
{
ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos
ImGuiInputEventMouseWheel MouseWheel; // if Type == ImGuiInputEventType_MouseWheel
ImGuiInputEventMouseButton MouseButton; // if Type == ImGuiInputEventType_MouseButton
ImGuiInputEventKey Key; // if Type == ImGuiInputEventType_Key
ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text
ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus
};
bool AddedByTestEngine;
ImGuiInputEvent() { memset(this, 0, sizeof(*this)); }
};
// FIXME-NAV: Clarify/expose various repeat delay/rate
enum ImGuiInputReadMode
{
ImGuiInputReadMode_Down,
ImGuiInputReadMode_Pressed,
ImGuiInputReadMode_Released,
ImGuiInputReadMode_Repeat,
ImGuiInputReadMode_RepeatSlow,
ImGuiInputReadMode_RepeatFast
};
//-----------------------------------------------------------------------------
// [SECTION] Clipper support
//-----------------------------------------------------------------------------
@ -1499,6 +1553,8 @@ struct ImGuiContext
bool Initialized;
bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it.
ImGuiIO IO;
ImVector<ImGuiInputEvent> InputEventsQueue; // Input events which will be tricked/written into IO structure.
ImVector<ImGuiInputEvent> InputEventsTrail; // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail.
ImGuiStyle Style;
ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back()
float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window.
@ -1553,7 +1609,7 @@ struct ImGuiContext
bool ActiveIdUsingMouseWheel; // Active widget will want to read mouse wheel. Blocks scrolling the underlying window.
ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)
ImU32 ActiveIdUsingNavInputMask; // Active widget will want to read those nav inputs.
ImU64 ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array.
ImBitArrayForNamedKeys ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array.
ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)
ImGuiWindow* ActiveIdWindow;
ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard)
@ -1705,8 +1761,8 @@ struct ImGuiContext
ImVector<ImGuiID> MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once
// Platform support
ImVec2 PlatformImePos; // Cursor position request & last passed to the OS Input Method Editor
ImVec2 PlatformImeLastPos;
ImGuiPlatformImeData PlatformImeData; // Data updated by current frame
ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data (when changing we will call io.SetPlatformImeDataFn
char PlatformLocaleDecimalPoint; // '.' or *localeconv()->decimal_point
// Settings
@ -1789,7 +1845,7 @@ struct ImGuiContext
ActiveIdUsingMouseWheel = false;
ActiveIdUsingNavDirMask = 0x00;
ActiveIdUsingNavInputMask = 0x00;
ActiveIdUsingKeyInputMask = 0x00;
ActiveIdUsingKeyInputMask.ClearAllBits();
ActiveIdClickOffset = ImVec2(-1, -1);
ActiveIdWindow = NULL;
ActiveIdSource = ImGuiInputSource_None;
@ -1870,7 +1926,8 @@ struct ImGuiContext
TooltipOverrideCount = 0;
TooltipSlowDelay = 0.50f;
PlatformImePos = PlatformImeLastPos = ImVec2(FLT_MAX, FLT_MAX);
PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
PlatformLocaleDecimalPoint = '.';
SettingsLoaded = false;
@ -2284,7 +2341,7 @@ struct IMGUI_API ImGuiTable
ImRect InnerRect; // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is
ImRect WorkRect;
ImRect InnerClipRect;
ImRect BgClipRect; // We use this to cpu-clip cell background color fill
ImRect BgClipRect; // We use this to cpu-clip cell background color fill, evolve during the frame as we cross frozen rows boundaries
ImRect Bg0ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped
ImRect Bg2ClipRectForDrawCmd; // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect.
ImRect HostClipRect; // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window.
@ -2453,6 +2510,7 @@ namespace ImGui
IMGUI_API void Shutdown(ImGuiContext* context); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext().
// NewFrame
IMGUI_API void UpdateInputEvents(bool trickle_fast_inputs);
IMGUI_API void UpdateHoveredWindowAndCaptureFlags();
IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window);
IMGUI_API void UpdateMouseMovingWindowNewFrame();
@ -2573,6 +2631,7 @@ namespace ImGui
IMGUI_API void NavMoveRequestCancel();
IMGUI_API void NavMoveRequestApplyResult();
IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
IMGUI_API const char* GetNavInputName(ImGuiNavInput n);
IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode);
IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f);
IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate);
@ -2589,16 +2648,23 @@ namespace ImGui
// Inputs
// FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; }
inline bool IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; }
inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; }
IMGUI_API ImGuiKeyData* GetKeyData(ImGuiKey key);
IMGUI_API void SetItemUsingMouseWheel();
IMGUI_API void SetActiveIdUsingNavAndKeys();
inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; }
inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; }
inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; IM_ASSERT(key < 64); return (g.ActiveIdUsingKeyInputMask & ((ImU64)1 << key)) != 0; }
inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask[key]; }
inline void SetActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key); }
IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f);
inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { ImGuiContext& g = *GImGui; const int key_index = g.IO.KeyMap[key]; return (key_index >= 0) ? IsKeyPressed(key_index, repeat) : false; }
inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; }
inline bool IsNavInputTest(ImGuiNavInput n, ImGuiInputReadMode rm) { return (GetNavInputAmount(n, rm) > 0.0f); }
IMGUI_API ImGuiKeyModFlags GetMergedKeyModFlags();
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); }
#endif
// Drag and Drop
IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id);
@ -2750,7 +2816,7 @@ namespace ImGui
IMGUI_API const ImGuiDataTypeInfo* DataTypeGetInfo(ImGuiDataType data_type);
IMGUI_API int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format);
IMGUI_API void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2);
IMGUI_API bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* p_data, const char* format);
IMGUI_API bool DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format);
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);
@ -2815,7 +2881,9 @@ struct ImFontBuilderIO
};
// Helper for font builder
#ifdef IMGUI_ENABLE_STB_TRUETYPE
IMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype();
#endif
IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent);
IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque);
@ -2839,7 +2907,8 @@ extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiI
#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,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log
#else
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)0)
#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) ((void)0)
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g)
#endif
//-----------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
// dear imgui, v1.86
// dear imgui, v1.87
// (tables and columns code)
/*
@ -1569,18 +1569,21 @@ ImGuiTableColumnFlags ImGui::TableGetColumnFlags(int column_n)
// Return the cell rectangle based on currently known height.
// - Important: we generally don't know our row height until the end of the row, so Max.y will be incorrect in many situations.
// The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it.
// The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it, or in TableEndRow() when we locked that height.
// - Important: if ImGuiTableFlags_PadOuterX is set but ImGuiTableFlags_PadInnerX is not set, the outer-most left and right
// columns report a small offset so their CellBgRect can extend up to the outer border.
// FIXME: But the rendering code in TableEndRow() nullifies that with clamping required for scrolling.
ImRect ImGui::TableGetCellBgRect(const ImGuiTable* table, int column_n)
{
const ImGuiTableColumn* column = &table->Columns[column_n];
float x1 = column->MinX;
float x2 = column->MaxX;
if (column->PrevEnabledColumn == -1)
x1 -= table->CellSpacingX1;
if (column->NextEnabledColumn == -1)
x2 += table->CellSpacingX2;
//if (column->PrevEnabledColumn == -1)
// x1 -= table->OuterPaddingX;
//if (column->NextEnabledColumn == -1)
// x2 += table->OuterPaddingX;
x1 = ImMax(x1, table->WorkRect.Min.x);
x2 = ImMin(x2, table->WorkRect.Max.x);
return ImRect(x1, table->RowPosY1, x2, table->RowPosY2);
}
@ -1797,10 +1800,12 @@ void ImGui::TableEndRow(ImGuiTable* table)
ImGuiTableCellData* cell_data_end = &table->RowCellData[table->RowCellDataCurrent];
for (ImGuiTableCellData* cell_data = &table->RowCellData[0]; cell_data <= cell_data_end; cell_data++)
{
// As we render the BG here we need to clip things (for layout we would not)
// FIXME: This cancels the OuterPadding addition done by TableGetCellBgRect(), need to keep it while rendering correctly while scrolling.
const ImGuiTableColumn* column = &table->Columns[cell_data->Column];
ImRect cell_bg_rect = TableGetCellBgRect(table, cell_data->Column);
cell_bg_rect.ClipWith(table->BgClipRect);
cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped
cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped when scrolling
cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX);
window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor);
}
@ -2353,7 +2358,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table)
// Don't attempt to merge if there are multiple draw calls within the column
ImDrawChannel* src_channel = &splitter->_Channels[channel_no];
if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0)
if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback != NULL) // Equivalent of PopUnusedDrawCmd()
src_channel->_CmdBuffer.pop_back();
if (src_channel->_CmdBuffer.Size != 1)
continue;

View File

@ -1,4 +1,4 @@
// dear imgui, v1.86
// dear imgui, v1.87
// (widgets code)
/*
@ -731,6 +731,7 @@ bool ImGui::SmallButton(const char* label)
// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id)
bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiButtonFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
@ -748,16 +749,17 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiBut
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags);
return pressed;
}
bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
const ImGuiID id = window->GetID(str_id);
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
const float default_size = GetFrameHeight();
@ -778,6 +780,7 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu
RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding);
RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), text_col, dir);
IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags);
return pressed;
}
@ -1992,24 +1995,10 @@ void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const
// User can input math operators (e.g. +100) to edit a numerical values.
// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess..
bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* p_data, const char* format)
bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format)
{
while (ImCharIsBlankA(*buf))
buf++;
// We don't support '-' op because it would conflict with inputing negative value.
// Instead you can use +-100 to subtract from an existing value
char op = buf[0];
if (op == '+' || op == '*' || op == '/')
{
buf++;
while (ImCharIsBlankA(*buf))
buf++;
}
else
{
op = 0;
}
if (!buf[0])
return false;
@ -2021,54 +2010,11 @@ bool ImGui::DataTypeApplyOpFromText(const char* buf, const char* initial_value_b
if (format == NULL)
format = type_info->ScanFmt;
// FIXME-LEGACY: The aim is to remove those operators and write a proper expression evaluator at some point..
int arg1i = 0;
if (data_type == ImGuiDataType_S32)
if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64 || data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
{
int* v = (int*)p_data;
int arg0i = *v;
float arg1f = 0.0f;
if (op && sscanf(initial_value_buf, format, &arg0i) < 1)
return false;
// Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision
if (op == '+') { if (sscanf(buf, "%d", &arg1i)) *v = (int)(arg0i + arg1i); } // Add (use "+-" to subtract)
else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (int)(arg0i * arg1f); } // Multiply
else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } // Divide
else { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; } // Assign constant
}
else if (data_type == ImGuiDataType_Float)
{
// For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in
format = "%f";
float* v = (float*)p_data;
float arg0f = *v, arg1f = 0.0f;
if (op && sscanf(initial_value_buf, format, &arg0f) < 1)
return false;
if (sscanf(buf, format, &arg1f) < 1)
return false;
if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract)
else if (op == '*') { *v = arg0f * arg1f; } // Multiply
else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
else { *v = arg1f; } // Assign constant
}
else if (data_type == ImGuiDataType_Double)
{
format = "%lf"; // scanf differentiate float/double unlike printf which forces everything to double because of ellipsis
double* v = (double*)p_data;
double arg0f = *v, arg1f = 0.0;
if (op && sscanf(initial_value_buf, format, &arg0f) < 1)
return false;
if (sscanf(buf, format, &arg1f) < 1)
return false;
if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract)
else if (op == '*') { *v = arg0f * arg1f; } // Multiply
else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide
else { *v = arg1f; } // Assign constant
}
else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64)
{
// All other types assign constant
// We don't bother handling support for legacy operators since they are a little too crappy. Instead we will later implement a proper expression evaluator in the future.
// For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf
if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
format = type_info->ScanFmt;
if (sscanf(buf, format, p_data) < 1)
return false;
}
@ -3387,8 +3333,6 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char*
// However this may not be ideal for all uses, as some user code may break on out of bound values.
bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max)
{
ImGuiContext& g = *GImGui;
char fmt_buf[32];
char data_buf[32];
format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf));
@ -3406,7 +3350,7 @@ bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImG
memcpy(&data_backup, p_data, data_type_size);
// Apply new value (or operations) then clamp
DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialTextA.Data, data_type, p_data, NULL);
DataTypeApplyFromText(data_buf, data_type, p_data, NULL);
if (p_clamp_min || p_clamp_max)
{
if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0)
@ -3453,7 +3397,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
PushID(label);
SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));
if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view
value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format);
value_changed = DataTypeApplyFromText(buf, data_type, p_data, format);
// Step buttons
const ImVec2 backup_frame_padding = style.FramePadding;
@ -3490,7 +3434,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data
else
{
if (InputText(label, buf, IM_ARRAYSIZE(buf), flags))
value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialTextA.Data, data_type, p_data, format);
value_changed = DataTypeApplyFromText(buf, data_type, p_data, format);
}
if (value_changed)
MarkItemEdited(g.LastItemData.ID);
@ -4117,11 +4061,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory))
g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);
g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Home) | ((ImU64)1 << ImGuiKey_End);
SetActiveIdUsingKey(ImGuiKey_Home);
SetActiveIdUsingKey(ImGuiKey_End);
if (is_multiline)
g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_PageUp) | ((ImU64)1 << ImGuiKey_PageDown);
if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character.
g.ActiveIdUsingKeyInputMask |= ((ImU64)1 << ImGuiKey_Tab);
{
SetActiveIdUsingKey(ImGuiKey_PageUp);
SetActiveIdUsingKey(ImGuiKey_PageDown);
}
if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character.
{
SetActiveIdUsingKey(ImGuiKey_Tab);
}
}
// We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)
@ -4251,7 +4201,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// It is ill-defined whether the backend needs to send a \t character when pressing the TAB keys.
// Win32 and GLFW naturally do it but not SDL.
const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper);
if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly)
if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressed(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly)
if (!io.InputQueueCharacters.contains('\t'))
{
unsigned int c = '\t'; // Insert TAB
@ -4298,27 +4248,27 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
const bool is_shift_key_only = (io.KeyMods == ImGuiKeyModFlags_Shift);
const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl);
const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection());
const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_readonly;
const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && !is_readonly && is_undoable);
const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && !is_readonly && is_undoable;
const bool is_cut = ((is_shortcut_key && IsKeyPressed(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
const bool is_copy = ((is_shortcut_key && IsKeyPressed(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection());
const bool is_paste = ((is_shortcut_key && IsKeyPressed(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_readonly;
const bool is_undo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Z)) && !is_readonly && is_undoable);
const bool is_redo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressed(ImGuiKey_Z))) && !is_readonly && is_undoable;
// We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful.
const bool is_validate_enter = IsKeyPressedMap(ImGuiKey_Enter) || IsKeyPressedMap(ImGuiKey_KeyPadEnter);
const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressedMap(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed);
const bool is_cancel = IsKeyPressedMap(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed);
const bool is_validate_enter = IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_KeypadEnter);
const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed) && !IsKeyPressed(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed);
const bool is_cancel = IsKeyPressed(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed);
if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; }
else if (IsKeyPressedMap(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; }
else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly && !is_cut) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_Backspace) && !is_readonly)
if (IsKeyPressed(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
else if (IsKeyPressed(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
else if (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
else if (IsKeyPressed(ImGuiKey_PageUp) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; }
else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline) { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; }
else if (IsKeyPressed(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
else if (IsKeyPressed(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }
else if (IsKeyPressed(ImGuiKey_Backspace) && !is_readonly)
{
if (!state->HasSelection())
{
@ -4357,7 +4307,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO);
state->ClearSelection();
}
else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A))
else if (is_shortcut_key && IsKeyPressed(ImGuiKey_A))
{
state->SelectAll();
state->CursorFollow = true;
@ -4463,18 +4413,18 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
ImGuiInputTextFlags event_flag = 0;
ImGuiKey event_key = ImGuiKey_COUNT;
if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab))
ImGuiKey event_key = ImGuiKey_None;
if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressed(ImGuiKey_Tab))
{
event_flag = ImGuiInputTextFlags_CallbackCompletion;
event_key = ImGuiKey_Tab;
}
else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow))
else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_UpArrow))
{
event_flag = ImGuiInputTextFlags_CallbackHistory;
event_key = ImGuiKey_UpArrow;
}
else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow))
else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_DownArrow))
{
event_flag = ImGuiInputTextFlags_CallbackHistory;
event_key = ImGuiKey_DownArrow;
@ -4760,7 +4710,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
// Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
if (!is_readonly)
g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
{
g.PlatformImeData.WantVisible = true;
g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
g.PlatformImeData.InputLineHeight = g.FontSize;
}
}
}
else
@ -4973,7 +4927,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
OpenPopupOnItemClick("context");
OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
}
}
else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
@ -5001,7 +4955,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'.
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
OpenPopupOnItemClick("context");
OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
}
ImGuiWindow* picker_active_window = NULL;
@ -5018,11 +4972,11 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
// Store current color and open a picker
g.ColorPickerRef = col_v4;
OpenPopup("picker");
SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(-1, style.ItemSpacing.y));
SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(0.0f, style.ItemSpacing.y));
}
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
OpenPopupOnItemClick("context");
OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
if (BeginPopup("picker"))
{
@ -5236,7 +5190,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
}
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
OpenPopupOnItemClick("context");
OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
}
else if (flags & ImGuiColorEditFlags_PickerHueBar)
{
@ -5253,7 +5207,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
value_changed = value_changed_sv = true;
}
if (!(flags & ImGuiColorEditFlags_NoOptions))
OpenPopupOnItemClick("context");
OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
// Hue bar logic
SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
@ -6934,6 +6888,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
BeginDisabled();
const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
bool pressed;
const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups;
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
{
// Menu inside an horizontal menu bar
@ -6944,7 +6899,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
float w = label_size.x;
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
pressed = Selectable("", menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups, ImVec2(w, 0.0f));
pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, 0.0f));
RenderText(text_pos, label);
PopStyleVar();
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
@ -6960,7 +6915,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame
float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
pressed = Selectable("", menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f));
pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f));
RenderText(text_pos, label);
if (icon_w > 0.0f)
RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);

View File

@ -4,6 +4,9 @@
ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
Module_t mod;
// On android, the path has to be relative, don't make it absolute
#ifndef __ANDROID__
if (!std::filesystem::exists(path)) {
spdlog::error("{0} does not exist", path);
mod.handle = NULL;
@ -14,6 +17,7 @@ ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
mod.handle = NULL;
return mod;
}
#endif
#ifdef _WIN32
mod.handle = LoadLibraryA(path.c_str());
if (mod.handle == NULL) {

View File

@ -1,55 +0,0 @@
#include <options.h>
#include <spdlog/spdlog.h>
#include <stdlib.h>
#include <filesystem>
namespace options {
CMDLineOptions opts;
void loadDefaults() {
#if defined(_WIN32)
opts.root = ".";
opts.showConsole = false;
#elif defined(IS_MACOS_BUNDLE)
std::string homedir = getenv("HOME");
opts.root = homedir + "/Library/Application Support/sdrpp";
#else
std::string homedir = getenv("HOME");
opts.root = homedir + "/.config/sdrpp";
#endif
opts.root = std::filesystem::absolute(opts.root).string();
opts.serverHost = "0.0.0.0";
opts.serverPort = 5259;
}
bool parse(int argc, char* argv[]) {
for (int i = 1; i < argc; i++) {
char* arg = argv[i];
if (!strcmp(arg, "-r") || !strcmp(arg, "--root")) {
if (i == argc - 1) { return false; }
opts.root = std::filesystem::absolute(argv[++i]).string();
}
else if (!strcmp(arg, "-s") || !strcmp(arg, "--show-console")) {
opts.showConsole = true;
}
else if (!strcmp(arg, "--server")) {
opts.serverMode = true;
}
else if (!strcmp(arg, "-a") || !strcmp(arg, "--addr")) {
if (i == argc - 1) { return false; }
opts.serverHost = argv[++i];
opts.showConsole = true;
}
else if (!strcmp(arg, "-p") || !strcmp(arg, "--port")) {
if (i == argc - 1) { return false; }
sscanf(argv[++i], "%d", &opts.serverPort);
opts.showConsole = true;
}
else {
spdlog::error("Invalid command line option: {0}", arg);
return false;
}
}
return true;
}
}

View File

@ -1,18 +0,0 @@
#pragma once
#include <string>
#include <module.h>
namespace options {
struct CMDLineOptions {
std::string root;
bool showConsole;
bool serverMode;
std::string serverHost;
int serverPort;
};
SDRPP_EXPORT CMDLineOptions opts;
void loadDefaults();
bool parse(int argc, char* argv[]);
}

View File

@ -3,7 +3,6 @@
#include <spdlog/spdlog.h>
#include <version.h>
#include <config.h>
#include <options.h>
#include <filesystem>
#include <dsp/types.h>
#include <signal_path/signal_path.h>
@ -75,6 +74,7 @@ namespace server {
// Initialize compressor
cctx = ZSTD_createCCtx();
// Load config
core::configManager.acquire();
std::string modulesDir = core::configManager.conf["modulesDirectory"];
std::vector<std::string> modules = core::configManager.conf["modules"];
@ -83,8 +83,10 @@ namespace server {
core::configManager.release();
modulesDir = std::filesystem::absolute(modulesDir).string();
spdlog::info("Loading modules");
// Intialize SmGui in server mode
SmGui::init(true);
spdlog::info("Loading modules");
// Load modules and check type to only load sources ( TODO: Have a proper type parameter int the info )
// TODO LATER: Add whitelist/blacklist stuff
if (std::filesystem::is_directory(modulesDir)) {
@ -146,10 +148,12 @@ namespace server {
sigpath::sourceManager.selectSource(sourceList[sourceId]);
// TODO: Use command line option
listener = net::listen(options::opts.serverHost, options::opts.serverPort);
std::string host = (std::string)core::args["addr"];
int port = (int)core::args["port"];
listener = net::listen(host, port);
listener->acceptAsync(_clientHandler, NULL);
spdlog::info("Ready, listening on {0}:{1}", options::opts.serverHost, options::opts.serverPort);
spdlog::info("Ready, listening on {0}:{1}", host, port);
while(1) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); }
return 0;

View File

@ -248,6 +248,7 @@ void SinkManager::showVolumeSlider(std::string name, std::string prefix, float w
}
float ypos = ImGui::GetCursorPosY();
float sliderOffset = 8.0f * style::uiScale;
if (streams.find(name) == streams.end() || name == "") {
float dummy = 0.0f;
@ -256,7 +257,7 @@ void SinkManager::showVolumeSlider(std::string name, std::string prefix, float w
ImGui::ImageButton(icons::MUTED, ImVec2(height, height), ImVec2(0, 0), ImVec2(1, 1), btwBorder);
ImGui::PopID();
ImGui::SameLine();
ImGui::SetNextItemWidth(width - height - 8);
ImGui::SetNextItemWidth(width - height - sliderOffset);
ImGui::SetCursorPosY(ypos + ((height - sliderHeight) / 2.0f) + btwBorder);
ImGui::SliderFloat((prefix + name).c_str(), &dummy, 0.0f, 1.0f, "");
style::endDisabled();
@ -289,7 +290,7 @@ void SinkManager::showVolumeSlider(std::string name, std::string prefix, float w
ImGui::SameLine();
ImGui::SetNextItemWidth(width - height - 8);
ImGui::SetNextItemWidth(width - height - sliderOffset);
ImGui::SetCursorPosY(ypos + ((height - sliderHeight) / 2.0f) + btwBorder);
if (ImGui::SliderFloat((prefix + name).c_str(), &stream->guiVolume, 0.0f, 1.0f, "")) {
stream->setVolume(stream->guiVolume);
@ -341,7 +342,7 @@ void SinkManager::loadSinksFromConfig() {
}
void SinkManager::showMenu() {
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
int count = 0;
int maxCount = streams.size();

View File

@ -2,7 +2,7 @@
#include <signal_path/source.h>
#include <spdlog/spdlog.h>
#include <signal_path/signal_path.h>
#include <options.h>
#include <core.h>
SourceManager::SourceManager() {
}
@ -50,7 +50,7 @@ void SourceManager::selectSource(std::string name) {
selectedHandler = sources[name];
selectedHandler->selectHandler(selectedHandler->ctx);
selectedName = name;
if (options::opts.serverMode) {
if (core::args["server"].b()) {
server::setInput(selectedHandler->stream);
}
else {

View File

@ -5,6 +5,9 @@
#include <GL/gl.h>
#elif defined(__APPLE__)
#include <OpenGL/gl.h>
#elif defined(__ANDROID__)
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#else
#include <GL/gl.h>
#endif

View File

@ -1,3 +1,3 @@
#pragma once
#define VERSION_STR "1.0.5"
#define VERSION_STR "1.0.6"

View File

@ -4,7 +4,6 @@
#include <gui/style.h>
#include <signal_path/signal_path.h>
#include <module.h>
#include <options.h>
#include <gui/gui.h>
#include <dsp/pll.h>
#include <dsp/stream.h>
@ -131,7 +130,7 @@ private:
static void menuHandler(void* ctx) {
Falcon9DecoderModule* _this = (Falcon9DecoderModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
if (!_this->enabled) { style::beginDisabled(); }

View File

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.13)
project(kg_sstv_decoder)
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
add_library(kg_sstv_decoder SHARED ${SRC})
target_link_libraries(kg_sstv_decoder PRIVATE sdrpp_core)
set_target_properties(kg_sstv_decoder PROPERTIES PREFIX "")
target_include_directories(kg_sstv_decoder PRIVATE "src/")
if (MSVC)
target_compile_options(kg_sstv_decoder PRIVATE /O2 /Ob2 /std:c++17 /EHsc)
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(kg_sstv_decoder PRIVATE -O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup)
else ()
target_compile_options(kg_sstv_decoder PRIVATE -O3 -std=c++17)
endif ()
# Install directives
install(TARGETS kg_sstv_decoder DESTINATION lib/sdrpp/plugins)

View File

@ -0,0 +1,133 @@
○ Transmission by MSK and 4L-FSK
MSK is a type of FSK method that is a modulation method for digital signals, and is the mark frequency of FSK.
Minimize the frequency between the number and the space frequency (Minimum Shift Keying)
4L-FSK is a 4-value FSK, and 4 modulation frequencies that can be taken with a type of FSK are provided.
This makes it possible to obtain twice the transmission speed compared to MSK.
4L-FSK is more susceptible to noise because the frequency interval is narrower than MSK.
It also becomes more susceptible to deterioration of transmission line characteristics.
There are two types of choices, but when the line condition such as FM is good, the image will be sent quickly, so
Or I think you should select 4L-FSK to send high quality images. wireless
If the SSB transmission in HF where the line condition is unstable is set to MSK, the data will be as it is.
I think it will be.
The first pull-down menu from the top of the pull-down menu on the right side of Send Image
You can select MSK or 4L-FSK in the new.
○ Transmission in convolutional code mode
The second pull-down menu from the top of the pull-down menu on the right side of Send Image
New is the choice of code processing. Here no code processing (NORM) or convolutional code processing
Set whether to perform (CONV).
When convolutional code processing is performed, it becomes stronger against noise, but the same data is sent.
It takes about twice as long to complete.
Please select the code processing on a case-by-case basis while checking the transmission status.
This setting can be changed even during transmission.
○ Sending and receiving text messages
With KG-STV, a maximum of 510 half-width characters (255 full-width characters) text message can be sent once.
You can send sage.
To send a text message, text in the input box under Resp BSR
And click Send Text on the right.
When you receive a text message, the text will be displayed in the box below.
○ KG-STV transmission standard
Modulation format: MSK or composite modulation of MSK and 4-value FSK
Modulation speed: 1200baud
Modulation frequency: MSK …… Space frequency 1200Hz Mark frequency 1800Hz
4L-FSK ...
'00' 1200Hz
'01' 1400Hz
'10' 1600Hz
'11' 1800Hz
Bandwidth: 500-2500Hz
Error correction: None or Viterbi code (NASA standard K = 7, R=1/2, P=[109, 79] code)
Whitening: Yes (add M-sequence code with a period of 127 bits to each bit)
Interleaver: None
Header: 01 repeat signal is 256 bits
Basic code configuration: Synchronous code + 54-bit length information chunk + arbitrary length data chunk
Error detection: Yes (CRC 16 CCITT)
Synchronous bit: 63-bit M-sequence code
Image compression: JPEG compatible (16x16 pixels, thinning 4: 1: 1)
Character code: Shift JIS compatible
Radio format: F1D (operation in SSB mode)
F2D (operation in FM mode)
Information chunk configuration (system code version '0')
┌─────┬─────┬───┬───┬───┬───┬────┬──────┬─────┐
│ sys │ com │ c │ m │ x │ y │ sc │ size │ CRC │
└─────┴─────┴───┴───┴───┴───┴────┴──────┴─────┘
0 4 8 9 10 16 22 26 38 54bit
Sys: system code
Com: command code
C: sign mode
M: Modulation mode
X: Image block position (X)
Y: Image block position (Y)
Sc: JPEG scale size
Size: data size
CRC: Error detection code
* Information chunks always perform MSK modulation + convolution processing.
Data chunk structure (system code version '0')
┌──────┬─────┐
│ data │ CRC │
└──────┴─────┘
* Data chunks are transmitted following the CRC code of the information chunks.
Command table (system code version '0')
┌─────┬────────┐
│ Command value │ Operation │ │
├─────┼────────┤
│ 0 │ text transmission │
├─────┼────────┤
│ 1 │ Image transmission │ │
├─────┼────────┤
│ 2 │ BSR response │ │
├─────┼────────┤
│ │ 3 │ end │ │
├─────┼────────┤
│ │ 4 │ BSR request │ │
├─────┼────────┤
│ 5 │ Canceled (suspended) │
├─────┼────────┤
│ 6 │ call sign │
└─────┴────────┘
* When starting transmission, the header code of 01 shall be transmitted first.
* One data format (code configuration) is
Synchronous code + information chunk + data chunk
It will be. However, the end signal, stop signal, and BSR signal do not include data chunks.
stomach.
* In the case of image data, this data format is continuous for the number of image blocks.
It shall be sent.
* In the case of text transmission, it shall be completed in one data format.
* When the transmission is completed normally, the end signal shall be transmitted three times at the end.
* When the transmission is canceled, the cancellation signal shall be transmitted three times at the end.
Synchronous code
000011100001001000110110010110101110111100110001010100111111010
Whitening code
1110110011000100100111001111100100000100011010101001101101001010000101100001100101111111010110111011110001110100010101110000001
○ KG-STV needs more experiments
KG-STV was born from the creator's own interest in digital image communication.
However, there is a lack of practical experience due to the ability of the person himself / herself.
Therefore, the actual luck is that the transmission time is slow, the image quality is poor, and it is difficult to receive.
I think there are many situations where it is not practical in terms of use.
In such a case, I would like to research as a new issue, so by all means
We hope that many people will use it and give us their impressions.
Inconvenience such as bugs and operational problems until the degree of completion is high after receiving your opinions
We apologize for the inconvenience, but thank you for your cooperation.
Creator: K.G (JJ0OBZ Myoko City, Niigata Prefecture)
-------------------------------------------------- -------------------------------------------------- --------------------
Created and written by K.G k.g8956@ymail.plala.or.jp
Today also from the day of the plane http://www2.plala.or.jp/hikokibiyori/
-------------------------------------------------- -------------------------------------------------- --------------------

View File

@ -0,0 +1,280 @@
#pragma once
#include <dsp/block.h>
#include <dsp/stream.h>
#include <dsp/types.h>
#include <dsp/routing.h>
#include <dsp/demodulator.h>
#include <dsp/sink.h>
#include <spdlog/spdlog.h>
extern "C" {
#include <correct.h>
}
#define KGSSTV_DEVIATION 300
#define KGSSTV_BAUDRATE 1200
#define KGSSTV_RRC_ALPHA 0.7f
#define KGSSTV_4FSK_HIGH_CUT 0.5f
// const uint8_t KGSSTV_SYNC_WORD[] = {
// 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
// 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0,
// 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0,
// 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0,
// 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
// 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,
// 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
// 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0
// };
const uint8_t KGSSTV_SYNC_WORD[] = {
0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0,
0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0,
1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1,
0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0
};
const uint8_t KGSSTV_SCRAMBLING[] = {
1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0,
1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1,
0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0,
1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0,
0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1,
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1,
1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0,
0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1
};
const uint8_t KGSSTV_SCRAMBLING_BYTES[] = {
0b11101100, 0b11000100, 0b10011100, 0b11111001, 0b00000100,
0b01101010, 0b10011011, 0b01001010, 0b00010110, 0b00011001,
0b01111111, 0b01011011, 0b10111100, 0b01110100, 0b01010111,
0b00000010
};
static const correct_convolutional_polynomial_t kgsstv_polynomial[] = {0155, 0117};
#define KGSSTV_SYNC_WORD_SIZE sizeof(KGSSTV_SYNC_WORD)
#define KGSSTV_SYNC_SCRAMBLING_SIZE sizeof(KGSSTV_SCRAMBLING)
namespace kgsstv {
// class Slice4FSK : public dsp::generic_block<Slice4FSK> {
// public:
// Slice4FSK() {}
// Slice4FSK(dsp::stream<float>* in) { init(in); }
// void init(dsp::stream<float>* in) {
// _in = in;
// dsp::generic_block<Slice4FSK>::registerInput(_in);
// dsp::generic_block<Slice4FSK>::registerOutput(&out);
// dsp::generic_block<Slice4FSK>::_block_init = true;
// }
// void setInput(dsp::stream<float>* in) {
// assert(dsp::generic_block<Slice4FSK>::_block_init);
// std::lock_guard<std::mutex> lck(dsp::generic_block<Slice4FSK>::ctrlMtx);
// dsp::generic_block<Slice4FSK>::tempStop();
// dsp::generic_block<Slice4FSK>::unregisterInput(_in);
// _in = in;
// dsp::generic_block<Slice4FSK>::registerInput(_in);
// dsp::generic_block<Slice4FSK>::tempStart();
// }
// int run() {
// int count = _in->read();
// if (count < 0) { return -1; }
// float val;
// for (int i = 0; i < count; i++) {
// val = _in->readBuf[i];
// out.writeBuf[i * 2] = (val > 0.0f);
// if (val > 0.0f) {
// out.writeBuf[(i * 2) + 1] = (val > KGSSTV_4FSK_HIGH_CUT);
// }
// else {
// out.writeBuf[(i * 2) + 1] = (val > -KGSSTV_4FSK_HIGH_CUT);
// }
// }
// _in->flush();
// if (!out.swap(count * 2)) { return -1; }
// return count;
// }
// dsp::stream<uint8_t> out;
// private:
// dsp::stream<float>* _in;
// };
class Deframer : public dsp::generic_block<Deframer> {
public:
Deframer() {}
Deframer(dsp::stream<float>* in) { init(in); }
void init(dsp::stream<float>* in) {
_in = in;
// TODO: Destroy
conv = correct_convolutional_create(2, 7, kgsstv_polynomial);
memset(convTmp, 0x00, 1024);
dsp::generic_block<Deframer>::registerInput(_in);
dsp::generic_block<Deframer>::registerOutput(&out);
dsp::generic_block<Deframer>::_block_init = true;
}
void setInput(dsp::stream<float>* in) {
assert(dsp::generic_block<Deframer>::_block_init);
std::lock_guard<std::mutex> lck(dsp::generic_block<Deframer>::ctrlMtx);
dsp::generic_block<Deframer>::tempStop();
dsp::generic_block<Deframer>::unregisterInput(_in);
_in = in;
dsp::generic_block<Deframer>::registerInput(_in);
dsp::generic_block<Deframer>::tempStart();
}
int run() {
int count = _in->read();
if (count < 0) { return -1; }
for (int i = 0; i < count; i++) {
if (syncing) {
// If sync broken, reset sync
if ((_in->readBuf[i] > 0.0f) && !KGSSTV_SYNC_WORD[match]) {
if (++err > 4) {
i -= match - 1;
match = 0;
err = 0;
continue;
}
}
// If full syncword was detected, switch to read mode
if (++match == KGSSTV_SYNC_WORD_SIZE) {
spdlog::warn("Frame detected");
syncing = false;
readCount = 0;
writeCount = 0;
}
}
else {
// // Process symbol
// if (!(readCount % 2)) {
// int bitOffset = writeCount & 0b111;
// int byteOffset = writeCount >> 3;
// if (!bitOffset) { convTmp[byteOffset] = 0; }
// convTmp[byteOffset] |= _in->readBuf[i] << (7 - bitOffset);
// writeCount++;
// }
// Process symbol
convTmp[readCount] = std::clamp<int>((_in->readBuf[i] + 1.0f) * 128.0f, 0, 255);
// When info was read, write data and get back to
if (++readCount == 108) {
match = 0;
err = 0;
syncing = true;
// Descramble
for (int j = 0; j < 108; j++) {
if (KGSSTV_SCRAMBLING[j]) {
convTmp[j] = 255 - convTmp[j];
}
//convTmp[j >> 3] ^= KGSSTV_SCRAMBLING[j] << (7 - (j & 0b111));
}
// Decode convolutional code
int convOutCount = correct_convolutional_decode_soft(conv, convTmp, 124, out.writeBuf);
spdlog::warn("Frames written: {0}, frameBytes: {1}", ++framesWritten, convOutCount);
if (!out.swap(7)) {
_in->flush();
return -1;
}
}
}
}
_in->flush();
return count;
}
dsp::stream<uint8_t> out;
private:
dsp::stream<float>* _in;
correct_convolutional* conv = NULL;
uint8_t convTmp[1024];
int match = 0;
int err = 0;
int readCount = 0;
int writeCount = 0;
bool syncing = true;
int framesWritten = 0;
};
class Decoder : public dsp::generic_hier_block<Decoder> {
public:
Decoder() {}
Decoder(dsp::stream<dsp::complex_t>* input, float sampleRate) {
init(input, sampleRate);
}
void init(dsp::stream<dsp::complex_t>* input, float sampleRate) {
_sampleRate = sampleRate;
demod.init(input, _sampleRate, KGSSTV_DEVIATION);
rrc.init(31, _sampleRate, KGSSTV_BAUDRATE, KGSSTV_RRC_ALPHA);
fir.init(&demod.out, &rrc);
recov.init(&fir.out, _sampleRate / KGSSTV_BAUDRATE, 1e-6f, 0.01f, 0.01f);
doubler.init(&recov.out);
//slicer.init(&doubler.outA);
deframer.init(&doubler.outA);
ns2.init(&deframer.out, "kgsstv_out.bin");
diagOut = &doubler.outB;
dsp::generic_hier_block<Decoder>::registerBlock(&demod);
dsp::generic_hier_block<Decoder>::registerBlock(&fir);
dsp::generic_hier_block<Decoder>::registerBlock(&recov);
dsp::generic_hier_block<Decoder>::registerBlock(&doubler);
//dsp::generic_hier_block<Decoder>::registerBlock(&slicer);
dsp::generic_hier_block<Decoder>::registerBlock(&deframer);
dsp::generic_hier_block<Decoder>::registerBlock(&ns2);
dsp::generic_hier_block<Decoder>::_block_init = true;
}
void setInput(dsp::stream<dsp::complex_t>* input) {
assert(dsp::generic_hier_block<Decoder>::_block_init);
demod.setInput(input);
}
dsp::stream<float>* diagOut = NULL;
private:
dsp::FloatFMDemod demod;
dsp::RRCTaps rrc;
dsp::FIR<float> fir;
dsp::MMClockRecovery<float> recov;
dsp::StreamDoubler<float> doubler;
// Slice4FSK slicer;
Deframer deframer;
dsp::FileSink<uint8_t> ns2;
float _sampleRate;
};
}

View File

@ -0,0 +1,193 @@
#include <imgui.h>
#include <config.h>
#include <core.h>
#include <gui/style.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <module.h>
#include <filesystem>
#include <dsp/pll.h>
#include <dsp/stream.h>
#include <dsp/demodulator.h>
#include <dsp/window.h>
#include <dsp/resampling.h>
#include <dsp/processing.h>
#include <dsp/routing.h>
#include <dsp/sink.h>
#include <gui/widgets/folder_select.h>
#include <gui/widgets/symbol_diagram.h>
#include <fstream>
#include <chrono>
#include "kg_sstv_dsp.h"
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{
/* Name: */ "kg_sstv_decoder",
/* Description: */ "KG-SSTV Digital SSTV Decoder for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
ConfigManager config;
#define INPUT_SAMPLE_RATE 6000
class M17DecoderModule : public ModuleManager::Instance {
public:
M17DecoderModule(std::string name) : diag(0.8, 480) {
this->name = name;
// Load config
config.acquire();
if (!config.conf.contains(name)) {
config.conf[name]["showLines"] = false;
}
showLines = config.conf[name]["showLines"];
if (showLines) {
diag.lines.push_back(-0.75f);
diag.lines.push_back(-0.25f);
diag.lines.push_back(0.25f);
diag.lines.push_back(0.75f);
}
config.release(true);
// Initialize VFO
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 3000, INPUT_SAMPLE_RATE, 3000, 3000, true);
vfo->setSnapInterval(250);
// Initialize DSP here
decoder.init(vfo->output, INPUT_SAMPLE_RATE);
reshape.init(decoder.diagOut, 480, 0);
diagHandler.init(&reshape.out, _diagHandler, this);
// Start DSO Here
decoder.start();
reshape.start();
diagHandler.start();
//stream.start();
gui::menu.registerEntry(name, menuHandler, this, this);
}
~M17DecoderModule() {
gui::menu.removeEntry(name);
// Stop DSP Here
if (enabled) {
decoder.stop();
reshape.stop();
diagHandler.stop();
sigpath::vfoManager.deleteVFO(vfo);
}
sigpath::sinkManager.unregisterStream(name);
}
void postInit() {}
void enable() {
double bw = gui::waterfall.getBandwidth();
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), 9600, INPUT_SAMPLE_RATE, 9600, 9600, true);
vfo->setSnapInterval(250);
// Set Input of demod here
decoder.setInput(vfo->output);
// Start DSP here
decoder.start();
reshape.start();
diagHandler.start();
enabled = true;
}
void disable() {
// Stop DSP here
decoder.stop();
reshape.stop();
diagHandler.stop();
sigpath::vfoManager.deleteVFO(vfo);
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
static void menuHandler(void* ctx) {
M17DecoderModule* _this = (M17DecoderModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvail().x;
if (!_this->enabled) { style::beginDisabled(); }
ImGui::SetNextItemWidth(menuWidth);
_this->diag.draw();
if (ImGui::Checkbox(CONCAT("Show Reference Lines##m17_showlines_", _this->name), &_this->showLines)) {
if (_this->showLines) {
_this->diag.lines.push_back(-0.75f);
_this->diag.lines.push_back(-0.25f);
_this->diag.lines.push_back(0.25f);
_this->diag.lines.push_back(0.75f);
}
else {
_this->diag.lines.clear();
}
config.acquire();
config.conf[_this->name]["showLines"] = _this->showLines;
config.release(true);
}
if (!_this->enabled) { style::endDisabled(); }
}
static void _diagHandler(float* data, int count, void* ctx) {
M17DecoderModule* _this = (M17DecoderModule*)ctx;
float* buf = _this->diag.acquireBuffer();
memcpy(buf, data, count * sizeof(float));
_this->diag.releaseBuffer();
}
std::string name;
bool enabled = true;
// DSP Chain
VFOManager::VFO* vfo;
kgsstv::Decoder decoder;
dsp::Reshaper<float> reshape;
dsp::HandlerSink<float> diagHandler;
dsp::stream<float> dummy;
ImGui::SymbolDiagram diag;
bool showLines = false;
};
MOD_EXPORT void _INIT_() {
// Create default recording directory
json def = json({});
config.setPath(core::args["root"].s() + "/kg_sstv_decoder_config.json");
config.load(def);
config.enableAutoSave();
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new M17DecoderModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (M17DecoderModule*)instance;
}
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}

View File

@ -5,7 +5,6 @@
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <module.h>
#include <options.h>
#include <filesystem>
#include <dsp/pll.h>
#include <dsp/stream.h>
@ -141,7 +140,7 @@ private:
static void menuHandler(void* ctx) {
M17DecoderModule* _this = (M17DecoderModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
if (!_this->enabled) { style::beginDisabled(); }
@ -296,7 +295,7 @@ private:
MOD_EXPORT void _INIT_() {
// Create default recording directory
json def = json({});
config.setPath(options::opts.root + "/m17_decoder_config.json");
config.setPath(core::args["root"].s() + "/m17_decoder_config.json");
config.load(def);
config.enableAutoSave();
}

View File

@ -5,7 +5,6 @@
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <module.h>
#include <options.h>
#include <filesystem>
#include <dsp/pll.h>
#include <dsp/stream.h>
@ -130,7 +129,7 @@ private:
static void menuHandler(void* ctx) {
MeteorDemodulatorModule* _this = (MeteorDemodulatorModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
if (!_this->enabled) { style::beginDisabled(); }
@ -244,14 +243,15 @@ private:
MOD_EXPORT void _INIT_() {
// Create default recording directory
if (!std::filesystem::exists(options::opts.root + "/recordings")) {
std::string root = (std::string)core::args["root"];
if (!std::filesystem::exists(root + "/recordings")) {
spdlog::warn("Recordings directory does not exist, creating it");
if (!std::filesystem::create_directory(options::opts.root + "/recordings")) {
if (!std::filesystem::create_directory(root + "/recordings")) {
spdlog::error("Could not create recordings directory");
}
}
json def = json({});
config.setPath(options::opts.root + "/meteor_demodulator_config.json");
config.setPath(root + "/meteor_demodulator_config.json");
config.load(def);
config.enableAutoSave();
}

View File

@ -1,5 +1,4 @@
#include "radio_module.h"
#include <options.h>
SDRPP_MOD_INFO{
/* Name: */ "radio",
@ -11,7 +10,7 @@ SDRPP_MOD_INFO{
MOD_EXPORT void _INIT_() {
json def = json({});
config.setPath(options::opts.root + "/radio_config.json");
config.setPath(core::args["root"].s() + "/radio_config.json");
config.load(def);
config.enableAutoSave();
}

View File

@ -204,7 +204,7 @@ private:
if (!_this->enabled) { style::beginDisabled(); }
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
ImGui::BeginGroup();
ImGui::Columns(4, CONCAT("RadioModeColumns##_", _this->name), false);

View File

@ -5,7 +5,6 @@
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <module.h>
#include <options.h>
#include <dsp/pll.h>
#include <dsp/stream.h>
@ -102,7 +101,7 @@ private:
static void menuHandler(void* ctx) {
WeatherSatDecoderModule* _this = (WeatherSatDecoderModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
if (!_this->enabled) { style::beginDisabled(); }

View File

@ -220,7 +220,7 @@ public:
if (ImGui::BeginTabItem("AVHRR RGB(221)")) {
ImGui::BeginChild("AVHRRRGBChild");
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
avhrrRGBImage.draw();
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
@ -229,7 +229,7 @@ public:
if (ImGui::BeginTabItem("AVHRR 1")) {
ImGui::BeginChild("AVHRR1Child");
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
avhrr1Image.draw();
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
@ -238,7 +238,7 @@ public:
if (ImGui::BeginTabItem("AVHRR 2")) {
ImGui::BeginChild("AVHRR2Child");
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
avhrr2Image.draw();
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
@ -247,7 +247,7 @@ public:
if (ImGui::BeginTabItem("AVHRR 3")) {
ImGui::BeginChild("AVHRR3Child");
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
avhrr3Image.draw();
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
@ -256,7 +256,7 @@ public:
if (ImGui::BeginTabItem("AVHRR 4")) {
ImGui::BeginChild("AVHRR4Child");
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
avhrr4Image.draw();
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();
@ -265,7 +265,7 @@ public:
if (ImGui::BeginTabItem("AVHRR 5")) {
ImGui::BeginChild("AVHRR5Child");
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth());
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
avhrr5Image.draw();
ImGui::SetScrollHereY(1.0f);
ImGui::EndChild();

View File

@ -5,7 +5,6 @@
#include <gui/style.h>
#include <core.h>
#include <thread>
#include <options.h>
#include <radio_interface.h>
#include <signal_path/signal_path.h>
#include <vector>
@ -196,7 +195,7 @@ private:
bool open = true;
gui::mainWindow.lockWaterfallControls = true;
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
std::string id = "New##freq_manager_new_popup_" + name;
ImGui::OpenPopup(id.c_str());
@ -244,7 +243,7 @@ private:
bool selectListsDialog() {
gui::mainWindow.lockWaterfallControls = true;
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
std::string id = "Select lists##freq_manager_sel_popup_" + name;
ImGui::OpenPopup(id.c_str());
@ -348,7 +347,7 @@ private:
static void menuHandler(void* ctx) {
FrequencyManagerModule* _this = (FrequencyManagerModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
// TODO: Replace with something that won't iterate every frame
std::vector<std::string> selectedNames;
@ -421,7 +420,7 @@ private:
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
if (ImGui::Button(("Add##_freq_mgr_add_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvailWidth(), 0))) {
if (ImGui::Button(("Add##_freq_mgr_add_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
// If there's no VFO selected, just save the center freq
if (gui::waterfall.selectedVFO == "") {
_this->editedBookmark.frequency = gui::waterfall.getCenterFrequency();
@ -459,13 +458,13 @@ private:
ImGui::TableSetColumnIndex(1);
if (selectedNames.size() == 0 && _this->selectedListName != "") { style::beginDisabled(); }
if (ImGui::Button(("Remove##_freq_mgr_rem_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvailWidth(), 0))) {
if (ImGui::Button(("Remove##_freq_mgr_rem_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
_this->deleteBookmarksOpen = true;
}
if (selectedNames.size() == 0 && _this->selectedListName != "") { style::endDisabled(); }
ImGui::TableSetColumnIndex(2);
if (selectedNames.size() != 1 && _this->selectedListName != "") { style::beginDisabled(); }
if (ImGui::Button(("Edit##_freq_mgr_edt_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvailWidth(), 0))) {
if (ImGui::Button(("Edit##_freq_mgr_edt_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
_this->editOpen = true;
_this->editedBookmark = _this->bookmarks[selectedNames[0]];
_this->editedBookmarkName = selectedNames[0];
@ -497,8 +496,7 @@ private:
if (ImGui::Selectable((name + "##_freq_mgr_bkm_name_" + _this->name).c_str(), &bm.selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SelectOnClick)) {
// if shift or control isn't pressed, deselect all others
if (!ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT) && !ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT) &&
!ImGui::IsKeyDown(GLFW_KEY_LEFT_CONTROL) && !ImGui::IsKeyDown(GLFW_KEY_RIGHT_CONTROL)) {
if (!ImGui::GetIO().KeyShift && !ImGui::GetIO().KeyCtrl) {
for (auto& [_name, _bm] : _this->bookmarks) {
if (name == _name) { continue; }
_bm.selected = false;
@ -530,14 +528,14 @@ private:
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
if (ImGui::Button(("Import##_freq_mgr_imp_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvailWidth(), 0)) && !_this->importOpen) {
if (ImGui::Button(("Import##_freq_mgr_imp_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0)) && !_this->importOpen) {
_this->importOpen = true;
_this->importDialog = new pfd::open_file("Import bookmarks", "", { "JSON Files (*.json)", "*.json", "All Files", "*" }, true);
}
ImGui::TableSetColumnIndex(1);
if (selectedNames.size() == 0 && _this->selectedListName != "") { style::beginDisabled(); }
if (ImGui::Button(("Export##_freq_mgr_exp_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvailWidth(), 0)) && !_this->exportOpen) {
if (ImGui::Button(("Export##_freq_mgr_exp_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0)) && !_this->exportOpen) {
_this->exportedBookmarks = json::object();
config.acquire();
for (auto& _name : selectedNames) {
@ -832,7 +830,7 @@ MOD_EXPORT void _INIT_() {
def["lists"]["General"]["showOnWaterfall"] = true;
def["lists"]["General"]["bookmarks"] = json::object();
config.setPath(options::opts.root + "/frequency_manager_config.json");
config.setPath(core::args["root"].s() + "/frequency_manager_config.json");
config.load(def);
config.enableAutoSave();

View File

@ -13,7 +13,6 @@
#include <gui/style.h>
#include <gui/widgets/volume_meter.h>
#include <regex>
#include <options.h>
#include <gui/widgets/folder_select.h>
#include <recorder_interface.h>
#include <core.h>
@ -29,11 +28,6 @@ SDRPP_MOD_INFO{
ConfigManager config;
std::string expandString(std::string input) {
input = std::regex_replace(input, std::regex("%ROOT%"), options::opts.root);
return std::regex_replace(input, std::regex("//"), "/");
}
std::string genFileName(std::string prefix, bool isVfo, std::string name = "") {
time_t now = time(0);
tm* ltm = localtime(&now);
@ -52,6 +46,8 @@ public:
RecorderModule(std::string name) : folderSelect("%ROOT%/recordings") {
this->name = name;
root = (std::string)core::args["root"];
// Load config
config.acquire();
bool created = false;
@ -193,7 +189,7 @@ private:
static void menuHandler(void* ctx) {
RecorderModule* _this = (RecorderModule*)ctx;
float menuColumnWidth = ImGui::GetContentRegionAvailWidth();
float menuColumnWidth = ImGui::GetContentRegionAvail().x;
// Recording mode
if (_this->recording) { style::beginDisabled(); }
@ -462,6 +458,11 @@ private:
}
}
std::string expandString(std::string input) {
input = std::regex_replace(input, std::regex("%ROOT%"), root);
return std::regex_replace(input, std::regex("//"), "/");
}
std::string name;
bool enabled = true;
@ -496,6 +497,7 @@ private:
std::string streamNamesTxt;
int streamId = 0;
std::string selectedStreamName = "";
std::string root;
// Baseband path
dsp::stream<dsp::complex_t> basebandStream;
@ -516,14 +518,15 @@ struct RecorderContext_t {
MOD_EXPORT void _INIT_() {
// Create default recording directory
if (!std::filesystem::exists(options::opts.root + "/recordings")) {
std::string root = (std::string)core::args["root"];
if (!std::filesystem::exists(root + "/recordings")) {
spdlog::warn("Recordings directory does not exist, creating it");
if (!std::filesystem::create_directory(options::opts.root + "/recordings")) {
if (!std::filesystem::create_directory(root + "/recordings")) {
spdlog::error("Could not create recordings directory");
}
}
json def = json({});
config.setPath(options::opts.root + "/recorder_config.json");
config.setPath(root + "/recorder_config.json");
config.load(def);
config.enableAutoSave();
}

View File

@ -8,7 +8,6 @@
#include <recorder_interface.h>
#include <meteor_demodulator_interface.h>
#include <config.h>
#include <options.h>
#include <cctype>
#include <radio_interface.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -107,7 +106,7 @@ public:
private:
static void menuHandler(void* ctx) {
SigctlServerModule* _this = (SigctlServerModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
bool listening = (_this->listener && _this->listener->isListening());
@ -734,7 +733,7 @@ private:
};
MOD_EXPORT void _INIT_() {
config.setPath(options::opts.root + "/rigctl_server_config.json");
config.setPath(core::args["root"].s() + "/rigctl_server_config.json");
config.load(json::object());
config.enableAutoSave();
}

View File

@ -100,8 +100,7 @@ private:
if (ImGui::Selectable((name + "##_freq_mgr_bkm_name_" + _this->name).c_str(), &bm.selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SelectOnClick)) {
// if shift or control isn't pressed, deselect all others
if (!ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT) && !ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT) &&
!ImGui::IsKeyDown(GLFW_KEY_LEFT_CONTROL) && !ImGui::IsKeyDown(GLFW_KEY_RIGHT_CONTROL)) {
if (!ImGui::GetIO().KeyShift && !ImGui::GetIO().KeyCtrl) {
for (auto& [_name, _bm] : _this->tasks) {
if (name == _name) { continue; }
_bm.selected = false;

View File

@ -77,8 +77,7 @@ public:
if (ImGui::Selectable((act->getName() + "##scheduler_task_actions_entry").c_str(), &act->selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SelectOnClick)) {
// if shift or control isn't pressed, deselect all others
if (!ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT) && !ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT) &&
!ImGui::IsKeyDown(GLFW_KEY_LEFT_CONTROL) && !ImGui::IsKeyDown(GLFW_KEY_RIGHT_CONTROL)) {
if (!ImGui::GetIO().KeyShift && !ImGui::GetIO().KeyCtrl) {
int _id = 0;
for (auto& _act : actions) {
if (_id == id) { continue; }

View File

@ -411,8 +411,12 @@ I will soon publish a contributing.md listing the code style to use.
* Eric Johnson
* Ernest Murphy (NH7L)
* Flinger Films
* gringogrigio
* Joe Cupano
* Kezza
* Krys Kamieniecki
* Lee Donaghy
* Lee KD1SQ
* .lozenge. (Hank Hill)
* ON4MU
* [Passion-Radio.com](https://passion-radio.com/)
@ -420,6 +424,7 @@ I will soon publish a contributing.md listing the code style to use.
* [SignalsEverywhere](https://signalseverywhere.com/)
* Syne Ardwin (WI9SYN)
* [W4IPA](https://twitter.com/W4IPAstroke5)
* [Zipper](github.com/reppiZ)
## Contributors
@ -445,6 +450,7 @@ I will soon publish a contributing.md listing the code style to use.
* [Tobias Mädel](https://github.com/Manawyrm)
* [Zimm](https://github.com/invader-zimm)
## Libraries used
* [SoapySDR (PothosWare)](https://github.com/pothosware/SoapySDR)

View File

@ -3,7 +3,7 @@
"country_name": "United Kingdom",
"country_code": "UK",
"author_name": "John Donkersley",
"author_url": "--",
"author_url": "",
"bands": [
{
"name": "Long Wave",
@ -29,18 +29,42 @@
"start": 1810000,
"end": 2000000
},
{
"name": "Maritime",
"type": "marine",
"start": 2045000,
"end": 2300000
},
{
"name": "120m Broadcast",
"type": "broadcast",
"start": 2300000,
"end": 2495000
},
{
"name": "Maritime",
"type": "marine",
"start": 2500000,
"end": 2850000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 2850000,
"end": 3155000
},
{
"name": "90m Broadcast",
"type": "broadcast",
"start": 3200000,
"end": 3400000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 3400000,
"end": 3500000
},
{
"name": "80m Ham Band",
"type": "amateur",
@ -53,6 +77,18 @@
"start": 3900000,
"end": 4000000
},
{
"name": "Maritime",
"type": "marine",
"start": 4063000,
"end": 4438000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 4650000,
"end": 4750000
},
{
"name": "60m Broadcast",
"type": "broadcast",
@ -65,12 +101,30 @@
"start": 5258500,
"end": 5406500
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 5450000,
"end": 5730000
},
{
"name": "49m Broadcast",
"type": "broadcast",
"start": 5900000,
"end": 6200000
},
{
"name": "Maritime",
"type": "marine",
"start": 6200000,
"end": 6525000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 6525000,
"end": 6765000
},
{
"name": "40m Ham Band",
"type": "amateur",
@ -83,23 +137,59 @@
"start": 7200000,
"end": 7450000
},
{
"name": "Maritime",
"type": "marine",
"start": 8195000,
"end": 8815000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 8815000,
"end": 9040000
},
{
"name": "31m Broadcast",
"type": "broadcast",
"start": 9400000,
"end": 9900000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 10005000,
"end": 10100000
},
{
"name": "30m Ham Band",
"type": "amateur",
"start": 10100000,
"end": 10150000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 11175000,
"end": 11400000
},
{
"name": "25m Broadcast",
"type": "broadcast",
"start": 11600000,
"end": 12100000
"end": 12230000
},
{
"name": "Maritime",
"type": "marine",
"start": 12230000,
"end": 13200000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 13200000,
"end": 13360000
},
{
"name": "22m Broadcast",
@ -113,6 +203,18 @@
"start": 14000000,
"end": 14350000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 15010000,
"end": 15100000
},
{
"name": "Maritime",
"type": "marine",
"start": 16360000,
"end": 17410000
},
{
"name": "19m Broadcast",
"type": "broadcast",
@ -125,18 +227,36 @@
"start": 17480000,
"end": 17900000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 17900000,
"end": 18030000
},
{
"name": "17m Ham Band",
"type": "amateur",
"start": 18068000,
"end": 18168000
},
{
"name": "Maritime - ship tx",
"type": "marine",
"start": 18780000,
"end": 18900000
},
{
"name": "15m Broadcast",
"type": "broadcast",
"start": 18900000,
"end": 19020000
},
{
"name": "Maritime - coast tx",
"type": "marine",
"start": 19680000,
"end": 19990000
},
{
"name": "15m Ham Band",
"type": "amateur",
@ -149,6 +269,18 @@
"start": 21450000,
"end": 21850000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 21870000,
"end": 22000000
},
{
"name": "Aeronautical Mobile",
"type": "aviation",
"start": 23200000,
"end": 23350000
},
{
"name": "12m Ham Band",
"type": "amateur",
@ -179,6 +311,18 @@
"start": 28000000,
"end": 29700000
},
{
"name": "Analogue Cordless Phones",
"type": "amateur",
"start": 31037500,
"end": 40112500
},
{
"name": "Low Power Devices",
"type": "amateur",
"start": 49820000,
"end": 49987500
},
{
"name": "6m Ham Band",
"type": "amateur",
@ -198,7 +342,7 @@
"end": 108000000
},
{
"name": "Air Band VOR/ILS",
"name": "Air Band TACAN/ILS",
"type": "aviation",
"start": 108000000,
"end": 118000000
@ -206,7 +350,7 @@
{
"name": "Air Band Voice",
"type": "aviation",
"start": 118000000,
"start": 117975000,
"end": 137000000
},
{
@ -222,11 +366,53 @@
"end": 146000000
},
{
"name": "Marine",
"name": "Land/Mountain Rescue",
"type": "PMR",
"start": 147343750,
"end": 147500000
},
{
"name": "Pagers - Flex/POCSAG",
"type": "PMR",
"start": 153025000,
"end": 153500000
},
{
"name": "Land/Mountain Rescue DMR",
"type": "PMR",
"start": 155000000,
"end": 156000000
},
{
"name": "Marine - ship tx",
"type": "marine",
"start": 156000000,
"end": 157850000
},
{
"name": "Short Term Hire",
"type": "PMR",
"start": 158787500,
"end": 159687500
},
{
"name": "Marine - coast tx",
"type": "marine",
"start": 160650000,
"end": 162025000
},
{
"name": "Short Term Hire",
"type": "PMR",
"start": 163287500,
"end": 164200000
},
{
"name": "Business Radio",
"type": "PMR",
"start": 165000000,
"end": 174000000
},
{
"name": "DAB Radio",
"type": "broadcast",
@ -239,6 +425,12 @@
"start": 230000000,
"end": 400000000
},
{
"name": "Private Mobile Radio inc trams",
"type": "PMR",
"start": 422000000,
"end": 424000000
},
{
"name": "70cm Ham Band",
"type": "amateur",
@ -247,16 +439,52 @@
},
{
"name": "PMR446",
"type": "amateur",
"type": "PMR",
"start": 446000000,
"end": 446200000
},
{
"name": "Outside Broadcast Talkback",
"type": "PMR",
"start": 446200000,
"end": 447512500
},
{
"name": "Private Mobile Radio",
"type": "PMR",
"start": 447600000,
"end": 454000000
},
{
"name": "Mosques",
"type": "amateur",
"start": 454000000,
"end": 455000000
},
{
"name": "Private Mobile Radio",
"type": "PMR",
"start": 455000000,
"end": 467200000
},
{
"name": "Outside Broadcast Talkback",
"type": "PMR",
"start": 467200000,
"end": 468600000
},
{
"name": "Digital TV Broadcast",
"type": "broadcast",
"start": 470000000,
"end": 790000000
},
{
"name": "Licence Exempt Short Range",
"type": "amateur",
"start": 862000000,
"end": 875800000
},
{
"name": "23cm Ham Band",
"type": "amateur",

View File

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.13)
project(audio_sink)
file(GLOB SRC "src/*.cpp")
add_library(audio_sink SHARED ${SRC})
target_link_libraries(audio_sink PRIVATE sdrpp_core)
set_target_properties(audio_sink PROPERTIES PREFIX "")
target_include_directories(audio_sink PRIVATE "src/")
target_compile_options(audio_sink PRIVATE -O3 -std=c++17)
target_link_libraries(audio_sink PRIVATE aaudio)

View File

@ -0,0 +1,195 @@
#include <imgui.h>
#include <module.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <signal_path/sink.h>
#include <dsp/audio.h>
#include <dsp/processing.h>
#include <spdlog/spdlog.h>
#include <config.h>
#include <utils/optionlist.h>
#include <aaudio/AAudio.h>
#include <core.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{
/* Name: */ "audio_sink",
/* Description: */ "Android audio sink module for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ 1
};
ConfigManager config;
class AudioSink : SinkManager::Sink {
public:
AudioSink(SinkManager::Stream* stream, std::string streamName) {
_stream = stream;
_streamName = streamName;
packer.init(_stream->sinkOut, 512);
// TODO: Add choice? I don't think anyone cares on android...
sampleRate = 48000;
_stream->setSampleRate(sampleRate);
}
~AudioSink() {
}
void start() {
if (running) {
return;
}
doStart();
running = true;
}
void stop() {
if (!running) {
return;
}
doStop();
running = false;
}
void menuHandler() {
// Draw menu here
}
private:
void doStart() {
// Create stream builder
AAudioStreamBuilder *builder;
aaudio_result_t result = AAudio_createStreamBuilder(&builder);
// Set stream options
bufferSize = round(sampleRate / 60.0);
AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_SHARED);
AAudioStreamBuilder_setSampleRate(builder, sampleRate);
AAudioStreamBuilder_setChannelCount(builder, 2);
AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
AAudioStreamBuilder_setBufferCapacityInFrames(builder, bufferSize);
AAudioStreamBuilder_setErrorCallback(builder, errorCallback, this);
packer.setSampleCount(bufferSize);
// Open the stream
AAudioStreamBuilder_openStream(builder, &stream);
// Stream stream and packer
packer.start();
AAudioStream_requestStart(stream);
// We no longer need the builder
AAudioStreamBuilder_delete(builder);
// Start worker thread
workerThread = std::thread(&AudioSink::worker, this);
}
void doStop() {
packer.stop();
packer.out.stopReader();
AAudioStream_requestStop(stream);
AAudioStream_close(stream);
if (workerThread.joinable()) { workerThread.join(); }
packer.out.clearReadStop();
}
void worker() {
while (true) {
int count = packer.out.read();
if (count < 0) { return; }
AAudioStream_write(stream, packer.out.readBuf, count, 100000000);
packer.out.flush();
}
}
static void errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error){
// detect an audio device detached and restart the stream
if (error == AAUDIO_ERROR_DISCONNECTED){
std::thread thr(&AudioSink::restart, (AudioSink*)userData);
thr.detach();
}
}
void restart() {
if (running) { doStop(); }
if (running) { doStart(); }
}
std::thread workerThread;
AAudioStream *stream = NULL;
SinkManager::Stream* _stream;
dsp::Packer<dsp::stereo_t> packer;
std::string _streamName;
double sampleRate;
int bufferSize;
bool running = false;
};
class AudioSinkModule : public ModuleManager::Instance {
public:
AudioSinkModule(std::string name) {
this->name = name;
provider.create = create_sink;
provider.ctx = this;
sigpath::sinkManager.registerSinkProvider("Audio", provider);
}
~AudioSinkModule() {
// Unregister sink, this will automatically stop and delete all instances of the audio sink
sigpath::sinkManager.unregisterSinkProvider("Audio");
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
static SinkManager::Sink* create_sink(SinkManager::Stream* stream, std::string streamName, void* ctx) {
return (SinkManager::Sink*)(new AudioSink(stream, streamName));
}
std::string name;
bool enabled = true;
SinkManager::SinkProvider provider;
};
MOD_EXPORT void _INIT_() {
json def = json({});
config.setPath(core::args["root"].s() + "/audio_sink_config.json");
config.load(def);
config.enableAutoSave();
}
MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) {
AudioSinkModule* instance = new AudioSinkModule(name);
return instance;
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (AudioSinkModule*)instance;
}
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}

View File

@ -8,7 +8,7 @@
#include <spdlog/spdlog.h>
#include <RtAudio.h>
#include <config.h>
#include <options.h>
#include <core.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -132,7 +132,7 @@ public:
}
void menuHandler() {
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(menuWidth);
if (ImGui::Combo(("##_audio_sink_dev_" + _streamName).c_str(), &devId, txtDevList.c_str())) {
@ -276,7 +276,7 @@ private:
MOD_EXPORT void _INIT_() {
json def = json({});
config.setPath(options::opts.root + "/audio_sink_config.json");
config.setPath(core::args["root"].s() + "/audio_sink_config.json");
config.load(def);
config.enableAutoSave();
}

View File

@ -8,8 +8,8 @@
#include <dsp/processing.h>
#include <spdlog/spdlog.h>
#include <config.h>
#include <options.h>
#include <gui/style.h>
#include <core.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -122,7 +122,7 @@ public:
}
void menuHandler() {
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
bool listening = (listener && listener->isListening()) || (conn && conn->isOpen());
@ -339,7 +339,7 @@ private:
MOD_EXPORT void _INIT_() {
json def = json({});
config.setPath(options::opts.root + "/network_sink_config.json");
config.setPath(core::args["root"].s() + "/network_sink_config.json");
config.load(def);
config.enableAutoSave();
}

View File

@ -9,7 +9,7 @@
#include <spdlog/spdlog.h>
#include <config.h>
#include <algorithm>
#include <options.h>
#include <core.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -137,7 +137,7 @@ public:
}
void menuHandler() {
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
// Select device
ImGui::SetNextItemWidth(menuWidth);
@ -424,7 +424,7 @@ private:
};
MOD_EXPORT void _INIT_() {
config.setPath(options::opts.root + "/new_audio_sink_config.json");
config.setPath(core::args["root"].s() + "/new_audio_sink_config.json");
config.load(json::object());
config.enableAutoSave();
}

View File

@ -7,6 +7,7 @@
#include <dsp/audio.h>
#include <dsp/processing.h>
#include <spdlog/spdlog.h>
#include <core.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -116,7 +117,7 @@ public:
}
void menuHandler() {
float menuWidth = ImGui::GetContentRegionAvailWidth();
float menuWidth = ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(menuWidth);
if (ImGui::Combo(("##_audio_sink_dev_" + _streamName).c_str(), &devListId, txtDevList.c_str())) {

View File

@ -24,6 +24,16 @@ if (MSVC)
target_include_directories(airspy_source PUBLIC "C:/Program Files/PothosSDR/include/libairspy/")
target_link_libraries(airspy_source PRIVATE airspy)
elseif (ANDROID)
target_include_directories(sdrpp_core PUBLIC
/mnt/android_sdr/libusb/libusb
/mnt/android_sdr/airspyone_host/libairspy/src
)
target_link_libraries(sdrpp_core PUBLIC
/mnt/android_sdr/output/libusb/${ANDROID_ABI}/libusb1.0.so
/mnt/android_sdr/output/libairspy/${ANDROID_ABI}/libairspy.so
)
else (MSVC)
find_package(PkgConfig)

View File

@ -6,10 +6,13 @@
#include <core.h>
#include <gui/style.h>
#include <config.h>
#include <options.h>
#include <gui/smgui.h>
#include <airspy.h>
#ifdef __ANDROID__
#include <android_backend.h>
#endif
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{
@ -75,6 +78,7 @@ public:
}
void refresh() {
#ifndef __ANDROID__
devList.clear();
devListTxt = "";
@ -88,6 +92,18 @@ public:
devListTxt += buf;
devListTxt += '\0';
}
#else
// Check for device presence
int vid, pid;
devFd = backend::getDeviceFD(vid, pid, backend::AIRSPY_VIDPIDS);
if (devFd < 0) { return; }
// Get device info
std::string fakeName = "Airspy USB";
devList.push_back(0xDEADBEEF);
devListTxt += fakeName;
devListTxt += '\0';
#endif
}
void selectFirst() {
@ -112,7 +128,11 @@ public:
void selectBySerial(uint64_t serial) {
airspy_device* dev;
try {
#ifndef __ANDROID__
int err = airspy_open_sn(&dev, serial);
#else
int err = airspy_open_sn(&dev, devFd);
#endif
if (err != 0) {
char buf[1024];
sprintf(buf, "%016" PRIX64, serial);
@ -245,7 +265,11 @@ private:
return;
}
#ifndef __ANDROID__
int err = airspy_open_sn(&_this->openDev, _this->selectedSerial);
#else
int err = airspy_open_sn(&_this->openDev, _this->devFd);
#endif
if (err != 0) {
char buf[1024];
sprintf(buf, "%016" PRIX64, _this->selectedSerial);
@ -571,6 +595,10 @@ private:
bool lnaAgc = false;
bool mixerAgc = false;
#ifdef __ANDROID__
int devFd = 0;
#endif
std::vector<uint64_t> devList;
std::string devListTxt;
std::vector<uint32_t> sampleRateList;
@ -581,7 +609,7 @@ MOD_EXPORT void _INIT_() {
json def = json({});
def["devices"] = json({});
def["device"] = "";
config.setPath(options::opts.root + "/airspy_config.json");
config.setPath(core::args["root"].s() + "/airspy_config.json");
config.load(def);
config.enableAutoSave();
}

View File

@ -24,6 +24,16 @@ if (MSVC)
target_include_directories(airspyhf_source PUBLIC "C:/Program Files/PothosSDR/include/libairspyhf/")
target_link_libraries(airspyhf_source PRIVATE airspyhf)
elseif (ANDROID)
target_include_directories(sdrpp_core PUBLIC
/mnt/android_sdr/libusb/libusb
/mnt/android_sdr/airspyhf/libairspyhf/src
)
target_link_libraries(sdrpp_core PUBLIC
/mnt/android_sdr/output/libusb/${ANDROID_ABI}/libusb1.0.so
/mnt/android_sdr/output/libairspyhf/${ANDROID_ABI}/libairspyhf.so
)
else (MSVC)
find_package(PkgConfig)

View File

@ -5,11 +5,14 @@
#include <core.h>
#include <gui/style.h>
#include <config.h>
#include <options.h>
#include <gui/smgui.h>
#include <airspyhf.h>
#include <gui/widgets/stepped_slider.h>
#ifdef __ANDROID__
#include <android_backend.h>
#endif
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{
@ -79,6 +82,7 @@ public:
devList.clear();
devListTxt = "";
#ifndef __ANDROID__
uint64_t serials[256];
int n = airspyhf_list_devices(serials, 256);
@ -89,6 +93,18 @@ public:
devListTxt += buf;
devListTxt += '\0';
}
#else
// Check for device presence
int vid, pid;
devFd = backend::getDeviceFD(vid, pid, backend::AIRSPYHF_VIDPIDS);
if (devFd < 0) { return; }
// Get device info
std::string fakeName = "Airspy HF+ USB";
devList.push_back(0xDEADBEEF);
devListTxt += fakeName;
devListTxt += '\0';
#endif
}
void selectFirst() {
@ -113,10 +129,14 @@ public:
void selectBySerial(uint64_t serial) {
airspyhf_device_t* dev;
try {
int err = airspyhf_open_sn(&dev, selectedSerial);
#ifndef __ANDROID__
int err = airspyhf_open_sn(&dev, serial);
#else
int err = airspyhf_open_sn(&dev, devFd);
#endif
if (err != 0) {
char buf[1024];
sprintf(buf, "%016" PRIX64, selectedSerial);
sprintf(buf, "%016" PRIX64, serial);
spdlog::error("Could not open Airspy HF+ {0}", buf);
selectedSerial = 0;
return;
@ -124,7 +144,7 @@ public:
}
catch (std::exception e) {
char buf[1024];
sprintf(buf, "%016" PRIX64, selectedSerial);
sprintf(buf, "%016" PRIX64, serial);
spdlog::error("Could not open Airspy HF+ {0}", buf);
}
@ -221,7 +241,11 @@ private:
return;
}
int err = airspyhf_open_sn(&_this->openDev, _this->selectedSerial);
#ifndef __ANDROID__
int err = airspyhf_open_sn(&_this->openDev, _this->selectedSerial);
#else
int err = airspyhf_open_sn(&_this->openDev, _this->devFd);
#endif
if (err != 0) {
char buf[1024];
sprintf(buf, "%016" PRIX64, _this->selectedSerial);
@ -368,6 +392,10 @@ private:
float atten = 0.0f;
std::string selectedSerStr = "";
#ifdef __ANDROID__
int devFd = 0;
#endif
std::vector<uint64_t> devList;
std::string devListTxt;
std::vector<uint32_t> sampleRateList;
@ -378,7 +406,7 @@ MOD_EXPORT void _INIT_() {
json def = json({});
def["devices"] = json({});
def["device"] = "";
config.setPath(options::opts.root + "/airspyhf_config.json");
config.setPath(core::args["root"].s() + "/airspyhf_config.json");
config.load(def);
config.enableAutoSave();
}

View File

@ -6,7 +6,6 @@
#include <core.h>
#include <gui/style.h>
#include <config.h>
#include <options.h>
#include <gui/widgets/stepped_slider.h>
#include <libbladeRF.h>
#include <dsp/processing.h>
@ -610,7 +609,7 @@ MOD_EXPORT void _INIT_() {
json def = json({});
def["devices"] = json({});
def["device"] = "";
config.setPath(options::opts.root + "/bladerf_config.json");
config.setPath(core::args["root"].s() + "/bladerf_config.json");
config.load(def);
config.enableAutoSave();
}

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